1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 29 /* 30 * Module: vfpops.c 31 * Synopsis: Implements virtual file protocol operations 32 * Description: 33 * 34 * This module implements the "Virtual File protocol" operations. These 35 * operations are intended to provide very fast access to file data, 36 * allowing a file to be accessed in very efficient ways with extremely 37 * low-cpu intensive operations. If possible file data is mapped directly 38 * into memory allowing the data to be accessed directly. If the data 39 * cannot be mapped directly into memory, memory will be allocated and 40 * the file data read directly into memory. If that fails currently the 41 * file data is not accessible. Other methods of making the file could 42 * be implemented in the future (e.g. stdio if all else fails). 43 * 44 * In general any code that uses stdio to access a file can be changed 45 * to use the various "vfp" operations to access a file, with a resulting 46 * increase in performance and decrease in cpu time required to access 47 * the file contents. 48 * 49 * Public Methods: 50 * 51 * vfpCheckpointFile - Create new VFP that checkpoints existing VFP 52 * vfpCheckpointOpen - open file, allocate storage, return pointer to VFP_T 53 * vfpClose - close file associated with vfp 54 * vfpDecCurrPtr - decrement current character pointer 55 * vfpGetBytesRemaining - get number of bytes remaining to read 56 * vfpGetCurrCharPtr - get pointer to current character 57 * vfpGetCurrPtrDelta - get number of bytes between current and specified char 58 * vfpGetFirstCharPtr - get pointer to first character 59 * vfpGetLastCharPtr - get pointer to last character 60 * vfpGetModifiedLen - get highest modified byte (length) contained in vfp 61 * vfpGetPath - get the path associated with the vfp 62 * vfpGetc - get current character and increment to next 63 * vfpGetcNoInc - get current character - do not increment 64 * vfpGets - get a string from the vfp into a fixed size buffer 65 * vfpIncCurrPtr - increment current character pointer 66 * vfpIncCurrPtrBy - increment current pointer by specified delta 67 * vfpOpen - open file on vfp 68 * vfpPutBytes - put fixed number of bytes to current character and increment 69 * vfpPutFormat - put format one arg to current character and increment 70 * vfpPutInteger - put integer to current character and increment 71 * vfpPutLong - put long to current character and increment 72 * vfpPutc - put current character and increment to next 73 * vfpPuts - put string to current character and increment 74 * vfpRewind - rewind file to first byte 75 * vfpSeekToEnd - seek to end of file 76 * vfpSetCurrCharPtr - set pointer to current character 77 * vfpSetFlags - set flags that affect file access 78 * vfpSetSize - set size of file (for writing) 79 * vfpTruncate - truncate file 80 * vfpWriteToFile - write data contained in vfp to specified file 81 */ 82 83 #include <stdio.h> 84 #include <limits.h> 85 #include <stdlib.h> 86 #include <string.h> 87 #include <strings.h> 88 #include <unistd.h> 89 #include <ctype.h> 90 #include <fcntl.h> 91 #include <sys/types.h> 92 #include <sys/stat.h> 93 #include <sys/mman.h> 94 #include <errno.h> 95 #include <libintl.h> 96 #include "pkglib.h" 97 #include "pkgstrct.h" 98 #include "pkglocale.h" 99 100 /* 101 * These are internal flags that occupy the high order byte of the VFPFLAGS_T 102 * flags element of the vfp. These flags may only occupy the high order order 103 * 16 bits of the 32-bit unsigned vfp "flags" object. 104 */ 105 106 #define _VFP_MMAP 0x00010000 /* mmap used */ 107 #define _VFP_MALLOC 0x00020000 /* malloc used */ 108 #define _VFP_WRITE 0x00040000 /* file opened for write */ 109 #define _VFP_READ 0x00080000 /* file opened for reading */ 110 #define _VFP_MODIFIED 0x00100000 /* contents are marked modified */ 111 112 /* path name given to "anonymous" (string) vfp */ 113 114 #define VFP_ANONYMOUS_PATH "<<string>>" 115 116 /* minimum size file to mmap (64mb) */ 117 118 #define MIN_MMAP_SIZE (64*1024) 119 120 /* 121 * ***************************************************************************** 122 * global external (public) functions 123 * ***************************************************************************** 124 */ 125 126 /* 127 * Name: vfpOpen 128 * Description: Open file on vfp, allocate storage, return pointer to VFP_T 129 * that can be used to access/modify file contents. 130 * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T 131 * char *a_path - path of file to open and associate with this VFP. 132 * - if the path is (char *)NULL then no file is associated 133 * with this VFP - this is a way to create a fixed length 134 * string that can be manipulated with the VFP operators. 135 * Before the VFP can be used "vfpSetSize" must be called 136 * to set the size of the string buffer. 137 * char *a_mode - fopen mode to open the file with 138 * VFPFLAGS_T a_flags - one or more flags to control the operation: 139 * - VFP_NONE - no special flags 140 * - VFP_NEEDNOW - file data needed in memory now 141 * - VFP_SEQUENTIAL - memory will be sequentially accessed 142 * - VFP_RANDOM - memory will be randomly accessed 143 * - VFP_NOMMAP - do not use mmap to access file 144 * - VFP_NOMALLOC - do not use malloc to buffer file 145 * Returns: int == 0 - operation was successful 146 * != 0 - operation failed, errno contains reason 147 * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp 148 * which can be used with the various vfp functions. 149 * errno -- contains system error number if return is != 0 150 */ 151 152 int 153 vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode, VFPFLAGS_T a_flags) 154 { 155 FILE *fp = (FILE *)NULL; 156 VFP_T *vfp; 157 int lerrno; 158 struct stat statbuf; 159 int pagesize = getpagesize(); 160 161 /* reset return VFP/FILE pointers */ 162 163 (*r_vfp) = (VFP_T *)NULL; 164 165 /* allocate pre-zeroed vfp object */ 166 167 vfp = (VFP_T *)calloc(sizeof (VFP_T), 1); 168 if (vfp == (VFP_T *)NULL) { 169 return (-1); 170 } 171 172 /* create "string" vfp if no path specified */ 173 174 if (a_path == (char *)NULL) { 175 /* 176 * no path specified - no open file associated with vfp 177 * The vfp is initialized to all zeros - initialize just those 178 * values that need to be non-zero. 179 */ 180 181 vfp->_vfpFlags = _VFP_MALLOC; 182 vfp->_vfpPath = strdup(VFP_ANONYMOUS_PATH); 183 (*r_vfp) = vfp; 184 return (0); 185 } 186 187 /* 188 * path specified - associate open file with vfp; 189 * return an error if no path or mode specified 190 */ 191 192 if (a_mode == (char *)NULL) { 193 errno = EFAULT; /* Bad address */ 194 (void) free(vfp); 195 return (-1); 196 } 197 198 /* return an error if an empty path or mode specified */ 199 200 if ((*a_path == '\0') || (*a_mode == '\0')) { 201 errno = EINVAL; /* Invalid argument */ 202 (void) free(vfp); 203 return (-1); 204 } 205 206 /* open the file */ 207 208 fp = fopen(a_path, a_mode); 209 if (fp == (FILE *)NULL) { 210 lerrno = errno; 211 (void) free(vfp); 212 errno = lerrno; 213 return (-1); 214 } 215 216 /* Get the file size */ 217 218 if (fstat(fileno(fp), &statbuf) != 0) { 219 lerrno = errno; 220 (void) fclose(fp); 221 (void) free(vfp); 222 errno = lerrno; 223 return (-1); 224 } 225 226 /* 227 * Obtain access to existing file contents: 228 * -> plan a: map contents file into memory 229 * -> plan b: on failure just read into large buffer 230 */ 231 232 /* attempt to mmap file if mmap is allowed */ 233 234 vfp->_vfpStart = MAP_FAILED; /* assume map failed if not allowed */ 235 236 /* 237 * if file is a regular file, and if mmap allowed, 238 * and (malloc not forbidden or size is > minumum size to mmap) 239 */ 240 241 if ((S_ISREG(statbuf.st_mode)) && (!(a_flags & VFP_NOMMAP)) && 242 ((a_flags & VFP_NOMALLOC) || statbuf.st_size > MIN_MMAP_SIZE)) { 243 char *p; 244 /* set size to current size of file */ 245 246 vfp->_vfpMapSize = statbuf.st_size; 247 248 /* 249 * compute proper size for mapping for the file contents; 250 * add in one extra page so falling off end when file size is 251 * exactly modulo page size does not cause a page fault to 252 * guarantee that the end of the file contents will always 253 * contain a '\0' null character. 254 */ 255 256 vfp->_vfpSize = (statbuf.st_size + pagesize + 257 (pagesize-(statbuf.st_size % pagesize))); 258 259 /* 260 * mmap allowed: mmap file into memory 261 * first allocate space on top of which the mapping can be done; 262 * this way we can guarantee that if the mapping happens to be 263 * an exact multiple of a page size, that there will be at least 264 * one byte past the end of the mapping that can be accessed and 265 * that is guaranteed to be zero. 266 */ 267 268 /* allocate backing space */ 269 270 p = (char *)memalign(pagesize, vfp->_vfpSize); 271 if (p == (char *)NULL) { 272 vfp->_vfpStart = MAP_FAILED; 273 } else { 274 /* guarantee first byte after end of data is zero */ 275 276 p[vfp->_vfpMapSize] = '\0'; 277 278 /* map file on top of the backing space */ 279 280 vfp->_vfpStart = mmap(p, vfp->_vfpMapSize, PROT_READ, 281 MAP_PRIVATE|MAP_FIXED, fileno(fp), (off_t)0); 282 283 /* if mmap succeeded set mmap used flag in vfp */ 284 285 if (vfp->_vfpStart != MAP_FAILED) { 286 vfp->_vfpFlags |= _VFP_MMAP; 287 } 288 } 289 } 290 291 /* if map failed (or not allowed) attempt malloc (if allowed) */ 292 293 if ((vfp->_vfpStart == MAP_FAILED) && (!(a_flags & VFP_NOMALLOC))) { 294 /* mmap failed - plan b: read directly into memory */ 295 ssize_t rlen; 296 297 /* 298 * compute proper size for allocating storage for file contents; 299 * add in one extra page so falling off end when file size is 300 * exactly modulo page size does not cause a page fault to 301 * guarantee that the end of the file contents will always 302 * contain a '\0' null character. 303 */ 304 305 vfp->_vfpSize = statbuf.st_size+pagesize; 306 307 /* allocate buffer to hold file data */ 308 309 vfp->_vfpStart = memalign((size_t)pagesize, vfp->_vfpSize); 310 if (vfp->_vfpStart == (char *)NULL) { 311 lerrno = errno; 312 (void) fclose(fp); 313 (void) free(vfp); 314 errno = lerrno; 315 return (-1); 316 } 317 318 /* read the file into the buffer */ 319 320 if (statbuf.st_size != 0) { 321 rlen = read(fileno(fp), vfp->_vfpStart, 322 statbuf.st_size); 323 if (rlen != statbuf.st_size) { 324 lerrno = errno; 325 if (lerrno == 0) { 326 lerrno = EIO; 327 } 328 (void) free(vfp->_vfpStart); 329 (void) fclose(fp); 330 (void) free(vfp); 331 errno = lerrno; 332 return (-1); 333 } 334 335 /* assure last byte+1 is null character */ 336 337 ((char *)vfp->_vfpStart)[statbuf.st_size] = '\0'; 338 } 339 340 /* set malloc used flag in vfp */ 341 342 vfp->_vfpFlags |= _VFP_MALLOC; 343 } 344 345 /* if no starting address all read methods failed */ 346 347 if (vfp->_vfpStart == MAP_FAILED) { 348 /* no mmap() - no read() - cannot allocate memory */ 349 (void) fclose(fp); 350 (void) free(vfp); 351 errno = ENOMEM; 352 return (-1); 353 } 354 355 /* 356 * initialize vfp contents 357 */ 358 359 /* _vfpCurr -> next byte to read */ 360 vfp->_vfpCurr = (char *)vfp->_vfpStart; 361 362 /* _vfpEnd -> last data byte */ 363 vfp->_vfpEnd = (((char *)vfp->_vfpStart) + statbuf.st_size)-1; 364 365 /* _vfpHighWater -> last byte written */ 366 vfp->_vfpHighWater = (char *)vfp->_vfpEnd; 367 368 /* _vfpFile -> associated FILE* object */ 369 vfp->_vfpFile = fp; 370 371 /* set flags as appropriate */ 372 373 (void) vfpSetFlags(vfp, a_flags); 374 375 /* retain path name */ 376 377 vfp->_vfpPath = strdup(a_path ? a_path : ""); 378 379 /* set read/write flags */ 380 381 if (*a_mode == 'w') { 382 vfp->_vfpFlags |= _VFP_WRITE; 383 } 384 385 if (*a_mode == 'r') { 386 vfp->_vfpFlags |= _VFP_READ; 387 } 388 389 /* set return vfp pointer */ 390 391 (*r_vfp) = vfp; 392 393 /* All OK */ 394 395 return (0); 396 } 397 398 /* 399 * Name: vfpClose 400 * Description: Close an open vfp, causing any modified data to be written out 401 * to the file associated with the vfp. 402 * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T returned by vfpOpen 403 * Returns: int == 0 - operation was successful 404 * != 0 - operation failed, errno contains reason 405 * Side Effects: r_vfp is set to (VFP_T)NULL 406 */ 407 408 int 409 vfpClose(VFP_T **r_vfp) 410 { 411 int ret; 412 int lerrno; 413 VFP_T *vfp; 414 415 /* return error if NULL VFP_T** provided */ 416 417 if (r_vfp == (VFP_T **)NULL) { 418 errno = EFAULT; 419 return (-1); 420 } 421 422 /* localize access to VFP_T */ 423 424 vfp = *r_vfp; 425 426 /* return successful if NULL VFP_T* provided */ 427 428 if (vfp == (VFP_T *)NULL) { 429 return (0); 430 } 431 432 /* reset return VFP_T* handle */ 433 434 *r_vfp = (VFP_T *)NULL; 435 436 /* 437 * if closing a file that is open for writing, commit all data if the 438 * backing memory is volatile and if there is a file open to write 439 * the data to. 440 */ 441 442 if (vfp->_vfpFlags & _VFP_WRITE) { 443 if ((vfp->_vfpFlags & _VFP_MALLOC) && 444 (vfp->_vfpFile != (FILE *)NULL)) { 445 size_t len; 446 447 /* determine number of bytes to write */ 448 len = vfpGetModifiedLen(vfp); 449 450 /* if modified bytes present commit data to the file */ 451 if (len > 0) { 452 (void) vfpSafePwrite(fileno(vfp->_vfpFile), 453 vfp->_vfpStart, len, (off_t)0); 454 } 455 } 456 } 457 458 /* deallocate any allocated storage/mappings/etc */ 459 460 if (vfp->_vfpFlags & _VFP_MALLOC) { 461 (void) free(vfp->_vfpStart); 462 } else if (vfp->_vfpFlags & _VFP_MMAP) { 463 /* unmap the file mapping */ 464 465 (void) munmap(vfp->_vfpStart, vfp->_vfpMapSize); 466 467 /* free the backing allocation */ 468 469 (void) free(vfp->_vfpStart); 470 } 471 472 /* free up path */ 473 474 (void) free(vfp->_vfpPath); 475 476 /* close the file */ 477 478 ret = 0; 479 if (vfp->_vfpFile != (FILE *)NULL) { 480 ret = fclose(vfp->_vfpFile); 481 lerrno = errno; 482 } 483 484 /* deallocate the vfp itself */ 485 486 (void) free(vfp); 487 488 /* if the fclose() failed, return error and errno */ 489 490 if (ret != 0) { 491 errno = lerrno; 492 return (-1); 493 } 494 495 return (0); 496 } 497 498 /* 499 * Name: vfpSetFlags 500 * Description: Modify operation of VFP according to flags specified 501 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set flags 502 * VFPFLAGS_T a_flags - one or more flags to control the operation: 503 * - VFP_NEEDNOW - file data needed in memory now 504 * - VFP_SEQUENTIAL - file data sequentially accessed 505 * - VFP_RANDOM - file data randomly accessed 506 * Any other flags specified are silently ignored. 507 * Returns: int == 0 - operation was successful 508 * != 0 - operation failed, errno contains reason 509 */ 510 511 int 512 vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags) 513 { 514 /* return if no vfp specified */ 515 516 if (a_vfp == (VFP_T *)NULL) { 517 return (0); 518 } 519 520 /* if file data mapped into memory, apply vm flags */ 521 522 if ((a_vfp->_vfpSize != 0) && (a_vfp->_vfpFlags & _VFP_MMAP)) { 523 /* mmap succeeded: properly advise vm system */ 524 525 if (a_flags & VFP_NEEDNOW) { 526 /* advise vm system data is needed now */ 527 (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpMapSize, 528 MADV_WILLNEED); 529 } 530 if (a_flags & VFP_SEQUENTIAL) { 531 /* advise vm system data access is sequential */ 532 (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize, 533 MADV_SEQUENTIAL); 534 } 535 if (a_flags & VFP_RANDOM) { 536 /* advise vm system data access is random */ 537 (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize, 538 MADV_RANDOM); 539 } 540 } 541 542 return (0); 543 } 544 545 /* 546 * Name: vfpRewind 547 * Description: Reset default pointer for next read/write to start of file data 548 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to rewind 549 * Returns: void 550 * Operation is always successful 551 */ 552 553 void 554 vfpRewind(VFP_T *a_vfp) 555 { 556 /* return if no vfp specified */ 557 558 if (a_vfp == (VFP_T *)NULL) { 559 return; 560 } 561 562 /* set high water mark of last modified data */ 563 564 if (a_vfp->_vfpCurr > a_vfp->_vfpHighWater) { 565 a_vfp->_vfpHighWater = a_vfp->_vfpCurr; 566 } 567 568 /* reset next character pointer to start of file data */ 569 570 a_vfp->_vfpCurr = a_vfp->_vfpStart; 571 } 572 573 /* 574 * Name: vfpSetSize 575 * Description: Set size of in-memory image associated with VFP 576 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set 577 * size_t a_size - number of bytes to associatge with VFP 578 * Returns: int == 0 - operation was successful 579 * != 0 - operation failed, errno contains reason 580 * Side Effects: 581 * Currently only a file that is in malloc()ed memory can 582 * have its in-memory size changed. 583 * An error is returned If the file is mapped into memory. 584 * A file cannot be decreased in size - if the specified 585 * size is less than the current size, the operation is 586 * successful but no change in file size occurs. 587 * If no file is associated with the VFP (no "name" was 588 * given to vfpOpen) the first call to vfpSetSize allocates 589 * the initial size of the file data - effectively calling 590 * "malloc" to allocate the initial memory for the file data. 591 * Once an initial allocation has been made, subsequent calls 592 * to vfpSetSize are effectively a "realloc" of the existing 593 * file data. 594 * All existing file data is preserved. 595 */ 596 597 int 598 vfpSetSize(VFP_T *a_vfp, size_t a_size) 599 { 600 char *np; 601 size_t curSize; 602 603 /* return if no vfp specified */ 604 605 if (a_vfp == (VFP_T *)NULL) { 606 return (0); 607 } 608 609 /* if malloc not used don't know how to set size right now */ 610 611 if (!(a_vfp->_vfpFlags & _VFP_MALLOC)) { 612 return (-1); 613 } 614 615 /* adjust size to reflect extra page of data maintained */ 616 617 a_size += getpagesize(); 618 619 /* if size is not larger than current nothing to do */ 620 621 if (a_size <= a_vfp->_vfpSize) { 622 return (0); 623 } 624 625 /* remember new size */ 626 627 curSize = a_vfp->_vfpSize; 628 a_vfp->_vfpSize = a_size; 629 630 /* allocate/reallocate memory as appropriate */ 631 632 if (a_vfp->_vfpStart != (char *)NULL) { 633 np = (char *)realloc(a_vfp->_vfpStart, a_vfp->_vfpSize+1); 634 if (np == (char *)NULL) { 635 return (-1); 636 } 637 np[curSize-1] = '\0'; 638 } else { 639 np = (char *)malloc(a_vfp->_vfpSize+1); 640 if (np == (char *)NULL) { 641 return (-1); 642 } 643 np[0] = '\0'; 644 } 645 646 /* make sure last allocated byte is a null */ 647 648 np[a_vfp->_vfpSize] = '\0'; 649 650 /* 651 * adjust all pointers to account for buffer address change 652 */ 653 654 /* _vfpCurr -> next byte to read */ 655 a_vfp->_vfpCurr = (char *)(((ptrdiff_t)a_vfp->_vfpCurr - 656 (ptrdiff_t)a_vfp->_vfpStart) + np); 657 658 /* _vfpHighWater -> last byte written */ 659 a_vfp->_vfpHighWater = (char *)(((ptrdiff_t)a_vfp->_vfpHighWater - 660 (ptrdiff_t)a_vfp->_vfpStart) + np); 661 662 /* _vfpEnd -> last data byte */ 663 a_vfp->_vfpEnd = (np + a_vfp->_vfpSize)-1; 664 665 /* _vfpStart -> first data byte */ 666 a_vfp->_vfpStart = np; 667 668 return (0); 669 } 670 671 /* 672 * Name: vfpTruncate 673 * Description: Truncate data associated with VFP 674 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to truncate 675 * Returns: void 676 * Operation is always successful. 677 * Side Effects: 678 * In memory data associated with file is believed to be empty. 679 * Actual memory associated with file is not affected. 680 * If a file is associated with the VFP, it is truncated. 681 */ 682 683 void 684 vfpTruncate(VFP_T *a_vfp) 685 { 686 /* return if no vfp specified */ 687 688 if (a_vfp == (VFP_T *)NULL) { 689 return; 690 } 691 692 /* 693 * reset all pointers so that no data is associated with file 694 */ 695 696 /* current byte is start of data area */ 697 698 a_vfp->_vfpCurr = a_vfp->_vfpStart; 699 700 /* last byte written is start of data area */ 701 702 a_vfp->_vfpHighWater = a_vfp->_vfpStart; 703 704 /* current character is NULL */ 705 706 *a_vfp->_vfpCurr = '\0'; 707 708 /* if file associated with VFP, truncate actual file */ 709 710 if (a_vfp->_vfpFile != (FILE *)NULL) { 711 (void) ftruncate(fileno(a_vfp->_vfpFile), 0); 712 } 713 } 714 715 /* 716 * Name: vfpWriteToFile 717 * Description: Write data associated with VFP to specified file 718 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to write 719 * char *a_path - path of file to write file data to 720 * Returns: int == 0 - operation was successful 721 * != 0 - operation failed, errno contains reason 722 */ 723 724 int 725 vfpWriteToFile(VFP_T *a_vfp, char *a_path) 726 { 727 int fd; 728 int lerrno = 0; 729 size_t len; 730 ssize_t result = 0; 731 732 /* return if no vfp specified */ 733 734 if (a_vfp == (VFP_T *)NULL) { 735 errno = EFAULT; 736 return (-1); 737 } 738 739 /* on buffer overflow generate error */ 740 741 if ((a_vfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(a_vfp) < 1)) { 742 errno = EFBIG; 743 return (-1); 744 } 745 746 /* open file to write data to */ 747 748 fd = open(a_path, O_WRONLY|O_CREAT|O_TRUNC, 0644); 749 if (fd < 0) { 750 return (-1); 751 } 752 753 /* determine number of bytes to write */ 754 755 len = vfpGetModifiedLen(a_vfp); 756 757 /* 758 * if there is data associated with the file, write it out; 759 * if an error occurs, close the file and return failure. 760 */ 761 762 if (len > 0) { 763 result = vfpSafeWrite(fd, a_vfp->_vfpStart, len); 764 if (result != len) { 765 /* error comitting data - return failure */ 766 lerrno = errno; 767 (void) close(fd); 768 errno = lerrno; 769 return (-1); 770 } 771 } 772 773 /* close the file */ 774 775 (void) close(fd); 776 777 /* data committed to backing store - clear the modified flag */ 778 779 (void) vfpClearModified(a_vfp); 780 781 /* return success */ 782 783 return (0); 784 } 785 786 /* 787 * Name: vfpCheckpointFile 788 * Description: Create new VFP that checkpoints existing VFP, can be used by 789 * subsequent call to vfpCheckpointOpen to open a file using the 790 * existing in-memory cache of the contents of the file 791 * Arguments: VFP_T **r_cpVfp - pointer to pointer to VFP_T to be filled in 792 * with "checkpointed file" VFP (backing store) 793 * VFP_T **a_vfp - pointer to pointer to VFP_T returned by vfpOpen 794 * representing the VFP to checkpoint 795 * char *a_path - path to file that is the backing store for the 796 * in-memory data represented by a_vfp - used to verify 797 * that the data in memory is not out of date with respect 798 * to the backing store when vfpCheckpointOpen is called 799 * == (char *)NULL - use path associated with a_vfp 800 * that is, the backing store file in use 801 * Returns: int == 0 - operation was successful 802 * - r_destVfp contains a pointer to a new VFP that 803 * may be used in a subsequent call to 804 * vfpCheckpointOpen 805 * - the VFP referenced by *a_vfp is free()ed and 806 * must no longer be referenced 807 * != 0 - operation failed, errno contains reason 808 * - the VFP referenced by *a_vfp is not affected; 809 * the caller may continue to use it 810 * Notes: If the data of a VFP to checkpoint is mmap()ed then this method 811 * returns failure - only malloc()ed data VFPs can be 812 * checkpointed. 813 */ 814 815 int 816 vfpCheckpointFile(VFP_T **r_cpVfp, VFP_T **a_vfp, char *a_path) 817 { 818 VFP_T *vfp; /* newly allocated checkpointed VFP */ 819 VFP_T *avfp; /* local -> to a_vfp */ 820 struct stat statbuf; /* stat(2) info for backing store */ 821 822 /* return error if NULL VFP_T** to checkpoint provided */ 823 824 if (r_cpVfp == (VFP_T **)NULL) { 825 errno = EFAULT; 826 return (-1); 827 } 828 829 /* reset return checkpoint VFP pointer */ 830 831 (*r_cpVfp) = (VFP_T *)NULL; 832 833 /* return error if no VFP to checkpoint specified */ 834 835 if (a_vfp == (VFP_T **)NULL) { 836 errno = EFAULT; 837 return (-1); 838 } 839 840 /* localize reference to a_vfp */ 841 842 avfp = *a_vfp; 843 844 /* return error if no VFP to checkpoint specified */ 845 846 if (avfp == (VFP_T *)NULL) { 847 errno = EFAULT; 848 return (-1); 849 } 850 851 /* on buffer overflow generate error */ 852 853 if ((avfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(avfp) < 1)) { 854 errno = EFBIG; 855 return (-1); 856 } 857 858 /* no checkpointing is possible if the existing VFP is mmap()ed */ 859 860 if (avfp->_vfpFlags & _VFP_MMAP) { 861 errno = EIO; 862 return (-1); 863 } 864 865 /* if no path specified, grab it from the VFP to checkpoint */ 866 867 if ((a_path == (char *)NULL) || (*a_path == '\0')) { 868 a_path = avfp->_vfpPath; 869 } 870 871 /* backing store required: if VFP is "string" then this is an error */ 872 873 if ((a_path == (char *)NULL) || 874 strcmp(a_path, VFP_ANONYMOUS_PATH) == 0) { 875 errno = EINVAL; 876 return (-1); 877 } 878 879 /* Get the VFP to checkpoint (backing store) file size */ 880 881 if (stat(a_path, &statbuf) != 0) { 882 return (-1); 883 } 884 885 /* allocate storage for checkpointed VFP (to return) */ 886 887 vfp = (VFP_T *)malloc(sizeof (VFP_T)); 888 if (vfp == (VFP_T *)NULL) { 889 return (-1); 890 } 891 892 /* 893 * close any file that is on the VFP to checkpoint (backing store); 894 * subsequent processes can modify the backing store data, and 895 * then when vfpCheckpointOpen is called, either the in-memory 896 * cached data will be used (if backing store unmodified) or else 897 * the in-memory data is released and the backing store is used. 898 */ 899 900 if (avfp->_vfpFile != (FILE *)NULL) { 901 (void) fclose(avfp->_vfpFile); 902 avfp->_vfpFile = (FILE *)NULL; 903 } 904 905 /* free any path associated with VFP to checkpoint (backing store) */ 906 907 if (avfp->_vfpPath != (char *)NULL) { 908 (void) free(avfp->_vfpPath); 909 avfp->_vfpPath = (char *)NULL; 910 } 911 912 /* copy contents of VFP to checkpoint to checkpointed VFP */ 913 914 (void) memcpy(vfp, avfp, sizeof (VFP_T)); 915 916 /* free contents of VFP to checkpoint */ 917 918 (void) free(avfp); 919 920 /* reset pointer to VFP that has been free'd */ 921 922 *a_vfp = (VFP_T *)NULL; 923 924 /* remember path associated with the checkpointed VFP (backing store) */ 925 926 vfp->_vfpPath = strdup(a_path); 927 928 /* save tokens that identify the backing store for the in-memory data */ 929 930 vfp->_vfpCkDev = statbuf.st_dev; /* devid holding st_ino inode */ 931 vfp->_vfpCkIno = statbuf.st_ino; /* backing store inode */ 932 vfp->_vfpCkMtime = statbuf.st_mtime; /* last data modification */ 933 vfp->_vfpCkSize = statbuf.st_size; /* backing store size (bytes) */ 934 vfp->_vfpCkStBlocks = statbuf.st_blocks; /* blocks allocated to file */ 935 936 /* pass checkpointed VFP to caller */ 937 938 (*r_cpVfp) = vfp; 939 940 /* success! */ 941 942 return (0); 943 } 944 945 /* 946 * Name: vfpCheckpointOpen 947 * Description: Open file on vfp, allocate storage, return pointer to VFP_T 948 * that can be used to access/modify file contents. If a VFP_T to 949 * a checkpointed VFP is passed in, and the in memory contents of 950 * the VFP are not out of date with respect to the backing store 951 * file, use the existing in-memory contents - otherwise, discard 952 * the in-memory contents and reopen and reread the file. 953 * Arguments: VFP_T **a_cpVfp - pointer to pointer to VFP_T that represents 954 * checkpointed VFP to use to open the file IF the contents 955 * of the backing store are identical to the in-memory data 956 * VFP_T **r_vfp - pointer to pointer to VFP_T to open file on 957 * char *a_path - path of file to open and associate with this VFP. 958 * - if the path is (char *)NULL then no file is associated 959 * with this VFP - this is a way to create a fixed length 960 * string that can be manipulated with the VFP operators. 961 * Before the VFP can be used "vfpSetSize" must be called 962 * to set the size of the string buffer. 963 * char *a_mode - fopen mode to open the file with 964 * VFPFLAGS_T a_flags - one or more flags to control the operation: 965 * - VFP_NONE - no special flags 966 * - VFP_NEEDNOW - file data needed in memory now 967 * - VFP_SEQUENTIAL - memory will be sequentially accessed 968 * - VFP_RANDOM - memory will be randomly accessed 969 * - VFP_NOMMAP - do not use mmap to access file 970 * - VFP_NOMALLOC - do not use malloc to buffer file 971 * Returns: int == 0 - operation was successful 972 * != 0 - operation failed, errno contains reason 973 * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp 974 * which can be used with the various VFP functions. 975 * a_cpVfp -- contents reset to zero if used to open the file 976 * errno -- contains system error number if return is != 0 977 */ 978 979 int 980 vfpCheckpointOpen(VFP_T **a_cpVfp, VFP_T **r_vfp, char *a_path, 981 char *a_mode, VFPFLAGS_T a_flags) 982 { 983 FILE *fp; /* backing store */ 984 VFP_T *cpVfp; /* local -> to a_cpVfp checkpointed VFP */ 985 VFP_T *vfp; /* new VFP open on checkpointed backing store */ 986 struct stat statbuf; /* stat(2) info on backing store */ 987 988 /* 989 * if no source VFP, or source VFP empty, 990 * or no backing store, just open file 991 */ 992 993 if ((a_cpVfp == (VFP_T **)NULL) || (*a_cpVfp == (VFP_T *)NULL) || 994 ((*a_cpVfp)->_vfpStart == (char *)NULL)) { 995 (void) vfpClose(a_cpVfp); 996 return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); 997 } 998 999 /* localize access to checkpointed VFP_T (*a_cpVfp) */ 1000 1001 cpVfp = *a_cpVfp; 1002 1003 /* if no path specified, grab it from the checkpointed VFP */ 1004 1005 if ((a_path == (char *)NULL) || (*a_path == '\0')) { 1006 a_path = cpVfp->_vfpPath; 1007 } 1008 1009 /* return error if no path specified and no path in checkpointed VFP */ 1010 1011 if ((a_path == (char *)NULL) && (*a_path == '\0')) { 1012 errno = EINVAL; 1013 return (-1); 1014 } 1015 1016 /* if no backing store path, then just open file */ 1017 1018 if (stat(a_path, &statbuf) != 0) { 1019 (void) vfpClose(a_cpVfp); 1020 return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); 1021 } 1022 1023 /* 1024 * if backing store tokens do not match checkpointed VFP, 1025 * the backing store has been updated since the VFP was checkpointed; 1026 * release the in-memory data, and open and read the backing store 1027 */ 1028 1029 if ((statbuf.st_size != cpVfp->_vfpCkSize) || 1030 (statbuf.st_mtime != cpVfp->_vfpCkMtime) || 1031 (statbuf.st_blocks != cpVfp->_vfpCkStBlocks) || 1032 (statbuf.st_ino != cpVfp->_vfpCkIno) || 1033 (statbuf.st_dev != cpVfp->_vfpCkDev)) { 1034 (void) vfpClose(a_cpVfp); 1035 return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); 1036 } 1037 1038 /* 1039 * backing store has not been updated since the VFP was checkpointed; 1040 * use the in-memory data without re-reading the backing store; open the 1041 * backing store file (if no file already open on the checkpointed VFP) 1042 * so there is an open file associated with the in-memory data 1043 */ 1044 1045 fp = cpVfp->_vfpFile; 1046 if (fp == (FILE *)NULL) { 1047 fp = fopen(a_path, a_mode); 1048 if (fp == (FILE *)NULL) { 1049 int lerrno; 1050 1051 lerrno = errno; 1052 (void) vfpClose(a_cpVfp); 1053 errno = lerrno; 1054 return (-1); 1055 } 1056 } 1057 1058 /* allocate new VFP object to return as open VFP */ 1059 1060 vfp = (VFP_T *)malloc(sizeof (VFP_T)); 1061 if (vfp == (VFP_T *)NULL) { 1062 (void) vfpClose(a_cpVfp); 1063 return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); 1064 } 1065 1066 /* copy cached checkpointed VFP to new VFP to return */ 1067 1068 (void) memcpy(vfp, cpVfp, sizeof (VFP_T)); 1069 1070 /* 1071 * initialize VFP to return contents 1072 */ 1073 1074 /* FILE -> file opened on the VFPs backing store */ 1075 1076 vfp->_vfpFile = fp; 1077 1078 /* release any existing path associated with the VFP */ 1079 1080 if (vfp->_vfpPath != (char *)NULL) { 1081 (void) free(vfp->_vfpPath); 1082 } 1083 1084 /* path associated with the backing store for this VFP */ 1085 1086 vfp->_vfpPath = strdup(a_path); 1087 1088 /* 1089 * data pointers associated with in memory copy of backing store 1090 * (such as _vfpHighWater, _vfpEnd, _vfpStart, etc.) 1091 * do not need to be modified because we are using the same backing 1092 * store as was checkpointed in cpVfp that is pointed to by vfp. 1093 */ 1094 1095 /* _vfpCurr -> next byte to read */ 1096 vfp->_vfpCurr = (char *)vfp->_vfpStart; 1097 1098 /* free checkpointed VFP as it is now open on "vfp" */ 1099 1100 (void) free(cpVfp); 1101 1102 /* reset callers -> checkpointed VFP */ 1103 1104 (*a_cpVfp) = (VFP_T *)NULL; 1105 1106 /* set return VFP pointer */ 1107 1108 (*r_vfp) = vfp; 1109 1110 /* success! */ 1111 1112 return (0); 1113 } 1114 1115 /* 1116 * Name: vfpClearModified 1117 * Description: Clear the "data is modified" indication from the VFP 1118 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to clear 1119 * the "data is modified" indication 1120 * Returns: int - previous setting of "data is modified" indication 1121 * == 0 - "data is modified" was NOT previously set 1122 * != 0 - "data is modified" WAS previously set 1123 */ 1124 1125 int 1126 vfpClearModified(VFP_T *a_vfp) 1127 { 1128 VFPFLAGS_T flags; 1129 1130 /* save current flags settings */ 1131 1132 flags = a_vfp->_vfpFlags; 1133 1134 /* clear "data is modified" flag */ 1135 1136 a_vfp->_vfpFlags &= (~_VFP_MODIFIED); 1137 1138 /* return previous "data is modified" flag setting */ 1139 1140 return ((flags & _VFP_MODIFIED) != 0); 1141 } 1142 1143 /* 1144 * Name: vfpSetModified 1145 * Description: Set the "data is modified" indication from the VFP 1146 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set 1147 * the "data is modified" indication 1148 * Returns: int - previous setting of "data is modified" indication 1149 * == 0 - "data is modified" was NOT previously set 1150 * != 0 - "data is modified" WAS previously set 1151 */ 1152 1153 int 1154 vfpSetModified(VFP_T *a_vfp) 1155 { 1156 VFPFLAGS_T flags; 1157 1158 /* save current flags settings */ 1159 1160 flags = a_vfp->_vfpFlags; 1161 1162 /* set "data is modified" flag */ 1163 1164 a_vfp->_vfpFlags |= _VFP_MODIFIED; 1165 1166 /* return previous "data is modified" flag setting */ 1167 1168 return ((flags & _VFP_MODIFIED) != 0); 1169 } 1170 1171 /* 1172 * Name: vfpGetModified 1173 * Description: Get the "data is modified" indication from the VFP 1174 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to get 1175 * the "data is modified" indication 1176 * Returns: int - current setting of "data is modified" indication 1177 * == 0 - "data is modified" is NOT set 1178 * != 0 - "data is modified" IS set 1179 */ 1180 1181 int 1182 vfpGetModified(VFP_T *a_vfp) 1183 { 1184 /* return current "data is modified" flag setting */ 1185 1186 return ((a_vfp->_vfpFlags & _VFP_MODIFIED) != 0); 1187 } 1188 1189 /* 1190 * Name: vfpSafeWrite 1191 * Description: write data to open file safely 1192 * Arguments: a_fildes - file descriptor to write data to 1193 * a_buf - pointer to buffer containing data to write 1194 * a_nbyte - number of bytes to write to open file 1195 * Returns: int 1196 * < 0 - error, errno set 1197 * >= 0 - success 1198 * NOTE: unlike write(2), vfpSafeWrite() handles partial writes, and will 1199 * ----- restart the write() until all bytes are written, or an error occurs. 1200 */ 1201 1202 ssize_t 1203 vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte) 1204 { 1205 ssize_t r; 1206 size_t bytes = a_nbyte; 1207 1208 for (;;) { 1209 /* write bytes to file */ 1210 r = write(a_fildes, a_buf, a_nbyte); 1211 1212 /* return error on failure of write() */ 1213 if (r < 0) { 1214 /* EAGAIN: try again */ 1215 if (errno == EAGAIN) { 1216 continue; 1217 } 1218 /* EINTR: interrupted - try again */ 1219 if (errno == EINTR) { 1220 continue; 1221 } 1222 return (r); 1223 } 1224 1225 /* return total bytes written on success */ 1226 if (r >= a_nbyte) { 1227 return (bytes); 1228 } 1229 1230 /* partial write, adjust pointers, call write again */ 1231 a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r); 1232 a_nbyte -= (size_t)r; 1233 } 1234 } 1235 1236 /* 1237 * Name: vfpSafePwrite 1238 * Description: write data to open file safely 1239 * Arguments: a_fildes - file descriptor to write data to 1240 * a_buf - pointer to buffer containing data to write 1241 * a_nbyte - number of bytes to write to open file 1242 * a_offset - offset into open file to write the first byte to 1243 * Returns: int 1244 * < 0 - error, errno set 1245 * >= 0 - success 1246 * NOTE: unlike pwrite(2), vfpSafePwrite() handles partial writes, and will 1247 * ----- restart the pwrite() until all bytes are written, or an error occurs. 1248 */ 1249 1250 ssize_t 1251 vfpSafePwrite(int a_fildes, void *a_buf, size_t a_nbyte, off_t a_offset) 1252 { 1253 ssize_t r; 1254 size_t bytes = a_nbyte; 1255 1256 for (;;) { 1257 /* write bytes to file */ 1258 r = pwrite(a_fildes, a_buf, a_nbyte, a_offset); 1259 1260 /* return error on failure of write() */ 1261 if (r < 0) { 1262 /* EAGAIN: try again */ 1263 if (errno == EAGAIN) { 1264 continue; 1265 } 1266 /* EINTR: interrupted - try again */ 1267 if (errno == EINTR) { 1268 continue; 1269 } 1270 return (r); 1271 } 1272 1273 /* return total bytes written on success */ 1274 if (r >= a_nbyte) { 1275 return (bytes); 1276 } 1277 1278 /* partial write, adjust pointers, call write again */ 1279 a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r); 1280 a_nbyte -= (size_t)r; 1281 a_offset += (off_t)r; 1282 } 1283 } 1284