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