Annotation of embedaddon/ntp/sntp/libopts/text_mmap.c, revision 1.1
1.1 ! misho 1: /**
! 2: * \file text_mmap.c
! 3: *
! 4: * Time-stamp: "2010-07-17 10:15:32 bkorb"
! 5: *
! 6: * This file is part of AutoOpts, a companion to AutoGen.
! 7: * AutoOpts is free software.
! 8: * AutoOpts is Copyright (c) 1992-2011 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: =*/
! 99: void*
! 100: text_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: =*/
! 312: int
! 313: text_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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>