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 }