1 /* 2 * This code is (c) 2012 Johannes Thoma 3 * 4 * modified 2018 for tmpfile by D. Mitch Bailey 5 * 6 * This file is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This file is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this file. If not, see <http://www.gnu.org/licenses/>. 18 * 19 * You can download original source from https://github.com/johannesthoma/mmap_allocator.git 20 */ 21 22 #include "mmap_file_pool.h" 23 #include "mmap_exception.h" 24 #include <stdio.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <fcntl.h> 28 #include <sys/mman.h> 29 #include <unistd.h> 30 #include <assert.h> 31 32 namespace mmap_allocator_namespace { 33 34 #ifndef MMAP_ALLOCATOR_DEBUG 35 #define MMAP_ALLOCATOR_DEBUG 0 36 #endif 37 38 int verbosity = MMAP_ALLOCATOR_DEBUG; 39 set_verbosity(int v)40 void set_verbosity(int v) 41 { 42 verbosity = v; 43 } 44 get_verbosity(void)45 int get_verbosity(void) 46 { 47 return verbosity; 48 } 49 filesize(int fd,std::string fname)50 off_t filesize(int fd, std::string fname) 51 { 52 struct stat buf; 53 if (fstat(fd, &buf) < 0) { 54 if (get_verbosity() > 0) { 55 perror("stat"); 56 } 57 58 throw mmap_allocator_exception("Cannot stat file" + fname); 59 } 60 61 return buf.st_size; 62 } 63 mmap_file_identifier(std::string fname,enum access_mode access_mode_param)64 mmap_file_identifier::mmap_file_identifier(std::string fname, enum access_mode access_mode_param) 65 { 66 struct stat buf; 67 if (stat(fname.c_str(), &buf) < 0) { 68 if (get_verbosity() > 0) { 69 perror("stat"); 70 } 71 throw mmap_allocator_exception("Cannot stat file "+fname); 72 } 73 74 device = buf.st_dev; 75 inode = buf.st_ino; 76 access_mode = access_mode_param; 77 } 78 mmap_file_identifier(const mmap_file_identifier & other)79 mmap_file_identifier::mmap_file_identifier(const mmap_file_identifier &other) 80 { 81 device = other.device; 82 inode = other.inode; 83 access_mode = other.access_mode; 84 } 85 operator ==(const mmap_file_identifier & other) const86 bool mmap_file_identifier::operator==(const mmap_file_identifier &other) const 87 { 88 return device == other.device && 89 inode == other.inode && 90 access_mode == other.access_mode; 91 } 92 operator <(const mmap_file_identifier & other) const93 bool mmap_file_identifier::operator<(const mmap_file_identifier &other) const 94 { 95 return inode < other.inode || 96 (inode == other.inode && access_mode < other.access_mode) || 97 (inode == other.inode && access_mode == other.access_mode && device < other.device); 98 99 } 100 remmap_file_for_read()101 void mmapped_file::remmap_file_for_read() 102 { 103 if ( msync(memory_area, size_mapped, MS_SYNC) < 0 ) { 104 if (get_verbosity() > 0) { 105 perror("msync"); 106 } 107 throw mmap_allocator_exception("Error in remmap(msync)"); 108 } 109 if ( munmap(memory_area, size_mapped) < 0) { 110 if (get_verbosity() > 0) { 111 perror("munmap"); 112 } 113 throw mmap_allocator_exception("Error in remmap(munmap)"); 114 } 115 if ( fd == -1 ) 116 throw mmap_allocator_exception("Error in remmap(fd)"); 117 118 void *last_address = memory_area; 119 #if defined(__FreeBSD__) || defined(__DragonFly__) 120 memory_area = mmap(last_address, size_mapped, PROT_READ, MAP_SHARED, fd, 0); 121 #else 122 memory_area = mmap(last_address, size_mapped, PROT_READ, MAP_SHARED | MAP_NORESERVE, fd, 0); 123 #endif 124 125 if (memory_area == MAP_FAILED) { 126 if (get_verbosity() > 0) { 127 perror("mmap"); 128 } 129 throw mmap_allocator_exception("Error in remmap(mmap)"); 130 } 131 if ( memory_area != last_address ) { 132 throw mmap_allocator_exception("Error in remmap(moved)"); 133 } 134 madvise(memory_area, size_mapped, MADV_RANDOM | MADV_DONTNEED); 135 } 136 open_and_mmap_file(std::string filename,enum access_mode access_mode,off_t offset,size_t length,bool map_whole_file,bool allow_remap)137 void *mmapped_file::open_and_mmap_file(std::string filename, enum access_mode access_mode, off_t offset, size_t length, bool map_whole_file, bool allow_remap) 138 { 139 /* Unspecified filename opens temporary file. 140 if (filename.c_str()[0] == '\0') { 141 throw mmap_allocator_exception("mmap_allocator not correctly initialized: filename is empty."); 142 } 143 */ 144 int mode; 145 int prot; 146 int mmap_mode = 0; 147 off_t offset_to_map; 148 size_t length_to_map; 149 void *address_to_map = NULL; 150 151 if (memory_area != NULL) { 152 address_to_map = memory_area; 153 154 /* do not use MAP_FIXED, since that may invalidate other memory 155 areas in the process, such as shared libraries, which would 156 lead to a mystic Segfault. */ 157 158 } 159 switch (access_mode) { 160 case READ_ONLY: mode = O_RDONLY; prot = PROT_READ; mmap_mode |= MAP_SHARED; break; 161 case READ_WRITE_SHARED: mode = O_RDWR; prot = PROT_READ | PROT_WRITE; mmap_mode |= MAP_SHARED; break; 162 case READ_WRITE_PRIVATE: mode = O_RDONLY; prot = PROT_READ | PROT_WRITE; mmap_mode |= MAP_PRIVATE; break; 163 default: throw mmap_allocator_exception("Internal error"); break; 164 } 165 166 FILE * myTemporaryFile; 167 if (fd == -1) { 168 if ( filename.empty() ) { 169 myTemporaryFile = tmpfile(); 170 fd = fileno(myTemporaryFile); 171 ftruncate(fd, length); // can not mmap empty file, so create it 172 } else { 173 fd = open(filename.c_str(), mode); 174 } 175 if (fd < 0) { 176 if (get_verbosity() > 0) { 177 perror("open"); 178 } 179 180 throw mmap_allocator_exception("Error opening file " + filename); 181 } 182 } 183 if (map_whole_file) { 184 offset_to_map = 0; 185 length_to_map = ( filename.empty() ) ? length : filesize(fd, filename); 186 } else { 187 offset_to_map = ALIGN_TO_PAGE(offset); 188 length_to_map = UPPER_ALIGN_TO_PAGE(length); 189 } 190 191 if (offset_to_map == offset_mapped && length_to_map == size_mapped) { 192 reference_count++; 193 return ((char*)memory_area)+offset-offset_mapped; 194 } 195 if (offset_to_map >= offset_mapped && length_to_map + offset_to_map - offset_mapped <= size_mapped) 196 { 197 reference_count++; 198 return ((char*)memory_area)+offset-offset_mapped; 199 } 200 201 if (memory_area != NULL) { 202 if (munmap(memory_area, size_mapped) < 0) { 203 if (get_verbosity() > 0) { 204 perror("munmap"); 205 } 206 throw mmap_allocator_exception("Error in munmap file "+filename); 207 } 208 } 209 memory_area = mmap(address_to_map, length_to_map, prot, mmap_mode, fd, offset_to_map); 210 if (address_to_map != NULL && !allow_remap && memory_area != MAP_FAILED && memory_area != address_to_map) { 211 if (munmap(memory_area, length_to_map) < 0) { 212 if (get_verbosity() > 0) { 213 perror("munmap"); 214 } 215 throw mmap_allocator_exception("Error in munmap" + filename); 216 } 217 throw mmap_allocator_exception("Request to remap area but allow_remap is not given (remapping "+filename+")"); 218 } 219 220 if (memory_area == MAP_FAILED) { 221 if (get_verbosity() > 0) { 222 perror("mmap"); 223 } 224 throw mmap_allocator_exception("Error in mmap "+filename); 225 } 226 offset_mapped = offset_to_map; 227 size_mapped = length_to_map; 228 reference_count++; 229 230 void *ret = ((char*)memory_area)+offset-offset_to_map; 231 // assert(ret >= memory_area && ret < (char*)memory_area+size_mapped); 232 233 return ret; 234 } 235 munmap_and_close_file(void)236 bool mmapped_file::munmap_and_close_file(void) 237 { 238 reference_count--; 239 if (reference_count > 0) { 240 return false; 241 } 242 if (munmap(memory_area, size_mapped) < 0) { 243 if (get_verbosity() > 0) { 244 perror("munmap"); 245 } 246 throw mmap_allocator_exception("Error in munmap"); 247 } 248 if (close(fd)) { 249 if (get_verbosity() > 0) { 250 perror("close"); 251 } 252 throw mmap_allocator_exception("Error in close"); 253 } 254 fd = -1; 255 return true; 256 } 257 mmap_file(std::string fname,enum access_mode access_mode,off_t offset,size_t length,bool map_whole_file,bool allow_remap)258 void *mmap_file_pool::mmap_file(std::string fname, enum access_mode access_mode, off_t offset, size_t length, bool map_whole_file, bool allow_remap) 259 { 260 mmap_file_identifier the_identifier(fname, access_mode); 261 mmapped_file_map_t::iterator it; 262 263 it = the_map.find(the_identifier); 264 if (it != the_map.end()) { 265 return it->second.open_and_mmap_file(fname, access_mode, offset, length, map_whole_file, allow_remap); 266 } else { 267 mmapped_file the_file; 268 void *ret; 269 270 ret = the_file.open_and_mmap_file(fname, access_mode, offset, length, map_whole_file, allow_remap); 271 the_map.insert(mmapped_file_pair_t(the_identifier, the_file)); 272 return ret; 273 } 274 } 275 munmap_file(std::string fname,enum access_mode access_mode,off_t offset,size_t length)276 void mmap_file_pool::munmap_file(std::string fname, enum access_mode access_mode, off_t offset, size_t length) 277 { 278 mmap_file_identifier the_identifier(fname, access_mode); 279 mmapped_file_map_t::iterator it; 280 281 it = the_map.find(the_identifier); 282 if (it != the_map.end()) { 283 if (it->second.munmap_and_close_file()) { 284 the_map.erase(it); 285 } 286 } else { 287 throw mmap_allocator_exception("File "+fname+" not found in pool"); 288 } 289 } 290 291 mmap_file_pool the_pool; /* TODO: move to app object */ 292 } 293