xref: /illumos-gate/usr/src/lib/libpkg/common/verify.c (revision 5c51f124)
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 <utime.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/statvfs.h>
41 #include <grp.h>
42 #include <pwd.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <stdarg.h>
46 #include <fcntl.h>
47 #include <sys/mkdev.h>
48 #include "pkgstrct.h"
49 #include "pkglib.h"
50 #include "pkglibmsgs.h"
51 #include "pkglocale.h"
52 
53 #define	WDMSK	0xFFFF
54 #define	DATEFMT	"%D %r"
55 #define	LONG_BOUNDARY	((sizeof (unsigned long))-1)
56 #define	CHUNK	1024*1024
57 
58 static char	theErrBuf[PATH_MAX+512] = {'\0'};
59 static char	*theErrStr = NULL;
60 
61 /* checksum disable switch */
62 static int	enable_checksum = 1;
63 
64 /* attribute disable flag */
65 static int	disable_attributes = 0;
66 
67 /* non-ABI symlinks supported */
68 static int	nonabi_symlinks;
69 
70 /*
71  * forward declarations
72  */
73 
74 static int	clear_target(char *path, char *ftype, int is_a_dir);
75 
76 unsigned	long compute_checksum(int *r_err, char *path);
77 
78 /* union used to generate checksum */
79 typedef union hilo {
80 	struct part {
81 		uint16_t hi;
82 		uint16_t lo;
83 	} hl;
84 	uint32_t	lg;
85 } CHECKSUM_T;
86 
87 /*PRINTFLIKE1*/
88 static void
89 reperr(char *fmt, ...)
90 {
91 	char	*pt;
92 	ssize_t	ptln;
93 	va_list	ap;
94 	int	n;
95 
96 	if (fmt == (char *)NULL) {
97 		theErrBuf[0] = '\0';
98 	} else {
99 		if (n = strlen(theErrBuf)) {
100 			pt = theErrBuf + n;
101 			*pt++ = '\n';
102 			*pt = '\0';
103 			ptln = sizeof (theErrBuf)-n;
104 		} else {
105 			pt = theErrBuf;
106 			ptln = sizeof (theErrBuf);
107 		}
108 		va_start(ap, fmt);
109 		/* LINTED variable format specifier to vsnprintf() */
110 		(void) vsnprintf(pt, ptln, fmt, ap);
111 		va_end(ap);
112 	}
113 }
114 
115 /*
116  * Name:	cverify
117  * Description:	This function verifies and (if fix > 0) fixes the contents
118  *		of the file at the path provided
119  * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
120  *		ftype - single character "type" the entry is supposed to be
121  *		path - path to file
122  *		cinfo - content info structure representing the contents
123  *			the entry is supposed to contain
124  *		allow_checksum - determine if checksumming should be disabled:
125  *		 == 0 - do not perform checksum ever - override enable_checksum.
126  *		 != 0 - use the default checksum flag "enable_checksum" to
127  *			determine if checksumming should be done.
128  * NOTE:	modification and creation times can be repaired; the contents
129  *		of the file cannot be corrected if the checksum indicates that
130  *		the contents are not correct - VE_CONT will be returned in this
131  *		case.
132  * Possible return values:
133  * - 0 = successful
134  * - VE_EXIST = path name does not exist
135  * - VE_FTYPE = path file type is not recognized, is not supported,
136  *		or is not what was expected
137  * - VE_ATTR = path mode/group/user is not what was expected
138  * - VE_CONT = mod time/link target/major/minor/size/file system type/current
139  *		directory is not what was expected
140  * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
141  *		chown failed
142  */
143 
144 int
145 cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
146 	int allow_checksum)
147 {
148 	struct stat	status; 	/* file status buffer */
149 	struct utimbuf	times;
150 	unsigned long	mycksum;
151 	int		setval, retcode;
152 	char		tbuf1[512];
153 	char		tbuf2[512];
154 	int		cksumerr;
155 
156 	setval = (*ftype == '?');
157 	retcode = 0;
158 	reperr(NULL);
159 
160 	if (stat(path, &status) < 0) {
161 		reperr(pkg_gt(ERR_EXIST));
162 		return (VE_EXIST);
163 	}
164 
165 	/* -1	requires modtimes to be the same */
166 	/*  0   reports modtime failure */
167 	/*  1   fixes modtimes */
168 
169 	if (setval || (cinfo->modtime == BADCONT)) {
170 		cinfo->modtime = status.st_mtime;
171 	} else if (status.st_mtime != cinfo->modtime) {
172 		if (fix > 0) {
173 			/* reset times on the file */
174 			times.actime = cinfo->modtime;
175 			times.modtime = cinfo->modtime;
176 			if (utime(path, &times)) {
177 				reperr(pkg_gt(ERR_MODFAIL));
178 				retcode = VE_FAIL;
179 			}
180 		} else if (fix < 0) {
181 			/* modtimes must be the same */
182 			if (strftime(tbuf1, sizeof (tbuf1), DATEFMT,
183 				localtime(&cinfo->modtime)) == 0) {
184 				reperr(pkg_gt(ERR_MEM));
185 			}
186 			if (strftime(tbuf2, sizeof (tbuf2), DATEFMT,
187 				localtime(&status.st_mtime)) == 0) {
188 				reperr(pkg_gt(ERR_MEM));
189 			}
190 			reperr(pkg_gt(ERR_MTIME), tbuf1, tbuf2);
191 			retcode = VE_CONT;
192 		}
193 	}
194 
195 	if (setval || (cinfo->size == (fsblkcnt_t)BADCONT)) {
196 		cinfo->size = status.st_size;
197 	} else if (status.st_size != cinfo->size) {
198 		if (!retcode) {
199 			retcode = VE_CONT;
200 		}
201 		reperr(pkg_gt(ERR_SIZE), cinfo->size, status.st_size);
202 	}
203 
204 	cksumerr = 0;
205 
206 	/*
207 	 * see if checksumming should be done: if checksumming is allowed,
208 	 * and checksumming is enabled, then checksum the file.
209 	 */
210 
211 	/* return if no need to compute checksum */
212 
213 	if ((allow_checksum == 0) || (enable_checksum == 0)) {
214 		return (retcode);
215 	}
216 
217 	/* compute checksum */
218 
219 	mycksum = compute_checksum(&cksumerr, path);
220 
221 	/* set value if not set or if checksum cannot be computed */
222 
223 	if (setval || (cinfo->cksum == BADCONT)) {
224 		cinfo->cksum = mycksum;
225 		return (retcode);
226 	}
227 
228 	/* report / return error if checksums mismatch or there is an error */
229 
230 	if ((mycksum != cinfo->cksum) || cksumerr) {
231 		if (!retcode) {
232 			retcode = VE_CONT;
233 		}
234 		if (!cksumerr) {
235 			reperr(pkg_gt(ERR_CKSUM), cinfo->cksum, mycksum);
236 		}
237 	}
238 
239 	return (retcode);
240 }
241 
242 /*
243  * Name:	compute_checksum
244  * Description:	generate checksum for specified file
245  * Arguments:	r_cksumerr (int *) [RO, *RW]
246  *			- pointer to integer that is set on return to:
247  *				== 0 - no error occurred
248  *				!= 0 - error occurred
249  *		a_path (char *) [RO, *RO]
250  *			- pointer to string representing path to file to
251  *			  generate checksum of
252  * Returns:	unsigned long - results:
253  *			- If *r_cksumerr == 0, checksum of specified file
254  *			- If *r_cksumerr != 0, undefined
255  */
256 unsigned long
257 compute_checksum(int *r_cksumerr, char *a_path)
258 {
259 	CHECKSUM_T	suma;	/* to split four-bytes into 2 two-byte values */
260 	CHECKSUM_T	tempa;
261 	int		fd;
262 	uint32_t	lg;	/* running checksum value */
263 	uint32_t	buf[CHUNK/4]; /* to read CHUNK bytes */
264 	uint32_t	lsavhi;	/* high order two-bytes of four-byte checksum */
265 	uint32_t	lsavlo;	/* low order two-bytes of four-byte checksum */
266 	int		leap = sizeof (uint32_t);
267 	int		notyet = 0;
268 	int		nread;
269 	struct stat64	sbuf;
270 
271 	/* reset error flag */
272 	*r_cksumerr = 0;
273 
274 	/* open file and obtain -> where file is mapped/read */
275 	if ((fd = open(a_path, O_RDONLY)) < 0) {
276 		*r_cksumerr = 1;
277 		reperr(pkg_gt(ERR_NO_CKSUM));
278 		perror(ERR_NO_CKSUM);
279 		return (0);
280 	}
281 
282 	if (fstat64(fd, &sbuf) != 0) {
283 		*r_cksumerr = 1;
284 		reperr(pkg_gt(ERR_NO_CKSUM));
285 		perror(ERR_NO_CKSUM);
286 		return (0);
287 	}
288 
289 	/* initialize checksum value */
290 	lg = 0;
291 
292 	/*
293 	 * Read CHUNK bytes off the file at a time; Read size of long bytes
294 	 * from memory at a time and process them.
295 	 * If last read, then read remnant bytes and process individually.
296 	 */
297 	errno = 0;
298 	while ((nread = read(fd, (void*)buf,
299 		    (sbuf.st_size < CHUNK) ? sbuf.st_size : CHUNK)) > 0) {
300 		uchar_t *s;
301 		uint32_t *p = buf;
302 
303 		notyet = nread % leap;
304 		nread -= notyet;
305 
306 		for (; nread > 0; nread -= leap) {
307 			lg += ((((*p)>>24)&0xFF) & WDMSK);
308 			lg += ((((*p)>>16)&0xFF) & WDMSK);
309 			lg += ((((*p)>>8)&0xFF) & WDMSK);
310 			lg += (((*p)&0xFF) & WDMSK);
311 			p++;
312 		}
313 		s = (uchar_t *)p;
314 		/* leftover bytes less than four in number */
315 		while (notyet--)
316 			lg += (((uint32_t)(*s++)) & WDMSK);
317 	}
318 
319 	/* wind up */
320 	(void) close(fd);
321 
322 	/* compute checksum components */
323 	suma.lg = lg;
324 	tempa.lg = (suma.hl.lo & WDMSK) + (suma.hl.hi & WDMSK);
325 	lsavhi = (uint32_t)tempa.hl.hi;
326 	lsavlo = (uint32_t)tempa.hl.lo;
327 
328 	/* return final checksum value */
329 	return (lsavhi+lsavlo);
330 }
331 
332 static 	struct stat	status; 	/* file status buffer */
333 static  struct statvfs	vfsstatus;	/* filesystem status buffer */
334 
335 /*
336  * Remove the thing that's currently in place so we can put down the package
337  * object. If we're replacing a directory with a directory, leave it alone.
338  * Returns 1 if all OK and 0 if failed.
339  */
340 static int
341 clear_target(char *path, char *ftype, int is_a_dir)
342 {
343 	int retcode = 1;
344 
345 	if (is_a_dir) {	/* if there's a directory there already ... */
346 		/* ... and this isn't, ... */
347 		if ((*ftype != 'd') && (*ftype != 'x')) {
348 			if (rmdir(path)) {	/* try to remove it. */
349 				reperr(pkg_gt(ERR_RMDIR), path);
350 				retcode = 0;
351 			}
352 		}
353 	} else {
354 		if (remove(path)) {
355 			if (errno != ENOENT) {
356 				retcode = 0;	/* It didn't work. */
357 			}
358 		}
359 	}
360 
361 	return (retcode);
362 }
363 
364 /*
365  * Name:	averify
366  * Description:	This function verifies and (if fix > 0) fixes the attributes
367  *		of the file at the path provided.
368  * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
369  *		ftype - single character "type" the entry is supposed to be
370  *		path - path to file
371  *		ainfo - attribute info structure representing the attributes
372  *			the entry is supposed to be
373  * NOTE:	attributes are links and permissions
374  * Possible return values:
375  * - 0 = successful
376  * - VE_EXIST = path name does not exist
377  * - VE_FTYPE = path file type is not recognized, is not supported,
378  *		or is not what was expected
379  * - VE_ATTR = path mode/group/user is not what was expected
380  * - VE_CONT = mod time/link target/major/minor/size/file system type/current
381  *		directory is not what was expected
382  * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
383  *		chown failed
384  */
385 int
386 averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
387 {
388 	struct group	*grp; 	/* group entry buffer */
389 	struct passwd	*pwd;
390 	int		n;
391 	int		setval;
392 	int		uid, gid;
393 	int		dochown;
394 	int		retcode;
395 	int		statError = 0;
396 	int		targ_is_dir = 0;	/* replacing a directory */
397 	char		myftype;
398 	char		buf[PATH_MAX];
399 	ino_t		my_ino;
400 	dev_t		my_dev;
401 	char 		cwd[MAXPATHLEN];
402 	char 		*cd;
403 	char 		*c;
404 
405 	setval = (*ftype == '?');
406 	retcode = 0;
407 	reperr(NULL);
408 
409 	if (get_disable_attribute_check()) {
410 		return (0);
411 	}
412 
413 	if (*ftype == 'l') {
414 		if (stat(path, &status) < 0) {
415 			retcode = VE_EXIST;
416 			reperr(pkg_gt(ERR_EXIST));
417 		}
418 
419 		my_ino = status.st_ino;
420 		my_dev = status.st_dev;
421 
422 		/* Get copy of the current working directory */
423 		if (getcwd(cwd, MAXPATHLEN) == NULL) {
424 			reperr(pkg_gt(ERR_GETWD), ainfo->local);
425 			return (VE_FAIL);
426 		}
427 
428 		/*
429 		 * Change to the directory in which the hard
430 		 * link is to be created.
431 		 */
432 		cd = strdup(path);
433 		c = strrchr(cd, '/');
434 		if (c) {
435 			/* bugid 4247895 */
436 			if (strcmp(cd, c) == 0)
437 				strcpy(cd, "/");
438 			else
439 				*c = NULL;
440 
441 			if (chdir(cd) != 0) {
442 				reperr(pkg_gt(ERR_CHDIR), cd);
443 				return (VE_FAIL);
444 			}
445 		}
446 		free(cd);
447 
448 		if (retcode || (status.st_nlink < 2) ||
449 		    (stat(ainfo->local, &status) < 0) ||
450 		    (my_dev != status.st_dev) || (my_ino != status.st_ino)) {
451 			if (fix) {
452 				/*
453 				 * Don't want to do a hard link to a
454 				 * directory.
455 				 */
456 				if (!isdir(ainfo->local)) {
457 					chdir(cwd);
458 					reperr(pkg_gt(ERR_LINKISDIR),
459 					    ainfo->local);
460 					return (VE_FAIL);
461 				}
462 				/* Now do the link. */
463 				if (!clear_target(path, ftype, targ_is_dir))
464 					return (VE_FAIL);
465 
466 				if (link(ainfo->local, path)) {
467 					chdir(cwd);
468 					reperr(pkg_gt(ERR_LINKFAIL),
469 					    ainfo->local);
470 					return (VE_FAIL);
471 				}
472 				retcode = 0;
473 			} else {
474 				/* Go back to previous working directory */
475 				if (chdir(cwd) != 0)
476 					reperr(pkg_gt(ERR_CHDIR), cwd);
477 
478 				reperr(pkg_gt(ERR_LINK), ainfo->local);
479 				return (VE_CONT);
480 			}
481 		}
482 
483 		/* Go back to previous working directory */
484 		if (chdir(cwd) != 0) {
485 			reperr(pkg_gt(ERR_CHDIR), cwd);
486 			return (VE_CONT);
487 		}
488 
489 		return (retcode);
490 	}
491 
492 	retcode = 0;
493 
494 	/* If we are to process symlinks the old way then we follow the link */
495 	if (nonABI_symlinks()) {
496 		if ((*ftype == 's') ? lstat(path, &status) :
497 			stat(path, &status)) {
498 			reperr(pkg_gt(ERR_EXIST));
499 			retcode = VE_EXIST;
500 			myftype = '?';
501 			statError++;
502 		}
503 	/* If not then we inspect the target of the link */
504 	} else {
505 		if ((n = lstat(path, &status)) == -1) {
506 			reperr(pkg_gt(ERR_EXIST));
507 			retcode = VE_EXIST;
508 			myftype = '?';
509 			statError++;
510 		}
511 	}
512 	if (!statError) {
513 		/* determining actual type of existing object */
514 		switch (status.st_mode & S_IFMT) {
515 		    case S_IFLNK:
516 			myftype = 's';
517 			break;
518 
519 		    case S_IFIFO:
520 			myftype = 'p';
521 			break;
522 
523 		    case S_IFCHR:
524 			myftype = 'c';
525 			break;
526 
527 		    case S_IFDIR:
528 			myftype = 'd';
529 			targ_is_dir = 1;
530 			break;
531 
532 		    case S_IFBLK:
533 			myftype = 'b';
534 			break;
535 
536 		    case S_IFREG:
537 		    case 0:
538 			myftype = 'f';
539 			break;
540 
541 		    case S_IFDOOR:
542 			myftype = 'D';
543 			break;
544 
545 		    default:
546 			reperr(pkg_gt(ERR_UNKNOWN));
547 			return (VE_FTYPE);
548 		}
549 	}
550 
551 	if (setval) {
552 		/*
553 		 * Check to make sure that a package or an installf that uses
554 		 * wild cards '?' to assume the ftype of an object on the
555 		 * system is not assuming a door ftype. Doors are not supported
556 		 * but should be ignored.
557 		 */
558 		if (myftype == 'D') {
559 			reperr(pkg_gt(ERR_FTYPED), path);
560 			retcode = VE_FTYPE;
561 			return (VE_FTYPE);
562 		} else {
563 			*ftype = myftype;
564 		}
565 	} else if (!retcode && (*ftype != myftype) &&
566 	    ((myftype != 'f') || !strchr("ilev", *ftype)) &&
567 	    ((myftype != 'd') || (*ftype != 'x'))) {
568 		reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
569 		retcode = VE_FTYPE;
570 	}
571 
572 	if (!retcode && (*ftype == 's')) {
573 		/* make sure that symbolic link is correct */
574 		n = readlink(path, buf, PATH_MAX);
575 		if (n < 0) {
576 			reperr(pkg_gt(ERR_SLINK), ainfo->local);
577 			retcode = VE_CONT;
578 		} else if (ainfo->local != NULL) {
579 			buf[n] = '\0';
580 			if (strcmp(buf, ainfo->local)) {
581 				reperr(pkg_gt(ERR_SLINK), ainfo->local);
582 				retcode = VE_CONT;
583 			}
584 		} else if (ainfo->local == NULL) {
585 			/*
586 			 * Since a sym link target exists, insert it
587 			 * into the ainfo structure
588 			 */
589 			buf[n] = '\0';
590 			ainfo->local = strdup(buf);
591 		}
592 	}
593 
594 	if (retcode) {
595 		/* The path doesn't exist or is different than it should be. */
596 		if (fix) {
597 			/*
598 			 * Clear the way for the write. If it won't clear,
599 			 * there's nothing we can do.
600 			 */
601 			if (!clear_target(path, ftype, targ_is_dir))
602 				return (VE_FAIL);
603 
604 			if ((*ftype == 'd') || (*ftype == 'x')) {
605 				char	*pt, *p;
606 
607 				/* Try to make it the easy way */
608 				if (mkdir(path, ainfo->mode)) {
609 					/*
610 					 * Failing that, walk through the
611 					 * parent directories creating
612 					 * whatever is needed.
613 					 */
614 					p = strdup(path);
615 					pt = (*p == '/') ? p+1 : p;
616 					do {
617 						if (pt = strchr(pt, '/'))
618 							*pt = '\0';
619 						if (access(p, 0) &&
620 						    mkdir(p, ainfo->mode))
621 							break;
622 						if (pt)
623 							*pt++ = '/';
624 					} while (pt);
625 					free(p);
626 				}
627 				if (stat(path, &status) < 0) {
628 					reperr(pkg_gt(ERR_DIRFAIL));
629 					return (VE_FAIL);
630 				}
631 			} else if (*ftype == 's') {
632 				if (symlink(ainfo->local, path)) {
633 					reperr(pkg_gt(ERR_SLINKFAIL),
634 					    ainfo->local);
635 					return (VE_FAIL);
636 				}
637 
638 			} else if (*ftype == 'c') {
639 				int wilddevno = 0;
640 				/*
641 				 * The next three if's support 2.4 and older
642 				 * packages that use "?" as device numbers.
643 				 * This should be considered for removal by
644 				 * release 2.7 or so.
645 				 */
646 				if (ainfo->major == BADMAJOR) {
647 					ainfo->major = 0;
648 					wilddevno = 1;
649 				}
650 
651 				if (ainfo->minor == BADMINOR) {
652 					ainfo->minor = 0;
653 					wilddevno = 1;
654 				}
655 
656 				if (wilddevno) {
657 					wilddevno = 0;
658 					logerr(MSG_WLDDEVNO, path,
659 					    ainfo->major, ainfo->minor);
660 				}
661 
662 				if (mknod(path, ainfo->mode | S_IFCHR,
663 #ifdef SUNOS41
664 				    makedev(ainfo->xmajor, ainfo->xminor)) ||
665 #else
666 				    makedev(ainfo->major, ainfo->minor)) ||
667 #endif
668 				    (stat(path, &status) < 0)) {
669 					reperr(pkg_gt(ERR_CDEVFAIL));
670 					return (VE_FAIL);
671 				}
672 			} else if (*ftype == 'b') {
673 				int wilddevno = 0;
674 				/*
675 				 * The next three if's support 2.4 and older
676 				 * packages that use "?" as device numbers.
677 				 * This should be considered for removal by
678 				 * release 2.7 or so.
679 				 */
680 				if (ainfo->major == BADMAJOR) {
681 					ainfo->major = 0;
682 					wilddevno = 1;
683 				}
684 
685 				if (ainfo->minor == BADMINOR) {
686 					ainfo->minor = 0;
687 					wilddevno = 1;
688 				}
689 
690 				if (wilddevno) {
691 					wilddevno = 0;
692 					logerr(MSG_WLDDEVNO, path,
693 					    ainfo->major, ainfo->minor);
694 				}
695 
696 				if (mknod(path, ainfo->mode | S_IFBLK,
697 #ifdef SUNOS41
698 				    makedev(ainfo->xmajor, ainfo->xminor)) ||
699 #else
700 				    makedev(ainfo->major, ainfo->minor)) ||
701 #endif
702 				    (stat(path, &status) < 0)) {
703 					reperr(pkg_gt(ERR_BDEVFAIL));
704 					return (VE_FAIL);
705 				}
706 			} else if (*ftype == 'p') {
707 				if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
708 				    (stat(path, &status) < 0)) {
709 					reperr(pkg_gt(ERR_PIPEFAIL));
710 					return (VE_FAIL);
711 				}
712 			} else
713 				return (retcode);
714 
715 		} else
716 			return (retcode);
717 	}
718 
719 	if (*ftype == 's')
720 		return (0); /* don't check anything else */
721 	if (*ftype == 'i')
722 		return (0); /* don't check anything else */
723 
724 	retcode = 0;
725 	if ((myftype == 'c') || (myftype == 'b')) {
726 #ifdef SUNOS41
727 		if (setval || (ainfo->xmajor < 0))
728 			ainfo->xmajor = ((status.st_rdev>>8)&0377);
729 		if (setval || (ainfo->xminor < 0))
730 			ainfo->xminor = (status.st_rdev&0377);
731 		/* check major & minor */
732 		if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
733 			reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
734 			    ainfo->xminor,
735 				(status.st_rdev>>8)&0377, status.st_rdev&0377);
736 			retcode = VE_CONT;
737 		}
738 #else
739 		if (setval || (ainfo->major == BADMAJOR))
740 			ainfo->major = major(status.st_rdev);
741 		if (setval || (ainfo->minor == BADMINOR))
742 			ainfo->minor = minor(status.st_rdev);
743 		/* check major & minor */
744 		if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
745 			reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
746 			    major(status.st_rdev), minor(status.st_rdev));
747 			retcode = VE_CONT;
748 		}
749 #endif
750 	}
751 
752 	/* compare specified mode w/ actual mode excluding sticky bit */
753 	if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
754 		ainfo->mode = status.st_mode & 07777;
755 	else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
756 		if (fix) {
757 			if ((ainfo->mode == BADMODE) ||
758 			    (chmod(path, ainfo->mode) < 0))
759 				retcode = VE_FAIL;
760 		} else {
761 			reperr(pkg_gt(ERR_PERM), ainfo->mode,
762 				status.st_mode & 07777);
763 			if (!retcode)
764 				retcode = VE_ATTR;
765 		}
766 	}
767 
768 	dochown = 0;
769 
770 	/* get group entry for specified group */
771 	if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
772 		grp = cgrgid(status.st_gid);
773 		if (grp)
774 			(void) strcpy(ainfo->group, grp->gr_name);
775 		else {
776 			if (!retcode)
777 				retcode = VE_ATTR;
778 			reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
779 		}
780 		gid = status.st_gid;
781 	} else if ((grp = cgrnam(ainfo->group)) == NULL) {
782 		reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
783 		if (!retcode)
784 			retcode = VE_ATTR;
785 	} else if ((gid = grp->gr_gid) != status.st_gid) {
786 		if (fix) {
787 			/* save specified GID */
788 			gid = grp->gr_gid;
789 			dochown++;
790 		} else {
791 			if ((grp = cgrgid((int)status.st_gid)) ==
792 			    (struct group *)NULL) {
793 				reperr(pkg_gt(ERR_GROUP), ainfo->group,
794 				    "(null)");
795 			} else {
796 				reperr(pkg_gt(ERR_GROUP), ainfo->group,
797 				    grp->gr_name);
798 			}
799 			if (!retcode)
800 				retcode = VE_ATTR;
801 		}
802 	}
803 
804 	/* get password entry for specified owner */
805 	if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
806 		pwd = cpwuid((int)status.st_uid);
807 		if (pwd)
808 			(void) strcpy(ainfo->owner, pwd->pw_name);
809 		else {
810 			if (!retcode)
811 				retcode = VE_ATTR;
812 			reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
813 		}
814 		uid = status.st_uid;
815 	} else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
816 		/* UID does not exist in password file */
817 		reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
818 		if (!retcode)
819 			retcode = VE_ATTR;
820 	} else if ((uid = pwd->pw_uid) != status.st_uid) {
821 		/* get owner name for actual UID */
822 		if (fix) {
823 			uid = pwd->pw_uid;
824 			dochown++;
825 		} else {
826 			pwd = cpwuid((int)status.st_uid);
827 			if (pwd == NULL)
828 				reperr(pkg_gt(ERR_BADUSRID),
829 				    (int)status.st_uid);
830 			else
831 				reperr(pkg_gt(ERR_OWNER), ainfo->owner,
832 				    pwd->pw_name);
833 
834 			if (!retcode)
835 				retcode = VE_ATTR;
836 		}
837 	}
838 
839 	if (statvfs(path, &vfsstatus) < 0) {
840 		reperr(pkg_gt(ERR_EXIST));
841 		retcode = VE_FAIL;
842 	} else {
843 		if (dochown) {
844 			/* pcfs doesn't support file ownership */
845 			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
846 			    chown(path, uid, gid) < 0) {
847 				retcode = VE_FAIL; /* chown failed */
848 			}
849 		}
850 	}
851 
852 	if (retcode == VE_FAIL)
853 		reperr(pkg_gt(ERR_ATTRFAIL));
854 	return (retcode);
855 }
856 
857 /*
858  * This is a special fast verify which basically checks the attributes
859  * and then, if all is OK, checks the size and mod time using the same
860  * stat and statvfs structures.
861  */
862 int
863 fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
864     struct cinfo *cinfo)
865 {
866 	int retval;
867 
868 	/* return success if attribute checks are disabled */
869 
870 	if (get_disable_attribute_check()) {
871 		return (0);
872 	}
873 
874 	if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
875 		if (*ftype == 'f' || *ftype == 'i') {
876 			if (cinfo->size != status.st_size) {
877 				reperr(pkg_gt(WRN_QV_SIZE), path);
878 				retval = VE_CONT;
879 			}
880 			/* pcfs doesn't support modification times */
881 			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
882 				if (cinfo->modtime != status.st_mtime) {
883 					reperr(pkg_gt(WRN_QV_MTIME), path);
884 					retval = VE_CONT;
885 				}
886 			}
887 		}
888 	}
889 
890 	return (retval);
891 }
892 
893 /*
894  * This function determines whether or not non-ABI symlinks are supported.
895  */
896 
897 int
898 nonABI_symlinks(void)
899 {
900 	return (nonabi_symlinks);
901 }
902 
903 void
904 set_nonABI_symlinks(void)
905 {
906 	nonabi_symlinks	= 1;
907 }
908 
909 /*
910  * Disable attribute checking. Only disable attribute checking if files
911  * are guaranteed to exist in the FS.
912  */
913 void
914 disable_attribute_check(void)
915 {
916 	disable_attributes = 1;
917 }
918 
919 /*
920  * This function determines whether or not to do attribute checking.
921  * Returns:  0 - Do attribute checking
922  *          !0 - Don't do attribute checking
923  */
924 int
925 get_disable_attribute_check(void)
926 {
927 	return (disable_attributes);
928 }
929 
930 /*
931  * This function returns the address of the "global" error buffer that
932  * is populated by the various functions in this module.
933  */
934 
935 char *
936 getErrbufAddr(void)
937 {
938 	return (theErrBuf);
939 }
940 
941 /*
942  * This function returns the size of the buffer returned by getErrbufAddr()
943  */
944 
945 int
946 getErrbufSize(void)
947 {
948 	return (sizeof (theErrBuf));
949 }
950 
951 /*
952  * This function returns the current global "error string"
953  */
954 
955 char *
956 getErrstr(void)
957 {
958 	return (theErrStr);
959 }
960 
961 /*
962  * This function sets the global "error string"
963  */
964 
965 void
966 setErrstr(char *a_errstr)
967 {
968 	theErrStr = a_errstr;
969 }
970 
971 /*
972  * This function enables checksumming
973  */
974 
975 void
976 checksum_on(void)
977 {
978 	enable_checksum = 1;
979 }
980 
981 /*
982  * This function disables checksumming
983  */
984 
985 void
986 checksum_off(void)
987 {
988 	enable_checksum = 0;
989 }
990