diff options
Diffstat (limited to 'usr/src/lib/libpkg/common/vfpops.c')
-rw-r--r-- | usr/src/lib/libpkg/common/vfpops.c | 1283 |
1 files changed, 1283 insertions, 0 deletions
diff --git a/usr/src/lib/libpkg/common/vfpops.c b/usr/src/lib/libpkg/common/vfpops.c new file mode 100644 index 0000000000..a4e6e54112 --- /dev/null +++ b/usr/src/lib/libpkg/common/vfpops.c @@ -0,0 +1,1283 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +/* + * Module: vfpops.c + * Synopsis: Implements virtual file protocol operations + * Description: + * + * This module implements the "Virtual File protocol" operations. These + * operations are intended to provide very fast access to file data, + * allowing a file to be accessed in very efficient ways with extremely + * low-cpu intensive operations. If possible file data is mapped directly + * into memory allowing the data to be accessed directly. If the data + * cannot be mapped directly into memory, memory will be allocated and + * the file data read directly into memory. If that fails currently the + * file data is not accessible. Other methods of making the file could + * be implemented in the future (e.g. stdio if all else fails). + * + * In general any code that uses stdio to access a file can be changed + * to use the various "vfp" operations to access a file, with a resulting + * increase in performance and decrease in cpu time required to access + * the file contents. + * + * Public Methods: + * + * vfpCheckpointFile - Create new VFP that checkpoints existing VFP + * vfpCheckpointOpen - open file, allocate storage, return pointer to VFP_T + * vfpClose - close file associated with vfp + * vfpDecCurrPtr - decrement current character pointer + * vfpGetBytesRemaining - get number of bytes remaining to read + * vfpGetCurrCharPtr - get pointer to current character + * vfpGetCurrPtrDelta - get number of bytes between current and specified char + * vfpGetFirstCharPtr - get pointer to first character + * vfpGetLastCharPtr - get pointer to last character + * vfpGetModifiedLen - get highest modified byte (length) contained in vfp + * vfpGetPath - get the path associated with the vfp + * vfpGetc - get current character and increment to next + * vfpGetcNoInc - get current character - do not increment + * vfpGets - get a string from the vfp into a fixed size buffer + * vfpIncCurrPtr - increment current character pointer + * vfpIncCurrPtrBy - increment current pointer by specified delta + * vfpOpen - open file on vfp + * vfpPutBytes - put fixed number of bytes to current character and increment + * vfpPutFormat - put format one arg to current character and increment + * vfpPutInteger - put integer to current character and increment + * vfpPutLong - put long to current character and increment + * vfpPutc - put current character and increment to next + * vfpPuts - put string to current character and increment + * vfpRewind - rewind file to first byte + * vfpSeekToEnd - seek to end of file + * vfpSetCurrCharPtr - set pointer to current character + * vfpSetFlags - set flags that affect file access + * vfpSetSize - set size of file (for writing) + * vfpTruncate - truncate file + * vfpWriteToFile - write data contained in vfp to specified file + */ + +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> +#include <libintl.h> +#include "pkglib.h" +#include "pkgstrct.h" +#include "pkglocale.h" + +/* + * These are internal flags that occupy the high order byte of the VFPFLAGS_T + * flags element of the vfp. These flags may only occupy the high order order + * 16 bits of the 32-bit unsigned vfp "flags" object. + */ + +#define _VFP_MMAP 0x00010000 /* mmap used */ +#define _VFP_MALLOC 0x00020000 /* malloc used */ +#define _VFP_WRITE 0x00040000 /* file opened for write */ +#define _VFP_READ 0x00080000 /* file opened for reading */ +#define _VFP_MODIFIED 0x00100000 /* contents are marked modified */ + +/* path name given to "anonymous" (string) vfp */ + +#define VFP_ANONYMOUS_PATH "<<string>>" + +/* minimum size file to mmap (64mb) */ + +#define MIN_MMAP_SIZE (64*1024) + +/* + * ***************************************************************************** + * global external (public) functions + * ***************************************************************************** + */ + +/* + * Name: vfpOpen + * Description: Open file on vfp, allocate storage, return pointer to VFP_T + * that can be used to access/modify file contents. + * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T + * char *a_path - path of file to open and associate with this VFP. + * - if the path is (char *)NULL then no file is associated + * with this VFP - this is a way to create a fixed length + * string that can be manipulated with the VFP operators. + * Before the VFP can be used "vfpSetSize" must be called + * to set the size of the string buffer. + * char *a_mode - fopen mode to open the file with + * VFPFLAGS_T a_flags - one or more flags to control the operation: + * - VFP_NONE - no special flags + * - VFP_NEEDNOW - file data needed in memory now + * - VFP_SEQUENTIAL - memory will be sequentially accessed + * - VFP_RANDOM - memory will be randomly accessed + * - VFP_NOMMAP - do not use mmap to access file + * - VFP_NOMALLOC - do not use malloc to buffer file + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp + * which can be used with the various vfp functions. + * errno -- contains system error number if return is != 0 + */ + +int +vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode, VFPFLAGS_T a_flags) +{ + FILE *fp = (FILE *)NULL; + VFP_T *vfp; + int lerrno; + struct stat statbuf; + int pagesize = getpagesize(); + + /* reset return VFP/FILE pointers */ + + (*r_vfp) = (VFP_T *)NULL; + + /* allocate pre-zeroed vfp object */ + + vfp = (VFP_T *)calloc(sizeof (VFP_T), 1); + if (vfp == (VFP_T *)NULL) { + return (-1); + } + + /* create "string" vfp if no path specified */ + + if (a_path == (char *)NULL) { + /* + * no path specified - no open file associated with vfp + * The vfp is initialized to all zeros - initialize just those + * values that need to be non-zero. + */ + + vfp->_vfpFlags = _VFP_MALLOC; + vfp->_vfpPath = strdup(VFP_ANONYMOUS_PATH); + (*r_vfp) = vfp; + return (0); + } + + /* + * path specified - associate open file with vfp; + * return an error if no path or mode specified + */ + + if (a_mode == (char *)NULL) { + errno = EFAULT; /* Bad address */ + (void) free(vfp); + return (-1); + } + + /* return an error if an empty path or mode specified */ + + if ((*a_path == '\0') || (*a_mode == '\0')) { + errno = EINVAL; /* Invalid argument */ + (void) free(vfp); + return (-1); + } + + /* open the file */ + + fp = fopen(a_path, a_mode); + if (fp == (FILE *)NULL) { + lerrno = errno; + (void) free(vfp); + errno = lerrno; + return (-1); + } + + /* Get the file size */ + + if (fstat(fileno(fp), &statbuf) != 0) { + lerrno = errno; + (void) fclose(fp); + (void) free(vfp); + errno = lerrno; + return (-1); + } + + /* + * Obtain access to existing file contents: + * -> plan a: map contents file into memory + * -> plan b: on failure just read into large buffer + */ + + /* attempt to mmap file if mmap is allowed */ + + vfp->_vfpStart = MAP_FAILED; /* assume map failed if not allowed */ + + /* + * if file is a regular file, and if mmap allowed, + * and (malloc not forbidden or size is > minumum size to mmap) + */ + + if ((S_ISREG(statbuf.st_mode)) && (!(a_flags & VFP_NOMMAP)) && + ((a_flags & VFP_NOMALLOC) || statbuf.st_size > MIN_MMAP_SIZE)) { + char *p; + /* set size to current size of file */ + + vfp->_vfpMapSize = statbuf.st_size; + + /* + * compute proper size for mapping for the file contents; + * add in one extra page so falling off end when file size is + * exactly modulo page size does not cause a page fault to + * guarantee that the end of the file contents will always + * contain a '\0' null character. + */ + + vfp->_vfpSize = (statbuf.st_size + pagesize + + (pagesize-(statbuf.st_size % pagesize))); + + /* + * mmap allowed: mmap file into memory + * first allocate space on top of which the mapping can be done; + * this way we can guarantee that if the mapping happens to be + * an exact multiple of a page size, that there will be at least + * one byte past the end of the mapping that can be accessed and + * that is guaranteed to be zero. + */ + + /* allocate backing space */ + + p = (char *)memalign(pagesize, vfp->_vfpSize); + if (p == (char *)NULL) { + vfp->_vfpStart = MAP_FAILED; + } else { + /* guarantee first byte after end of data is zero */ + + p[vfp->_vfpMapSize] = '\0'; + + /* map file on top of the backing space */ + + vfp->_vfpStart = mmap(p, vfp->_vfpMapSize, PROT_READ, + MAP_PRIVATE|MAP_FIXED, fileno(fp), (off_t)0); + + /* if mmap succeeded set mmap used flag in vfp */ + + if (vfp->_vfpStart != MAP_FAILED) { + vfp->_vfpFlags |= _VFP_MMAP; + } + } + } + + /* if map failed (or not allowed) attempt malloc (if allowed) */ + + if ((vfp->_vfpStart == MAP_FAILED) && (!(a_flags & VFP_NOMALLOC))) { + /* mmap failed - plan b: read directly into memory */ + ssize_t rlen; + + /* + * compute proper size for allocating storage for file contents; + * add in one extra page so falling off end when file size is + * exactly modulo page size does not cause a page fault to + * guarantee that the end of the file contents will always + * contain a '\0' null character. + */ + + vfp->_vfpSize = statbuf.st_size+pagesize; + + /* allocate buffer to hold file data */ + + vfp->_vfpStart = memalign((size_t)pagesize, vfp->_vfpSize); + if (vfp->_vfpStart == (char *)NULL) { + lerrno = errno; + (void) fclose(fp); + (void) free(vfp); + errno = lerrno; + return (-1); + } + + /* read the file into the buffer */ + + if (statbuf.st_size != 0) { + rlen = read(fileno(fp), vfp->_vfpStart, + statbuf.st_size); + if (rlen != statbuf.st_size) { + lerrno = errno; + if (lerrno == 0) { + lerrno = EIO; + } + (void) free(vfp->_vfpStart); + (void) fclose(fp); + (void) free(vfp); + errno = lerrno; + return (-1); + } + + /* assure last byte+1 is null character */ + + ((char *)vfp->_vfpStart)[statbuf.st_size] = '\0'; + } + + /* set malloc used flag in vfp */ + + vfp->_vfpFlags |= _VFP_MALLOC; + } + + /* if no starting address all read methods failed */ + + if (vfp->_vfpStart == MAP_FAILED) { + /* no mmap() - no read() - cannot allocate memory */ + (void) fclose(fp); + (void) free(vfp); + errno = ENOMEM; + return (-1); + } + + /* + * initialize vfp contents + */ + + /* _vfpCurr -> next byte to read */ + vfp->_vfpCurr = (char *)vfp->_vfpStart; + + /* _vfpEnd -> last data byte */ + vfp->_vfpEnd = (((char *)vfp->_vfpStart) + statbuf.st_size)-1; + + /* _vfpHighWater -> last byte written */ + vfp->_vfpHighWater = (char *)vfp->_vfpEnd; + + /* _vfpFile -> associated FILE* object */ + vfp->_vfpFile = fp; + + /* set flags as appropriate */ + + (void) vfpSetFlags(vfp, a_flags); + + /* retain path name */ + + vfp->_vfpPath = strdup(a_path ? a_path : ""); + + /* set read/write flags */ + + if (*a_mode == 'w') { + vfp->_vfpFlags |= _VFP_WRITE; + } + + if (*a_mode == 'r') { + vfp->_vfpFlags |= _VFP_READ; + } + + /* set return vfp pointer */ + + (*r_vfp) = vfp; + + /* All OK */ + + return (0); +} + +/* + * Name: vfpClose + * Description: Close an open vfp, causing any modified data to be written out + * to the file associated with the vfp. + * Arguments: VFP_T **r_vfp - pointer to pointer to VFP_T returned by vfpOpen + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + * Side Effects: r_vfp is set to (VFP_T)NULL + */ + +int +vfpClose(VFP_T **r_vfp) +{ + int ret; + int lerrno; + VFP_T *vfp; + + /* return error if NULL VFP_T** provided */ + + if (r_vfp == (VFP_T **)NULL) { + errno = EFAULT; + return (-1); + } + + /* localize access to VFP_T */ + + vfp = *r_vfp; + + /* return successful if NULL VFP_T* provided */ + + if (vfp == (VFP_T *)NULL) { + return (0); + } + + /* reset return VFP_T* handle */ + + *r_vfp = (VFP_T *)NULL; + + /* + * if closing a file that is open for writing, commit all data if the + * backing memory is volatile and if there is a file open to write + * the data to. + */ + + if (vfp->_vfpFlags & _VFP_WRITE) { + if ((vfp->_vfpFlags & _VFP_MALLOC) && + (vfp->_vfpFile != (FILE *)NULL)) { + size_t len; + + /* determine number of bytes to write */ + len = vfpGetModifiedLen(vfp); + + /* if modified bytes present commit data to the file */ + if (len > 0) { + (void) vfpSafePwrite(fileno(vfp->_vfpFile), + vfp->_vfpStart, len, (off_t)0); + } + } + } + + /* deallocate any allocated storage/mappings/etc */ + + if (vfp->_vfpFlags & _VFP_MALLOC) { + (void) free(vfp->_vfpStart); + } else if (vfp->_vfpFlags & _VFP_MMAP) { + /* unmap the file mapping */ + + (void) munmap(vfp->_vfpStart, vfp->_vfpMapSize); + + /* free the backing allocation */ + + (void) free(vfp->_vfpStart); + } + + /* free up path */ + + (void) free(vfp->_vfpPath); + + /* close the file */ + + ret = 0; + if (vfp->_vfpFile != (FILE *)NULL) { + ret = fclose(vfp->_vfpFile); + lerrno = errno; + } + + /* deallocate the vfp itself */ + + (void) free(vfp); + + /* if the fclose() failed, return error and errno */ + + if (ret != 0) { + errno = lerrno; + return (-1); + } + + return (0); +} + +/* + * Name: vfpSetFlags + * Description: Modify operation of VFP according to flags specified + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set flags + * VFPFLAGS_T a_flags - one or more flags to control the operation: + * - VFP_NEEDNOW - file data needed in memory now + * - VFP_SEQUENTIAL - file data sequentially accessed + * - VFP_RANDOM - file data randomly accessed + * Any other flags specified are silently ignored. + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + */ + +int +vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags) +{ + /* return if no vfp specified */ + + if (a_vfp == (VFP_T *)NULL) { + return (0); + } + + /* if file data mapped into memory, apply vm flags */ + + if ((a_vfp->_vfpSize != 0) && (a_vfp->_vfpFlags & _VFP_MMAP)) { + /* mmap succeeded: properly advise vm system */ + + if (a_flags & VFP_NEEDNOW) { + /* advise vm system data is needed now */ + (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpMapSize, + MADV_WILLNEED); + } + if (a_flags & VFP_SEQUENTIAL) { + /* advise vm system data access is sequential */ + (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize, + MADV_SEQUENTIAL); + } + if (a_flags & VFP_RANDOM) { + /* advise vm system data access is random */ + (void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize, + MADV_RANDOM); + } + } + + return (0); +} + +/* + * Name: vfpRewind + * Description: Reset default pointer for next read/write to start of file data + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to rewind + * Returns: void + * Operation is always successful + */ + +void +vfpRewind(VFP_T *a_vfp) +{ + /* return if no vfp specified */ + + if (a_vfp == (VFP_T *)NULL) { + return; + } + + /* set high water mark of last modified data */ + + if (a_vfp->_vfpCurr > a_vfp->_vfpHighWater) { + a_vfp->_vfpHighWater = a_vfp->_vfpCurr; + } + + /* reset next character pointer to start of file data */ + + a_vfp->_vfpCurr = a_vfp->_vfpStart; +} + +/* + * Name: vfpSetSize + * Description: Set size of in-memory image associated with VFP + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set + * size_t a_size - number of bytes to associatge with VFP + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + * Side Effects: + * Currently only a file that is in malloc()ed memory can + * have its in-memory size changed. + * An error is returned If the file is mapped into memory. + * A file cannot be decreased in size - if the specified + * size is less than the current size, the operation is + * successful but no change in file size occurs. + * If no file is associated with the VFP (no "name" was + * given to vfpOpen) the first call to vfpSetSize allocates + * the initial size of the file data - effectively calling + * "malloc" to allocate the initial memory for the file data. + * Once an initial allocation has been made, subsequent calls + * to vfpSetSize are effectively a "realloc" of the existing + * file data. + * All existing file data is preserved. + */ + +int +vfpSetSize(VFP_T *a_vfp, size_t a_size) +{ + char *np; + size_t curSize; + + /* return if no vfp specified */ + + if (a_vfp == (VFP_T *)NULL) { + return (0); + } + + /* if malloc not used don't know how to set size right now */ + + if (!(a_vfp->_vfpFlags & _VFP_MALLOC)) { + return (-1); + } + + /* adjust size to reflect extra page of data maintained */ + + a_size += getpagesize(); + + /* if size is not larger than current nothing to do */ + + if (a_size <= a_vfp->_vfpSize) { + return (0); + } + + /* remember new size */ + + curSize = a_vfp->_vfpSize; + a_vfp->_vfpSize = a_size; + + /* allocate/reallocate memory as appropriate */ + + if (a_vfp->_vfpStart != (char *)NULL) { + np = (char *)realloc(a_vfp->_vfpStart, a_vfp->_vfpSize+1); + if (np == (char *)NULL) { + return (-1); + } + np[curSize-1] = '\0'; + } else { + np = (char *)malloc(a_vfp->_vfpSize+1); + if (np == (char *)NULL) { + return (-1); + } + np[0] = '\0'; + } + + /* make sure last allocated byte is a null */ + + np[a_vfp->_vfpSize] = '\0'; + + /* + * adjust all pointers to account for buffer address change + */ + + /* _vfpCurr -> next byte to read */ + a_vfp->_vfpCurr = (char *)(((ptrdiff_t)a_vfp->_vfpCurr - + (ptrdiff_t)a_vfp->_vfpStart) + np); + + /* _vfpHighWater -> last byte written */ + a_vfp->_vfpHighWater = (char *)(((ptrdiff_t)a_vfp->_vfpHighWater - + (ptrdiff_t)a_vfp->_vfpStart) + np); + + /* _vfpEnd -> last data byte */ + a_vfp->_vfpEnd = (np + a_vfp->_vfpSize)-1; + + /* _vfpStart -> first data byte */ + a_vfp->_vfpStart = np; + + return (0); +} + +/* + * Name: vfpTruncate + * Description: Truncate data associated with VFP + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to truncate + * Returns: void + * Operation is always successful. + * Side Effects: + * In memory data associated with file is believed to be empty. + * Actual memory associated with file is not affected. + * If a file is associated with the VFP, it is truncated. + */ + +void +vfpTruncate(VFP_T *a_vfp) +{ + /* return if no vfp specified */ + + if (a_vfp == (VFP_T *)NULL) { + return; + } + + /* + * reset all pointers so that no data is associated with file + */ + + /* current byte is start of data area */ + + a_vfp->_vfpCurr = a_vfp->_vfpStart; + + /* last byte written is start of data area */ + + a_vfp->_vfpHighWater = a_vfp->_vfpStart; + + /* current character is NULL */ + + *a_vfp->_vfpCurr = '\0'; + + /* if file associated with VFP, truncate actual file */ + + if (a_vfp->_vfpFile != (FILE *)NULL) { + (void) ftruncate(fileno(a_vfp->_vfpFile), 0); + } +} + +/* + * Name: vfpWriteToFile + * Description: Write data associated with VFP to specified file + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to write + * char *a_path - path of file to write file data to + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + */ + +int +vfpWriteToFile(VFP_T *a_vfp, char *a_path) +{ + int fd; + int lerrno = 0; + size_t len; + ssize_t result = 0; + + /* return if no vfp specified */ + + if (a_vfp == (VFP_T *)NULL) { + errno = EFAULT; + return (-1); + } + + /* on buffer overflow generate error */ + + if ((a_vfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(a_vfp) < 1)) { + errno = EFBIG; + return (-1); + } + + /* open file to write data to */ + + fd = open(a_path, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd < 0) { + return (-1); + } + + /* determine number of bytes to write */ + + len = vfpGetModifiedLen(a_vfp); + + /* + * if there is data associated with the file, write it out; + * if an error occurs, close the file and return failure. + */ + + if (len > 0) { + result = vfpSafeWrite(fd, a_vfp->_vfpStart, len); + if (result != len) { + /* error comitting data - return failure */ + lerrno = errno; + (void) close(fd); + errno = lerrno; + return (-1); + } + } + + /* close the file */ + + (void) close(fd); + + /* data committed to backing store - clear the modified flag */ + + (void) vfpClearModified(a_vfp); + + /* return success */ + + return (0); +} + +/* + * Name: vfpCheckpointFile + * Description: Create new VFP that checkpoints existing VFP, can be used by + * subsequent call to vfpCheckpointOpen to open a file using the + * existing in-memory cache of the contents of the file + * Arguments: VFP_T **r_cpVfp - pointer to pointer to VFP_T to be filled in + * with "checkpointed file" VFP (backing store) + * VFP_T **a_vfp - pointer to pointer to VFP_T returned by vfpOpen + * representing the VFP to checkpoint + * char *a_path - path to file that is the backing store for the + * in-memory data represented by a_vfp - used to verify + * that the data in memory is not out of date with respect + * to the backing store when vfpCheckpointOpen is called + * == (char *)NULL - use path associated with a_vfp + * that is, the backing store file in use + * Returns: int == 0 - operation was successful + * - r_destVfp contains a pointer to a new VFP that + * may be used in a subsequent call to + * vfpCheckpointOpen + * - the VFP referenced by *a_vfp is free()ed and + * must no longer be referenced + * != 0 - operation failed, errno contains reason + * - the VFP referenced by *a_vfp is not affected; + * the caller may continue to use it + * Notes: If the data of a VFP to checkpoint is mmap()ed then this method + * returns failure - only malloc()ed data VFPs can be + * checkpointed. + */ + +int +vfpCheckpointFile(VFP_T **r_cpVfp, VFP_T **a_vfp, char *a_path) +{ + VFP_T *vfp; /* newly allocated checkpointed VFP */ + VFP_T *avfp; /* local -> to a_vfp */ + struct stat statbuf; /* stat(2) info for backing store */ + + /* return error if NULL VFP_T** to checkpoint provided */ + + if (r_cpVfp == (VFP_T **)NULL) { + errno = EFAULT; + return (-1); + } + + /* reset return checkpoint VFP pointer */ + + (*r_cpVfp) = (VFP_T *)NULL; + + /* return error if no VFP to checkpoint specified */ + + if (a_vfp == (VFP_T **)NULL) { + errno = EFAULT; + return (-1); + } + + /* localize reference to a_vfp */ + + avfp = *a_vfp; + + /* return error if no VFP to checkpoint specified */ + + if (avfp == (VFP_T *)NULL) { + errno = EFAULT; + return (-1); + } + + /* on buffer overflow generate error */ + + if ((avfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(avfp) < 1)) { + errno = EFBIG; + return (-1); + } + + /* no checkpointing is possible if the existing VFP is mmap()ed */ + + if (avfp->_vfpFlags & _VFP_MMAP) { + errno = EIO; + return (-1); + } + + /* if no path specified, grab it from the VFP to checkpoint */ + + if ((a_path == (char *)NULL) || (*a_path == '\0')) { + a_path = avfp->_vfpPath; + } + + /* backing store required: if VFP is "string" then this is an error */ + + if ((a_path == (char *)NULL) || + strcmp(a_path, VFP_ANONYMOUS_PATH) == 0) { + errno = EINVAL; + return (-1); + } + + /* Get the VFP to checkpoint (backing store) file size */ + + if (stat(a_path, &statbuf) != 0) { + return (-1); + } + + /* allocate storage for checkpointed VFP (to return) */ + + vfp = (VFP_T *)malloc(sizeof (VFP_T)); + if (vfp == (VFP_T *)NULL) { + return (-1); + } + + /* + * close any file that is on the VFP to checkpoint (backing store); + * subsequent processes can modify the backing store data, and + * then when vfpCheckpointOpen is called, either the in-memory + * cached data will be used (if backing store unmodified) or else + * the in-memory data is released and the backing store is used. + */ + + if (avfp->_vfpFile != (FILE *)NULL) { + (void) fclose(avfp->_vfpFile); + avfp->_vfpFile = (FILE *)NULL; + } + + /* free any path associated with VFP to checkpoint (backing store) */ + + if (avfp->_vfpPath != (char *)NULL) { + (void) free(avfp->_vfpPath); + avfp->_vfpPath = (char *)NULL; + } + + /* copy contents of VFP to checkpoint to checkpointed VFP */ + + memcpy(vfp, avfp, sizeof (VFP_T)); + + /* free contents of VFP to checkpoint */ + + (void) free(avfp); + + /* reset pointer to VFP that has been free'd */ + + *a_vfp = (VFP_T *)NULL; + + /* remember path associated with the checkpointed VFP (backing store) */ + + vfp->_vfpPath = strdup(a_path); + + /* save tokens that identify the backing store for the in-memory data */ + + vfp->_vfpCkDev = statbuf.st_dev; /* devid holding st_ino inode */ + vfp->_vfpCkIno = statbuf.st_ino; /* backing store inode */ + vfp->_vfpCkMtime = statbuf.st_mtime; /* last data modification */ + vfp->_vfpCkSize = statbuf.st_size; /* backing store size (bytes) */ + vfp->_vfpCkStBlocks = statbuf.st_blocks; /* blocks allocated to file */ + + /* pass checkpointed VFP to caller */ + + (*r_cpVfp) = vfp; + + /* success! */ + + return (0); +} + +/* + * Name: vfpCheckpointOpen + * Description: Open file on vfp, allocate storage, return pointer to VFP_T + * that can be used to access/modify file contents. If a VFP_T to + * a checkpointed VFP is passed in, and the in memory contents of + * the VFP are not out of date with respect to the backing store + * file, use the existing in-memory contents - otherwise, discard + * the in-memory contents and reopen and reread the file. + * Arguments: VFP_T **a_cpVfp - pointer to pointer to VFP_T that represents + * checkpointed VFP to use to open the file IF the contents + * of the backing store are identical to the in-memory data + * VFP_T **r_vfp - pointer to pointer to VFP_T to open file on + * char *a_path - path of file to open and associate with this VFP. + * - if the path is (char *)NULL then no file is associated + * with this VFP - this is a way to create a fixed length + * string that can be manipulated with the VFP operators. + * Before the VFP can be used "vfpSetSize" must be called + * to set the size of the string buffer. + * char *a_mode - fopen mode to open the file with + * VFPFLAGS_T a_flags - one or more flags to control the operation: + * - VFP_NONE - no special flags + * - VFP_NEEDNOW - file data needed in memory now + * - VFP_SEQUENTIAL - memory will be sequentially accessed + * - VFP_RANDOM - memory will be randomly accessed + * - VFP_NOMMAP - do not use mmap to access file + * - VFP_NOMALLOC - do not use malloc to buffer file + * Returns: int == 0 - operation was successful + * != 0 - operation failed, errno contains reason + * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp + * which can be used with the various VFP functions. + * a_cpVfp -- contents reset to zero if used to open the file + * errno -- contains system error number if return is != 0 + */ + +int +vfpCheckpointOpen(VFP_T **a_cpVfp, VFP_T **r_vfp, char *a_path, + char *a_mode, VFPFLAGS_T a_flags) +{ + FILE *fp; /* backing store */ + VFP_T *cpVfp; /* local -> to a_cpVfp checkpointed VFP */ + VFP_T *vfp; /* new VFP open on checkpointed backing store */ + struct stat statbuf; /* stat(2) info on backing store */ + + /* + * if no source VFP, or source VFP empty, + * or no backing store, just open file + */ + + if ((a_cpVfp == (VFP_T **)NULL) || (*a_cpVfp == (VFP_T *)NULL) || + ((*a_cpVfp)->_vfpStart == (char *)NULL)) { + (void) vfpClose(a_cpVfp); + return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); + } + + /* localize access to checkpointed VFP_T (*a_cpVfp) */ + + cpVfp = *a_cpVfp; + + /* if no path specified, grab it from the checkpointed VFP */ + + if ((a_path == (char *)NULL) || (*a_path == '\0')) { + a_path = cpVfp->_vfpPath; + } + + /* return error if no path specified and no path in checkpointed VFP */ + + if ((a_path == (char *)NULL) && (*a_path == '\0')) { + errno = EINVAL; + return (-1); + } + + /* if no backing store path, then just open file */ + + if (stat(a_path, &statbuf) != 0) { + (void) vfpClose(a_cpVfp); + return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); + } + + /* + * if backing store tokens do not match checkpointed VFP, + * the backing store has been updated since the VFP was checkpointed; + * release the in-memory data, and open and read the backing store + */ + + if ((statbuf.st_size != cpVfp->_vfpCkSize) || + (statbuf.st_mtime != cpVfp->_vfpCkMtime) || + (statbuf.st_blocks != cpVfp->_vfpCkStBlocks) || + (statbuf.st_ino != cpVfp->_vfpCkIno) || + (statbuf.st_dev != cpVfp->_vfpCkDev)) { + (void) vfpClose(a_cpVfp); + return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); + } + + /* + * backing store has not been updated since the VFP was checkpointed; + * use the in-memory data without re-reading the backing store; open the + * backing store file (if no file already open on the checkpointed VFP) + * so there is an open file associated with the in-memory data + */ + + fp = cpVfp->_vfpFile; + if (fp == (FILE *)NULL) { + fp = fopen(a_path, a_mode); + if (fp == (FILE *)NULL) { + int lerrno; + + lerrno = errno; + (void) vfpClose(a_cpVfp); + errno = lerrno; + return (-1); + } + } + + /* allocate new VFP object to return as open VFP */ + + vfp = (VFP_T *)malloc(sizeof (VFP_T)); + if (vfp == (VFP_T *)NULL) { + (void) vfpClose(a_cpVfp); + return (vfpOpen(r_vfp, a_path, a_mode, a_flags)); + } + + /* copy cached checkpointed VFP to new VFP to return */ + + (void) memcpy(vfp, cpVfp, sizeof (VFP_T)); + + /* + * initialize VFP to return contents + */ + + /* FILE -> file opened on the VFPs backing store */ + + vfp->_vfpFile = fp; + + /* release any existing path associated with the VFP */ + + if (vfp->_vfpPath != (char *)NULL) { + (void) free(vfp->_vfpPath); + } + + /* path associated with the backing store for this VFP */ + + vfp->_vfpPath = strdup(a_path); + + /* + * data pointers associated with in memory copy of backing store + * (such as _vfpHighWater, _vfpEnd, _vfpStart, etc.) + * do not need to be modified because we are using the same backing + * store as was checkpointed in cpVfp that is pointed to by vfp. + */ + + /* _vfpCurr -> next byte to read */ + vfp->_vfpCurr = (char *)vfp->_vfpStart; + + /* free checkpointed VFP as it is now open on "vfp" */ + + (void) free(cpVfp); + + /* reset callers -> checkpointed VFP */ + + (*a_cpVfp) = (VFP_T *)NULL; + + /* set return VFP pointer */ + + (*r_vfp) = vfp; + + /* success! */ + + return (0); +} + +/* + * Name: vfpClearModified + * Description: Clear the "data is modified" indication from the VFP + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to clear + * the "data is modified" indication + * Returns: int - previous setting of "data is modified" indication + * == 0 - "data is modified" was NOT previously set + * != 0 - "data is modified" WAS previously set + */ + +int +vfpClearModified(VFP_T *a_vfp) +{ + VFPFLAGS_T flags; + + /* save current flags settings */ + + flags = a_vfp->_vfpFlags; + + /* clear "data is modified" flag */ + + a_vfp->_vfpFlags &= (~_VFP_MODIFIED); + + /* return previous "data is modified" flag setting */ + + return ((flags & _VFP_MODIFIED) != 0); +} + +/* + * Name: vfpSetModified + * Description: Set the "data is modified" indication from the VFP + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to set + * the "data is modified" indication + * Returns: int - previous setting of "data is modified" indication + * == 0 - "data is modified" was NOT previously set + * != 0 - "data is modified" WAS previously set + */ + +int +vfpSetModified(VFP_T *a_vfp) +{ + VFPFLAGS_T flags; + + /* save current flags settings */ + + flags = a_vfp->_vfpFlags; + + /* set "data is modified" flag */ + + a_vfp->_vfpFlags |= _VFP_MODIFIED; + + /* return previous "data is modified" flag setting */ + + return ((flags & _VFP_MODIFIED) != 0); +} + +/* + * Name: vfpGetModified + * Description: Get the "data is modified" indication from the VFP + * Arguments: VFP_T *a_vfp - VFP_T pointer associated with file to get + * the "data is modified" indication + * Returns: int - current setting of "data is modified" indication + * == 0 - "data is modified" is NOT set + * != 0 - "data is modified" IS set + */ + +int +vfpGetModified(VFP_T *a_vfp) +{ + /* return current "data is modified" flag setting */ + + return ((a_vfp->_vfpFlags & _VFP_MODIFIED) != 0); +} + +/* + * Name: vfpSafeWrite + * Description: write data to open file safely + * Arguments: a_fildes - file descriptor to write data to + * a_buf - pointer to buffer containing data to write + * a_nbyte - number of bytes to write to open file + * Returns: int + * < 0 - error, errno set + * >= 0 - success + * NOTE: unlike write(2), vfpSafeWrite() handles partial writes, and will + * ----- restart the write() until all bytes are written, or an error occurs. + */ + +ssize_t +vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte) +{ + ssize_t r; + size_t bytes = a_nbyte; + + for (;;) { + /* write bytes to file */ + r = write(a_fildes, a_buf, a_nbyte); + + /* return error on failure of write() */ + if (r < 0) { + /* EAGAIN: try again */ + if (errno == EAGAIN) { + continue; + } + /* EINTR: interrupted - try again */ + if (errno == EINTR) { + continue; + } + return (r); + } + + /* return total bytes written on success */ + if (r >= a_nbyte) { + return (bytes); + } + + /* partial write, adjust pointers, call write again */ + a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r); + a_nbyte -= (size_t)r; + } +} + +/* + * Name: vfpSafePwrite + * Description: write data to open file safely + * Arguments: a_fildes - file descriptor to write data to + * a_buf - pointer to buffer containing data to write + * a_nbyte - number of bytes to write to open file + * a_offset - offset into open file to write the first byte to + * Returns: int + * < 0 - error, errno set + * >= 0 - success + * NOTE: unlike pwrite(2), vfpSafePwrite() handles partial writes, and will + * ----- restart the pwrite() until all bytes are written, or an error occurs. + */ + +ssize_t +vfpSafePwrite(int a_fildes, void *a_buf, size_t a_nbyte, off_t a_offset) +{ + ssize_t r; + size_t bytes = a_nbyte; + + for (;;) { + /* write bytes to file */ + r = pwrite(a_fildes, a_buf, a_nbyte, a_offset); + + /* return error on failure of write() */ + if (r < 0) { + /* EAGAIN: try again */ + if (errno == EAGAIN) { + continue; + } + /* EINTR: interrupted - try again */ + if (errno == EINTR) { + continue; + } + return (r); + } + + /* return total bytes written on success */ + if (r >= a_nbyte) { + return (bytes); + } + + /* partial write, adjust pointers, call write again */ + a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r); + a_nbyte -= (size_t)r; + a_offset += (off_t)r; + } +} |