xref: /illumos-gate/usr/src/lib/libpkg/common/verify.c (revision 0ba964ea)
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 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		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), ainfo->local);
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 				strcpy(cd, "/");
437 			else
438 				*c = NULL;
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 					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 					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 	/* Evaluate the file type of existing object */
494 	retcode = eval_ftype(path, *ftype, &myftype);
495 
496 	/*
497 	 * If path file type is not recognized, is not supported or
498 	 * is not what is expected, return
499 	 */
500 	if (retcode == VE_FTYPE)
501 		return (retcode);
502 
503 	/* If existing type is Directory, then set flag */
504 	if (myftype == 'd')
505 		targ_is_dir = 1;
506 
507 	if (setval) {
508 		/*
509 		 * Check to make sure that a package or an installf that uses
510 		 * wild cards '?' to assume the ftype of an object on the
511 		 * system is not assuming a door ftype. Doors are not supported
512 		 * but should be ignored.
513 		 */
514 		if (myftype == 'D') {
515 			reperr(pkg_gt(ERR_FTYPED), path);
516 			retcode = VE_FTYPE;
517 			return (VE_FTYPE);
518 		} else {
519 			*ftype = myftype;
520 		}
521 	} else if (!retcode && (*ftype != myftype) &&
522 	    ((myftype != 'f') || !strchr("ilev", *ftype)) &&
523 	    ((myftype != 'd') || (*ftype != 'x'))) {
524 		reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
525 		retcode = VE_FTYPE;
526 	}
527 
528 	if (!retcode && (*ftype == 's')) {
529 		/* make sure that symbolic link is correct */
530 		n = readlink(path, buf, PATH_MAX);
531 		if (n < 0) {
532 			reperr(pkg_gt(ERR_SLINK), ainfo->local);
533 			retcode = VE_CONT;
534 		} else if (ainfo->local != NULL) {
535 			buf[n] = '\0';
536 			if (strcmp(buf, ainfo->local)) {
537 				reperr(pkg_gt(ERR_SLINK), ainfo->local);
538 				retcode = VE_CONT;
539 			}
540 		} else if (ainfo->local == NULL) {
541 			/*
542 			 * Since a sym link target exists, insert it
543 			 * into the ainfo structure
544 			 */
545 			buf[n] = '\0';
546 			ainfo->local = strdup(buf);
547 		}
548 	}
549 
550 	if (retcode) {
551 		/* The path doesn't exist or is different than it should be. */
552 		if (fix) {
553 			/*
554 			 * Clear the way for the write. If it won't clear,
555 			 * there's nothing we can do.
556 			 */
557 			if (!clear_target(path, ftype, targ_is_dir))
558 				return (VE_FAIL);
559 
560 			if ((*ftype == 'd') || (*ftype == 'x')) {
561 				char	*pt, *p;
562 
563 				/* Try to make it the easy way */
564 				if (mkdir(path, ainfo->mode)) {
565 					/*
566 					 * Failing that, walk through the
567 					 * parent directories creating
568 					 * whatever is needed.
569 					 */
570 					p = strdup(path);
571 					pt = (*p == '/') ? p+1 : p;
572 					do {
573 						if (pt = strchr(pt, '/'))
574 							*pt = '\0';
575 						if (access(p, 0) &&
576 						    mkdir(p, ainfo->mode))
577 							break;
578 						if (pt)
579 							*pt++ = '/';
580 					} while (pt);
581 					free(p);
582 				}
583 				if (stat(path, &status) < 0) {
584 					reperr(pkg_gt(ERR_DIRFAIL));
585 					return (VE_FAIL);
586 				}
587 			} else if (*ftype == 's') {
588 				if (symlink(ainfo->local, path)) {
589 					reperr(pkg_gt(ERR_SLINKFAIL),
590 					    ainfo->local);
591 					return (VE_FAIL);
592 				}
593 
594 			} else if (*ftype == 'c') {
595 				int wilddevno = 0;
596 				/*
597 				 * The next three if's support 2.4 and older
598 				 * packages that use "?" as device numbers.
599 				 * This should be considered for removal by
600 				 * release 2.7 or so.
601 				 */
602 				if (ainfo->major == BADMAJOR) {
603 					ainfo->major = 0;
604 					wilddevno = 1;
605 				}
606 
607 				if (ainfo->minor == BADMINOR) {
608 					ainfo->minor = 0;
609 					wilddevno = 1;
610 				}
611 
612 				if (wilddevno) {
613 					wilddevno = 0;
614 					logerr(MSG_WLDDEVNO, path,
615 					    ainfo->major, ainfo->minor);
616 				}
617 
618 				if (mknod(path, ainfo->mode | S_IFCHR,
619 #ifdef SUNOS41
620 				    makedev(ainfo->xmajor, ainfo->xminor)) ||
621 #else
622 				    makedev(ainfo->major, ainfo->minor)) ||
623 #endif
624 				    (stat(path, &status) < 0)) {
625 					reperr(pkg_gt(ERR_CDEVFAIL));
626 					return (VE_FAIL);
627 				}
628 			} else if (*ftype == 'b') {
629 				int wilddevno = 0;
630 				/*
631 				 * The next three if's support 2.4 and older
632 				 * packages that use "?" as device numbers.
633 				 * This should be considered for removal by
634 				 * release 2.7 or so.
635 				 */
636 				if (ainfo->major == BADMAJOR) {
637 					ainfo->major = 0;
638 					wilddevno = 1;
639 				}
640 
641 				if (ainfo->minor == BADMINOR) {
642 					ainfo->minor = 0;
643 					wilddevno = 1;
644 				}
645 
646 				if (wilddevno) {
647 					wilddevno = 0;
648 					logerr(MSG_WLDDEVNO, path,
649 					    ainfo->major, ainfo->minor);
650 				}
651 
652 				if (mknod(path, ainfo->mode | S_IFBLK,
653 #ifdef SUNOS41
654 				    makedev(ainfo->xmajor, ainfo->xminor)) ||
655 #else
656 				    makedev(ainfo->major, ainfo->minor)) ||
657 #endif
658 				    (stat(path, &status) < 0)) {
659 					reperr(pkg_gt(ERR_BDEVFAIL));
660 					return (VE_FAIL);
661 				}
662 			} else if (*ftype == 'p') {
663 				if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
664 				    (stat(path, &status) < 0)) {
665 					reperr(pkg_gt(ERR_PIPEFAIL));
666 					return (VE_FAIL);
667 				}
668 			} else
669 				return (retcode);
670 
671 		} else
672 			return (retcode);
673 	}
674 
675 	if (*ftype == 's')
676 		return (0); /* don't check anything else */
677 	if (*ftype == 'i')
678 		return (0); /* don't check anything else */
679 
680 	retcode = 0;
681 	if ((myftype == 'c') || (myftype == 'b')) {
682 #ifdef SUNOS41
683 		if (setval || (ainfo->xmajor < 0))
684 			ainfo->xmajor = ((status.st_rdev>>8)&0377);
685 		if (setval || (ainfo->xminor < 0))
686 			ainfo->xminor = (status.st_rdev&0377);
687 		/* check major & minor */
688 		if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
689 			reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
690 			    ainfo->xminor,
691 				(status.st_rdev>>8)&0377, status.st_rdev&0377);
692 			retcode = VE_CONT;
693 		}
694 #else
695 		if (setval || (ainfo->major == BADMAJOR))
696 			ainfo->major = major(status.st_rdev);
697 		if (setval || (ainfo->minor == BADMINOR))
698 			ainfo->minor = minor(status.st_rdev);
699 		/* check major & minor */
700 		if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
701 			reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
702 			    major(status.st_rdev), minor(status.st_rdev));
703 			retcode = VE_CONT;
704 		}
705 #endif
706 	}
707 
708 	/* compare specified mode w/ actual mode excluding sticky bit */
709 	if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
710 		ainfo->mode = status.st_mode & 07777;
711 	else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
712 		if (fix) {
713 			if ((ainfo->mode == BADMODE) ||
714 			    (chmod(path, ainfo->mode) < 0))
715 				retcode = VE_FAIL;
716 		} else {
717 			reperr(pkg_gt(ERR_PERM), ainfo->mode,
718 				status.st_mode & 07777);
719 			if (!retcode)
720 				retcode = VE_ATTR;
721 		}
722 	}
723 
724 	dochown = 0;
725 
726 	/* get group entry for specified group */
727 	if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
728 		grp = cgrgid(status.st_gid);
729 		if (grp)
730 			(void) strcpy(ainfo->group, grp->gr_name);
731 		else {
732 			if (!retcode)
733 				retcode = VE_ATTR;
734 			reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
735 		}
736 		gid = status.st_gid;
737 	} else if ((grp = cgrnam(ainfo->group)) == NULL) {
738 		reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
739 		if (!retcode)
740 			retcode = VE_ATTR;
741 	} else if ((gid = grp->gr_gid) != status.st_gid) {
742 		if (fix) {
743 			/* save specified GID */
744 			gid = grp->gr_gid;
745 			dochown++;
746 		} else {
747 			if ((grp = cgrgid((int)status.st_gid)) ==
748 			    (struct group *)NULL) {
749 				reperr(pkg_gt(ERR_GROUP), ainfo->group,
750 				    "(null)");
751 			} else {
752 				reperr(pkg_gt(ERR_GROUP), ainfo->group,
753 				    grp->gr_name);
754 			}
755 			if (!retcode)
756 				retcode = VE_ATTR;
757 		}
758 	}
759 
760 	/* get password entry for specified owner */
761 	if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
762 		pwd = cpwuid((int)status.st_uid);
763 		if (pwd)
764 			(void) strcpy(ainfo->owner, pwd->pw_name);
765 		else {
766 			if (!retcode)
767 				retcode = VE_ATTR;
768 			reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
769 		}
770 		uid = status.st_uid;
771 	} else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
772 		/* UID does not exist in password file */
773 		reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
774 		if (!retcode)
775 			retcode = VE_ATTR;
776 	} else if ((uid = pwd->pw_uid) != status.st_uid) {
777 		/* get owner name for actual UID */
778 		if (fix) {
779 			uid = pwd->pw_uid;
780 			dochown++;
781 		} else {
782 			pwd = cpwuid((int)status.st_uid);
783 			if (pwd == NULL)
784 				reperr(pkg_gt(ERR_BADUSRID),
785 				    (int)status.st_uid);
786 			else
787 				reperr(pkg_gt(ERR_OWNER), ainfo->owner,
788 				    pwd->pw_name);
789 
790 			if (!retcode)
791 				retcode = VE_ATTR;
792 		}
793 	}
794 
795 	if (statvfs(path, &vfsstatus) < 0) {
796 		reperr(pkg_gt(ERR_EXIST));
797 		retcode = VE_FAIL;
798 	} else {
799 		if (dochown) {
800 			/* pcfs doesn't support file ownership */
801 			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
802 			    chown(path, uid, gid) < 0) {
803 				retcode = VE_FAIL; /* chown failed */
804 			}
805 		}
806 	}
807 
808 	if (retcode == VE_FAIL)
809 		reperr(pkg_gt(ERR_ATTRFAIL));
810 	return (retcode);
811 }
812 
813 /*
814  * This is a special fast verify which basically checks the attributes
815  * and then, if all is OK, checks the size and mod time using the same
816  * stat and statvfs structures.
817  */
818 int
819 fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
820     struct cinfo *cinfo)
821 {
822 	int retval;
823 
824 	/* return success if attribute checks are disabled */
825 
826 	if (get_disable_attribute_check()) {
827 		return (0);
828 	}
829 
830 	if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
831 		if (*ftype == 'f' || *ftype == 'i') {
832 			if (cinfo->size != status.st_size) {
833 				reperr(pkg_gt(WRN_QV_SIZE), path);
834 				retval = VE_CONT;
835 			}
836 			/* pcfs doesn't support modification times */
837 			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
838 				if (cinfo->modtime != status.st_mtime) {
839 					reperr(pkg_gt(WRN_QV_MTIME), path);
840 					retval = VE_CONT;
841 				}
842 			}
843 		}
844 	}
845 
846 	return (retval);
847 }
848 
849 /*
850  * This function determines whether or not non-ABI symlinks are supported.
851  */
852 
853 int
854 nonABI_symlinks(void)
855 {
856 	return (nonabi_symlinks);
857 }
858 
859 void
860 set_nonABI_symlinks(void)
861 {
862 	nonabi_symlinks	= 1;
863 }
864 
865 /*
866  * Disable attribute checking. Only disable attribute checking if files
867  * are guaranteed to exist in the FS.
868  */
869 void
870 disable_attribute_check(void)
871 {
872 	disable_attributes = 1;
873 }
874 
875 /*
876  * This function determines whether or not to do attribute checking.
877  * Returns:  0 - Do attribute checking
878  *          !0 - Don't do attribute checking
879  */
880 int
881 get_disable_attribute_check(void)
882 {
883 	return (disable_attributes);
884 }
885 
886 /*
887  * This function returns the address of the "global" error buffer that
888  * is populated by the various functions in this module.
889  */
890 
891 char *
892 getErrbufAddr(void)
893 {
894 	return (theErrBuf);
895 }
896 
897 /*
898  * This function returns the size of the buffer returned by getErrbufAddr()
899  */
900 
901 int
902 getErrbufSize(void)
903 {
904 	return (sizeof (theErrBuf));
905 }
906 
907 /*
908  * This function returns the current global "error string"
909  */
910 
911 char *
912 getErrstr(void)
913 {
914 	return (theErrStr);
915 }
916 
917 /*
918  * This function sets the global "error string"
919  */
920 
921 void
922 setErrstr(char *a_errstr)
923 {
924 	theErrStr = a_errstr;
925 }
926 
927 /*
928  * This function enables checksumming
929  */
930 
931 void
932 checksum_on(void)
933 {
934 	enable_checksum = 1;
935 }
936 
937 /*
938  * This function disables checksumming
939  */
940 
941 void
942 checksum_off(void)
943 {
944 	enable_checksum = 0;
945 }
946