8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libpkg / common / vfpops.c
blob4c4b49c47ca21fdb286df228ce8d05dedb0d3809
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
30 * Module: vfpops.c
31 * Synopsis: Implements virtual file protocol operations
32 * Description:
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).
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.
49 * Public Methods:
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
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"
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.
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 */
112 /* path name given to "anonymous" (string) vfp */
114 #define VFP_ANONYMOUS_PATH "<<string>>"
116 /* minimum size file to mmap (64mb) */
118 #define MIN_MMAP_SIZE (64*1024)
121 * *****************************************************************************
122 * global external (public) functions
123 * *****************************************************************************
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
153 vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode, VFPFLAGS_T a_flags)
155 FILE *fp = (FILE *)NULL;
156 VFP_T *vfp;
157 int lerrno;
158 struct stat statbuf;
159 int pagesize = getpagesize();
161 /* reset return VFP/FILE pointers */
163 (*r_vfp) = (VFP_T *)NULL;
165 /* allocate pre-zeroed vfp object */
167 vfp = (VFP_T *)calloc(sizeof (VFP_T), 1);
168 if (vfp == (VFP_T *)NULL) {
169 return (-1);
172 /* create "string" vfp if no path specified */
174 if (a_path == (char *)NULL) {
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.
181 vfp->_vfpFlags = _VFP_MALLOC;
182 vfp->_vfpPath = strdup(VFP_ANONYMOUS_PATH);
183 (*r_vfp) = vfp;
184 return (0);
188 * path specified - associate open file with vfp;
189 * return an error if no path or mode specified
192 if (a_mode == (char *)NULL) {
193 errno = EFAULT; /* Bad address */
194 (void) free(vfp);
195 return (-1);
198 /* return an error if an empty path or mode specified */
200 if ((*a_path == '\0') || (*a_mode == '\0')) {
201 errno = EINVAL; /* Invalid argument */
202 (void) free(vfp);
203 return (-1);
206 /* open the file */
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);
216 /* Get the file size */
218 if (fstat(fileno(fp), &statbuf) != 0) {
219 lerrno = errno;
220 (void) fclose(fp);
221 (void) free(vfp);
222 errno = lerrno;
223 return (-1);
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
232 /* attempt to mmap file if mmap is allowed */
234 vfp->_vfpStart = MAP_FAILED; /* assume map failed if not allowed */
237 * if file is a regular file, and if mmap allowed,
238 * and (malloc not forbidden or size is > minumum size to mmap)
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 */
246 vfp->_vfpMapSize = statbuf.st_size;
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.
256 vfp->_vfpSize = (statbuf.st_size + pagesize +
257 (pagesize-(statbuf.st_size % pagesize)));
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.
268 /* allocate backing space */
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 */
276 p[vfp->_vfpMapSize] = '\0';
278 /* map file on top of the backing space */
280 vfp->_vfpStart = mmap(p, vfp->_vfpMapSize, PROT_READ,
281 MAP_PRIVATE|MAP_FIXED, fileno(fp), (off_t)0);
283 /* if mmap succeeded set mmap used flag in vfp */
285 if (vfp->_vfpStart != MAP_FAILED) {
286 vfp->_vfpFlags |= _VFP_MMAP;
291 /* if map failed (or not allowed) attempt malloc (if allowed) */
293 if ((vfp->_vfpStart == MAP_FAILED) && (!(a_flags & VFP_NOMALLOC))) {
294 /* mmap failed - plan b: read directly into memory */
295 ssize_t rlen;
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.
305 vfp->_vfpSize = statbuf.st_size+pagesize;
307 /* allocate buffer to hold file data */
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);
318 /* read the file into the buffer */
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;
328 (void) free(vfp->_vfpStart);
329 (void) fclose(fp);
330 (void) free(vfp);
331 errno = lerrno;
332 return (-1);
335 /* assure last byte+1 is null character */
337 ((char *)vfp->_vfpStart)[statbuf.st_size] = '\0';
340 /* set malloc used flag in vfp */
342 vfp->_vfpFlags |= _VFP_MALLOC;
345 /* if no starting address all read methods failed */
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);
356 * initialize vfp contents
359 /* _vfpCurr -> next byte to read */
360 vfp->_vfpCurr = (char *)vfp->_vfpStart;
362 /* _vfpEnd -> last data byte */
363 vfp->_vfpEnd = (((char *)vfp->_vfpStart) + statbuf.st_size)-1;
365 /* _vfpHighWater -> last byte written */
366 vfp->_vfpHighWater = (char *)vfp->_vfpEnd;
368 /* _vfpFile -> associated FILE* object */
369 vfp->_vfpFile = fp;
371 /* set flags as appropriate */
373 (void) vfpSetFlags(vfp, a_flags);
375 /* retain path name */
377 vfp->_vfpPath = strdup(a_path ? a_path : "");
379 /* set read/write flags */
381 if (*a_mode == 'w') {
382 vfp->_vfpFlags |= _VFP_WRITE;
385 if (*a_mode == 'r') {
386 vfp->_vfpFlags |= _VFP_READ;
389 /* set return vfp pointer */
391 (*r_vfp) = vfp;
393 /* All OK */
395 return (0);
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
409 vfpClose(VFP_T **r_vfp)
411 int ret;
412 int lerrno;
413 VFP_T *vfp;
415 /* return error if NULL VFP_T** provided */
417 if (r_vfp == (VFP_T **)NULL) {
418 errno = EFAULT;
419 return (-1);
422 /* localize access to VFP_T */
424 vfp = *r_vfp;
426 /* return successful if NULL VFP_T* provided */
428 if (vfp == (VFP_T *)NULL) {
429 return (0);
432 /* reset return VFP_T* handle */
434 *r_vfp = (VFP_T *)NULL;
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.
442 if (vfp->_vfpFlags & _VFP_WRITE) {
443 if ((vfp->_vfpFlags & _VFP_MALLOC) &&
444 (vfp->_vfpFile != (FILE *)NULL)) {
445 size_t len;
447 /* determine number of bytes to write */
448 len = vfpGetModifiedLen(vfp);
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);
458 /* deallocate any allocated storage/mappings/etc */
460 if (vfp->_vfpFlags & _VFP_MALLOC) {
461 (void) free(vfp->_vfpStart);
462 } else if (vfp->_vfpFlags & _VFP_MMAP) {
463 /* unmap the file mapping */
465 (void) munmap(vfp->_vfpStart, vfp->_vfpMapSize);
467 /* free the backing allocation */
469 (void) free(vfp->_vfpStart);
472 /* free up path */
474 (void) free(vfp->_vfpPath);
476 /* close the file */
478 ret = 0;
479 if (vfp->_vfpFile != (FILE *)NULL) {
480 ret = fclose(vfp->_vfpFile);
481 lerrno = errno;
484 /* deallocate the vfp itself */
486 (void) free(vfp);
488 /* if the fclose() failed, return error and errno */
490 if (ret != 0) {
491 errno = lerrno;
492 return (-1);
495 return (0);
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
512 vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags)
514 /* return if no vfp specified */
516 if (a_vfp == (VFP_T *)NULL) {
517 return (0);
520 /* if file data mapped into memory, apply vm flags */
522 if ((a_vfp->_vfpSize != 0) && (a_vfp->_vfpFlags & _VFP_MMAP)) {
523 /* mmap succeeded: properly advise vm system */
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);
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);
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);
542 return (0);
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
553 void
554 vfpRewind(VFP_T *a_vfp)
556 /* return if no vfp specified */
558 if (a_vfp == (VFP_T *)NULL) {
559 return;
562 /* set high water mark of last modified data */
564 if (a_vfp->_vfpCurr > a_vfp->_vfpHighWater) {
565 a_vfp->_vfpHighWater = a_vfp->_vfpCurr;
568 /* reset next character pointer to start of file data */
570 a_vfp->_vfpCurr = a_vfp->_vfpStart;
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.
598 vfpSetSize(VFP_T *a_vfp, size_t a_size)
600 char *np;
601 size_t curSize;
603 /* return if no vfp specified */
605 if (a_vfp == (VFP_T *)NULL) {
606 return (0);
609 /* if malloc not used don't know how to set size right now */
611 if (!(a_vfp->_vfpFlags & _VFP_MALLOC)) {
612 return (-1);
615 /* adjust size to reflect extra page of data maintained */
617 a_size += getpagesize();
619 /* if size is not larger than current nothing to do */
621 if (a_size <= a_vfp->_vfpSize) {
622 return (0);
625 /* remember new size */
627 curSize = a_vfp->_vfpSize;
628 a_vfp->_vfpSize = a_size;
630 /* allocate/reallocate memory as appropriate */
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);
637 np[curSize-1] = '\0';
638 } else {
639 np = (char *)malloc(a_vfp->_vfpSize+1);
640 if (np == (char *)NULL) {
641 return (-1);
643 np[0] = '\0';
646 /* make sure last allocated byte is a null */
648 np[a_vfp->_vfpSize] = '\0';
651 * adjust all pointers to account for buffer address change
654 /* _vfpCurr -> next byte to read */
655 a_vfp->_vfpCurr = (char *)(((ptrdiff_t)a_vfp->_vfpCurr -
656 (ptrdiff_t)a_vfp->_vfpStart) + np);
658 /* _vfpHighWater -> last byte written */
659 a_vfp->_vfpHighWater = (char *)(((ptrdiff_t)a_vfp->_vfpHighWater -
660 (ptrdiff_t)a_vfp->_vfpStart) + np);
662 /* _vfpEnd -> last data byte */
663 a_vfp->_vfpEnd = (np + a_vfp->_vfpSize)-1;
665 /* _vfpStart -> first data byte */
666 a_vfp->_vfpStart = np;
668 return (0);
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.
683 void
684 vfpTruncate(VFP_T *a_vfp)
686 /* return if no vfp specified */
688 if (a_vfp == (VFP_T *)NULL) {
689 return;
693 * reset all pointers so that no data is associated with file
696 /* current byte is start of data area */
698 a_vfp->_vfpCurr = a_vfp->_vfpStart;
700 /* last byte written is start of data area */
702 a_vfp->_vfpHighWater = a_vfp->_vfpStart;
704 /* current character is NULL */
706 *a_vfp->_vfpCurr = '\0';
708 /* if file associated with VFP, truncate actual file */
710 if (a_vfp->_vfpFile != (FILE *)NULL) {
711 (void) ftruncate(fileno(a_vfp->_vfpFile), 0);
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
725 vfpWriteToFile(VFP_T *a_vfp, char *a_path)
727 int fd;
728 int lerrno = 0;
729 size_t len;
730 ssize_t result = 0;
732 /* return if no vfp specified */
734 if (a_vfp == (VFP_T *)NULL) {
735 errno = EFAULT;
736 return (-1);
739 /* on buffer overflow generate error */
741 if ((a_vfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(a_vfp) < 1)) {
742 errno = EFBIG;
743 return (-1);
746 /* open file to write data to */
748 fd = open(a_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
749 if (fd < 0) {
750 return (-1);
753 /* determine number of bytes to write */
755 len = vfpGetModifiedLen(a_vfp);
758 * if there is data associated with the file, write it out;
759 * if an error occurs, close the file and return failure.
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);
773 /* close the file */
775 (void) close(fd);
777 /* data committed to backing store - clear the modified flag */
779 (void) vfpClearModified(a_vfp);
781 /* return success */
783 return (0);
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.
816 vfpCheckpointFile(VFP_T **r_cpVfp, VFP_T **a_vfp, char *a_path)
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 */
822 /* return error if NULL VFP_T** to checkpoint provided */
824 if (r_cpVfp == (VFP_T **)NULL) {
825 errno = EFAULT;
826 return (-1);
829 /* reset return checkpoint VFP pointer */
831 (*r_cpVfp) = (VFP_T *)NULL;
833 /* return error if no VFP to checkpoint specified */
835 if (a_vfp == (VFP_T **)NULL) {
836 errno = EFAULT;
837 return (-1);
840 /* localize reference to a_vfp */
842 avfp = *a_vfp;
844 /* return error if no VFP to checkpoint specified */
846 if (avfp == (VFP_T *)NULL) {
847 errno = EFAULT;
848 return (-1);
851 /* on buffer overflow generate error */
853 if ((avfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(avfp) < 1)) {
854 errno = EFBIG;
855 return (-1);
858 /* no checkpointing is possible if the existing VFP is mmap()ed */
860 if (avfp->_vfpFlags & _VFP_MMAP) {
861 errno = EIO;
862 return (-1);
865 /* if no path specified, grab it from the VFP to checkpoint */
867 if ((a_path == (char *)NULL) || (*a_path == '\0')) {
868 a_path = avfp->_vfpPath;
871 /* backing store required: if VFP is "string" then this is an error */
873 if ((a_path == (char *)NULL) ||
874 strcmp(a_path, VFP_ANONYMOUS_PATH) == 0) {
875 errno = EINVAL;
876 return (-1);
879 /* Get the VFP to checkpoint (backing store) file size */
881 if (stat(a_path, &statbuf) != 0) {
882 return (-1);
885 /* allocate storage for checkpointed VFP (to return) */
887 vfp = (VFP_T *)malloc(sizeof (VFP_T));
888 if (vfp == (VFP_T *)NULL) {
889 return (-1);
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.
900 if (avfp->_vfpFile != (FILE *)NULL) {
901 (void) fclose(avfp->_vfpFile);
902 avfp->_vfpFile = (FILE *)NULL;
905 /* free any path associated with VFP to checkpoint (backing store) */
907 if (avfp->_vfpPath != (char *)NULL) {
908 (void) free(avfp->_vfpPath);
909 avfp->_vfpPath = (char *)NULL;
912 /* copy contents of VFP to checkpoint to checkpointed VFP */
914 (void) memcpy(vfp, avfp, sizeof (VFP_T));
916 /* free contents of VFP to checkpoint */
918 (void) free(avfp);
920 /* reset pointer to VFP that has been free'd */
922 *a_vfp = (VFP_T *)NULL;
924 /* remember path associated with the checkpointed VFP (backing store) */
926 vfp->_vfpPath = strdup(a_path);
928 /* save tokens that identify the backing store for the in-memory data */
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 */
936 /* pass checkpointed VFP to caller */
938 (*r_cpVfp) = vfp;
940 /* success! */
942 return (0);
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
980 vfpCheckpointOpen(VFP_T **a_cpVfp, VFP_T **r_vfp, char *a_path,
981 char *a_mode, VFPFLAGS_T a_flags)
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 */
989 * if no source VFP, or source VFP empty,
990 * or no backing store, just open file
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));
999 /* localize access to checkpointed VFP_T (*a_cpVfp) */
1001 cpVfp = *a_cpVfp;
1003 /* if no path specified, grab it from the checkpointed VFP */
1005 if ((a_path == (char *)NULL) || (*a_path == '\0')) {
1006 a_path = cpVfp->_vfpPath;
1009 /* return error if no path specified and no path in checkpointed VFP */
1011 if ((a_path == (char *)NULL) && (*a_path == '\0')) {
1012 errno = EINVAL;
1013 return (-1);
1016 /* if no backing store path, then just open file */
1018 if (stat(a_path, &statbuf) != 0) {
1019 (void) vfpClose(a_cpVfp);
1020 return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
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
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));
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
1045 fp = cpVfp->_vfpFile;
1046 if (fp == (FILE *)NULL) {
1047 fp = fopen(a_path, a_mode);
1048 if (fp == (FILE *)NULL) {
1049 int lerrno;
1051 lerrno = errno;
1052 (void) vfpClose(a_cpVfp);
1053 errno = lerrno;
1054 return (-1);
1058 /* allocate new VFP object to return as open VFP */
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));
1066 /* copy cached checkpointed VFP to new VFP to return */
1068 (void) memcpy(vfp, cpVfp, sizeof (VFP_T));
1071 * initialize VFP to return contents
1074 /* FILE -> file opened on the VFPs backing store */
1076 vfp->_vfpFile = fp;
1078 /* release any existing path associated with the VFP */
1080 if (vfp->_vfpPath != (char *)NULL) {
1081 (void) free(vfp->_vfpPath);
1084 /* path associated with the backing store for this VFP */
1086 vfp->_vfpPath = strdup(a_path);
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.
1095 /* _vfpCurr -> next byte to read */
1096 vfp->_vfpCurr = (char *)vfp->_vfpStart;
1098 /* free checkpointed VFP as it is now open on "vfp" */
1100 (void) free(cpVfp);
1102 /* reset callers -> checkpointed VFP */
1104 (*a_cpVfp) = (VFP_T *)NULL;
1106 /* set return VFP pointer */
1108 (*r_vfp) = vfp;
1110 /* success! */
1112 return (0);
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
1126 vfpClearModified(VFP_T *a_vfp)
1128 VFPFLAGS_T flags;
1130 /* save current flags settings */
1132 flags = a_vfp->_vfpFlags;
1134 /* clear "data is modified" flag */
1136 a_vfp->_vfpFlags &= (~_VFP_MODIFIED);
1138 /* return previous "data is modified" flag setting */
1140 return ((flags & _VFP_MODIFIED) != 0);
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
1154 vfpSetModified(VFP_T *a_vfp)
1156 VFPFLAGS_T flags;
1158 /* save current flags settings */
1160 flags = a_vfp->_vfpFlags;
1162 /* set "data is modified" flag */
1164 a_vfp->_vfpFlags |= _VFP_MODIFIED;
1166 /* return previous "data is modified" flag setting */
1168 return ((flags & _VFP_MODIFIED) != 0);
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
1182 vfpGetModified(VFP_T *a_vfp)
1184 /* return current "data is modified" flag setting */
1186 return ((a_vfp->_vfpFlags & _VFP_MODIFIED) != 0);
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.
1202 ssize_t
1203 vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte)
1205 ssize_t r;
1206 size_t bytes = a_nbyte;
1208 for (;;) {
1209 /* write bytes to file */
1210 r = write(a_fildes, a_buf, a_nbyte);
1212 /* return error on failure of write() */
1213 if (r < 0) {
1214 /* EAGAIN: try again */
1215 if (errno == EAGAIN) {
1216 continue;
1218 /* EINTR: interrupted - try again */
1219 if (errno == EINTR) {
1220 continue;
1222 return (r);
1225 /* return total bytes written on success */
1226 if (r >= a_nbyte) {
1227 return (bytes);
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;
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.
1250 ssize_t
1251 vfpSafePwrite(int a_fildes, void *a_buf, size_t a_nbyte, off_t a_offset)
1253 ssize_t r;
1254 size_t bytes = a_nbyte;
1256 for (;;) {
1257 /* write bytes to file */
1258 r = pwrite(a_fildes, a_buf, a_nbyte, a_offset);
1260 /* return error on failure of write() */
1261 if (r < 0) {
1262 /* EAGAIN: try again */
1263 if (errno == EAGAIN) {
1264 continue;
1266 /* EINTR: interrupted - try again */
1267 if (errno == EINTR) {
1268 continue;
1270 return (r);
1273 /* return total bytes written on success */
1274 if (r >= a_nbyte) {
1275 return (bytes);
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;