OpenSolaris_b135/lib/libpkg/common/verify.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */



#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/mkdev.h>
#include "pkgstrct.h"
#include "pkglib.h"
#include "pkglibmsgs.h"
#include "pkglocale.h"

#define	WDMSK	0xFFFF
#define	DATEFMT	"%D %r"
#define	LONG_BOUNDARY	((sizeof (unsigned long))-1)
#define	CHUNK	1024*1024

static char	theErrBuf[PATH_MAX+512] = {'\0'};
static char	*theErrStr = NULL;

/* checksum disable switch */
static int	enable_checksum = 1;

/* attribute disable flag */
static int	disable_attributes = 0;

/* non-ABI symlinks supported */
static int	nonabi_symlinks;

/*
 * forward declarations
 */

static int	clear_target(char *path, char *ftype, int is_a_dir);

unsigned	long compute_checksum(int *r_err, char *path);

/* union used to generate checksum */
typedef union hilo {
	struct part {
		uint16_t hi;
		uint16_t lo;
	} hl;
	uint32_t	lg;
} CHECKSUM_T;

/*PRINTFLIKE1*/
static void
reperr(char *fmt, ...)
{
	char	*pt;
	ssize_t	ptln;
	va_list	ap;
	int	n;

	if (fmt == (char *)NULL) {
		theErrBuf[0] = '\0';
	} else {
		if (n = strlen(theErrBuf)) {
			pt = theErrBuf + n;
			*pt++ = '\n';
			*pt = '\0';
			ptln = sizeof (theErrBuf)-n;
		} else {
			pt = theErrBuf;
			ptln = sizeof (theErrBuf);
		}
		va_start(ap, fmt);
		/* LINTED variable format specifier to vsnprintf() */
		(void) vsnprintf(pt, ptln, fmt, ap);
		va_end(ap);
	}
}

/*
 * Name:	cverify
 * Description:	This function verifies and (if fix > 0) fixes the contents
 *		of the file at the path provided
 * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
 *		ftype - single character "type" the entry is supposed to be
 *		path - path to file
 *		cinfo - content info structure representing the contents
 *			the entry is supposed to contain
 *		allow_checksum - determine if checksumming should be disabled:
 *		 == 0 - do not perform checksum ever - override enable_checksum.
 *		 != 0 - use the default checksum flag "enable_checksum" to
 *			determine if checksumming should be done.
 * NOTE:	modification and creation times can be repaired; the contents
 *		of the file cannot be corrected if the checksum indicates that
 *		the contents are not correct - VE_CONT will be returned in this
 *		case.
 * Possible return values:
 * - 0 = successful
 * - VE_EXIST = path name does not exist
 * - VE_FTYPE = path file type is not recognized, is not supported,
 *		or is not what was expected
 * - VE_ATTR = path mode/group/user is not what was expected
 * - VE_CONT = mod time/link target/major/minor/size/file system type/current
 *		directory is not what was expected
 * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
 *		chown failed
 */

int
cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
	int allow_checksum)
{
	struct stat	status; 	/* file status buffer */
	struct utimbuf	times;
	unsigned long	mycksum;
	int		setval, retcode;
	char		tbuf1[512];
	char		tbuf2[512];
	int		cksumerr;

	setval = (*ftype == '?');
	retcode = 0;
	reperr(NULL);

	if (stat(path, &status) < 0) {
		reperr(pkg_gt(ERR_EXIST));
		return (VE_EXIST);
	}

	/* -1	requires modtimes to be the same */
	/*  0   reports modtime failure */
	/*  1   fixes modtimes */

	if (setval || (cinfo->modtime == BADCONT)) {
		cinfo->modtime = status.st_mtime;
	} else if (status.st_mtime != cinfo->modtime) {
		if (fix > 0) {
			/* reset times on the file */
			times.actime = cinfo->modtime;
			times.modtime = cinfo->modtime;
			if (utime(path, &times)) {
				reperr(pkg_gt(ERR_MODFAIL));
				retcode = VE_FAIL;
			}
		} else if (fix < 0) {
			/* modtimes must be the same */
			if (strftime(tbuf1, sizeof (tbuf1), DATEFMT,
				localtime(&cinfo->modtime)) == 0) {
				reperr(pkg_gt(ERR_MEM));
			}
			if (strftime(tbuf2, sizeof (tbuf2), DATEFMT,
				localtime(&status.st_mtime)) == 0) {
				reperr(pkg_gt(ERR_MEM));
			}
			reperr(pkg_gt(ERR_MTIME), tbuf1, tbuf2);
			retcode = VE_CONT;
		}
	}

	if (setval || (cinfo->size == (fsblkcnt_t)BADCONT)) {
		cinfo->size = status.st_size;
	} else if (status.st_size != cinfo->size) {
		if (!retcode) {
			retcode = VE_CONT;
		}
		reperr(pkg_gt(ERR_SIZE), cinfo->size, status.st_size);
	}

	cksumerr = 0;

	/*
	 * see if checksumming should be done: if checksumming is allowed,
	 * and checksumming is enabled, then checksum the file.
	 */

	/* return if no need to compute checksum */

	if ((allow_checksum == 0) || (enable_checksum == 0)) {
		return (retcode);
	}

	/* compute checksum */

	mycksum = compute_checksum(&cksumerr, path);

	/* set value if not set or if checksum cannot be computed */

	if (setval || (cinfo->cksum == BADCONT)) {
		cinfo->cksum = mycksum;
		return (retcode);
	}

	/* report / return error if checksums mismatch or there is an error */

	if ((mycksum != cinfo->cksum) || cksumerr) {
		if (!retcode) {
			retcode = VE_CONT;
		}
		if (!cksumerr) {
			reperr(pkg_gt(ERR_CKSUM), cinfo->cksum, mycksum);
		}
	}

	return (retcode);
}

/*
 * Name:	compute_checksum
 * Description:	generate checksum for specified file
 * Arguments:	r_cksumerr (int *) [RO, *RW]
 *			- pointer to integer that is set on return to:
 *				== 0 - no error occurred
 *				!= 0 - error occurred
 *		a_path (char *) [RO, *RO]
 *			- pointer to string representing path to file to
 *			  generate checksum of
 * Returns:	unsigned long - results:
 *			- If *r_cksumerr == 0, checksum of specified file
 *			- If *r_cksumerr != 0, undefined
 */
unsigned long
compute_checksum(int *r_cksumerr, char *a_path)
{
	CHECKSUM_T	suma;	/* to split four-bytes into 2 two-byte values */
	CHECKSUM_T	tempa;
	int		fd;
	uint32_t	lg;	/* running checksum value */
	uint32_t	buf[CHUNK/4]; /* to read CHUNK bytes */
	uint32_t	lsavhi;	/* high order two-bytes of four-byte checksum */
	uint32_t	lsavlo;	/* low order two-bytes of four-byte checksum */
	int		leap = sizeof (uint32_t);
	int		notyet = 0;
	int		nread;
	struct stat64	sbuf;

	/* reset error flag */
	*r_cksumerr = 0;

	/* open file and obtain -> where file is mapped/read */
	if ((fd = open(a_path, O_RDONLY)) < 0) {
		*r_cksumerr = 1;
		reperr(pkg_gt(ERR_NO_CKSUM));
		perror(ERR_NO_CKSUM);
		return (0);
	}

	if (fstat64(fd, &sbuf) != 0) {
		*r_cksumerr = 1;
		reperr(pkg_gt(ERR_NO_CKSUM));
		perror(ERR_NO_CKSUM);
		return (0);
	}

	/* initialize checksum value */
	lg = 0;

	/*
	 * Read CHUNK bytes off the file at a time; Read size of long bytes
	 * from memory at a time and process them.
	 * If last read, then read remnant bytes and process individually.
	 */
	errno = 0;
	while ((nread = read(fd, (void*)buf,
		    (sbuf.st_size < CHUNK) ? sbuf.st_size : CHUNK)) > 0) {
		uchar_t *s;
		uint32_t *p = buf;

		notyet = nread % leap;
		nread -= notyet;

		for (; nread > 0; nread -= leap) {
			lg += ((((*p)>>24)&0xFF) & WDMSK);
			lg += ((((*p)>>16)&0xFF) & WDMSK);
			lg += ((((*p)>>8)&0xFF) & WDMSK);
			lg += (((*p)&0xFF) & WDMSK);
			p++;
		}
		s = (uchar_t *)p;
		/* leftover bytes less than four in number */
		while (notyet--)
			lg += (((uint32_t)(*s++)) & WDMSK);
	}

	/* wind up */
	(void) close(fd);

	/* compute checksum components */
	suma.lg = lg;
	tempa.lg = (suma.hl.lo & WDMSK) + (suma.hl.hi & WDMSK);
	lsavhi = (uint32_t)tempa.hl.hi;
	lsavlo = (uint32_t)tempa.hl.lo;

	/* return final checksum value */
	return (lsavhi+lsavlo);
}

static 	struct stat	status; 	/* file status buffer */
static  struct statvfs	vfsstatus;	/* filesystem status buffer */

/*
 * Remove the thing that's currently in place so we can put down the package
 * object. If we're replacing a directory with a directory, leave it alone.
 * Returns 1 if all OK and 0 if failed.
 */
static int
clear_target(char *path, char *ftype, int is_a_dir)
{
	int retcode = 1;

	if (is_a_dir) {	/* if there's a directory there already ... */
		/* ... and this isn't, ... */
		if ((*ftype != 'd') && (*ftype != 'x')) {
			if (rmdir(path)) {	/* try to remove it. */
				reperr(pkg_gt(ERR_RMDIR), path);
				retcode = 0;
			}
		}
	} else {
		if (remove(path)) {
			if (errno != ENOENT) {
				retcode = 0;	/* It didn't work. */
			}
		}
	}

	return (retcode);
}

/*
 * Name:	averify
 * Description:	This function verifies and (if fix > 0) fixes the attributes
 *		of the file at the path provided.
 * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
 *		ftype - single character "type" the entry is supposed to be
 *		path - path to file
 *		ainfo - attribute info structure representing the attributes
 *			the entry is supposed to be
 * NOTE:	attributes are links and permissions
 * Possible return values:
 * - 0 = successful
 * - VE_EXIST = path name does not exist
 * - VE_FTYPE = path file type is not recognized, is not supported,
 *		or is not what was expected
 * - VE_ATTR = path mode/group/user is not what was expected
 * - VE_CONT = mod time/link target/major/minor/size/file system type/current
 *		directory is not what was expected
 * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
 *		chown failed
 */
int
averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
{
	struct group	*grp; 	/* group entry buffer */
	struct passwd	*pwd;
	int		n;
	int		setval;
	int		uid, gid;
	int		dochown;
	int		retcode;
	int		statError = 0;
	int		targ_is_dir = 0;	/* replacing a directory */
	char		myftype;
	char		buf[PATH_MAX];
	ino_t		my_ino;
	dev_t		my_dev;
	char 		cwd[MAXPATHLEN];
	char 		*cd;
	char 		*c;

	setval = (*ftype == '?');
	retcode = 0;
	reperr(NULL);

	if (get_disable_attribute_check()) {
		return (0);
	}

	if (*ftype == 'l') {
		if (stat(path, &status) < 0) {
			retcode = VE_EXIST;
			reperr(pkg_gt(ERR_EXIST));
		}

		my_ino = status.st_ino;
		my_dev = status.st_dev;

		/* Get copy of the current working directory */
		if (getcwd(cwd, MAXPATHLEN) == NULL) {
			reperr(pkg_gt(ERR_GETWD), ainfo->local);
			return (VE_FAIL);
		}

		/*
		 * Change to the directory in which the hard
		 * link is to be created.
		 */
		cd = strdup(path);
		c = strrchr(cd, '/');
		if (c) {
			/* bugid 4247895 */
			if (strcmp(cd, c) == 0)
				strcpy(cd, "/");
			else
				*c = NULL;

			if (chdir(cd) != 0) {
				reperr(pkg_gt(ERR_CHDIR), cd);
				return (VE_FAIL);
			}
		}
		free(cd);

		if (retcode || (status.st_nlink < 2) ||
		    (stat(ainfo->local, &status) < 0) ||
		    (my_dev != status.st_dev) || (my_ino != status.st_ino)) {
			if (fix) {
				/*
				 * Don't want to do a hard link to a
				 * directory.
				 */
				if (!isdir(ainfo->local)) {
					chdir(cwd);
					reperr(pkg_gt(ERR_LINKISDIR),
					    ainfo->local);
					return (VE_FAIL);
				}
				/* Now do the link. */
				if (!clear_target(path, ftype, targ_is_dir))
					return (VE_FAIL);

				if (link(ainfo->local, path)) {
					chdir(cwd);
					reperr(pkg_gt(ERR_LINKFAIL),
					    ainfo->local);
					return (VE_FAIL);
				}
				retcode = 0;
			} else {
				/* Go back to previous working directory */
				if (chdir(cwd) != 0)
					reperr(pkg_gt(ERR_CHDIR), cwd);

				reperr(pkg_gt(ERR_LINK), ainfo->local);
				return (VE_CONT);
			}
		}

		/* Go back to previous working directory */
		if (chdir(cwd) != 0) {
			reperr(pkg_gt(ERR_CHDIR), cwd);
			return (VE_CONT);
		}

		return (retcode);
	}

	retcode = 0;

	/* If we are to process symlinks the old way then we follow the link */
	if (nonABI_symlinks()) {
		if ((*ftype == 's') ? lstat(path, &status) :
			stat(path, &status)) {
			reperr(pkg_gt(ERR_EXIST));
			retcode = VE_EXIST;
			myftype = '?';
			statError++;
		}
	/* If not then we inspect the target of the link */
	} else {
		if ((n = lstat(path, &status)) == -1) {
			reperr(pkg_gt(ERR_EXIST));
			retcode = VE_EXIST;
			myftype = '?';
			statError++;
		}
	}
	if (!statError) {
		/* determining actual type of existing object */
		switch (status.st_mode & S_IFMT) {
		    case S_IFLNK:
			myftype = 's';
			break;

		    case S_IFIFO:
			myftype = 'p';
			break;

		    case S_IFCHR:
			myftype = 'c';
			break;

		    case S_IFDIR:
			myftype = 'd';
			targ_is_dir = 1;
			break;

		    case S_IFBLK:
			myftype = 'b';
			break;

		    case S_IFREG:
		    case 0:
			myftype = 'f';
			break;

		    case S_IFDOOR:
			myftype = 'D';
			break;

		    default:
			reperr(pkg_gt(ERR_UNKNOWN));
			return (VE_FTYPE);
		}
	}

	if (setval) {
		/*
		 * Check to make sure that a package or an installf that uses
		 * wild cards '?' to assume the ftype of an object on the
		 * system is not assuming a door ftype. Doors are not supported
		 * but should be ignored.
		 */
		if (myftype == 'D') {
			reperr(pkg_gt(ERR_FTYPED), path);
			retcode = VE_FTYPE;
			return (VE_FTYPE);
		} else {
			*ftype = myftype;
		}
	} else if (!retcode && (*ftype != myftype) &&
	    ((myftype != 'f') || !strchr("ilev", *ftype)) &&
	    ((myftype != 'd') || (*ftype != 'x'))) {
		reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
		retcode = VE_FTYPE;
	}

	if (!retcode && (*ftype == 's')) {
		/* make sure that symbolic link is correct */
		n = readlink(path, buf, PATH_MAX);
		if (n < 0) {
			reperr(pkg_gt(ERR_SLINK), ainfo->local);
			retcode = VE_CONT;
		} else if (ainfo->local != NULL) {
			buf[n] = '\0';
			if (strcmp(buf, ainfo->local)) {
				reperr(pkg_gt(ERR_SLINK), ainfo->local);
				retcode = VE_CONT;
			}
		} else if (ainfo->local == NULL) {
			/*
			 * Since a sym link target exists, insert it
			 * into the ainfo structure
			 */
			buf[n] = '\0';
			ainfo->local = strdup(buf);
		}
	}

	if (retcode) {
		/* The path doesn't exist or is different than it should be. */
		if (fix) {
			/*
			 * Clear the way for the write. If it won't clear,
			 * there's nothing we can do.
			 */
			if (!clear_target(path, ftype, targ_is_dir))
				return (VE_FAIL);

			if ((*ftype == 'd') || (*ftype == 'x')) {
				char	*pt, *p;

				/* Try to make it the easy way */
				if (mkdir(path, ainfo->mode)) {
					/*
					 * Failing that, walk through the
					 * parent directories creating
					 * whatever is needed.
					 */
					p = strdup(path);
					pt = (*p == '/') ? p+1 : p;
					do {
						if (pt = strchr(pt, '/'))
							*pt = '\0';
						if (access(p, 0) &&
						    mkdir(p, ainfo->mode))
							break;
						if (pt)
							*pt++ = '/';
					} while (pt);
					free(p);
				}
				if (stat(path, &status) < 0) {
					reperr(pkg_gt(ERR_DIRFAIL));
					return (VE_FAIL);
				}
			} else if (*ftype == 's') {
				if (symlink(ainfo->local, path)) {
					reperr(pkg_gt(ERR_SLINKFAIL),
					    ainfo->local);
					return (VE_FAIL);
				}

			} else if (*ftype == 'c') {
				int wilddevno = 0;
				/*
				 * The next three if's support 2.4 and older
				 * packages that use "?" as device numbers.
				 * This should be considered for removal by
				 * release 2.7 or so.
				 */
				if (ainfo->major == BADMAJOR) {
					ainfo->major = 0;
					wilddevno = 1;
				}

				if (ainfo->minor == BADMINOR) {
					ainfo->minor = 0;
					wilddevno = 1;
				}

				if (wilddevno) {
					wilddevno = 0;
					logerr(MSG_WLDDEVNO, path,
					    ainfo->major, ainfo->minor);
				}

				if (mknod(path, ainfo->mode | S_IFCHR,
#ifdef SUNOS41
				    makedev(ainfo->xmajor, ainfo->xminor)) ||
#else
				    makedev(ainfo->major, ainfo->minor)) ||
#endif
				    (stat(path, &status) < 0)) {
					reperr(pkg_gt(ERR_CDEVFAIL));
					return (VE_FAIL);
				}
			} else if (*ftype == 'b') {
				int wilddevno = 0;
				/*
				 * The next three if's support 2.4 and older
				 * packages that use "?" as device numbers.
				 * This should be considered for removal by
				 * release 2.7 or so.
				 */
				if (ainfo->major == BADMAJOR) {
					ainfo->major = 0;
					wilddevno = 1;
				}

				if (ainfo->minor == BADMINOR) {
					ainfo->minor = 0;
					wilddevno = 1;
				}

				if (wilddevno) {
					wilddevno = 0;
					logerr(MSG_WLDDEVNO, path,
					    ainfo->major, ainfo->minor);
				}

				if (mknod(path, ainfo->mode | S_IFBLK,
#ifdef SUNOS41
				    makedev(ainfo->xmajor, ainfo->xminor)) ||
#else
				    makedev(ainfo->major, ainfo->minor)) ||
#endif
				    (stat(path, &status) < 0)) {
					reperr(pkg_gt(ERR_BDEVFAIL));
					return (VE_FAIL);
				}
			} else if (*ftype == 'p') {
				if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
				    (stat(path, &status) < 0)) {
					reperr(pkg_gt(ERR_PIPEFAIL));
					return (VE_FAIL);
				}
			} else
				return (retcode);

		} else
			return (retcode);
	}

	if (*ftype == 's')
		return (0); /* don't check anything else */
	if (*ftype == 'i')
		return (0); /* don't check anything else */

	retcode = 0;
	if ((myftype == 'c') || (myftype == 'b')) {
#ifdef SUNOS41
		if (setval || (ainfo->xmajor < 0))
			ainfo->xmajor = ((status.st_rdev>>8)&0377);
		if (setval || (ainfo->xminor < 0))
			ainfo->xminor = (status.st_rdev&0377);
		/* check major & minor */
		if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
			reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
			    ainfo->xminor,
				(status.st_rdev>>8)&0377, status.st_rdev&0377);
			retcode = VE_CONT;
		}
#else
		if (setval || (ainfo->major == BADMAJOR))
			ainfo->major = major(status.st_rdev);
		if (setval || (ainfo->minor == BADMINOR))
			ainfo->minor = minor(status.st_rdev);
		/* check major & minor */
		if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
			reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
			    major(status.st_rdev), minor(status.st_rdev));
			retcode = VE_CONT;
		}
#endif
	}

	/* compare specified mode w/ actual mode excluding sticky bit */
	if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
		ainfo->mode = status.st_mode & 07777;
	else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
		if (fix) {
			if ((ainfo->mode == BADMODE) ||
			    (chmod(path, ainfo->mode) < 0))
				retcode = VE_FAIL;
		} else {
			reperr(pkg_gt(ERR_PERM), ainfo->mode,
				status.st_mode & 07777);
			if (!retcode)
				retcode = VE_ATTR;
		}
	}

	dochown = 0;

	/* get group entry for specified group */
	if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
		grp = cgrgid(status.st_gid);
		if (grp)
			(void) strcpy(ainfo->group, grp->gr_name);
		else {
			if (!retcode)
				retcode = VE_ATTR;
			reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
		}
		gid = status.st_gid;
	} else if ((grp = cgrnam(ainfo->group)) == NULL) {
		reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
		if (!retcode)
			retcode = VE_ATTR;
	} else if ((gid = grp->gr_gid) != status.st_gid) {
		if (fix) {
			/* save specified GID */
			gid = grp->gr_gid;
			dochown++;
		} else {
			if ((grp = cgrgid((int)status.st_gid)) ==
			    (struct group *)NULL) {
				reperr(pkg_gt(ERR_GROUP), ainfo->group,
				    "(null)");
			} else {
				reperr(pkg_gt(ERR_GROUP), ainfo->group,
				    grp->gr_name);
			}
			if (!retcode)
				retcode = VE_ATTR;
		}
	}

	/* get password entry for specified owner */
	if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
		pwd = cpwuid((int)status.st_uid);
		if (pwd)
			(void) strcpy(ainfo->owner, pwd->pw_name);
		else {
			if (!retcode)
				retcode = VE_ATTR;
			reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
		}
		uid = status.st_uid;
	} else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
		/* UID does not exist in password file */
		reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
		if (!retcode)
			retcode = VE_ATTR;
	} else if ((uid = pwd->pw_uid) != status.st_uid) {
		/* get owner name for actual UID */
		if (fix) {
			uid = pwd->pw_uid;
			dochown++;
		} else {
			pwd = cpwuid((int)status.st_uid);
			if (pwd == NULL)
				reperr(pkg_gt(ERR_BADUSRID),
				    (int)status.st_uid);
			else
				reperr(pkg_gt(ERR_OWNER), ainfo->owner,
				    pwd->pw_name);

			if (!retcode)
				retcode = VE_ATTR;
		}
	}

	if (statvfs(path, &vfsstatus) < 0) {
		reperr(pkg_gt(ERR_EXIST));
		retcode = VE_FAIL;
	} else {
		if (dochown) {
			/* pcfs doesn't support file ownership */
			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
			    chown(path, uid, gid) < 0) {
				retcode = VE_FAIL; /* chown failed */
			}
		}
	}

	if (retcode == VE_FAIL)
		reperr(pkg_gt(ERR_ATTRFAIL));
	return (retcode);
}

/*
 * This is a special fast verify which basically checks the attributes
 * and then, if all is OK, checks the size and mod time using the same
 * stat and statvfs structures.
 */
int
fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
    struct cinfo *cinfo)
{
	int retval;

	/* return success if attribute checks are disabled */

	if (get_disable_attribute_check()) {
		return (0);
	}

	if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
		if (*ftype == 'f' || *ftype == 'i') {
			if (cinfo->size != status.st_size) {
				reperr(pkg_gt(WRN_QV_SIZE), path);
				retval = VE_CONT;
			}
			/* pcfs doesn't support modification times */
			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
				if (cinfo->modtime != status.st_mtime) {
					reperr(pkg_gt(WRN_QV_MTIME), path);
					retval = VE_CONT;
				}
			}
		}
	}

	return (retval);
}

/*
 * This function determines whether or not non-ABI symlinks are supported.
 */

int
nonABI_symlinks(void)
{
	return (nonabi_symlinks);
}

void
set_nonABI_symlinks(void)
{
	nonabi_symlinks	= 1;
}

/*
 * Disable attribute checking. Only disable attribute checking if files
 * are guaranteed to exist in the FS.
 */
void
disable_attribute_check(void)
{
	disable_attributes = 1;
}

/*
 * This function determines whether or not to do attribute checking.
 * Returns:  0 - Do attribute checking
 *          !0 - Don't do attribute checking
 */
int
get_disable_attribute_check(void)
{
	return (disable_attributes);
}

/*
 * This function returns the address of the "global" error buffer that
 * is populated by the various functions in this module.
 */

char *
getErrbufAddr(void)
{
	return (theErrBuf);
}

/*
 * This function returns the size of the buffer returned by getErrbufAddr()
 */

int
getErrbufSize(void)
{
	return (sizeof (theErrBuf));
}

/*
 * This function returns the current global "error string"
 */

char *
getErrstr(void)
{
	return (theErrStr);
}

/*
 * This function sets the global "error string"
 */

void
setErrstr(char *a_errstr)
{
	theErrStr = a_errstr;
}

/*
 * This function enables checksumming
 */

void
checksum_on(void)
{
	enable_checksum = 1;
}

/*
 * This function disables checksumming
 */

void
checksum_off(void)
{
	enable_checksum = 0;
}