1181834Sroberto/* 2181834Sroberto * $Id: text_mmap.c,v 4.15 2006/11/27 01:52:23 bkorb Exp $ 3181834Sroberto * 4181834Sroberto * Time-stamp: "2006-09-10 14:50:04 bkorb" 5181834Sroberto */ 6181834Sroberto 7181834Sroberto#ifndef MAP_ANONYMOUS 8181834Sroberto# ifdef MAP_ANON 9181834Sroberto# define MAP_ANONYMOUS MAP_ANON 10181834Sroberto# endif 11181834Sroberto#endif 12181834Sroberto 13181834Sroberto/* 14181834Sroberto * Some weird systems require that a specifically invalid FD number 15181834Sroberto * get passed in as an argument value. Which value is that? Well, 16181834Sroberto * as everybody knows, if open(2) fails, it returns -1, so that must 17181834Sroberto * be the value. :) 18181834Sroberto */ 19181834Sroberto#define AO_INVALID_FD -1 20181834Sroberto 21181834Sroberto#define FILE_WRITABLE(_prt,_flg) \ 22181834Sroberto ( (_prt & PROT_WRITE) \ 23181834Sroberto && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED)) 24181834Sroberto#define MAP_FAILED_PTR ((void*)MAP_FAILED) 25181834Sroberto 26181834Sroberto/*=export_func text_mmap 27181834Sroberto * private: 28181834Sroberto * 29181834Sroberto * what: map a text file with terminating NUL 30181834Sroberto * 31181834Sroberto * arg: char const*, pzFile, name of the file to map 32181834Sroberto * arg: int, prot, mmap protections (see mmap(2)) 33181834Sroberto * arg: int, flags, mmap flags (see mmap(2)) 34181834Sroberto * arg: tmap_info_t*, mapinfo, returned info about the mapping 35181834Sroberto * 36181834Sroberto * ret-type: void* 37181834Sroberto * ret-desc: The mmaped data address 38181834Sroberto * 39181834Sroberto * doc: 40181834Sroberto * 41181834Sroberto * This routine will mmap a file into memory ensuring that there is at least 42181834Sroberto * one @file{NUL} character following the file data. It will return the 43181834Sroberto * address where the file contents have been mapped into memory. If there is a 44181834Sroberto * problem, then it will return @code{MAP_FAILED} and set @file{errno} 45181834Sroberto * appropriately. 46181834Sroberto * 47181834Sroberto * The named file does not exist, @code{stat(2)} will set @file{errno} as it 48181834Sroberto * will. If the file is not a regular file, @file{errno} will be 49181834Sroberto * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access 50181834Sroberto * bits set appropriately for the requested @code{mmap(2)} protections and flag 51181834Sroberto * bits. On failure, @file{errno} will be set according to the documentation 52181834Sroberto * for @code{open(2)}. If @code{mmap(2)} fails, @file{errno} will be set as 53181834Sroberto * that routine sets it. If @code{text_mmap} works to this point, a valid 54181834Sroberto * address will be returned, but there may still be ``issues''. 55181834Sroberto * 56181834Sroberto * If the file size is not an even multiple of the system page size, then 57181834Sroberto * @code{text_map} will return at this point and @file{errno} will be zero. 58181834Sroberto * Otherwise, an anonymous map is attempted. If not available, then an attempt 59181834Sroberto * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the 60181834Sroberto * address of the file's data is returned, bug @code{no} @file{NUL} characters 61181834Sroberto * are mapped after the end of the data. 62181834Sroberto * 63181834Sroberto * see: mmap(2), open(2), stat(2) 64181834Sroberto * 65181834Sroberto * err: Any error code issued by mmap(2), open(2), stat(2) is possible. 66181834Sroberto * Additionally, if the specified file is not a regular file, then 67181834Sroberto * errno will be set to @code{EINVAL}. 68181834Sroberto * 69181834Sroberto * example: 70181834Sroberto * #include <mylib.h> 71181834Sroberto * tmap_info_t mi; 72181834Sroberto * int no_nul; 73181834Sroberto * void* data = text_mmap( "file", PROT_WRITE, MAP_PRIVATE, &mi ); 74181834Sroberto * if (data == MAP_FAILED) return; 75181834Sroberto * no_nul = (mi.txt_size == mi.txt_full_size); 76181834Sroberto * << use the data >> 77181834Sroberto * text_munmap( &mi ); 78181834Sroberto=*/ 79181834Srobertovoid* 80181834Srobertotext_mmap( char const* pzFile, int prot, int flags, tmap_info_t* pMI ) 81181834Sroberto{ 82181834Sroberto memset( pMI, 0, sizeof(*pMI) ); 83181834Sroberto#ifdef HAVE_MMAP 84181834Sroberto pMI->txt_zero_fd = -1; 85181834Sroberto#endif 86181834Sroberto pMI->txt_fd = -1; 87181834Sroberto 88181834Sroberto /* 89181834Sroberto * Make sure we can stat the regular file. Save the file size. 90181834Sroberto */ 91181834Sroberto { 92181834Sroberto struct stat sb; 93181834Sroberto if (stat( pzFile, &sb ) != 0) { 94181834Sroberto pMI->txt_errno = errno; 95181834Sroberto return MAP_FAILED_PTR; 96181834Sroberto } 97181834Sroberto 98181834Sroberto if (! S_ISREG( sb.st_mode )) { 99181834Sroberto pMI->txt_errno = errno = EINVAL; 100181834Sroberto return MAP_FAILED_PTR; 101181834Sroberto } 102181834Sroberto 103181834Sroberto pMI->txt_size = sb.st_size; 104181834Sroberto } 105181834Sroberto 106181834Sroberto /* 107181834Sroberto * Map mmap flags and protections into open flags and do the open. 108181834Sroberto */ 109181834Sroberto { 110181834Sroberto int o_flag; 111181834Sroberto /* 112181834Sroberto * See if we will be updating the file. If we can alter the memory 113181834Sroberto * and if we share the data and we are *not* copy-on-writing the data, 114181834Sroberto * then our updates will show in the file, so we must open with 115181834Sroberto * write access. 116181834Sroberto */ 117181834Sroberto if (FILE_WRITABLE(prot,flags)) 118181834Sroberto o_flag = O_RDWR; 119181834Sroberto else 120181834Sroberto o_flag = O_RDONLY; 121181834Sroberto 122181834Sroberto /* 123181834Sroberto * If you're not sharing the file and you are writing to it, 124181834Sroberto * then don't let anyone else have access to the file. 125181834Sroberto */ 126181834Sroberto if (((flags & MAP_SHARED) == 0) && (prot & PROT_WRITE)) 127181834Sroberto o_flag |= O_EXCL; 128181834Sroberto 129181834Sroberto pMI->txt_fd = open( pzFile, o_flag ); 130181834Sroberto } 131181834Sroberto 132181834Sroberto if (pMI->txt_fd == AO_INVALID_FD) { 133181834Sroberto pMI->txt_errno = errno; 134181834Sroberto return MAP_FAILED_PTR; 135181834Sroberto } 136181834Sroberto 137181834Sroberto#ifdef HAVE_MMAP /* * * * * WITH MMAP * * * * * */ 138181834Sroberto /* 139181834Sroberto * do the mmap. If we fail, then preserve errno, close the file and 140181834Sroberto * return the failure. 141181834Sroberto */ 142181834Sroberto pMI->txt_data = 143181834Sroberto mmap(NULL, pMI->txt_size+1, prot, flags, pMI->txt_fd, (size_t)0); 144181834Sroberto if (pMI->txt_data == MAP_FAILED_PTR) { 145181834Sroberto pMI->txt_errno = errno; 146181834Sroberto goto fail_return; 147181834Sroberto } 148181834Sroberto 149181834Sroberto /* 150181834Sroberto * Most likely, everything will turn out fine now. The only difficult 151181834Sroberto * part at this point is coping with files with sizes that are a multiple 152181834Sroberto * of the page size. Handling that is what this whole thing is about. 153181834Sroberto */ 154181834Sroberto pMI->txt_zero_fd = -1; 155181834Sroberto pMI->txt_errno = 0; 156181834Sroberto 157181834Sroberto { 158181834Sroberto void* pNuls; 159181834Sroberto#ifdef _SC_PAGESIZE 160181834Sroberto size_t pgsz = sysconf(_SC_PAGESIZE); 161181834Sroberto#else 162181834Sroberto size_t pgsz = getpagesize(); 163181834Sroberto#endif 164181834Sroberto /* 165181834Sroberto * Compute the pagesize rounded mapped memory size. 166181834Sroberto * IF this is not the same as the file size, then there are NUL's 167181834Sroberto * at the end of the file mapping and all is okay. 168181834Sroberto */ 169181834Sroberto pMI->txt_full_size = (pMI->txt_size + (pgsz - 1)) & ~(pgsz - 1); 170181834Sroberto if (pMI->txt_size != pMI->txt_full_size) 171181834Sroberto return pMI->txt_data; 172181834Sroberto 173181834Sroberto /* 174181834Sroberto * Still here? We have to remap the trailing inaccessible page 175181834Sroberto * either anonymously or to /dev/zero. 176181834Sroberto */ 177181834Sroberto pMI->txt_full_size += pgsz; 178181834Sroberto#if defined(MAP_ANONYMOUS) 179181834Sroberto pNuls = mmap( 180181834Sroberto (void*)(((char*)pMI->txt_data) + pMI->txt_size), 181181834Sroberto pgsz, PROT_READ|PROT_WRITE, 182181834Sroberto MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, AO_INVALID_FD, (size_t)0); 183181834Sroberto 184181834Sroberto if (pNuls != MAP_FAILED_PTR) 185181834Sroberto return pMI->txt_data; 186181834Sroberto 187181834Sroberto pMI->txt_errno = errno; 188181834Sroberto 189181834Sroberto#elif defined(HAVE_DEV_ZERO) 190181834Sroberto pMI->txt_zero_fd = open( "/dev/zero", O_RDONLY ); 191181834Sroberto 192181834Sroberto if (pMI->txt_zero_fd == AO_INVALID_FD) { 193181834Sroberto pMI->txt_errno = errno; 194181834Sroberto 195181834Sroberto } else { 196181834Sroberto pNuls = mmap( 197181834Sroberto (void*)(((char*)pMI->txt_data) + pMI->txt_size), pgsz, 198181834Sroberto PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 199181834Sroberto pMI->txt_zero_fd, 0 ); 200181834Sroberto 201181834Sroberto if (pNuls != MAP_FAILED_PTR) 202181834Sroberto return pMI->txt_data; 203181834Sroberto 204181834Sroberto pMI->txt_errno = errno; 205181834Sroberto close( pMI->txt_zero_fd ); 206181834Sroberto pMI->txt_zero_fd = -1; 207181834Sroberto } 208181834Sroberto#endif 209181834Sroberto 210181834Sroberto pMI->txt_full_size = pMI->txt_size; 211181834Sroberto } 212181834Sroberto 213181834Sroberto { 214181834Sroberto void* p = AGALOC( pMI->txt_size+1, "file text" ); 215181834Sroberto memcpy( p, pMI->txt_data, pMI->txt_size ); 216181834Sroberto ((char*)p)[pMI->txt_size] = NUL; 217181834Sroberto munmap(pMI->txt_data, pMI->txt_size ); 218181834Sroberto pMI->txt_data = p; 219181834Sroberto } 220181834Sroberto pMI->txt_alloc = 1; 221181834Sroberto return pMI->txt_data; 222181834Sroberto 223181834Sroberto#else /* * * * * * no HAVE_MMAP * * * * * */ 224181834Sroberto 225181834Sroberto pMI->txt_data = AGALOC( pMI->txt_size+1, "file text" ); 226181834Sroberto if (pMI->txt_data == NULL) { 227181834Sroberto pMI->txt_errno = ENOMEM; 228181834Sroberto goto fail_return; 229181834Sroberto } 230181834Sroberto 231181834Sroberto { 232181834Sroberto size_t sz = pMI->txt_size; 233181834Sroberto char* pz = pMI->txt_data; 234181834Sroberto 235181834Sroberto while (sz > 0) { 236181834Sroberto ssize_t rdct = read( pMI->txt_fd, pz, sz ); 237181834Sroberto if (rdct <= 0) { 238181834Sroberto pMI->txt_errno = errno; 239181834Sroberto fprintf( stderr, zFSErrReadFile, 240181834Sroberto errno, strerror( errno ), pzFile ); 241181834Sroberto free( pMI->txt_data ); 242181834Sroberto goto fail_return; 243181834Sroberto } 244181834Sroberto 245181834Sroberto pz += rdct; 246181834Sroberto sz -= rdct; 247181834Sroberto } 248181834Sroberto 249181834Sroberto *pz = NUL; 250181834Sroberto } 251181834Sroberto 252181834Sroberto /* 253181834Sroberto * We never need a dummy page mapped in 254181834Sroberto */ 255181834Sroberto pMI->txt_zero_fd = -1; 256181834Sroberto pMI->txt_errno = 0; 257181834Sroberto 258181834Sroberto return pMI->txt_data; 259181834Sroberto 260181834Sroberto#endif /* * * * * * no HAVE_MMAP * * * * * */ 261181834Sroberto 262181834Sroberto fail_return: 263181834Sroberto if (pMI->txt_fd >= 0) { 264181834Sroberto close( pMI->txt_fd ); 265181834Sroberto pMI->txt_fd = -1; 266181834Sroberto } 267181834Sroberto errno = pMI->txt_errno; 268181834Sroberto pMI->txt_data = MAP_FAILED_PTR; 269181834Sroberto return pMI->txt_data; 270181834Sroberto} 271181834Sroberto 272181834Sroberto 273181834Sroberto/*=export_func text_munmap 274181834Sroberto * private: 275181834Sroberto * 276181834Sroberto * what: unmap the data mapped in by text_mmap 277181834Sroberto * 278181834Sroberto * arg: tmap_info_t*, mapinfo, info about the mapping 279181834Sroberto * 280181834Sroberto * ret-type: int 281181834Sroberto * ret-desc: -1 or 0. @file{errno} will have the error code. 282181834Sroberto * 283181834Sroberto * doc: 284181834Sroberto * 285181834Sroberto * This routine will unmap the data mapped in with @code{text_mmap} and close 286181834Sroberto * the associated file descriptors opened by that function. 287181834Sroberto * 288181834Sroberto * see: munmap(2), close(2) 289181834Sroberto * 290181834Sroberto * err: Any error code issued by munmap(2) or close(2) is possible. 291181834Sroberto=*/ 292181834Srobertoint 293181834Srobertotext_munmap( tmap_info_t* pMI ) 294181834Sroberto{ 295181834Sroberto#ifdef HAVE_MMAP 296181834Sroberto int res = 0; 297181834Sroberto if (pMI->txt_alloc) { 298181834Sroberto /* 299181834Sroberto * IF the user has write permission and the text is not mapped private, 300181834Sroberto * then write back any changes. Hopefully, nobody else has modified 301181834Sroberto * the file in the mean time. 302181834Sroberto */ 303181834Sroberto if ( ((pMI->txt_prot & PROT_WRITE) != 0) 304181834Sroberto && ((pMI->txt_flags & MAP_PRIVATE) == 0)) { 305181834Sroberto 306181834Sroberto if (lseek(pMI->txt_fd, (size_t)0, SEEK_SET) != 0) 307181834Sroberto goto error_return; 308181834Sroberto 309181834Sroberto res = (write( pMI->txt_fd, pMI->txt_data, pMI->txt_size ) < 0) 310181834Sroberto ? errno : 0; 311181834Sroberto } 312181834Sroberto 313181834Sroberto AGFREE( pMI->txt_data ); 314181834Sroberto errno = res; 315181834Sroberto } else { 316181834Sroberto res = munmap( pMI->txt_data, pMI->txt_full_size ); 317181834Sroberto } 318181834Sroberto if (res != 0) 319181834Sroberto goto error_return; 320181834Sroberto 321181834Sroberto res = close( pMI->txt_fd ); 322181834Sroberto if (res != 0) 323181834Sroberto goto error_return; 324181834Sroberto 325181834Sroberto pMI->txt_fd = -1; 326181834Sroberto errno = 0; 327181834Sroberto if (pMI->txt_zero_fd != -1) { 328181834Sroberto res = close( pMI->txt_zero_fd ); 329181834Sroberto pMI->txt_zero_fd = -1; 330181834Sroberto } 331181834Sroberto 332181834Sroberto error_return: 333181834Sroberto pMI->txt_errno = errno; 334181834Sroberto return res; 335181834Sroberto#else /* HAVE_MMAP */ 336181834Sroberto 337181834Sroberto errno = 0; 338181834Sroberto /* 339181834Sroberto * IF the memory is writable *AND* it is not private (copy-on-write) 340181834Sroberto * *AND* the memory is "sharable" (seen by other processes) 341181834Sroberto * THEN rewrite the data. 342181834Sroberto */ 343181834Sroberto if ( FILE_WRITABLE(pMI->txt_prot, pMI->txt_flags) 344181834Sroberto && (lseek( pMI->txt_fd, 0, SEEK_SET ) >= 0) ) { 345181834Sroberto write( pMI->txt_fd, pMI->txt_data, pMI->txt_size ); 346181834Sroberto } 347181834Sroberto 348181834Sroberto close( pMI->txt_fd ); 349181834Sroberto pMI->txt_fd = -1; 350181834Sroberto pMI->txt_errno = errno; 351181834Sroberto free( pMI->txt_data ); 352181834Sroberto 353181834Sroberto return pMI->txt_errno; 354181834Sroberto#endif /* HAVE_MMAP */ 355181834Sroberto} 356181834Sroberto 357181834Sroberto/* 358181834Sroberto * Local Variables: 359181834Sroberto * mode: C 360181834Sroberto * c-file-style: "stroustrup" 361181834Sroberto * indent-tabs-mode: nil 362181834Sroberto * End: 363181834Sroberto * end of autoopts/text_mmap.c */ 364