1 #include "cado.h" // IWYU pragma: keep
2 #include <cstdio>
3 #include <string>       // std::string
4 #include <exception>    // std::terminate
5 
6 #include <sys/mman.h>  // for PROT_READ, mmap, munmap, MAP_SHARED, PROT_WRITE
7 #include <sys/stat.h>  // for fstat, stat
8 #include <fcntl.h>      // O_RDONLY etc // IWYU pragma: keep
9 #include <unistd.h>    // for sysconf, _SC_PAGE_SIZE, close
10 
11 #include "mmap_allocator.hpp"
12 
13 
14 /* This is inspired from https://github.com/johannesthoma/mmap_allocator
15  * License is LGPL.
16  * The version here has been trimmed down significantly, look for uptream
17  * version for more features */
18 
19 #define ALIGN_TO_PAGE(x) ((x) & ~(sysconf(_SC_PAGE_SIZE) - 1))
20 #define UPPER_ALIGN_TO_PAGE(x) ALIGN_TO_PAGE((x)+(sysconf(_SC_PAGE_SIZE)-1))
21 #define OFFSET_INTO_PAGE(x) ((x) & (sysconf(_SC_PAGE_SIZE) - 1))
22 
23 namespace mmap_allocator_details {
mapping(const char * filename,access_mode amode,offset_type offset,size_type length)24     mmapped_file::mapping::mapping(const char * filename, access_mode amode, offset_type offset, size_type length) {
25         if (!filename || *filename == '\0') {
26             throw mmap_allocator_exception("mmapped_file not correctly initialized: filename is empty.");
27         }
28         int mode;
29         int prot;
30         int mmap_mode = 0;
31 
32         switch (amode) {
33             case READ_ONLY:
34                 mode = O_RDONLY;
35                 prot = PROT_READ;
36                 mmap_mode |= MAP_SHARED;
37                 break;
38             case READ_WRITE_SHARED:
39                 mode = O_RDWR;
40                 prot = PROT_READ | PROT_WRITE;
41                 mmap_mode |= MAP_SHARED;
42                 break;
43             case READ_WRITE_PRIVATE:
44                 mode = O_RDONLY;
45                 prot = PROT_READ | PROT_WRITE;
46                 mmap_mode |= MAP_PRIVATE;
47                 break;
48             default:
49                 throw mmap_allocator_exception("Internal error");
50                 break;
51         }
52 
53         fd = open(filename, mode);
54         if (fd < 0)
55             throw mmap_allocator_exception(std::string("Error opening file ") + filename);
56 
57         if (length == std::numeric_limits<size_type>::max()) {
58             /* well, we really want the file length, not more ! */
59             struct stat sbuf[1];
60             if (fstat(fd, sbuf) < 0)
61                 throw mmap_allocator_exception("stat() error");
62             length = sbuf->st_size;
63         }
64         offset_mapped = ALIGN_TO_PAGE(offset);
65         length_mapped = UPPER_ALIGN_TO_PAGE(length + offset - offset_mapped);
66         area = mmap(NULL, length_mapped, prot, mmap_mode, fd, offset_mapped);
67     }
68 
~mapping()69     mmapped_file::mapping::~mapping() {
70         if (munmap(area, length_mapped) < 0)
71             std::terminate(); /* munmap() error in dtor, fatal */
72         if (close(fd) < 0)
73             std::terminate(); /* close() error in dtor, fatal */
74     }
75 
get(offset_type offset,size_type length)76     void * mmapped_file::mapping::get(offset_type offset, size_type length) {
77         if (offset >= offset_mapped && length + offset <= offset_mapped + length_mapped) {
78             return ((char*)area)+offset-offset_mapped;
79         } else {
80             throw mmap_allocator_exception("Cannot get range outside mapping bounds");
81         }
82     }
83 
put(void *,offset_type,size_type)84     void mmapped_file::mapping::put(void *, offset_type, size_type)
85     {
86         /* in fact, we do nothing. We _could_ imagine keeping track
87          * of things, but what for, really ?
88          */
89     }
90 }
91