1/* 2 * $Id: 14e1f51d1a5a31d8395fdf1e93a07bace1c59e87 $ 3 * 4 * Time-stamp: "2007-07-04 11:35:49 bkorb" 5 * 6 * This file is part of AutoOpts, a companion to AutoGen. 7 * AutoOpts is free software. 8 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved 9 * 10 * AutoOpts is available under any one of two licenses. The license 11 * in use must be one of these two and the choice is under the control 12 * of the user of the license. 13 * 14 * The GNU Lesser General Public License, version 3 or later 15 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 16 * 17 * The Modified Berkeley Software Distribution License 18 * See the file "COPYING.mbsd" 19 * 20 * These files have the following md5sums: 21 * 22 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 23 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 24 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd 25 */ 26 27#ifndef MAP_ANONYMOUS 28# ifdef MAP_ANON 29# define MAP_ANONYMOUS MAP_ANON 30# endif 31#endif 32 33/* 34 * Some weird systems require that a specifically invalid FD number 35 * get passed in as an argument value. Which value is that? Well, 36 * as everybody knows, if open(2) fails, it returns -1, so that must 37 * be the value. :) 38 */ 39#define AO_INVALID_FD -1 40 41#define FILE_WRITABLE(_prt,_flg) \ 42 ( (_prt & PROT_WRITE) \ 43 && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED)) 44#define MAP_FAILED_PTR ((void*)MAP_FAILED) 45 46/*=export_func text_mmap 47 * private: 48 * 49 * what: map a text file with terminating NUL 50 * 51 * arg: char const*, pzFile, name of the file to map 52 * arg: int, prot, mmap protections (see mmap(2)) 53 * arg: int, flags, mmap flags (see mmap(2)) 54 * arg: tmap_info_t*, mapinfo, returned info about the mapping 55 * 56 * ret-type: void* 57 * ret-desc: The mmaped data address 58 * 59 * doc: 60 * 61 * This routine will mmap a file into memory ensuring that there is at least 62 * one @file{NUL} character following the file data. It will return the 63 * address where the file contents have been mapped into memory. If there is a 64 * problem, then it will return @code{MAP_FAILED} and set @file{errno} 65 * appropriately. 66 * 67 * The named file does not exist, @code{stat(2)} will set @file{errno} as it 68 * will. If the file is not a regular file, @file{errno} will be 69 * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access 70 * bits set appropriately for the requested @code{mmap(2)} protections and flag 71 * bits. On failure, @file{errno} will be set according to the documentation 72 * for @code{open(2)}. If @code{mmap(2)} fails, @file{errno} will be set as 73 * that routine sets it. If @code{text_mmap} works to this point, a valid 74 * address will be returned, but there may still be ``issues''. 75 * 76 * If the file size is not an even multiple of the system page size, then 77 * @code{text_map} will return at this point and @file{errno} will be zero. 78 * Otherwise, an anonymous map is attempted. If not available, then an attempt 79 * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the 80 * address of the file's data is returned, bug @code{no} @file{NUL} characters 81 * are mapped after the end of the data. 82 * 83 * see: mmap(2), open(2), stat(2) 84 * 85 * err: Any error code issued by mmap(2), open(2), stat(2) is possible. 86 * Additionally, if the specified file is not a regular file, then 87 * errno will be set to @code{EINVAL}. 88 * 89 * example: 90 * #include <mylib.h> 91 * tmap_info_t mi; 92 * int no_nul; 93 * void* data = text_mmap( "file", PROT_WRITE, MAP_PRIVATE, &mi ); 94 * if (data == MAP_FAILED) return; 95 * no_nul = (mi.txt_size == mi.txt_full_size); 96 * << use the data >> 97 * text_munmap( &mi ); 98=*/ 99void* 100text_mmap( char const* pzFile, int prot, int flags, tmap_info_t* pMI ) 101{ 102 memset( pMI, 0, sizeof(*pMI) ); 103#ifdef HAVE_MMAP 104 pMI->txt_zero_fd = -1; 105#endif 106 pMI->txt_fd = -1; 107 108 /* 109 * Make sure we can stat the regular file. Save the file size. 110 */ 111 { 112 struct stat sb; 113 if (stat( pzFile, &sb ) != 0) { 114 pMI->txt_errno = errno; 115 return MAP_FAILED_PTR; 116 } 117 118 if (! S_ISREG( sb.st_mode )) { 119 pMI->txt_errno = errno = EINVAL; 120 return MAP_FAILED_PTR; 121 } 122 123 pMI->txt_size = sb.st_size; 124 } 125 126 /* 127 * Map mmap flags and protections into open flags and do the open. 128 */ 129 { 130 int o_flag; 131 /* 132 * See if we will be updating the file. If we can alter the memory 133 * and if we share the data and we are *not* copy-on-writing the data, 134 * then our updates will show in the file, so we must open with 135 * write access. 136 */ 137 if (FILE_WRITABLE(prot,flags)) 138 o_flag = O_RDWR; 139 else 140 o_flag = O_RDONLY; 141 142 /* 143 * If you're not sharing the file and you are writing to it, 144 * then don't let anyone else have access to the file. 145 */ 146 if (((flags & MAP_SHARED) == 0) && (prot & PROT_WRITE)) 147 o_flag |= O_EXCL; 148 149 pMI->txt_fd = open( pzFile, o_flag ); 150 } 151 152 if (pMI->txt_fd == AO_INVALID_FD) { 153 pMI->txt_errno = errno; 154 return MAP_FAILED_PTR; 155 } 156 157#ifdef HAVE_MMAP /* * * * * WITH MMAP * * * * * */ 158 /* 159 * do the mmap. If we fail, then preserve errno, close the file and 160 * return the failure. 161 */ 162 pMI->txt_data = 163 mmap(NULL, pMI->txt_size+1, prot, flags, pMI->txt_fd, (size_t)0); 164 if (pMI->txt_data == MAP_FAILED_PTR) { 165 pMI->txt_errno = errno; 166 goto fail_return; 167 } 168 169 /* 170 * Most likely, everything will turn out fine now. The only difficult 171 * part at this point is coping with files with sizes that are a multiple 172 * of the page size. Handling that is what this whole thing is about. 173 */ 174 pMI->txt_zero_fd = -1; 175 pMI->txt_errno = 0; 176 177 { 178 void* pNuls; 179#ifdef _SC_PAGESIZE 180 size_t pgsz = sysconf(_SC_PAGESIZE); 181#else 182 size_t pgsz = getpagesize(); 183#endif 184 /* 185 * Compute the pagesize rounded mapped memory size. 186 * IF this is not the same as the file size, then there are NUL's 187 * at the end of the file mapping and all is okay. 188 */ 189 pMI->txt_full_size = (pMI->txt_size + (pgsz - 1)) & ~(pgsz - 1); 190 if (pMI->txt_size != pMI->txt_full_size) 191 return pMI->txt_data; 192 193 /* 194 * Still here? We have to remap the trailing inaccessible page 195 * either anonymously or to /dev/zero. 196 */ 197 pMI->txt_full_size += pgsz; 198#if defined(MAP_ANONYMOUS) 199 pNuls = mmap( 200 (void*)(((char*)pMI->txt_data) + pMI->txt_size), 201 pgsz, PROT_READ|PROT_WRITE, 202 MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, AO_INVALID_FD, (size_t)0); 203 204 if (pNuls != MAP_FAILED_PTR) 205 return pMI->txt_data; 206 207 pMI->txt_errno = errno; 208 209#elif defined(HAVE_DEV_ZERO) 210 pMI->txt_zero_fd = open( "/dev/zero", O_RDONLY ); 211 212 if (pMI->txt_zero_fd == AO_INVALID_FD) { 213 pMI->txt_errno = errno; 214 215 } else { 216 pNuls = mmap( 217 (void*)(((char*)pMI->txt_data) + pMI->txt_size), pgsz, 218 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 219 pMI->txt_zero_fd, 0 ); 220 221 if (pNuls != MAP_FAILED_PTR) 222 return pMI->txt_data; 223 224 pMI->txt_errno = errno; 225 close( pMI->txt_zero_fd ); 226 pMI->txt_zero_fd = -1; 227 } 228#endif 229 230 pMI->txt_full_size = pMI->txt_size; 231 } 232 233 { 234 void* p = AGALOC( pMI->txt_size+1, "file text" ); 235 memcpy( p, pMI->txt_data, pMI->txt_size ); 236 ((char*)p)[pMI->txt_size] = NUL; 237 munmap(pMI->txt_data, pMI->txt_size ); 238 pMI->txt_data = p; 239 } 240 pMI->txt_alloc = 1; 241 return pMI->txt_data; 242 243#else /* * * * * * no HAVE_MMAP * * * * * */ 244 245 pMI->txt_data = AGALOC( pMI->txt_size+1, "file text" ); 246 if (pMI->txt_data == NULL) { 247 pMI->txt_errno = ENOMEM; 248 goto fail_return; 249 } 250 251 { 252 size_t sz = pMI->txt_size; 253 char* pz = pMI->txt_data; 254 255 while (sz > 0) { 256 ssize_t rdct = read( pMI->txt_fd, pz, sz ); 257 if (rdct <= 0) { 258 pMI->txt_errno = errno; 259 fprintf( stderr, zFSErrReadFile, 260 errno, strerror( errno ), pzFile ); 261 free( pMI->txt_data ); 262 goto fail_return; 263 } 264 265 pz += rdct; 266 sz -= rdct; 267 } 268 269 *pz = NUL; 270 } 271 272 /* 273 * We never need a dummy page mapped in 274 */ 275 pMI->txt_zero_fd = -1; 276 pMI->txt_errno = 0; 277 278 return pMI->txt_data; 279 280#endif /* * * * * * no HAVE_MMAP * * * * * */ 281 282 fail_return: 283 if (pMI->txt_fd >= 0) { 284 close( pMI->txt_fd ); 285 pMI->txt_fd = -1; 286 } 287 errno = pMI->txt_errno; 288 pMI->txt_data = MAP_FAILED_PTR; 289 return pMI->txt_data; 290} 291 292 293/*=export_func text_munmap 294 * private: 295 * 296 * what: unmap the data mapped in by text_mmap 297 * 298 * arg: tmap_info_t*, mapinfo, info about the mapping 299 * 300 * ret-type: int 301 * ret-desc: -1 or 0. @file{errno} will have the error code. 302 * 303 * doc: 304 * 305 * This routine will unmap the data mapped in with @code{text_mmap} and close 306 * the associated file descriptors opened by that function. 307 * 308 * see: munmap(2), close(2) 309 * 310 * err: Any error code issued by munmap(2) or close(2) is possible. 311=*/ 312int 313text_munmap( tmap_info_t* pMI ) 314{ 315#ifdef HAVE_MMAP 316 int res = 0; 317 if (pMI->txt_alloc) { 318 /* 319 * IF the user has write permission and the text is not mapped private, 320 * then write back any changes. Hopefully, nobody else has modified 321 * the file in the mean time. 322 */ 323 if ( ((pMI->txt_prot & PROT_WRITE) != 0) 324 && ((pMI->txt_flags & MAP_PRIVATE) == 0)) { 325 326 if (lseek(pMI->txt_fd, (size_t)0, SEEK_SET) != 0) 327 goto error_return; 328 329 res = (write( pMI->txt_fd, pMI->txt_data, pMI->txt_size ) < 0) 330 ? errno : 0; 331 } 332 333 AGFREE( pMI->txt_data ); 334 errno = res; 335 } else { 336 res = munmap( pMI->txt_data, pMI->txt_full_size ); 337 } 338 if (res != 0) 339 goto error_return; 340 341 res = close( pMI->txt_fd ); 342 if (res != 0) 343 goto error_return; 344 345 pMI->txt_fd = -1; 346 errno = 0; 347 if (pMI->txt_zero_fd != -1) { 348 res = close( pMI->txt_zero_fd ); 349 pMI->txt_zero_fd = -1; 350 } 351 352 error_return: 353 pMI->txt_errno = errno; 354 return res; 355#else /* HAVE_MMAP */ 356 357 errno = 0; 358 /* 359 * IF the memory is writable *AND* it is not private (copy-on-write) 360 * *AND* the memory is "sharable" (seen by other processes) 361 * THEN rewrite the data. 362 */ 363 if ( FILE_WRITABLE(pMI->txt_prot, pMI->txt_flags) 364 && (lseek( pMI->txt_fd, 0, SEEK_SET ) >= 0) ) { 365 write( pMI->txt_fd, pMI->txt_data, pMI->txt_size ); 366 } 367 368 close( pMI->txt_fd ); 369 pMI->txt_fd = -1; 370 pMI->txt_errno = errno; 371 free( pMI->txt_data ); 372 373 return pMI->txt_errno; 374#endif /* HAVE_MMAP */ 375} 376 377/* 378 * Local Variables: 379 * mode: C 380 * c-file-style: "stroustrup" 381 * indent-tabs-mode: nil 382 * End: 383 * end of autoopts/text_mmap.c */ 384