Commit ac29ec5d authored by Dan Good's avatar Dan Good Committed by Rusty Russell

rszshm: New module

rszshm - resizable pointer-safe shared memory
Signed-off-by: default avatarDan Good <dan@dancancode.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 64e9e714
......@@ -100,6 +100,7 @@ MODS_WITH_SRC := aga \
rbuf \
read_write_all \
rfc822 \
rszshm \
siphash \
sparse_bsearch \
str \
......
../../licenses/APACHE-2
\ No newline at end of file
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* rszshm - resizable pointer-safe shared memory
*
* If two separate processes have shared mappings of the same file at the
* same address, then pointers to addresses within the region can be shared
* between the processes and safely dereferenced.
*
* Mapping to the same address in unrelated processes is nontrivial. One can
* request a specific address, but mmap will return another in case of an
* overlap. One can require a specific address, but mmap will unmap anything
* overlapped. On Linux boxes it can be seen that the used addresses clump
* at either end of the address range. rszshm tries to mmap in the middle
* of the address range, and checks if the requested address matches the
* returned address. If not, additional addresses are tried. Once mapped,
* the address is recorded to a header in the file. Another process reads the
* header and requests the saved address from mmap. If the returned address
* matches, work proceeds. While the defaults provide a propitious search,
* all the search parameters may be specified.
*
* To accommodate resizing, rszshm first maps a large, private, noreserve map.
* This serves to claim a span of addresses. The shared file mapping then
* overlays the beginning of the span. Later calls to extend the mapping
* overlay more of the span. Attempts to extend beyond the end of the span
* return an error.
*
* Example:
* // fork x times, grow and fill shared memory cooperatively
* #include <assert.h>
* #include <err.h>
* #include <signal.h>
* #include <stdio.h>
* #include <unistd.h>
* #include <ccan/rszshm/rszshm.h>
*
* #define ok(x) ({ int n = (x); if (n == -1) err(1, "%s", #x); n; })
*
* int main(int argc, char *argv[]) {
* int pidcnt, stopval, n, i;
* struct rszshm *r;
* char *m;
*
* assert(argc == 3);
* pidcnt = atoi(argv[1]);
* stopval = atoi(argv[2]);
* assert(pidcnt > 0 && stopval > 0);
*
* if (!rszshm_mkm(r, 4096, NULL))
* err(1, "rszshm_mkm");
*
* printf("%s\n", r->fname);
*
* for (n = 0; n < pidcnt - 1; n++)
* if (ok(fork()) == 0)
* break;
*
* m = (char *) r->dat + sizeof(int);
* #define next() (__sync_fetch_and_add((int *) r->dat, 1))
*
* for(i = next(); i < stopval; i = next()) {
* if (i >= r->cap - sizeof(int))
* ok(rszshm_grow(r));
* assert(m[i] == '\0');
* m[i] = 'A' + n;
* kill(0, 0); // busy work
* }
*
* rszshm_free(r);
* return 0;
* }
* // $ ./foo 8 $((4*1024*1024-28))
* // /dev/shm/rszshm_LAsEvt/0
* // $ tail -c +29 /dev/shm/rszshm_LAsEvt/0 | sed 's/./&\n/g' | sort | uniq -c | tr '\n' '\t'; echo
* // 515532 A 527251 B 512930 C 513062 D 544326 E 545876 F 512936 G 522363 H
*
* License: APACHE-2
* Author: Dan Good <dan@dancancode.com>
*
* Ccanlint:
* // tests use optional macros containing statement expressions
* tests_compile_without_features FAIL
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0)
return 0;
return 1;
}
/* Licensed under Apache License v2.0 - see LICENSE file for details */
#include "config.h"
#include "rszshm.h"
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/file.h>
#define pgup(x, pgsz) (((x) + (pgsz) - 1) & ~((pgsz) - 1))
void *rszshm_mk(struct rszshm *r, size_t flen, const char *fname, struct rszshm_scan scan)
{
long pgsz = sysconf(_SC_PAGE_SIZE);
int i, errno_;
char *m, *tgt, *p = NULL;
if (!r || flen == 0 || scan.len < flen + sizeof(*r->hdr) ||
!scan.start || scan.len == 0 || scan.hop == 0 || scan.iter == 0 ||
(fname && strnlen(fname, RSZSHM_PATH_MAX) == RSZSHM_PATH_MAX)) {
errno = EINVAL;
return NULL;
}
*r = (typeof(*r)) { -1, 0, "", NULL, NULL };
strcpy(r->fname, fname ? fname : RSZSHM_DFLT_FNAME);
flen = pgup(flen + sizeof(*r->hdr), pgsz);
scan.len = pgup(scan.len, pgsz);
for (i = 1, tgt = scan.start; i <= scan.iter; i++) {
m = mmap(tgt, scan.len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_NORESERVE, -1, 0);
if (m == MAP_FAILED)
return NULL;
if (m == tgt)
break;
munmap(m, scan.len);
m = NULL;
tgt += (i % 2 == 0 ? 1 : -1) * i * scan.hop;
}
if (!m) {
errno = ENOSPC;
return NULL;
}
if ((p = strstr(r->fname, "XXXXXX/")) != NULL) {
p += 6;
*p = '\0';
if (!mkdtemp(r->fname))
goto err;
*p = '/';
}
if ((r->fd = open(r->fname, O_CREAT|O_EXCL|O_RDWR, p ? 0600 : 0666)) == -1)
goto err;
if (ftruncate(r->fd, flen) == -1)
goto err;
if (mmap(m, flen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, r->fd, 0) == MAP_FAILED)
goto err;
*(r->hdr = (typeof(r->hdr)) m) = (typeof(*r->hdr)) { flen, scan.len, m };
if (msync(m, sizeof(*r->hdr), MS_SYNC) == -1)
goto err;
r->flen = flen;
r->cap = flen - sizeof(*r->hdr);
r->dat = m + sizeof(*r->hdr);
return r->dat;
err:
errno_ = errno;
if (m && m != MAP_FAILED)
munmap(m, scan.len);
if (r->fd != -1) {
close(r->fd);
unlink(r->fname);
}
if (p) {
*p = '\0';
rmdir(r->fname);
*p = '/';
}
errno = errno_;
return NULL;
}
void *rszshm_at(struct rszshm *r, const char *fname)
{
struct rszshm_hdr h;
int fd = -1, ret, errno_;
char *m = NULL;
if (!r || !fname || !fname[0] ||
strnlen(fname, RSZSHM_PATH_MAX) == RSZSHM_PATH_MAX) {
errno = EINVAL;
return NULL;
}
if ((fd = open(fname, O_RDWR)) == -1)
return NULL;
if ((ret = read(fd, &h, sizeof(h))) == -1)
goto err;
if (ret != sizeof(h) || !h.addr || h.flen == 0 || h.max == 0) {
errno = ENODATA;
goto err;
}
m = mmap(h.addr, h.max, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_NORESERVE, -1, 0);
if (m == MAP_FAILED)
goto err;
if (m != h.addr) {
errno = ENOSPC;
goto err;
}
if (mmap(m, h.flen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0) == MAP_FAILED)
goto err;
*r = (typeof(*r)) { .fd = fd, .flen = h.flen, .hdr = (typeof(r->hdr)) m,
.dat = m + sizeof(h), .cap = h.flen - sizeof(h) };
strcpy(r->fname, fname);
return r->dat;
err:
errno_ = errno;
if (m && m != MAP_FAILED)
munmap(m, h.max);
close(fd);
errno = errno_;
return NULL;
}
#undef rszshm_up
int rszshm_up(struct rszshm *r)
{
size_t flen;
assert(r);
flen = r->hdr->flen;
if (r->flen == flen)
return 0;
if (mmap(r->hdr, flen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, r->fd, 0) == MAP_FAILED)
return -1;
r->flen = flen;
r->cap = flen - sizeof(*r->hdr);
return 1;
}
int rszshm_grow(struct rszshm *r)
{
int ret;
assert(r);
if ((ret = rszshm_up(r)) != 0)
return ret;
if (r->flen == r->hdr->max) {
errno = ENOMEM;
return -1;
}
if (flock(r->fd, LOCK_EX) == -1)
return -1;
if ((ret = rszshm_up(r)) == 0) {
int flen = r->hdr->flen * 2 < r->hdr->max ? r->hdr->flen * 2 : r->hdr->max;
if (ftruncate(r->fd, flen) != -1 &&
mmap(r->hdr, flen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, r->fd, 0) != MAP_FAILED) {
r->flen = r->hdr->flen = flen;
r->cap = flen - sizeof(*r->hdr);
ret = 1;
}
else
ret = -1;
}
flock(r->fd, LOCK_UN);
return ret;
}
int rszshm_dt(struct rszshm *r)
{
int ret[3];
assert(r);
/* ok to call twice, since free macro calls this */
if (r->fd == -1)
return 0;
ret[0] = msync(r->hdr, r->flen, MS_SYNC);
ret[1] = munmap(r->hdr, r->hdr->max);
ret[2] = close(r->fd);
r->fd = -1;
r->flen = 0;
r->hdr = NULL;
r->dat = NULL;
r->cap = 0;
return ret[0] == 0 && ret[1] == 0 && ret[2] == 0 ? 0 : -1;
}
int rszshm_unlink(struct rszshm *r)
{
assert(r);
return unlink(r->fname);
}
int rszshm_rmdir(struct rszshm *r)
{
int ret;
char *p;
assert(r);
if ((p = strrchr(r->fname, '/')) == NULL) {
errno = ENOTDIR;
return -1;
}
*p = '\0';
ret = rmdir(r->fname);
*p = '/';
return ret;
}
/* Licensed under Apache License v2.0 - see LICENSE file for details */
#ifndef CCAN_RSZSHM_H
#define CCAN_RSZSHM_H
#include "config.h"
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
/**
* struct rszshm_scan - parameters for the free region search
* @start: first address to test
* @len: size of region to test
* @hop: offset of the next test
* @iter: number of attempts
*
* See rszshm_mk for search details.
*/
struct rszshm_scan {
void *start;
size_t len;
size_t hop;
unsigned iter;
};
#define KiB (1024UL)
#define MiB (KiB*KiB)
#define GiB (MiB*KiB)
#ifdef __x86_64__
#define TiB (GiB*KiB)
#define RSZSHM_DFLT_SCAN (struct rszshm_scan) { (void *) (64*TiB), 4*GiB, 1*TiB, 10 }
#else
#define RSZSHM_DFLT_SCAN (struct rszshm_scan) { (void *) ((1024+512)*MiB), 256*MiB, 256*MiB, 10 }
#endif
/**
* struct rszshm_hdr - header describing mapped memory
* @flen: length of the shared file mapping
* @max: length of the private mapping
* @addr: address of the mapping
*
* The shared region is mapped over the private region.
* max is the maximum size the shared region can be extended.
* addr and max are set at creation time and do not change.
* flen is updated each time the file and shared region is grown.
*/
struct rszshm_hdr {
size_t flen;
size_t max;
void *addr;
};
/**
* struct rszshm - handle for a mapped region
* @fd: file descriptor of the mapped file
* @flen: length of the mapped shared file in this process
* @fname: path of the mapped file
* @hdr: pointer to the mapped region header
* @dat: pointer to the usable space after the header
* @cap: length of the usable space after the header
*
* flen is updated by rszshm_grow, or by rszshm_up.
*/
#define RSZSHM_PATH_MAX 128
#define RSZSHM_DFLT_FNAME "/dev/shm/rszshm_XXXXXX/0"
struct rszshm {
int fd;
size_t flen;
char fname[RSZSHM_PATH_MAX];
struct rszshm_hdr *hdr;
void *dat;
size_t cap;
};
/**
* rszshm_mk - make and mmap a shareable region
* @r: pointer to handle
* @flen: initial length of shared mapping
* @fname: path to file to be created, may be NULL or contain template
* @scan: struct specifying search parameters
*
* The handle pointed to by r is populated by rszshm_mk. flen is increased
* by the size of struct rszshm_hdr and rounded up to the next multiple of
* page size. If the directory portion of fname ends with XXXXXX, mkdtemp(3)
* is used. If fname is NULL, a default path with template is used.
*
* If rszshm_mk is called with only three arguments, a default scan struct
* is used. To supply a struct via compound literal, wrap the argument in
* parenthesis to avoid macro failure.
*
* rszshm_mk attempts to mmap a region of scan.len size at scan.start address.
* This is a private anonymous noreserve map used to claim an address space.
* If the mapping returns a different address, the region is unmapped, and
* another attempt is made at scan.start - scan.hop. If necessary, the next
* address tried is scan.start + scan.hop, then scan.start - (2 * scan.hop),
* and so on for at most scan.iter iterations. The pattern can be visualized
* as a counterclockwise spiral. If no match is found, NULL is returned and
* errno is set to ENOSPC.
*
* When an mmap returns an address matching the requested address, that region
* is used. If fname contains a template, mkdtemp(3) is called. A file is
* created, and extended to flen bytes. It must not already exist. This file
* is mmap'd over the region using MAP_FIXED. The mapping may later be extended
* by rszshm_grow consuming more of the claimed address space.
*
* The initial portion of the mapped file is populated with a struct rszshm_hdr,
* and msync called to write out the header.
*
* Example:
* struct rszshm r, s, t;
*
* if (!rszshm_mk(&r, 4*MiB, NULL))
* err(1, "rszshm_mk");
* // map at 0x400000000000
*
* if (!rszshm_mk(&s, 4*MiB, "/var/tmp/dat"))
* err(1, "rszshm_mk");
* // map at 0x3f0000000000
*
* if (!rszshm_mk(&t, 4*MiB, NULL, ((struct rszshm_scan) { (void *) (48*TiB), 4*GiB, 1*TiB, 10 })))
* err(1, "rszshm_mk");
* // map at 0x300000000000
*
* Returns: r->dat address on success, NULL on error
*/
void *rszshm_mk(struct rszshm *r, size_t flen, const char *fname, struct rszshm_scan scan);
#define __4args(a,b,c,d,...) a, b, c, d
#define rszshm_mk(...) rszshm_mk(__4args(__VA_ARGS__, RSZSHM_DFLT_SCAN))
#if HAVE_STATEMENT_EXPR
/**
* rszshm_mkm - malloc handle and run rszshm_mk
* @r: pointer to handle
* @flen: initial length of shared mapping
* @fname: path to file to be created, may be NULL or contain template
*
* Example:
* struct rszshm *r;
*
* if (!rszshm_mkm(r, 4*MiB, NULL))
* err(1, "rszshm_mkm");
*
* Returns: result of rszshm_mk
*/
#define rszshm_mkm(r, fl, fn) ({ \
void *__p = NULL; \
r = malloc(sizeof(*r)); \
if (r && !(__p = rszshm_mk(r, fl, fn))) { \
free(r); \
r = 0; \
} \
__p; \
})
#endif
/**
* rszshm_at - mmap ("attach") an existing shared region
* @r: pointer to handle
* @fname: path to file
*
* rszshm_at lets unrelated processes attach an existing shared region.
* fname must name a file previously created by rszshm_mk in another process.
* Note, fork'd children of the creating process inherit the mapping and
* should *not* call rszshm_at.
*
* rszshm_at opens and reads the header from the file. It makes a private
* anonymous noreserve mapping at the address recorded in the header.
* If mmap returns an address other than the requested one, munmap
* is called, errno is set to ENOSPC, and NULL is returned.
*
* Once the address space is claimed, the file is mmap'd over the region
* using MAP_FIXED. The remaining claimed address space will be used by
* later calls to rszshm_grow. Finally, the handle is populated and r->dat
* returned.
*
* Example:
* struct rszshm r;
*
* if (!rszshm_at(&r, "/dev/shm/rszshm_LAsEvt/0"))
* err(1, "rszshm_at");
*
* Returns: r->dat address on success, NULL on error
*/
void *rszshm_at(struct rszshm *r, const char *fname);
#if HAVE_STATEMENT_EXPR
/**
* rszshm_atm - malloc handle and run rszshm_at
* @r: pointer to handle
* @fname: path to file
*
* Example:
* struct rszshm *r;
*
* if (!rszshm_atm(r, "/dev/shm/rszshm_LAsEvt/0"))
* err(1, "rszshm_atm");
*
* Returns: result of rszshm_at
*/
#define rszshm_atm(r, f) ({ \
void *__p = NULL; \
r = malloc(sizeof(*r)); \
if (r && !(__p = rszshm_at(r, f))) { \
free(r); \
r = 0; \
} \
__p; \
})
#endif
/**
* rszshm_dt - unmap ("detach") shared region
* @r: pointer to handle
*
* Calls msync, munmap, and close. Resets handle values except fname.
* (fname is used by rszshm_rm*.)
*
* Returns: 0 on success, -1 if any call failed
*/
int rszshm_dt(struct rszshm *r);
/**
* rszshm_up - update mapping of shared region
* @r: pointer to handle
*
* Check if flen from the region header matches flen from the handle.
* They will diverge when another process runs rszshm_grow.
* If they are different, call mmap with the header flen and MAP_FIXED,
* and update handle.
*
* Returns: -1 if mmap fails, 0 for no change, 1 is mapping updated
*/
int rszshm_up(struct rszshm *r);
#define rszshm_up(r) (assert(r), (r)->flen == (r)->hdr->flen ? 0 : rszshm_up(r))
/**
* rszshm_grow - double the shared region, conditionally
* @r: pointer to handle
*
* If the region is already at capacity, set errno to ENOMEM, and return -1.
*
* rszshm_up is called, to see if another process has already grown the region.
* If not, a lock is acquired and the check repeated, to avoid races.
* The file is extended, and mmap called with MAP_FIXED. The header and handle
* are updated.
*
* Returns: 1 on success, -1 on error
*/
int rszshm_grow(struct rszshm *r);
/**
* rszshm_unlink - unlink shared file
* @r: pointer to handle
*
* Returns: result of unlink
*/
int rszshm_unlink(struct rszshm *r);
/**
* rszshm_rmdir - rmdir of fname directory
* @r: pointer to handle
*
* Returns: result of rmdir
*/
int rszshm_rmdir(struct rszshm *r);
#if HAVE_STATEMENT_EXPR
/**
* rszshm_rm - remove file and directory
* @r: pointer to handle
*
* Calls rszshm_unlink and rszshm_rmdir.
*
* Returns: 0 on success, -1 on error
*/
#define rszshm_rm(r) ({ \
int __ret; \
assert(r); \
__ret = rszshm_unlink(r); \
if (__ret == 0) \
__ret = rszshm_rmdir(r); \
__ret; \
})
#endif
#if HAVE_STATEMENT_EXPR
/**
* rszshm_free - run rszshm_dt and free malloced handle
* @r: pointer to handle
*
* Returns: result of rszshm_dt
*/
#define rszshm_free(r) ({ \
int __i = rszshm_dt(r); \
free(r); \
r = 0; \
__i; \
})
#endif
#endif
#define _GNU_SOURCE
#include <err.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ccan/rszshm/rszshm.h>
#include <ccan/tap/tap.h>
#include <sys/mman.h>
#include <sys/file.h>
int fail_close, fail_flock, fail_ftruncate, fail_msync, fail_munmap, fail_open;
#define close(...) (fail_close ? errno = 9000, -1 : close(__VA_ARGS__))
#define flock(...) (fail_flock ? errno = 9001, -1 : flock(__VA_ARGS__))
#define ftruncate(...) (fail_ftruncate ? errno = 9002, -1 : ftruncate(__VA_ARGS__))
#define msync(...) (fail_msync ? errno = 9003, -1 : msync(__VA_ARGS__))
#define munmap(...) (fail_munmap ? errno = 9004, -1 : munmap(__VA_ARGS__))
#define open(...) (fail_open ? errno = 9005, -1 : open(__VA_ARGS__))
int fail_read, short_read;
#define read(...) (fail_read ? errno = 9006, -1 : short_read ? 1 : read(__VA_ARGS__))
int fail_mmap_anon, fail_mmap_fixed, bad_mmap_addr;
#define mmap(adr, len, rw, flags, fd, off) ( \
fail_mmap_anon && (flags) & MAP_ANON ? errno = 9010, MAP_FAILED : \
fail_mmap_fixed && (flags) & MAP_FIXED ? errno = 9011, MAP_FAILED : \
bad_mmap_addr ? NULL : \
mmap(adr, len, rw, flags, fd, off) \
)
#include <ccan/rszshm/rszshm.c>
#define noerr(x) ({ int n = (x); if (n == -1) err(1, "%s", #x); n; })
#define longstr \
".................................................................................................................................."
static jmp_buf j;
static struct sigaction sa;
static void segvjmp(int signum)
{
longjmp(j, 1);
}
int main(void)
{
plan_tests(37);
ok1(rszshm_mk(NULL, 0, NULL) == NULL && errno == EINVAL);
struct rszshm s, t;
ok1(rszshm_mk(&s, 0, NULL) == NULL && errno == EINVAL);
ok1(rszshm_mk(&s, 4096, longstr) == NULL && errno == EINVAL);
fail_mmap_anon = 1;
ok1(rszshm_mk(&s, 4096, NULL) == NULL && errno == 9010);
rszshm_rm(&s);
fail_mmap_anon = 0;
fail_open = 1;
ok1(rszshm_mk(&s, 4096, NULL) == NULL && errno == 9005);
rszshm_rm(&s);
fail_open = 0;
fail_ftruncate = 1;
ok1(rszshm_mk(&s, 4096, NULL) == NULL && errno == 9002);
rszshm_rm(&s);
fail_ftruncate = 0;
fail_mmap_fixed = 1;
ok1(rszshm_mk(&s, 4096, NULL) == NULL && errno == 9011);
rszshm_rm(&s);
fail_mmap_fixed = 0;
fail_msync = 1;
ok1(rszshm_mk(&s, 4096, NULL) == NULL && errno == 9003);
rszshm_rm(&s);
fail_msync = 0;
ok1(rszshm_mk(&s, 4096, NULL) != NULL);
struct rszshm_scan scan = RSZSHM_DFLT_SCAN;
scan.iter = 1;
ok1(rszshm_mk(&t, 4096, NULL, scan) == NULL && errno == ENOSPC);
ok1(rszshm_dt(&s) == 0);
ok1(rszshm_rm(&s) == 0);
long pgsz = sysconf(_SC_PAGE_SIZE);
scan.len = UINT64_MAX - pgsz;
ok1(rszshm_mk(&t, 4096, NULL, scan) == NULL && errno == ENOMEM);
ok1(rszshm_mk(&t, 4096, "foo/bar_XXXXXX/0") == NULL && errno == ENOENT);
struct rszshm *r;
ok1(rszshm_mkm(r, 4096, NULL) != NULL);
pid_t p, *pp;
noerr(p = fork());
char *fname = strdupa(r->fname);
if (p)
waitpid(p, NULL, 0);
else {
ok1(rszshm_free(r) == 0);
struct rszshm *q;
ok1(rszshm_atm(q, fname) != NULL);
*((pid_t *) q->dat) = getpid();
ok1(rszshm_up(q) == 0);
ok1(rszshm_grow(q) == 1);
ok1(rszshm_free(q) == 0);
exit(0);
}
pp = (pid_t *) r->dat;
ok1(p == *pp);
fail_mmap_fixed = 1;
ok1(rszshm_up(r) == -1 && errno == 9011);
fail_mmap_fixed = 0;
ok1(rszshm_grow(r) == 1);
ok1(rszshm_dt(r) == 0);
sa.sa_handler = segvjmp;
sa.sa_flags = SA_RESETHAND;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
if (setjmp(j) == 0)
fail("still mapped after detach: %d", *pp);
else
pass("access after detach gives segv, OK!");
ok1(rszshm_at(r, longstr) == NULL && errno == EINVAL);
fail_open = 1;
ok1(rszshm_at(r, fname) == NULL && errno == 9005);
fail_open = 0;
fail_read = 1;
ok1(rszshm_at(r, fname) == NULL && errno == 9006);
fail_read = 0;
short_read = 1;
ok1(rszshm_at(r, fname) == NULL && errno == ENODATA);
short_read = 0;
fail_mmap_anon = 1;
ok1(rszshm_at(r, fname) == NULL && errno == 9010);
fail_mmap_anon = 0;
bad_mmap_addr = 1;
ok1(rszshm_at(r, fname) == NULL && errno == ENOSPC);
bad_mmap_addr = 0;
fail_mmap_fixed = 1;
ok1(rszshm_at(r, fname) == NULL && errno == 9011);
fail_mmap_fixed = 0;
ok1(rszshm_at(r, fname) != NULL);
ok1(p == *pp);
struct rszshm_hdr save = *r->hdr;
r->hdr->flen = r->flen;
r->hdr->max = r->flen;
ok1(rszshm_grow(r) == -1 && errno == ENOMEM);
*r->hdr = save;
fail_flock = 1;
ok1(rszshm_grow(r) == -1 && errno == 9001);
fail_flock = 0;
fail_ftruncate = 1;
ok1(rszshm_grow(r) == -1 && errno == 9002);
fail_ftruncate = 0;
ok1(rszshm_grow(r) == 1);
ok1(rszshm_dt(r) == 0);
ok1(rszshm_rm(r) == 0);
r->fname[0] = '\0';
ok1(rszshm_rmdir(r) == -1 && errno == ENOTDIR);
ok1(rszshm_free(r) == 0);
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment