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