xref: /illumos-gate/usr/src/lib/libpkg/common/gpkgmap.c (revision 0ccfe5834c3b867c1b6c273cae02ec8922bc0fd2)
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 (c) 2017 Peter Tribble.
24  */
25 
26 /*
27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
33 
34 
35 
36 #include <stdio.h>
37 #include <limits.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <errno.h>
46 #include "pkgstrct.h"
47 #include "pkglib.h"
48 #include "pkglibmsgs.h"
49 #include "pkglocale.h"
50 
51 #define	ERR_CANT_READ_LCLPATH		"unable to read local pathname"
52 #define	ERR_BAD_VOLUME_NUMBER		"bad volume number"
53 #define	ERR_CANNOT_READ_PATHNAME_FIELD	"unable to read pathname field"
54 #define	ERR_CANNOT_READ_CONTENT_INFO	"unable to read content info"
55 #define	ERR_EXTRA_TOKENS_PRESENT	"extra tokens on input line"
56 #define	ERR_CANNOT_READ_CLASS_TOKEN	"unable to read class token"
57 #define	ERR_BAD_LINK_SPEC		"missing or invalid link specification"
58 #define	ERR_UNKNOWN_FTYPE		"unknown ftype"
59 #define	ERR_NO_LINKSOURCE		"no link source specified"
60 #define	ERR_CANNOT_READ_MM_DEVNUMS	"unable to read major/minor "\
61 					"device numbers"
62 static int	eatwhite(FILE *fp);
63 static int	getend(FILE *fp);
64 static int	getstr(FILE *fp, char *sep, int n, char *str);
65 static int	getnum(FILE *fp, int base, long *d, long bad);
66 static int	getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
67 static int	getvalmode(FILE *fp, mode_t *d, long bad, int map);
68 
69 static int	getendvfp(char **cp);
70 static void	findendvfp(char **cp);
71 static int	getstrvfp(char **cp, char *sep, int n, char *str);
72 static int	getvalmodevfp(char **cp, mode_t *d, long bad, int map);
73 int		getnumvfp(char **cp, int base, long *d, long bad);
74 int		getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
75 
76 static char	mypath[PATH_MAX];
77 static char	mylocal[PATH_MAX];
78 static int	mapmode = MAPNONE;
79 static char	*maptype = "";
80 static mode_t	d_mode = BADMODE;
81 static char	*d_owner = BADOWNER;
82 static char	*d_group = BADGROUP;
83 
84 /*
85  * These determine how gpkgmap() deals with mode, owner and group defaults.
86  * It is assumed that the owner and group arguments represent static fields
87  * which will persist until attrdefault() is called.
88  */
89 void
90 attrpreset(int mode, char *owner, char *group)
91 {
92 	d_mode = mode;
93 	d_owner = owner;
94 	d_group = group;
95 }
96 
97 void
98 attrdefault()
99 {
100 	d_mode = NOMODE;
101 	d_owner = NOOWNER;
102 	d_group = NOGROUP;
103 }
104 
105 /*
106  * This determines how gpkgmap() deals with environment variables in the
107  * mode, owner and group. Path is evaluated at a higher level based upon
108  * other circumstances.
109  */
110 void
111 setmapmode(int mode)
112 {
113 	if (mode >= 0 || mode <= 3) {
114 		mapmode = mode;
115 		if (mode == MAPBUILD)
116 			maptype = " build";
117 		else if (mode == MAPINSTALL)
118 			maptype = " install";
119 		else
120 			maptype = "";
121 	}
122 }
123 
124 /* This is the external query interface for mapmode. */
125 int
126 getmapmode(void)
127 {
128 	return (mapmode);
129 }
130 
131 /*
132  * Unpack the pkgmap or the contents file or whatever file is in that format.
133  * Based upon mapmode, environment parameters will be resolved for mode,
134  * owner and group.
135  */
136 
137 int
138 gpkgmap(struct cfent *ept, FILE *fp)
139 {
140 	int		c;
141 	boolean_t	first_char = B_TRUE;
142 
143 	setErrstr(NULL);
144 	ept->volno = 0;
145 	ept->ftype = BADFTYPE;
146 	(void) strcpy(ept->pkg_class, BADCLASS);
147 	ept->pkg_class_idx = -1;
148 	ept->path = NULL;
149 	ept->ainfo.local = NULL;
150 	/* default attributes were supplied, so don't reset */
151 	ept->ainfo.mode = d_mode;
152 	(void) strcpy(ept->ainfo.owner, d_owner);
153 	(void) strcpy(ept->ainfo.group, d_group);
154 	ept->ainfo.major = BADMAJOR;
155 	ept->ainfo.minor = BADMINOR;
156 	ept->cinfo.cksum = -1L;
157 	ept->cinfo.modtime = -1L;
158 	ept->cinfo.size = -1L;
159 
160 	ept->npkgs = 0;
161 
162 	if (!fp)
163 		return (-1);
164 readline:
165 	c = eatwhite(fp);
166 
167 	/*
168 	 * If the first character is not a digit, we assume that the
169 	 * volume number is 1.
170 	 */
171 	if (first_char && !isdigit(c)) {
172 		ept->volno = 1;
173 	}
174 	first_char = B_FALSE;
175 
176 	switch (c) {
177 	case EOF:
178 		return (0);
179 
180 	case '0':
181 	case '1':
182 	case '2':
183 	case '3':
184 	case '4':
185 	case '5':
186 	case '6':
187 	case '7':
188 	case '8':
189 	case '9':
190 		if (ept->volno) {
191 			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
192 			goto error;
193 		}
194 		do {
195 			ept->volno = (ept->volno*10)+c-'0';
196 			c = getc(fp);
197 		} while (isdigit(c));
198 		if (ept->volno == 0)
199 			ept->volno = 1;
200 
201 		goto readline;
202 
203 	case ':':
204 	case '#':
205 		(void) getend(fp);
206 		/*FALLTHRU*/
207 	case '\n':
208 		/*
209 		 * Since we are going to scan the next line,
210 		 * we need to reset volume number and first_char.
211 		 */
212 		ept->volno = 0;
213 		first_char = B_TRUE;
214 		goto readline;
215 
216 	case 'i':
217 		ept->ftype = (char)c;
218 		c = eatwhite(fp);
219 		/*FALLTHRU*/
220 	case '.':
221 	case '/':
222 		(void) ungetc(c, fp);
223 
224 		if (getstr(fp, "=", PATH_MAX, mypath)) {
225 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
226 			goto error;
227 		}
228 		ept->path = mypath;
229 		c = getc(fp);
230 		if (c == '=') {
231 			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
232 				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
233 				goto error;
234 			}
235 			ept->ainfo.local = mylocal;
236 		} else
237 			(void) ungetc(c, fp);
238 
239 		if (ept->ftype == 'i') {
240 			/* content info might exist */
241 			if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
242 			    BADCONT) &&
243 			    (getnum(fp, 10, (long *)&ept->cinfo.cksum,
244 			    BADCONT) ||
245 			    getnum(fp, 10, (long *)&ept->cinfo.modtime,
246 			    BADCONT))) {
247 				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
248 				goto error;
249 			}
250 		}
251 		if (getend(fp)) {
252 			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
253 			return (-1);
254 		}
255 		return (1);
256 
257 	case '?':
258 	case 'f':
259 	case 'v':
260 	case 'e':
261 	case 'l':
262 	case 's':
263 	case 'p':
264 	case 'c':
265 	case 'b':
266 	case 'd':
267 	case 'x':
268 		ept->ftype = (char)c;
269 		if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
270 			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
271 			goto error;
272 		}
273 		if (getstr(fp, "=", PATH_MAX, mypath)) {
274 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
275 			goto error;
276 		}
277 		ept->path = mypath;
278 
279 		c = getc(fp);
280 		if (c == '=') {
281 			/* local path */
282 			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
283 				if (ept->ftype == 's' || ept->ftype == 'l') {
284 					setErrstr(pkg_gt(ERR_READLINK));
285 				} else {
286 					setErrstr(
287 					    pkg_gt(ERR_CANT_READ_LCLPATH));
288 				}
289 				goto error;
290 			}
291 			ept->ainfo.local = mylocal;
292 		} else if (strchr("sl", ept->ftype)) {
293 			if ((c != EOF) && (c != '\n'))
294 				(void) getend(fp);
295 			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
296 			return (-1);
297 		} else
298 			(void) ungetc(c, fp);
299 		break;
300 
301 	default:
302 		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
303 error:
304 		(void) getend(fp);
305 		return (-1);
306 	}
307 
308 	if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
309 		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
310 		goto error;
311 	}
312 
313 	if (strchr("cb", ept->ftype)) {
314 		ept->ainfo.major = BADMAJOR;
315 		ept->ainfo.minor = BADMINOR;
316 		if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
317 		    getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR)) {
318 			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
319 			goto error;
320 		}
321 	}
322 
323 	/*
324 	 * Links and information files don't have attributes associated with
325 	 * them. The following either resolves potential variables or passes
326 	 * them through. Mode is tested for validity to some degree. BAD???
327 	 * is returned to indicate that no meaningful mode was provided. A
328 	 * higher authority will decide if that's OK or not. CUR??? means that
329 	 * the prototype file specifically requires a wildcard ('?') for
330 	 * that entry. We issue an error if attributes were entered wrong.
331 	 * We just return BAD??? if there was no entry at all.
332 	 */
333 	if (strchr("cbdxpfve", ept->ftype)) {
334 		int retval;
335 
336 		if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
337 		    (mapmode != MAPNONE))) == 1)
338 			goto end;	/* nothing else on the line */
339 		else if (retval == 2)
340 			goto error;	/* mode is too no good */
341 
342 		/* owner & group should be here */
343 		if ((retval = getstr(fp, NULL, ATRSIZ,
344 		    ept->ainfo.owner)) == 1)
345 			goto end;	/* no owner or group - warning */
346 		if (retval == -1) {
347 			setErrstr(pkg_gt(ERR_OWNTOOLONG));
348 			goto error;
349 		}
350 
351 		if ((retval = getstr(fp, NULL, ATRSIZ,
352 		    ept->ainfo.group)) == 1)
353 			goto end;	/* no group - warning */
354 		if (retval == -1) {
355 			setErrstr(pkg_gt(ERR_GRPTOOLONG));
356 			goto error;
357 		}
358 
359 		/* Resolve the parameters if required. */
360 		if (mapmode != MAPNONE) {
361 			if (mapvar(mapmode, ept->ainfo.owner)) {
362 				(void) snprintf(getErrbufAddr(),
363 				    getErrbufSize(),
364 				    pkg_gt(ERR_NOVAR),
365 				    maptype, ept->ainfo.owner);
366 				setErrstr(getErrbufAddr());
367 				goto error;
368 			}
369 			if (mapvar(mapmode, ept->ainfo.group)) {
370 				(void) snprintf(getErrbufAddr(),
371 				    getErrbufSize(), pkg_gt(ERR_NOVAR),
372 				    maptype, ept->ainfo.group);
373 				setErrstr(getErrbufAddr());
374 				goto error;
375 			}
376 		}
377 	}
378 
379 	if (strchr("ifve", ept->ftype)) {
380 		/* look for content description */
381 		if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
382 		    (getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
383 		    getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
384 			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
385 			goto error;
386 		}
387 	}
388 
389 	if (ept->ftype == 'i')
390 		goto end;
391 
392 end:
393 	if (getend(fp) && ept->pinfo) {
394 		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
395 		return (-1);
396 	}
397 
398 	return (1);
399 }
400 
401 /*
402  * Get and validate the mode attribute. This returns an error if
403  *	1. the mode string is too long
404  *	2. the mode string includes alpha characters
405  *	3. the mode string is not octal
406  *	4. mode string is an install parameter
407  *	5. mode is an unresolved build parameter and MAPBUILD is
408  *	   in effect.
409  * If the mode is a build parameter, it is
410  *	1. returned as is if MAPNONE is in effect
411  *	2. evaluated if MAPBUILD is in effect
412  *
413  * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
414  * time. At install time we just fix a mode with bad bits set by
415  * setting it to CURMODE. This should be an error in a few releases
416  * (2.8 maybe) but faulty modes are so common in existing packages
417  * that this is a reasonable exception. -- JST 1994-11-9
418  *
419  * RETURNS
420  *	0 if mode is being returned as a valid value
421  *	1 if no attributes are present on the line
422  *	2 if there was a fundamental error
423  */
424 static int
425 getvalmode(FILE *fp, mode_t *d, long bad, int map)
426 {
427 	char tempmode[20];
428 	mode_t tempmode_t;
429 	int retval;
430 
431 	if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
432 		return (1);
433 	else if (retval == -1) {
434 		setErrstr(pkg_gt(ERR_MODELONG));
435 		return (2);
436 	} else {
437 		/*
438 		 * If it isn't a '?' (meaning go with whatever mode is
439 		 * there), validate the mode and convert it to a mode_t. The
440 		 * "bad" variable here is a misnomer. It doesn't necessarily
441 		 * mean bad.
442 		 */
443 		if (tempmode[0] == '?') {
444 			*d = WILDCARD;
445 		} else {
446 			/*
447 			 * Mode may not be an install parameter or a
448 			 * non-build parameter.
449 			 */
450 			if (tempmode[0] == '$' &&
451 			    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
452 				setErrstr(pkg_gt(ERR_IMODE));
453 				return (2);
454 			}
455 
456 			if ((map) && (mapvar(mapmode, tempmode))) {
457 				(void) snprintf(getErrbufAddr(),
458 				    getErrbufSize(),
459 				    pkg_gt(ERR_NOVAR),
460 				    maptype, tempmode);
461 				setErrstr(getErrbufAddr());
462 				return (2);
463 			}
464 
465 
466 			if (tempmode[0] == '$') {
467 				*d = BADMODE;	/* may be a problem */
468 			} else {
469 				/*
470 				 * At this point it's supposed to be
471 				 * something we can convert to a number.
472 				 */
473 				int n = 0;
474 
475 				/*
476 				 * We reject it if it contains nonnumbers or
477 				 * it's not octal.
478 				 */
479 				while (tempmode[n] && !isspace(tempmode[n])) {
480 					if (!isdigit(tempmode[n])) {
481 						setErrstr(
482 						    pkg_gt(ERR_MODEALPHA));
483 						return (2);
484 					}
485 
486 					if (strchr("89abcdefABCDEF",
487 					    tempmode[n])) {
488 						setErrstr(
489 						    pkg_gt(ERR_BASEINVAL));
490 						return (2);
491 					}
492 					n++;
493 				}
494 
495 				tempmode_t = strtol(tempmode, NULL, 8);
496 
497 				/*
498 				 * We reject it if it contains inappropriate
499 				 * bits.
500 				 */
501 				if (tempmode_t & ~(S_IAMB |
502 				    S_ISUID | S_ISGID | S_ISVTX)) {
503 					if (mapmode != MAPBUILD) {
504 						tempmode_t = bad;
505 					} else {
506 						setErrstr(pkg_gt(ERR_MODEBITS));
507 						return (2);
508 					}
509 				}
510 				*d = tempmode_t;
511 			}
512 		}
513 		return (0);
514 	}
515 }
516 
517 static int
518 getnum(FILE *fp, int base, long *d, long bad)
519 {
520 	int c, b;
521 
522 	/* leading white space ignored */
523 	c = eatwhite(fp);
524 	if (c == '?') {
525 		*d = bad;
526 		return (0);
527 	}
528 
529 	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
530 		(void) ungetc(c, fp);
531 		return (1);
532 	}
533 
534 	*d = 0;
535 	while (isdigit(c)) {
536 		b = (c & 017);
537 		if (b >= base)
538 			return (2);
539 		*d = (*d * base) + b;
540 		c = getc(fp);
541 	}
542 	(void) ungetc(c, fp);
543 	return (0);
544 }
545 
546 static int
547 getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
548 {
549 	int c, b;
550 
551 	/* leading white space ignored */
552 	c = eatwhite(fp);
553 	if (c == '?') {
554 		*d = bad;
555 		return (0);
556 	}
557 
558 	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
559 		(void) ungetc(c, fp);
560 		return (1);
561 	}
562 
563 	*d = 0;
564 	while (isdigit(c)) {
565 		b = (c & 017);
566 		if (b >= base)
567 			return (2);
568 		*d = (*d * base) + b;
569 		c = getc(fp);
570 	}
571 	(void) ungetc(c, fp);
572 	return (0);
573 }
574 
575 /*
576  *  Get a string from the file. Returns
577  *	0 if all OK
578  *	1 if nothing there
579  *	-1 if string is too long
580  */
581 static int
582 getstr(FILE *fp, char *sep, int n, char *str)
583 {
584 	int c;
585 
586 	/* leading white space ignored */
587 	c = eatwhite(fp);
588 	if ((c == EOF) || (c == '\n')) {
589 		(void) ungetc(c, fp);
590 		return (1); /* nothing there */
591 	}
592 
593 	/* fill up string until space, tab, or separator */
594 	while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
595 		if (n-- < 1) {
596 			*str = '\0';
597 			return (-1); /* too long */
598 		}
599 		*str++ = (char)c;
600 		c = getc(fp);
601 		if ((c == EOF) || (c == '\n'))
602 			break; /* no more on this line */
603 	}
604 	*str = '\0';
605 	(void) ungetc(c, fp);
606 
607 	return (0);
608 }
609 
610 static int
611 getend(FILE *fp)
612 {
613 	int c;
614 	int n;
615 
616 	n = 0;
617 	do {
618 		if ((c = getc(fp)) == EOF)
619 			return (n);
620 		if (!isspace(c))
621 			n++;
622 	} while (c != '\n');
623 	return (n);
624 }
625 
626 static int
627 eatwhite(FILE *fp)
628 {
629 	int c;
630 
631 	/* this test works around a side effect of getc() */
632 	if (feof(fp))
633 		return (EOF);
634 	do {
635 		c = getc(fp);
636 	} while ((c == ' ') || (c == '\t'));
637 	return (c);
638 }
639 
640 int
641 gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
642 {
643 	int		c;
644 	boolean_t	first_char = B_TRUE;
645 	(void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
646 	(void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
647 	(void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
648 
649 	setErrstr(NULL);
650 	ept->volno = 0;
651 	ept->ftype = BADFTYPE;
652 	ept->pkg_class_idx = -1;
653 	ept->path = NULL;
654 	ept->ainfo.local = NULL;
655 	ept->ainfo.mode = d_mode;
656 	ept->ainfo.major = BADMAJOR;
657 	ept->ainfo.minor = BADMINOR;
658 	ept->cinfo.cksum = (-1L);
659 	ept->cinfo.modtime = (-1L);
660 	ept->cinfo.size = (-1L);
661 
662 	ept->npkgs = 0;
663 
664 	/* return error if no vfp specified */
665 
666 	if (vfp == (VFP_T *)NULL) {
667 		return (-1);
668 	}
669 
670 readline:
671 	while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
672 		;
673 
674 	/*
675 	 * If the first character is not a digit, we assume that the
676 	 * volume number is 1.
677 	 */
678 	if (first_char && !isdigit(c)) {
679 		ept->volno = 1;
680 	}
681 	first_char = B_FALSE;
682 
683 	/*
684 	 * In case of hsfs the zero-padding of partial pages
685 	 * returned by mmap is not done properly. A separate bug has been filed
686 	 * on this.
687 	 */
688 
689 	if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
690 		return (0);
691 	}
692 
693 	switch (c) {
694 	case '\0':
695 		return (0);
696 
697 	case '0':
698 	case '1':
699 	case '2':
700 	case '3':
701 	case '4':
702 	case '5':
703 	case '6':
704 	case '7':
705 	case '8':
706 	case '9':
707 		if (ept->volno) {
708 			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
709 			goto error;
710 		}
711 		do {
712 			ept->volno = (ept->volno*10)+c-'0';
713 			c = vfpGetc(vfp);
714 		} while (isdigit(c));
715 		if (ept->volno == 0) {
716 			ept->volno = 1;
717 		}
718 
719 		goto readline;
720 
721 	case ':':
722 	case '#':
723 		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
724 		/*FALLTHRU*/
725 	case '\n':
726 		/*
727 		 * Since we are going to scan the next line,
728 		 * we need to reset volume number and first_char.
729 		 */
730 		ept->volno = 0;
731 		first_char = B_TRUE;
732 		goto readline;
733 
734 	case 'i':
735 		ept->ftype = (char)c;
736 		while (((c = vfpGetcNoInc(vfp)) != '\0') &&
737 		    (isspace(vfpGetc(vfp))))
738 			;
739 		/*FALLTHRU*/
740 	case '.':
741 	case '/':
742 		vfpDecCurrPtr(vfp);
743 
744 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
745 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
746 			goto error;
747 		}
748 		ept->path = mypath;
749 		c = vfpGetc(vfp);
750 		if (c == '=') {
751 			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
752 			    mylocal)) {
753 				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
754 				goto error;
755 			}
756 			ept->ainfo.local = mylocal;
757 		} else {
758 			vfpDecCurrPtr(vfp);
759 		}
760 
761 		if (ept->ftype == 'i') {
762 			/* content info might exist */
763 			if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
764 			    (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
765 			    (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
766 			    (long *)&ept->cinfo.cksum, BADCONT) ||
767 			    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
768 			    (long *)&ept->cinfo.modtime, BADCONT))) {
769 				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
770 				goto error;
771 			}
772 		}
773 
774 		if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
775 			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
776 			return (-1);
777 		}
778 		return (1);
779 
780 	case '?':
781 	case 'f':
782 	case 'v':
783 	case 'e':
784 	case 'l':
785 	case 's':
786 	case 'p':
787 	case 'c':
788 	case 'b':
789 	case 'd':
790 	case 'x':
791 		ept->ftype = (char)c;
792 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
793 		    CLSSIZ, ept->pkg_class)) {
794 			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
795 			goto error;
796 		}
797 		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
798 			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
799 			goto error;
800 		}
801 		ept->path = mypath;
802 
803 		c = vfpGetc(vfp);
804 		if (c == '=') {
805 			/* local path */
806 			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
807 			    PATH_MAX, mylocal)) {
808 				if (ept->ftype == 's' || ept->ftype == 'l') {
809 					setErrstr(pkg_gt(ERR_READLINK));
810 				} else {
811 					setErrstr(
812 					    pkg_gt(ERR_CANT_READ_LCLPATH));
813 				}
814 				goto error;
815 			}
816 			ept->ainfo.local = mylocal;
817 		} else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
818 			if ((c != '\0') && (c != '\n'))
819 				(void) findendvfp(&vfpGetCurrCharPtr(vfp));
820 			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
821 			return (-1);
822 		} else {
823 			vfpDecCurrPtr(vfp);
824 		}
825 		break;
826 
827 	default:
828 		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
829 error:
830 		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
831 		return (-1);
832 	}
833 
834 	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
835 	    (ept->ainfo.local == NULL)) {
836 		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
837 		goto error;
838 	}
839 
840 	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
841 		ept->ainfo.major = BADMAJOR;
842 		ept->ainfo.minor = BADMINOR;
843 
844 		if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
845 		    (long *)&ept->ainfo.major, BADMAJOR) ||
846 		    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
847 		    (long *)&ept->ainfo.minor, BADMINOR)) {
848 			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
849 			goto error;
850 		}
851 	}
852 
853 	/*
854 	 * Links and information files don't have attributes associated with
855 	 * them. The following either resolves potential variables or passes
856 	 * them through. Mode is tested for validity to some degree. BAD???
857 	 * is returned to indicate that no meaningful mode was provided. A
858 	 * higher authority will decide if that's OK or not. CUR??? means that
859 	 * the prototype file specifically requires a wildcard ('?') for
860 	 * that entry. We issue an error if attributes were entered wrong.
861 	 * We just return BAD??? if there was no entry at all.
862 	 */
863 	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
864 	    (ept->ftype == 'b') || (ept->ftype == 'p') ||
865 	    (ept->ftype == 'f') || (ept->ftype == 'v') ||
866 	    (ept->ftype == 'e')) {
867 		int retval;
868 
869 		retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
870 		    &(ept->ainfo.mode), CURMODE, (mapmode != MAPNONE));
871 
872 		if (retval == 1) {
873 			goto end;	/* nothing else on the line */
874 		} else if (retval == 2) {
875 			goto error;	/* mode is too no good */
876 		}
877 
878 		/* owner & group should be here */
879 		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
880 		    ept->ainfo.owner)) == 1)
881 			goto end;	/* no owner or group - warning */
882 		if (retval == -1) {
883 			setErrstr(pkg_gt(ERR_OWNTOOLONG));
884 			goto error;
885 		}
886 
887 		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
888 		    ept->ainfo.group)) == 1)
889 			goto end;	/* no group - warning */
890 		if (retval == -1) {
891 			setErrstr(pkg_gt(ERR_GRPTOOLONG));
892 			goto error;
893 		}
894 
895 		/* Resolve the parameters if required. */
896 		if (mapmode != MAPNONE) {
897 			if (mapvar(mapmode, ept->ainfo.owner)) {
898 				(void) snprintf(getErrbufAddr(),
899 				    getErrbufSize(), pkg_gt(ERR_NOVAR),
900 				    maptype, ept->ainfo.owner);
901 				setErrstr(getErrbufAddr());
902 				goto error;
903 			}
904 			if (mapvar(mapmode, ept->ainfo.group)) {
905 				(void) snprintf(getErrbufAddr(),
906 				    getErrbufSize(), pkg_gt(ERR_NOVAR),
907 				    maptype, ept->ainfo.group);
908 				setErrstr(getErrbufAddr());
909 				goto error;
910 			}
911 		}
912 	}
913 
914 	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
915 	    (ept->ftype == 'v') || (ept->ftype == 'e')) {
916 		/* look for content description */
917 		if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
918 		    (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
919 		    (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
920 		    (long *)&ept->cinfo.cksum, BADCONT) ||
921 		    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
922 		    (long *)&ept->cinfo.modtime, BADCONT))) {
923 			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
924 			goto error;
925 		}
926 	}
927 
928 	if (ept->ftype == 'i')
929 		goto end;
930 
931 end:
932 	if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
933 		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
934 		return (-1);
935 	}
936 
937 	return (1);
938 }
939 
940 /*
941  * Get and validate the mode attribute. This returns an error if
942  *	1. the mode string is too long
943  *	2. the mode string includes alpha characters
944  *	3. the mode string is not octal
945  *	4. mode string is an install parameter
946  *	5. mode is an unresolved build parameter and MAPBUILD is
947  *	   in effect.
948  * If the mode is a build parameter, it is
949  *	1. returned as is if MAPNONE is in effect
950  *	2. evaluated if MAPBUILD is in effect
951  *
952  * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
953  * time. At install time we just fix a mode with bad bits set by
954  * setting it to CURMODE. This should be an error in a few releases
955  * (2.8 maybe) but faulty modes are so common in existing packages
956  * that this is a reasonable exception. -- JST 1994-11-9
957  *
958  * RETURNS
959  *	0 if mode is being returned as a valid value
960  *	1 if no attributes are present on the line
961  *	2 if there was a fundamental error
962  */
963 static int
964 getvalmodevfp(char **cp, mode_t *d, long bad, int map)
965 {
966 	char	tempmode[ATRSIZ+1];
967 	mode_t	tempmode_t;
968 	int	retval;
969 	int	n;
970 
971 	if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
972 		return (1);
973 	} else if (retval == -1) {
974 		setErrstr(pkg_gt(ERR_MODELONG));
975 		return (2);
976 	}
977 
978 	/*
979 	 * If it isn't a '?' (meaning go with whatever mode is
980 	 * there), validate the mode and convert it to a mode_t. The
981 	 * "bad" variable here is a misnomer. It doesn't necessarily
982 	 * mean bad.
983 	 */
984 	if (tempmode[0] == '?') {
985 		*d = WILDCARD;
986 		return (0);
987 	}
988 
989 	/*
990 	 * Mode may not be an install parameter or a
991 	 * non-build parameter.
992 	 */
993 
994 	if (tempmode[0] == '$' &&
995 	    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
996 		setErrstr(pkg_gt(ERR_IMODE));
997 		return (2);
998 	}
999 
1000 	if ((map) && (mapvar(mapmode, tempmode))) {
1001 		(void) snprintf(getErrbufAddr(), getErrbufSize(),
1002 		    pkg_gt(ERR_NOVAR), maptype, tempmode);
1003 		setErrstr(getErrbufAddr());
1004 		return (2);
1005 	}
1006 
1007 	if (tempmode[0] == '$') {
1008 		*d = BADMODE;	/* may be a problem */
1009 		return (0);
1010 	}
1011 
1012 	/* it's supposed to be something we can convert to a number */
1013 
1014 	n = 0;
1015 
1016 	/* reject it if it contains nonnumbers or it's not octal */
1017 
1018 	while (tempmode[n] && !isspace(tempmode[n])) {
1019 		if (!isdigit(tempmode[n])) {
1020 			setErrstr(pkg_gt(ERR_MODEALPHA));
1021 			return (2);
1022 		}
1023 
1024 		if (strchr("89abcdefABCDEF", tempmode[n])) {
1025 			setErrstr(pkg_gt(ERR_BASEINVAL));
1026 			return (2);
1027 		}
1028 		n++;
1029 	}
1030 
1031 	tempmode_t = strtol(tempmode, NULL, 8);
1032 
1033 	/*
1034 	 * We reject it if it contains inappropriate
1035 	 * bits.
1036 	 */
1037 	if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
1038 		if (mapmode == MAPBUILD) {
1039 			setErrstr(pkg_gt(ERR_MODEBITS));
1040 			return (2);
1041 		}
1042 		tempmode_t = bad;
1043 	}
1044 
1045 	*d = tempmode_t;
1046 
1047 	return (0);
1048 }
1049 
1050 int
1051 getnumvfp(char **cp, int base, long *d, long bad)
1052 {
1053 	int c;
1054 	char	*p = *cp;
1055 
1056 	if (*p == '\0') {
1057 		return (0);
1058 	}
1059 
1060 	/* leading white space ignored */
1061 	while (((c = *p) != '\0') && (isspace(*p++)))
1062 		;
1063 	if (c == '?') {
1064 		*d = bad;
1065 		*cp = p;
1066 		return (0);
1067 	}
1068 
1069 	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
1070 		p--;
1071 		*cp = p;
1072 		return (1);
1073 	}
1074 
1075 	*d = 0;
1076 	while (isdigit(c)) {
1077 		*d = (*d * base) + (c & 017);
1078 		c = *p++;
1079 	}
1080 	p--;
1081 	*cp = p;
1082 	return (0);
1083 }
1084 
1085 int
1086 getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
1087 {
1088 	int c;
1089 	char	*p = *cp;
1090 
1091 	if (*p == '\0') {
1092 		return (0);
1093 	}
1094 
1095 	/* leading white space ignored */
1096 	while (((c = *p) != '\0') && (isspace(*p++)))
1097 		;
1098 	if (c == '?') {
1099 		*d = bad;
1100 		*cp = p;
1101 		return (0);
1102 	}
1103 
1104 	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
1105 		p--;
1106 		*cp = p;
1107 		return (1);
1108 	}
1109 
1110 	*d = 0;
1111 	while (isdigit(c)) {
1112 		*d = (*d * base) + (c & 017);
1113 		c = *p++;
1114 	}
1115 	p--;
1116 	*cp = p;
1117 	return (0);
1118 }
1119 
1120 static int
1121 getstrvfp(char **cp, char *sep, int n, char *str)
1122 {
1123 	char	delims[256];
1124 	int	c;
1125 	char	*p = *cp;
1126 	char	*p1;
1127 	size_t	len;
1128 
1129 	if (*p == '\0') {
1130 		return (1);
1131 	}
1132 
1133 	/* leading white space ignored */
1134 
1135 	while (((c = *p) != '\0') && (isspace(*p++)))
1136 		;
1137 	if ((c == '\0') || (c == '\n')) {
1138 		p--;
1139 		*cp = p;
1140 		return (1); /* nothing there */
1141 	}
1142 
1143 	p--;
1144 
1145 	/* generate complete list of delimiters to scan for */
1146 
1147 	(void) strlcpy(delims, " \t\n", sizeof (delims));
1148 	if ((sep != (char *)NULL) && (*sep != '\0')) {
1149 		(void) strlcat(delims, sep, sizeof (delims));
1150 	}
1151 
1152 	/* compute length based on delimiter found or not */
1153 
1154 	p1 = strpbrk(p, delims);
1155 	if (p1 == (char *)NULL) {
1156 		len = strlen(p);
1157 	} else {
1158 		len = (ptrdiff_t)p1 - (ptrdiff_t)p;
1159 	}
1160 
1161 	/* if string will fit in result buffer copy string and return success */
1162 
1163 	if (len < n) {
1164 		(void) memcpy(str, p, len);
1165 		str[len] = '\0';
1166 		p += len;
1167 		*cp = p;
1168 		return (0);
1169 	}
1170 
1171 	/* result buffer too small; copy partial string, return error */
1172 	(void) memcpy(str, p, n-1);
1173 	str[n-1] = '\0';
1174 	p += n;
1175 	*cp = p;
1176 	return (-1);
1177 }
1178 
1179 /*
1180  * Name:	getendvfp
1181  * Description:	Locate the end of the current line given a pointer into a buffer
1182  *		containing characters that is null terminated.
1183  * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
1184  * Returns:	int == 0 -- no non-space characters preceeded the newline
1185  *		    != 0 -- one or more non-space characters preceeded newline
1186  * Effects:	cp is updated to point to the first character PAST the first new
1187  *		line character found. If no newline character is found, cp is
1188  *		updated to point to the '\0' at the end of the buffer.
1189  */
1190 
1191 static int
1192 getendvfp(char **cp)
1193 {
1194 	int	n;
1195 	char	*p = *cp;
1196 
1197 	n = 0;
1198 
1199 	/* if at end of buffer return no more characters left */
1200 
1201 	if (*p == '\0') {
1202 		return (0);
1203 	}
1204 
1205 	/* find the first null or end of line character */
1206 
1207 	while ((*p != '\0') && (*p != '\n')) {
1208 		if (n == 0) {
1209 			if (!isspace(*p)) {
1210 				n++;
1211 			}
1212 		}
1213 		p++;
1214 	}
1215 
1216 	/* if at newline, increment pointer to first character past newline */
1217 
1218 	if (*p == '\n') {
1219 		p++;
1220 	}
1221 
1222 	/* set return pointer to null or first character past newline */
1223 
1224 	*cp = p;
1225 
1226 	/* return space/nospace indicator */
1227 
1228 	return (n);
1229 }
1230 
1231 /*
1232  * Name:	findendvfp
1233  * Description:	Locate the end of the current line given a pointer into a buffer
1234  *		containing characters that is null terminated.
1235  * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
1236  * Returns:	none
1237  * Effects:	cp is updated to point to the first character PAST the first new
1238  *		line character found. If no newline character is found, cp is
1239  *		updated to point to the '\0' at the end of the buffer.
1240  */
1241 
1242 static void
1243 findendvfp(char **cp)
1244 {
1245 	char	*p1;
1246 	char	*p = *cp;
1247 
1248 	/* if at end of buffer return no more characters left */
1249 
1250 	if (*p == '\0') {
1251 		return;
1252 	}
1253 
1254 	/* find the end of the line */
1255 
1256 	p1 = strchr(p, '\n');
1257 	if (p1 != (char *)NULL) {
1258 		*cp = ++p1;
1259 		return;
1260 	}
1261 
1262 	/* no newline found - point to null terminator */
1263 
1264 	*cp = strchr(p, '\0');
1265 }
1266