Data-RingBuffer-Shared
view release on metacpan or search on metacpan
h->elem_size = hdr->elem_size;
h->path = path ? strdup(path) : NULL;
h->notify_fd = -1;
h->backing_fd = bfd;
return h;
}
/* 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 ring_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) return fd;
if (errno != EEXIST) { RING_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 */
RING_ERR("open %s: %s", path, strerror(errno)); /* ELOOP => symlink rejected */
return -1;
}
RING_ERR("open %s: create/attach kept racing", path);
return -1;
}
static RingHandle *ring_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) { RING_ERR("capacity must be > 0"); return NULL; }
if (elem_size == 0) { RING_ERR("elem_size must be > 0"); return NULL; }
if (capacity > (UINT64_MAX - sizeof(RingHeader)) / (sizeof(uint64_t) + elem_size)) {
RING_ERR("capacity * elem_size overflow"); return NULL;
}
uint64_t total = ring_data_off(capacity) + capacity * elem_size;
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) { RING_ERR("mmap: %s", strerror(errno)); return NULL; }
} else {
fd = ring_secure_open(path, mode, errbuf);
if (fd < 0) return NULL;
if (flock(fd, LOCK_EX) < 0) { RING_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
struct stat st;
if (fstat(fd, &st) < 0) { RING_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(RingHeader)) {
RING_ERR("%s: file too small (%lld)", path, (long long)st.st_size);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (is_new && (st.st_uid != geteuid() || fchmod(fd, mode) < 0)) {
RING_ERR("%s: refusing to initialize file not owned by us", path);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (is_new && ftruncate(fd, (off_t)total) < 0) {
RING_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) { RING_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
if (!is_new) {
if (!ring_validate_header((RingHeader *)base, (uint64_t)st.st_size, variant_id)) {
RING_ERR("invalid ring file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
}
flock(fd, LOCK_UN); close(fd);
return ring_setup(base, map_size, path, -1);
}
}
ring_init_header(base, total, elem_size, variant_id, capacity);
if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
return ring_setup(base, map_size, path, -1);
}
static RingHandle *ring_create_memfd(const char *name, uint64_t capacity,
uint32_t elem_size, uint32_t variant_id,
char *errbuf) {
if (errbuf) errbuf[0] = '\0';
if (capacity == 0) { RING_ERR("capacity must be > 0"); return NULL; }
if (elem_size == 0) { RING_ERR("elem_size must be > 0"); return NULL; }
if (capacity > (UINT64_MAX - sizeof(RingHeader)) / (sizeof(uint64_t) + elem_size)) {
RING_ERR("capacity * elem_size overflow"); return NULL;
}
uint64_t total = ring_data_off(capacity) + capacity * elem_size;
int fd = memfd_create(name ? name : "ring", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd < 0) { RING_ERR("memfd_create: %s", strerror(errno)); return NULL; }
if (ftruncate(fd, (off_t)total) < 0) { RING_ERR("ftruncate: %s", strerror(errno)); close(fd); return NULL; }
(void)fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW);
void *base = mmap(NULL, (size_t)total, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { RING_ERR("mmap: %s", strerror(errno)); close(fd); return NULL; }
ring_init_header(base, total, elem_size, variant_id, capacity);
return ring_setup(base, (size_t)total, NULL, fd);
}
static RingHandle *ring_open_fd(int fd, uint32_t variant_id, char *errbuf) {
if (errbuf) errbuf[0] = '\0';
struct stat st;
if (fstat(fd, &st) < 0) { RING_ERR("fstat: %s", strerror(errno)); return NULL; }
if ((uint64_t)st.st_size < sizeof(RingHeader)) { RING_ERR("too small"); return NULL; }
size_t ms = (size_t)st.st_size;
void *base = mmap(NULL, ms, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { RING_ERR("mmap: %s", strerror(errno)); return NULL; }
if (!ring_validate_header((RingHeader *)base, (uint64_t)st.st_size, variant_id)) {
RING_ERR("invalid ring"); munmap(base, ms); return NULL;
}
int myfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (myfd < 0) { RING_ERR("fcntl: %s", strerror(errno)); munmap(base, ms); return NULL; }
return ring_setup(base, ms, NULL, myfd);
}
static void ring_destroy(RingHandle *h) {
if (!h) return;
( run in 2.508 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )