rcx

miscellaneous C library
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

vmem.c (3019B)


      1 #include <errno.h>
      2 #include <fcntl.h>
      3 #include <string.h>
      4 #include <sys/mman.h>
      5 #include <sys/stat.h>
      6 #include <unistd.h>
      7 
      8 #include "debug.h"
      9 #include "log.h"
     10 #include "rcx.h"
     11 #include "vmem.h"
     12 
     13 #define ASSERT_ALIGNED(p, func) \
     14 	ASSERT(((uptr)p & (uptr)(r_vmem_page_size() - 1)) == 0, \
     15 		func ": misaligned pointer");
     16 
     17 usize
     18 r_vmem_page_size(void) {
     19 	long ps = sysconf(_SC_PAGE_SIZE);
     20 
     21 	REQUIRE(ps > 0,
     22 		"r_vmem_page_size: unable to determine page size");
     23 
     24 	REQUIRE((ps & (ps - 1)) == 0,
     25 		"r_vmem_page_size: page size not a power of 2");
     26 
     27 	return ps;
     28 }
     29 
     30 void *
     31 r_vmem_alloc(void *p, usize size) {
     32 	ASSERT_ALIGNED(p, "r_vmem_alloc");
     33 
     34 	int flags = MAP_PRIVATE | MAP_ANONYMOUS
     35 		| (p ? MAP_FIXED : 0);
     36 
     37 	void *q = mmap(p, size, PROT_READ | PROT_WRITE, flags, -1, 0);
     38 	if (q == MAP_FAILED) return 0;
     39 
     40 	return q;
     41 }
     42 
     43 void *
     44 r_vmem_reserve(void *p, usize size, bool swap) {
     45 	ASSERT_ALIGNED(p, "r_vmem_reserve");
     46 
     47 	int flags = MAP_PRIVATE | MAP_ANONYMOUS
     48 		| (p ? MAP_FIXED : 0)
     49 		| (!swap ? MAP_NORESERVE : 0);
     50 
     51 	void *q = mmap(p, size, PROT_NONE, flags, -1, 0);
     52 	if (q == MAP_FAILED) return 0;
     53 
     54 	return q;
     55 }
     56 
     57 void *
     58 r_vmem_open(
     59 	usize *size, void *p, char *path,
     60 	usize target_size, char *opt, ... /* mode_t mode */
     61 ) {
     62 	ASSERT_ALIGNED(p, "r_vmem_open");
     63 
     64 	if (strspn(opt, "crstwx") != strlen(opt)) {
     65 		errno = EINVAL;
     66 		return 0;
     67 	}
     68 
     69 	bool c = !!strchr(opt, 'c');
     70 	bool r = !!strchr(opt, 'r');
     71 	bool s = !!strchr(opt, 's');
     72 	bool t = !!strchr(opt, 't');
     73 	bool w = !!strchr(opt, 'w');
     74 	bool x = !!strchr(opt, 'x');
     75 	if (x) c = true;
     76 
     77 	if (t && target_size == 0) {
     78 		errno = EINVAL;
     79 		return 0;
     80 	}
     81 
     82 	mode_t mode = 0;
     83 	if (c) {
     84 		va_list args;
     85 		va_start(args, opt);
     86 		mode = va_arg(args, mode_t);
     87 		va_end(args);
     88 	}
     89 
     90 	int oflags = O_CLOEXEC
     91 		| (c ? O_CREAT : 0)
     92 		| (t ? O_TRUNC : 0)
     93 		| (x ? O_EXCL : 0)
     94 		| ((w && s) || t || target_size > 0 ? O_RDWR : O_RDONLY);
     95 	int prot = r || w
     96 		? (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0)
     97 		: PROT_NONE;
     98 	int mflags = (s ? MAP_SHARED : MAP_PRIVATE)
     99 		| (p ? MAP_FIXED : 0);
    100 
    101 	int fd = open(path, oflags, mode);
    102 	if (fd < 0) return 0;
    103 
    104 	struct stat sb;
    105 	if (fstat(fd, &sb) < 0) goto fail_after_open;
    106 
    107 	/* Sanity check */
    108 	ASSERT(!t || sb.st_size == 0,
    109 		"r_vmem_open: expected st_size == 0 after open with O_TRUNC");
    110 
    111 	if (target_size > sb.st_size) {
    112 		if (ftruncate(fd, target_size) < 0) goto fail_after_open;
    113 	}
    114 
    115 	usize mapping_size = target_size > 0 ? target_size : sb.st_size;
    116 	void *q = mmap(p, mapping_size, prot, mflags, fd, 0);
    117 	{
    118 		int e = errno;
    119 		if (close(fd) < 0)
    120 			r_errorf("r_vmem_open: close: %s", strerror(errno));
    121 		errno = e;
    122 	}
    123 	if (q == MAP_FAILED) return 0;
    124 
    125 	if (size) *size = mapping_size;
    126 	return q;
    127 
    128 fail_after_open:
    129 	{
    130 		int e = errno;
    131 		if (close(fd) < 0)
    132 			r_errorf("r_vmem_open: close: %s", strerror(errno));
    133 		errno = e;
    134 	}
    135 	return 0;
    136 }
    137 
    138 void
    139 r_vmem_free(void *p, usize size) {
    140 	ASSERT_ALIGNED(p, "r_vmem_free");
    141 
    142 	int ret = munmap(p, size);
    143 	/* munmap should never fail. */
    144 	if (ret < 0) r_errorf("r_vmem_free: munmap: %s", strerror(errno));
    145 }