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