root/usr/src/lib/libpkg/common/verify.c
/*
 * 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);
                (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));
                        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)
                                (void) strcpy(cd, "/");
                        else
                                *c = '\0';

                        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)) {
                                        (void) 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)) {
                                        (void) 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,
                                    makedev(ainfo->major, ainfo->minor)) ||
                                    (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,
                                    makedev(ainfo->major, ainfo->minor)) ||
                                    (stat(path, &status) < 0)) {
                                        reperr(pkg_gt(ERR_BDEVFAIL));
                                        return (VE_FAIL);
                                }
                        } else if (*ftype == 'p') {
                                if (mknod(path, ainfo->mode | S_IFIFO, 0) ||
                                    (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')) {
                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;
                }
        }

        /* 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;
}