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