Data-Pool-Shared

 view release on metacpan or  search on metacpan

pool.h  view on Meta::CPAN


/* Fixed element width for the typed variants; 0 for RAW/STR (caller-chosen).
 * A crafted file could pair a typed variant_id with a wrong elem_size and drive
 * the typed accessors past a slot or into a misaligned atomic; pin it here. */
static inline uint32_t pool_variant_elem_size(uint32_t variant_id) {
    switch (variant_id) {
        case POOL_VAR_I64: case POOL_VAR_F64: return 8;
        case POOL_VAR_I32:                    return 4;
        default:                              return 0;
    }
}

/* Validate header: magic, version, variant, sizes, AND layout offsets. */
static inline int pool_validate_header(const PoolHeader *hdr, uint64_t file_size,
                                        uint32_t expected_variant) {
    if (hdr->magic != POOL_MAGIC) return 0;
    if (hdr->version != POOL_VERSION) return 0;
    if (hdr->variant_id != expected_variant) return 0;
    { uint32_t w = pool_variant_elem_size(hdr->variant_id);
      if (w && hdr->elem_size != w) return 0; }   /* typed variant: elem_size must match its fixed width */
    if (hdr->capacity == 0 || hdr->capacity > POOL_MAX_CAPACITY) return 0;
    if (hdr->elem_size == 0) return 0;
    if (hdr->capacity > (UINT64_MAX - sizeof(PoolHeader)) / hdr->elem_size) return 0;
    if (hdr->total_size != file_size) return 0;

    uint64_t bm_off, own_off, dat_off, total;
    pool_calc_layout(hdr->capacity, hdr->elem_size, &bm_off, &own_off, &dat_off, &total);

    if (hdr->bitmap_off != bm_off) return 0;
    if (hdr->owners_off != own_off) return 0;
    if (hdr->data_off   != dat_off) return 0;
    if (hdr->total_size != total)   return 0;
    return 1;
}

static inline PoolHandle *pool_setup_handle(void *base, size_t map_size,
                                             const char *path, int backing_fd) {
    PoolHeader *hdr = (PoolHeader *)base;
    PoolHandle *h = (PoolHandle *)calloc(1, sizeof(PoolHandle));
    if (!h) { munmap(base, map_size); return NULL; }

    h->hdr         = hdr;
    h->bitmap      = (uint64_t *)((uint8_t *)base + hdr->bitmap_off);
    h->owners      = (uint32_t *)((uint8_t *)base + hdr->owners_off);
    h->data        = (uint8_t *)base + hdr->data_off;
    h->mmap_size   = map_size;
    h->bitmap_words = (uint32_t)((hdr->capacity + 63) / 64);
    h->path        = path ? strdup(path) : NULL;
    h->notify_fd   = -1;
    h->backing_fd  = backing_fd;
    h->scan_hint   = (uint32_t)getpid() % h->bitmap_words;

    return h;
}

/* Securely obtain a fd: create exclusively (O_CREAT|O_EXCL|O_NOFOLLOW at mode,
 * default 0600), or attach an existing file (O_RDWR|O_NOFOLLOW, no O_CREAT). */
static int pool_secure_open(const char *path, mode_t mode, char *errbuf) {
    for (int attempt = 0; attempt < 100; attempt++) {
        int fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC, mode);
        if (fd >= 0) { (void)fchmod(fd, mode); return fd; }   /* exact mode: umask narrowed the O_EXCL create */
        if (errno != EEXIST) { POOL_ERR("create %s: %s", path, strerror(errno)); return -1; }
        fd = open(path, O_RDWR|O_NOFOLLOW|O_CLOEXEC);
        if (fd >= 0) return fd;
        if (errno == ENOENT) continue;   /* creator unlinked between our two opens; retry */
        POOL_ERR("open %s: %s", path, strerror(errno));  /* ELOOP => symlink rejected */
        return -1;
    }
    POOL_ERR("open %s: create/attach kept racing", path);
    return -1;
}

static PoolHandle *pool_create(const char *path, uint64_t capacity,
                                uint32_t elem_size, uint32_t variant_id,
                                mode_t mode, char *errbuf) {
    if (errbuf) errbuf[0] = '\0';

    if (capacity == 0) { POOL_ERR("capacity must be > 0"); return NULL; }
    if (elem_size == 0) { POOL_ERR("elem_size must be > 0"); return NULL; }
    if (capacity > POOL_MAX_CAPACITY) { POOL_ERR("capacity too large"); return NULL; }
    if (capacity > (UINT64_MAX - sizeof(PoolHeader)) / elem_size) {
        POOL_ERR("capacity * elem_size overflow"); return NULL;
    }

    uint64_t bm_off, own_off, dat_off, total;
    pool_calc_layout(capacity, elem_size, &bm_off, &own_off, &dat_off, &total);

    int fd = -1;
    int anonymous = (path == NULL);
    size_t map_size;
    void *base;

    if (anonymous) {
        map_size = (size_t)total;
        base = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                     MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        if (base == MAP_FAILED) {
            POOL_ERR("mmap(anonymous): %s", strerror(errno));
            return NULL;
        }
    } else {
        fd = pool_secure_open(path, mode, errbuf);
        if (fd < 0) return NULL;

        if (flock(fd, LOCK_EX) < 0) {
            POOL_ERR("flock(%s): %s", path, strerror(errno));
            close(fd); return NULL;
        }

        struct stat st;
        if (fstat(fd, &st) < 0) {
            POOL_ERR("fstat(%s): %s", path, strerror(errno));
            flock(fd, LOCK_UN); close(fd); return NULL;
        }

        int is_new = (st.st_size == 0);

        if (!is_new && (uint64_t)st.st_size < sizeof(PoolHeader)) {
            POOL_ERR("%s: file too small (%lld)", path, (long long)st.st_size);
            flock(fd, LOCK_UN); close(fd); return NULL;
        }



( run in 2.189 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )