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
58static char	theErrBuf[PATH_MAX+512] = {'\0'};
59static char	*theErrStr = NULL;
60
61/* checksum disable switch */
62static int	enable_checksum = 1;
63
64/* attribute disable flag */
65static int	disable_attributes = 0;
66
67/* non-ABI symlinks supported */
68static int	nonabi_symlinks;
69
70/*
71 * forward declarations
72 */
73
74static int	clear_target(char *path, char *ftype, int is_a_dir);
75
76unsigned	long compute_checksum(int *r_err, char *path);
77
78/* union used to generate checksum */
79typedef 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*/
88static void
89reperr(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
144int
145cverify(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 */
256unsigned long
257compute_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
332static 	struct stat	status; 	/* file status buffer */
333static  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 */
340static int
341clear_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 */
385int
386averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
387{
388	struct group	*grp; 	/* group entry buffer */
389	struct passwd	*pwd;
390	int		n;
391	int		setval;
392	int		uid, gid;
393	int		dochown;
394	int		retcode;
395	int		statError = 0;
396	int		targ_is_dir = 0;	/* replacing a directory */
397	char		myftype;
398	char		buf[PATH_MAX];
399	ino_t		my_ino;
400	dev_t		my_dev;
401	char 		cwd[MAXPATHLEN];
402	char 		*cd;
403	char 		*c;
404
405	setval = (*ftype == '?');
406	retcode = 0;
407	reperr(NULL);
408
409	if (get_disable_attribute_check()) {
410		return (0);
411	}
412
413	if (*ftype == 'l') {
414		if (stat(path, &status) < 0) {
415			retcode = VE_EXIST;
416			reperr(pkg_gt(ERR_EXIST));
417		}
418
419		my_ino = status.st_ino;
420		my_dev = status.st_dev;
421
422		/* Get copy of the current working directory */
423		if (getcwd(cwd, MAXPATHLEN) == NULL) {
424			reperr(pkg_gt(ERR_GETWD), ainfo->local);
425			return (VE_FAIL);
426		}
427
428		/*
429		 * Change to the directory in which the hard
430		 * link is to be created.
431		 */
432		cd = strdup(path);
433		c = strrchr(cd, '/');
434		if (c) {
435			/* bugid 4247895 */
436			if (strcmp(cd, c) == 0)
437				strcpy(cd, "/");
438			else
439				*c = NULL;
440
441			if (chdir(cd) != 0) {
442				reperr(pkg_gt(ERR_CHDIR), cd);
443				return (VE_FAIL);
444			}
445		}
446		free(cd);
447
448		if (retcode || (status.st_nlink < 2) ||
449		    (stat(ainfo->local, &status) < 0) ||
450		    (my_dev != status.st_dev) || (my_ino != status.st_ino)) {
451			if (fix) {
452				/*
453				 * Don't want to do a hard link to a
454				 * directory.
455				 */
456				if (!isdir(ainfo->local)) {
457					chdir(cwd);
458					reperr(pkg_gt(ERR_LINKISDIR),
459					    ainfo->local);
460					return (VE_FAIL);
461				}
462				/* Now do the link. */
463				if (!clear_target(path, ftype, targ_is_dir))
464					return (VE_FAIL);
465
466				if (link(ainfo->local, path)) {
467					chdir(cwd);
468					reperr(pkg_gt(ERR_LINKFAIL),
469					    ainfo->local);
470					return (VE_FAIL);
471				}
472				retcode = 0;
473			} else {
474				/* Go back to previous working directory */
475				if (chdir(cwd) != 0)
476					reperr(pkg_gt(ERR_CHDIR), cwd);
477
478				reperr(pkg_gt(ERR_LINK), ainfo->local);
479				return (VE_CONT);
480			}
481		}
482
483		/* Go back to previous working directory */
484		if (chdir(cwd) != 0) {
485			reperr(pkg_gt(ERR_CHDIR), cwd);
486			return (VE_CONT);
487		}
488
489		return (retcode);
490	}
491
492	retcode = 0;
493
494	/* If we are to process symlinks the old way then we follow the link */
495	if (nonABI_symlinks()) {
496		if ((*ftype == 's') ? lstat(path, &status) :
497			stat(path, &status)) {
498			reperr(pkg_gt(ERR_EXIST));
499			retcode = VE_EXIST;
500			myftype = '?';
501			statError++;
502		}
503	/* If not then we inspect the target of the link */
504	} else {
505		if ((n = lstat(path, &status)) == -1) {
506			reperr(pkg_gt(ERR_EXIST));
507			retcode = VE_EXIST;
508			myftype = '?';
509			statError++;
510		}
511	}
512	if (!statError) {
513		/* determining actual type of existing object */
514		switch (status.st_mode & S_IFMT) {
515		    case S_IFLNK:
516			myftype = 's';
517			break;
518
519		    case S_IFIFO:
520			myftype = 'p';
521			break;
522
523		    case S_IFCHR:
524			myftype = 'c';
525			break;
526
527		    case S_IFDIR:
528			myftype = 'd';
529			targ_is_dir = 1;
530			break;
531
532		    case S_IFBLK:
533			myftype = 'b';
534			break;
535
536		    case S_IFREG:
537		    case 0:
538			myftype = 'f';
539			break;
540
541		    case S_IFDOOR:
542			myftype = 'D';
543			break;
544
545		    default:
546			reperr(pkg_gt(ERR_UNKNOWN));
547			return (VE_FTYPE);
548		}
549	}
550
551	if (setval) {
552		/*
553		 * Check to make sure that a package or an installf that uses
554		 * wild cards '?' to assume the ftype of an object on the
555		 * system is not assuming a door ftype. Doors are not supported
556		 * but should be ignored.
557		 */
558		if (myftype == 'D') {
559			reperr(pkg_gt(ERR_FTYPED), path);
560			retcode = VE_FTYPE;
561			return (VE_FTYPE);
562		} else {
563			*ftype = myftype;
564		}
565	} else if (!retcode && (*ftype != myftype) &&
566	    ((myftype != 'f') || !strchr("ilev", *ftype)) &&
567	    ((myftype != 'd') || (*ftype != 'x'))) {
568		reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
569		retcode = VE_FTYPE;
570	}
571
572	if (!retcode && (*ftype == 's')) {
573		/* make sure that symbolic link is correct */
574		n = readlink(path, buf, PATH_MAX);
575		if (n < 0) {
576			reperr(pkg_gt(ERR_SLINK), ainfo->local);
577			retcode = VE_CONT;
578		} else if (ainfo->local != NULL) {
579			buf[n] = '\0';
580			if (strcmp(buf, ainfo->local)) {
581				reperr(pkg_gt(ERR_SLINK), ainfo->local);
582				retcode = VE_CONT;
583			}
584		} else if (ainfo->local == NULL) {
585			/*
586			 * Since a sym link target exists, insert it
587			 * into the ainfo structure
588			 */
589			buf[n] = '\0';
590			ainfo->local = strdup(buf);
591		}
592	}
593
594	if (retcode) {
595		/* The path doesn't exist or is different than it should be. */
596		if (fix) {
597			/*
598			 * Clear the way for the write. If it won't clear,
599			 * there's nothing we can do.
600			 */
601			if (!clear_target(path, ftype, targ_is_dir))
602				return (VE_FAIL);
603
604			if ((*ftype == 'd') || (*ftype == 'x')) {
605				char	*pt, *p;
606
607				/* Try to make it the easy way */
608				if (mkdir(path, ainfo->mode)) {
609					/*
610					 * Failing that, walk through the
611					 * parent directories creating
612					 * whatever is needed.
613					 */
614					p = strdup(path);
615					pt = (*p == '/') ? p+1 : p;
616					do {
617						if (pt = strchr(pt, '/'))
618							*pt = '\0';
619						if (access(p, 0) &&
620						    mkdir(p, ainfo->mode))
621							break;
622						if (pt)
623							*pt++ = '/';
624					} while (pt);
625					free(p);
626				}
627				if (stat(path, &status) < 0) {
628					reperr(pkg_gt(ERR_DIRFAIL));
629					return (VE_FAIL);
630				}
631			} else if (*ftype == 's') {
632				if (symlink(ainfo->local, path)) {
633					reperr(pkg_gt(ERR_SLINKFAIL),
634					    ainfo->local);
635					return (VE_FAIL);
636				}
637
638			} else if (*ftype == 'c') {
639				int wilddevno = 0;
640				/*
641				 * The next three if's support 2.4 and older
642				 * packages that use "?" as device numbers.
643				 * This should be considered for removal by
644				 * release 2.7 or so.
645				 */
646				if (ainfo->major == BADMAJOR) {
647					ainfo->major = 0;
648					wilddevno = 1;
649				}
650
651				if (ainfo->minor == BADMINOR) {
652					ainfo->minor = 0;
653					wilddevno = 1;
654				}
655
656				if (wilddevno) {
657					wilddevno = 0;
658					logerr(MSG_WLDDEVNO, path,
659					    ainfo->major, ainfo->minor);
660				}
661
662				if (mknod(path, ainfo->mode | S_IFCHR,
663#ifdef SUNOS41
664				    makedev(ainfo->xmajor, ainfo->xminor)) ||
665#else
666				    makedev(ainfo->major, ainfo->minor)) ||
667#endif
668				    (stat(path, &status) < 0)) {
669					reperr(pkg_gt(ERR_CDEVFAIL));
670					return (VE_FAIL);
671				}
672			} else if (*ftype == 'b') {
673				int wilddevno = 0;
674				/*
675				 * The next three if's support 2.4 and older
676				 * packages that use "?" as device numbers.
677				 * This should be considered for removal by
678				 * release 2.7 or so.
679				 */
680				if (ainfo->major == BADMAJOR) {
681					ainfo->major = 0;
682					wilddevno = 1;
683				}
684
685				if (ainfo->minor == BADMINOR) {
686					ainfo->minor = 0;
687					wilddevno = 1;
688				}
689
690				if (wilddevno) {
691					wilddevno = 0;
692					logerr(MSG_WLDDEVNO, path,
693					    ainfo->major, ainfo->minor);
694				}
695
696				if (mknod(path, ainfo->mode | S_IFBLK,
697#ifdef SUNOS41
698				    makedev(ainfo->xmajor, ainfo->xminor)) ||
699#else
700				    makedev(ainfo->major, ainfo->minor)) ||
701#endif
702				    (stat(path, &status) < 0)) {
703					reperr(pkg_gt(ERR_BDEVFAIL));
704					return (VE_FAIL);
705				}
706			} else if (*ftype == 'p') {
707				if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
708				    (stat(path, &status) < 0)) {
709					reperr(pkg_gt(ERR_PIPEFAIL));
710					return (VE_FAIL);
711				}
712			} else
713				return (retcode);
714
715		} else
716			return (retcode);
717	}
718
719	if (*ftype == 's')
720		return (0); /* don't check anything else */
721	if (*ftype == 'i')
722		return (0); /* don't check anything else */
723
724	retcode = 0;
725	if ((myftype == 'c') || (myftype == 'b')) {
726#ifdef SUNOS41
727		if (setval || (ainfo->xmajor < 0))
728			ainfo->xmajor = ((status.st_rdev>>8)&0377);
729		if (setval || (ainfo->xminor < 0))
730			ainfo->xminor = (status.st_rdev&0377);
731		/* check major & minor */
732		if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
733			reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
734			    ainfo->xminor,
735				(status.st_rdev>>8)&0377, status.st_rdev&0377);
736			retcode = VE_CONT;
737		}
738#else
739		if (setval || (ainfo->major == BADMAJOR))
740			ainfo->major = major(status.st_rdev);
741		if (setval || (ainfo->minor == BADMINOR))
742			ainfo->minor = minor(status.st_rdev);
743		/* check major & minor */
744		if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
745			reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
746			    major(status.st_rdev), minor(status.st_rdev));
747			retcode = VE_CONT;
748		}
749#endif
750	}
751
752	/* compare specified mode w/ actual mode excluding sticky bit */
753	if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
754		ainfo->mode = status.st_mode & 07777;
755	else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
756		if (fix) {
757			if ((ainfo->mode == BADMODE) ||
758			    (chmod(path, ainfo->mode) < 0))
759				retcode = VE_FAIL;
760		} else {
761			reperr(pkg_gt(ERR_PERM), ainfo->mode,
762				status.st_mode & 07777);
763			if (!retcode)
764				retcode = VE_ATTR;
765		}
766	}
767
768	dochown = 0;
769
770	/* get group entry for specified group */
771	if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
772		grp = cgrgid(status.st_gid);
773		if (grp)
774			(void) strcpy(ainfo->group, grp->gr_name);
775		else {
776			if (!retcode)
777				retcode = VE_ATTR;
778			reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
779		}
780		gid = status.st_gid;
781	} else if ((grp = cgrnam(ainfo->group)) == NULL) {
782		reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
783		if (!retcode)
784			retcode = VE_ATTR;
785	} else if ((gid = grp->gr_gid) != status.st_gid) {
786		if (fix) {
787			/* save specified GID */
788			gid = grp->gr_gid;
789			dochown++;
790		} else {
791			if ((grp = cgrgid((int)status.st_gid)) ==
792			    (struct group *)NULL) {
793				reperr(pkg_gt(ERR_GROUP), ainfo->group,
794				    "(null)");
795			} else {
796				reperr(pkg_gt(ERR_GROUP), ainfo->group,
797				    grp->gr_name);
798			}
799			if (!retcode)
800				retcode = VE_ATTR;
801		}
802	}
803
804	/* get password entry for specified owner */
805	if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
806		pwd = cpwuid((int)status.st_uid);
807		if (pwd)
808			(void) strcpy(ainfo->owner, pwd->pw_name);
809		else {
810			if (!retcode)
811				retcode = VE_ATTR;
812			reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
813		}
814		uid = status.st_uid;
815	} else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
816		/* UID does not exist in password file */
817		reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
818		if (!retcode)
819			retcode = VE_ATTR;
820	} else if ((uid = pwd->pw_uid) != status.st_uid) {
821		/* get owner name for actual UID */
822		if (fix) {
823			uid = pwd->pw_uid;
824			dochown++;
825		} else {
826			pwd = cpwuid((int)status.st_uid);
827			if (pwd == NULL)
828				reperr(pkg_gt(ERR_BADUSRID),
829				    (int)status.st_uid);
830			else
831				reperr(pkg_gt(ERR_OWNER), ainfo->owner,
832				    pwd->pw_name);
833
834			if (!retcode)
835				retcode = VE_ATTR;
836		}
837	}
838
839	if (statvfs(path, &vfsstatus) < 0) {
840		reperr(pkg_gt(ERR_EXIST));
841		retcode = VE_FAIL;
842	} else {
843		if (dochown) {
844			/* pcfs doesn't support file ownership */
845			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
846			    chown(path, uid, gid) < 0) {
847				retcode = VE_FAIL; /* chown failed */
848			}
849		}
850	}
851
852	if (retcode == VE_FAIL)
853		reperr(pkg_gt(ERR_ATTRFAIL));
854	return (retcode);
855}
856
857/*
858 * This is a special fast verify which basically checks the attributes
859 * and then, if all is OK, checks the size and mod time using the same
860 * stat and statvfs structures.
861 */
862int
863fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
864    struct cinfo *cinfo)
865{
866	int retval;
867
868	/* return success if attribute checks are disabled */
869
870	if (get_disable_attribute_check()) {
871		return (0);
872	}
873
874	if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
875		if (*ftype == 'f' || *ftype == 'i') {
876			if (cinfo->size != status.st_size) {
877				reperr(pkg_gt(WRN_QV_SIZE), path);
878				retval = VE_CONT;
879			}
880			/* pcfs doesn't support modification times */
881			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
882				if (cinfo->modtime != status.st_mtime) {
883					reperr(pkg_gt(WRN_QV_MTIME), path);
884					retval = VE_CONT;
885				}
886			}
887		}
888	}
889
890	return (retval);
891}
892
893/*
894 * This function determines whether or not non-ABI symlinks are supported.
895 */
896
897int
898nonABI_symlinks(void)
899{
900	return (nonabi_symlinks);
901}
902
903void
904set_nonABI_symlinks(void)
905{
906	nonabi_symlinks	= 1;
907}
908
909/*
910 * Disable attribute checking. Only disable attribute checking if files
911 * are guaranteed to exist in the FS.
912 */
913void
914disable_attribute_check(void)
915{
916	disable_attributes = 1;
917}
918
919/*
920 * This function determines whether or not to do attribute checking.
921 * Returns:  0 - Do attribute checking
922 *          !0 - Don't do attribute checking
923 */
924int
925get_disable_attribute_check(void)
926{
927	return (disable_attributes);
928}
929
930/*
931 * This function returns the address of the "global" error buffer that
932 * is populated by the various functions in this module.
933 */
934
935char *
936getErrbufAddr(void)
937{
938	return (theErrBuf);
939}
940
941/*
942 * This function returns the size of the buffer returned by getErrbufAddr()
943 */
944
945int
946getErrbufSize(void)
947{
948	return (sizeof (theErrBuf));
949}
950
951/*
952 * This function returns the current global "error string"
953 */
954
955char *
956getErrstr(void)
957{
958	return (theErrStr);
959}
960
961/*
962 * This function sets the global "error string"
963 */
964
965void
966setErrstr(char *a_errstr)
967{
968	theErrStr = a_errstr;
969}
970
971/*
972 * This function enables checksumming
973 */
974
975void
976checksum_on(void)
977{
978	enable_checksum = 1;
979}
980
981/*
982 * This function disables checksumming
983 */
984
985void
986checksum_off(void)
987{
988	enable_checksum = 0;
989}
990