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