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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
31 * Synopsis: Implements virtual file protocol operations
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
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
91 #include <sys/types.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 * *****************************************************************************
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
;
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
) {
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
);
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 */
198 /* return an error if an empty path or mode specified */
200 if ((*a_path
== '\0') || (*a_mode
== '\0')) {
201 errno
= EINVAL
; /* Invalid argument */
208 fp
= fopen(a_path
, a_mode
);
209 if (fp
== (FILE *)NULL
) {
216 /* Get the file size */
218 if (fstat(fileno(fp
), &statbuf
) != 0) {
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
)) {
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
;
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 */
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
) {
318 /* read the file into the buffer */
320 if (statbuf
.st_size
!= 0) {
321 rlen
= read(fileno(fp
), vfp
->_vfpStart
,
323 if (rlen
!= statbuf
.st_size
) {
328 (void) free(vfp
->_vfpStart
);
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 */
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 */
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 */
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
)
415 /* return error if NULL VFP_T** provided */
417 if (r_vfp
== (VFP_T
**)NULL
) {
422 /* localize access to VFP_T */
426 /* return successful if NULL VFP_T* provided */
428 if (vfp
== (VFP_T
*)NULL
) {
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
442 if (vfp
->_vfpFlags
& _VFP_WRITE
) {
443 if ((vfp
->_vfpFlags
& _VFP_MALLOC
) &&
444 (vfp
->_vfpFile
!= (FILE *)NULL
)) {
447 /* determine number of bytes to write */
448 len
= vfpGetModifiedLen(vfp
);
450 /* if modified bytes present commit data to the file */
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
);
474 (void) free(vfp
->_vfpPath
);
479 if (vfp
->_vfpFile
!= (FILE *)NULL
) {
480 ret
= fclose(vfp
->_vfpFile
);
484 /* deallocate the vfp itself */
488 /* if the fclose() failed, return error and errno */
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
) {
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
,
530 if (a_flags
& VFP_SEQUENTIAL
) {
531 /* advise vm system data access is sequential */
532 (void) madvise(a_vfp
->_vfpStart
, a_vfp
->_vfpSize
,
535 if (a_flags
& VFP_RANDOM
) {
536 /* advise vm system data access is random */
537 (void) madvise(a_vfp
->_vfpStart
, a_vfp
->_vfpSize
,
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
550 * Operation is always successful
554 vfpRewind(VFP_T
*a_vfp
)
556 /* return if no vfp specified */
558 if (a_vfp
== (VFP_T
*)NULL
) {
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
;
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
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
594 * All existing file data is preserved.
598 vfpSetSize(VFP_T
*a_vfp
, size_t a_size
)
603 /* return if no vfp specified */
605 if (a_vfp
== (VFP_T
*)NULL
) {
609 /* if malloc not used don't know how to set size right now */
611 if (!(a_vfp
->_vfpFlags
& _VFP_MALLOC
)) {
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
) {
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
) {
637 np
[curSize
-1] = '\0';
639 np
= (char *)malloc(a_vfp
->_vfpSize
+1);
640 if (np
== (char *)NULL
) {
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
;
673 * Description: Truncate data associated with VFP
674 * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to truncate
676 * Operation is always successful.
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.
684 vfpTruncate(VFP_T
*a_vfp
)
686 /* return if no vfp specified */
688 if (a_vfp
== (VFP_T
*)NULL
) {
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
)
732 /* return if no vfp specified */
734 if (a_vfp
== (VFP_T
*)NULL
) {
739 /* on buffer overflow generate error */
741 if ((a_vfp
->_vfpOverflow
!= 0) || (vfpGetBytesAvailable(a_vfp
) < 1)) {
746 /* open file to write data to */
748 fd
= open(a_path
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
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.
763 result
= vfpSafeWrite(fd
, a_vfp
->_vfpStart
, len
);
765 /* error comitting data - return failure */
777 /* data committed to backing store - clear the modified flag */
779 (void) vfpClearModified(a_vfp
);
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
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
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
) {
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
) {
840 /* localize reference to a_vfp */
844 /* return error if no VFP to checkpoint specified */
846 if (avfp
== (VFP_T
*)NULL
) {
851 /* on buffer overflow generate error */
853 if ((avfp
->_vfpOverflow
!= 0) || (vfpGetBytesAvailable(avfp
) < 1)) {
858 /* no checkpointing is possible if the existing VFP is mmap()ed */
860 if (avfp
->_vfpFlags
& _VFP_MMAP
) {
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) {
879 /* Get the VFP to checkpoint (backing store) file size */
881 if (stat(a_path
, &statbuf
) != 0) {
885 /* allocate storage for checkpointed VFP (to return) */
887 vfp
= (VFP_T
*)malloc(sizeof (VFP_T
));
888 if (vfp
== (VFP_T
*)NULL
) {
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 */
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 */
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) */
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')) {
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
) {
1052 (void) vfpClose(a_cpVfp
);
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 */
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" */
1102 /* reset callers -> checkpointed VFP */
1104 (*a_cpVfp
) = (VFP_T
*)NULL
;
1106 /* set return VFP pointer */
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
)
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
)
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
1196 * < 0 - error, errno set
1198 * NOTE: unlike write(2), vfpSafeWrite() handles partial writes, and will
1199 * ----- restart the write() until all bytes are written, or an error occurs.
1203 vfpSafeWrite(int a_fildes
, void *a_buf
, size_t a_nbyte
)
1206 size_t bytes
= a_nbyte
;
1209 /* write bytes to file */
1210 r
= write(a_fildes
, a_buf
, a_nbyte
);
1212 /* return error on failure of write() */
1214 /* EAGAIN: try again */
1215 if (errno
== EAGAIN
) {
1218 /* EINTR: interrupted - try again */
1219 if (errno
== EINTR
) {
1225 /* return total bytes written on success */
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
1244 * < 0 - error, errno set
1246 * NOTE: unlike pwrite(2), vfpSafePwrite() handles partial writes, and will
1247 * ----- restart the pwrite() until all bytes are written, or an error occurs.
1251 vfpSafePwrite(int a_fildes
, void *a_buf
, size_t a_nbyte
, off_t a_offset
)
1254 size_t bytes
= a_nbyte
;
1257 /* write bytes to file */
1258 r
= pwrite(a_fildes
, a_buf
, a_nbyte
, a_offset
);
1260 /* return error on failure of write() */
1262 /* EAGAIN: try again */
1263 if (errno
== EAGAIN
) {
1266 /* EINTR: interrupted - try again */
1267 if (errno
== EINTR
) {
1273 /* return total bytes written on success */
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
;