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