Data-RadixTree-Shared

 view release on metacpan or  search on metacpan

radix.h  view on Meta::CPAN

        memset(&nodes[0], 0, sizeof(RdxNode));   /* NIL sentinel */
        memset(&nodes[1], 0, sizeof(RdxNode));   /* root: empty label, no value, no children */
        hdr->root      = 1;
        hdr->node_used = 2;                      /* NIL + root */
        hdr->arena_used = 0;
        hdr->keys      = 0;
    }
    __atomic_thread_fence(__ATOMIC_SEQ_CST);
}

static inline RdxHandle *rdx_setup(void *base, size_t map_size,
                                   const char *path, int backing_fd) {
    RdxHeader *hdr = (RdxHeader *)base;
    RdxHandle *h = (RdxHandle *)calloc(1, sizeof(RdxHandle));
    if (!h) {
        munmap(base, map_size);
        if (backing_fd >= 0) close(backing_fd);
        return NULL;
    }
    h->hdr          = hdr;
    h->base         = base;
    h->reader_slots = (RdxReaderSlot *)((uint8_t *)base + hdr->reader_slots_off);
    h->mmap_size    = map_size;
    h->path         = path ? strdup(path) : NULL;
    h->backing_fd   = backing_fd;
    h->my_slot_idx  = UINT32_MAX;
    return h;
}

/* Validate a mapped header (shared by rdx_create reopen and rdx_open_fd).
 * Stored geometry wins on reopen; require total_size to equal both the size
 * the stored caps imply AND the actual file size, and all offsets to match
 * the canonical layout. */
static inline int rdx_validate_header(const RdxHeader *hdr, uint64_t file_size) {
    if (hdr->magic != RDX_MAGIC) return 0;
    if (hdr->version != RDX_VERSION) return 0;
    if (hdr->node_cap < 2 || hdr->node_cap > RDX_MAX_NODES) return 0;
    if (hdr->arena_cap < 1 || hdr->arena_cap > RDX_MAX_ARENA) return 0;
    if (hdr->total_size != file_size) return 0;
    if (hdr->total_size != rdx_total_size(hdr->node_cap, hdr->arena_cap)) return 0;
    RdxLayout L = rdx_layout(hdr->node_cap);
    if (hdr->reader_slots_off != L.reader_slots) return 0;
    if (hdr->node_pool_off != L.node_pool) return 0;
    if (hdr->arena_off != L.arena) return 0;
    if (hdr->root == 0 || hdr->root >= hdr->node_cap) return 0;
    if (hdr->node_used < 2 || hdr->node_used > hdr->node_cap) return 0;
    if (hdr->arena_used > hdr->arena_cap) return 0;
    return 1;
}

/* Securely obtain a fd for a file-backed segment: create it exclusively
 * (O_CREAT|O_EXCL|O_NOFOLLOW at `mode`, default 0600 = owner-only), or, if it
 * already exists, attach to it (O_RDWR|O_NOFOLLOW, no O_CREAT). O_EXCL blocks a
 * pre-seeded or hard-linked file and O_NOFOLLOW a symlink swap, so a local
 * attacker can no longer redirect or poison the backing store through the path.
 * Cross-user sharing is opt-in via a wider `mode` (e.g. 0660); the caller still
 * validates the file's contents. */
static int rdx_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) { RDX_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 */
        RDX_ERR("open %s: %s", path, strerror(errno));  /* ELOOP => symlink rejected */
        return -1;
    }
    RDX_ERR("open %s: create/attach kept racing", path);
    return -1;
}

static RdxHandle *rdx_create(const char *path, uint64_t node_cap_in, uint64_t arena_cap_in, mode_t mode, char *errbuf) {
    if (!rdx_validate_create_args(node_cap_in, arena_cap_in, errbuf)) return NULL;
    uint32_t node_cap = (uint32_t)node_cap_in;
    uint32_t arena_cap = (uint32_t)arena_cap_in;

    uint64_t total = rdx_total_size(node_cap, arena_cap);
    int anonymous = (path == NULL);
    int fd = -1;
    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) { RDX_ERR("mmap: %s", strerror(errno)); return NULL; }
    } else {
        fd = rdx_secure_open(path, mode, errbuf);
        if (fd < 0) return NULL;
        if (flock(fd, LOCK_EX) < 0) { RDX_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
        struct stat st;
        if (fstat(fd, &st) < 0) { RDX_ERR("fstat: %s", 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(RdxHeader)) {
            RDX_ERR("%s: file too small (%lld)", path, (long long)st.st_size);
            flock(fd, LOCK_UN); close(fd); return NULL;
        }
        if (is_new && ftruncate(fd, (off_t)total) < 0) {
            RDX_ERR("ftruncate: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL;
        }
        map_size = is_new ? (size_t)total : (size_t)st.st_size;
        base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (base == MAP_FAILED) { RDX_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
        if (!is_new) {
            if (!rdx_validate_header((RdxHeader *)base, (uint64_t)st.st_size)) {
                RDX_ERR("invalid radix-tree file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
            }
            flock(fd, LOCK_UN); close(fd);
            return rdx_setup(base, map_size, path, -1);
        }
    }
    rdx_init_header(base, node_cap, arena_cap, total);
    if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
    return rdx_setup(base, map_size, path, -1);
}

static RdxHandle *rdx_create_memfd(const char *name, uint64_t node_cap_in, uint64_t arena_cap_in, char *errbuf) {
    if (!rdx_validate_create_args(node_cap_in, arena_cap_in, errbuf)) return NULL;
    uint32_t node_cap = (uint32_t)node_cap_in;
    uint32_t arena_cap = (uint32_t)arena_cap_in;



( run in 1.210 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )