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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
37 #include <sys/types.h>
38 #include <sys/param.h>
40 #include <sys/statvfs.h>
47 #include <sys/mkdev.h>
50 #include "pkglibmsgs.h"
51 #include "pkglocale.h"
54 #define DATEFMT "%D %r"
55 #define LONG_BOUNDARY ((sizeof (unsigned long))-1)
56 #define CHUNK 1024*1024
58 static char theErrBuf
[PATH_MAX
+512] = {'\0'};
59 static char *theErrStr
= NULL
;
61 /* checksum disable switch */
62 static int enable_checksum
= 1;
64 /* attribute disable flag */
65 static int disable_attributes
= 0;
67 /* non-ABI symlinks supported */
68 static int nonabi_symlinks
;
71 * forward declarations
74 static int clear_target(char *path
, char *ftype
, int is_a_dir
);
76 unsigned long compute_checksum(int *r_err
, char *path
);
78 /* union used to generate checksum */
89 reperr(char *fmt
, ...)
96 if (fmt
== (char *)NULL
) {
99 if (n
= strlen(theErrBuf
)) {
103 ptln
= sizeof (theErrBuf
)-n
;
106 ptln
= sizeof (theErrBuf
);
109 (void) vsnprintf(pt
, ptln
, fmt
, ap
);
116 * Description: This function verifies and (if fix > 0) fixes the contents
117 * of the file at the path provided
118 * Arguments: fix - 0 - do not fix entries, 1 - fix entries
119 * ftype - single character "type" the entry is supposed to be
120 * path - path to file
121 * cinfo - content info structure representing the contents
122 * the entry is supposed to contain
123 * allow_checksum - determine if checksumming should be disabled:
124 * == 0 - do not perform checksum ever - override enable_checksum.
125 * != 0 - use the default checksum flag "enable_checksum" to
126 * determine if checksumming should be done.
127 * NOTE: modification and creation times can be repaired; the contents
128 * of the file cannot be corrected if the checksum indicates that
129 * the contents are not correct - VE_CONT will be returned in this
131 * Possible return values:
133 * - VE_EXIST = path name does not exist
134 * - VE_FTYPE = path file type is not recognized, is not supported,
135 * or is not what was expected
136 * - VE_ATTR = path mode/group/user is not what was expected
137 * - VE_CONT = mod time/link target/major/minor/size/file system type/current
138 * directory is not what was expected
139 * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
144 cverify(int fix
, char *ftype
, char *path
, struct cinfo
*cinfo
,
147 struct stat status
; /* file status buffer */
148 struct utimbuf times
;
149 unsigned long mycksum
;
155 setval
= (*ftype
== '?');
159 if (stat(path
, &status
) < 0) {
160 reperr(pkg_gt(ERR_EXIST
));
164 /* -1 requires modtimes to be the same */
165 /* 0 reports modtime failure */
166 /* 1 fixes modtimes */
168 if (setval
|| (cinfo
->modtime
== BADCONT
)) {
169 cinfo
->modtime
= status
.st_mtime
;
170 } else if (status
.st_mtime
!= cinfo
->modtime
) {
172 /* reset times on the file */
173 times
.actime
= cinfo
->modtime
;
174 times
.modtime
= cinfo
->modtime
;
175 if (utime(path
, ×
)) {
176 reperr(pkg_gt(ERR_MODFAIL
));
179 } else if (fix
< 0) {
180 /* modtimes must be the same */
181 if (strftime(tbuf1
, sizeof (tbuf1
), DATEFMT
,
182 localtime(&cinfo
->modtime
)) == 0) {
183 reperr(pkg_gt(ERR_MEM
));
185 if (strftime(tbuf2
, sizeof (tbuf2
), DATEFMT
,
186 localtime(&status
.st_mtime
)) == 0) {
187 reperr(pkg_gt(ERR_MEM
));
189 reperr(pkg_gt(ERR_MTIME
), tbuf1
, tbuf2
);
194 if (setval
|| (cinfo
->size
== (fsblkcnt_t
)BADCONT
)) {
195 cinfo
->size
= status
.st_size
;
196 } else if (status
.st_size
!= cinfo
->size
) {
200 reperr(pkg_gt(ERR_SIZE
), cinfo
->size
, status
.st_size
);
206 * see if checksumming should be done: if checksumming is allowed,
207 * and checksumming is enabled, then checksum the file.
210 /* return if no need to compute checksum */
212 if ((allow_checksum
== 0) || (enable_checksum
== 0)) {
216 /* compute checksum */
218 mycksum
= compute_checksum(&cksumerr
, path
);
220 /* set value if not set or if checksum cannot be computed */
222 if (setval
|| (cinfo
->cksum
== BADCONT
)) {
223 cinfo
->cksum
= mycksum
;
227 /* report / return error if checksums mismatch or there is an error */
229 if ((mycksum
!= cinfo
->cksum
) || cksumerr
) {
234 reperr(pkg_gt(ERR_CKSUM
), cinfo
->cksum
, mycksum
);
242 * Name: compute_checksum
243 * Description: generate checksum for specified file
244 * Arguments: r_cksumerr (int *) [RO, *RW]
245 * - pointer to integer that is set on return to:
246 * == 0 - no error occurred
247 * != 0 - error occurred
248 * a_path (char *) [RO, *RO]
249 * - pointer to string representing path to file to
250 * generate checksum of
251 * Returns: unsigned long - results:
252 * - If *r_cksumerr == 0, checksum of specified file
253 * - If *r_cksumerr != 0, undefined
256 compute_checksum(int *r_cksumerr
, char *a_path
)
258 CHECKSUM_T suma
; /* to split four-bytes into 2 two-byte values */
261 uint32_t lg
; /* running checksum value */
262 uint32_t buf
[CHUNK
/4]; /* to read CHUNK bytes */
263 uint32_t lsavhi
; /* high order two-bytes of four-byte checksum */
264 uint32_t lsavlo
; /* low order two-bytes of four-byte checksum */
265 int leap
= sizeof (uint32_t);
270 /* reset error flag */
273 /* open file and obtain -> where file is mapped/read */
274 if ((fd
= open(a_path
, O_RDONLY
)) < 0) {
276 reperr(pkg_gt(ERR_NO_CKSUM
));
277 perror(ERR_NO_CKSUM
);
281 if (fstat64(fd
, &sbuf
) != 0) {
283 reperr(pkg_gt(ERR_NO_CKSUM
));
284 perror(ERR_NO_CKSUM
);
288 /* initialize checksum value */
292 * Read CHUNK bytes off the file at a time; Read size of long bytes
293 * from memory at a time and process them.
294 * If last read, then read remnant bytes and process individually.
297 while ((nread
= read(fd
, (void*)buf
,
298 (sbuf
.st_size
< CHUNK
) ? sbuf
.st_size
: CHUNK
)) > 0) {
302 notyet
= nread
% leap
;
305 for (; nread
> 0; nread
-= leap
) {
306 lg
+= ((((*p
)>>24)&0xFF) & WDMSK
);
307 lg
+= ((((*p
)>>16)&0xFF) & WDMSK
);
308 lg
+= ((((*p
)>>8)&0xFF) & WDMSK
);
309 lg
+= (((*p
)&0xFF) & WDMSK
);
313 /* leftover bytes less than four in number */
315 lg
+= (((uint32_t)(*s
++)) & WDMSK
);
321 /* compute checksum components */
323 tempa
.lg
= (suma
.hl
.lo
& WDMSK
) + (suma
.hl
.hi
& WDMSK
);
324 lsavhi
= (uint32_t)tempa
.hl
.hi
;
325 lsavlo
= (uint32_t)tempa
.hl
.lo
;
327 /* return final checksum value */
328 return (lsavhi
+lsavlo
);
331 static struct stat status
; /* file status buffer */
332 static struct statvfs vfsstatus
; /* filesystem status buffer */
335 * Remove the thing that's currently in place so we can put down the package
336 * object. If we're replacing a directory with a directory, leave it alone.
337 * Returns 1 if all OK and 0 if failed.
340 clear_target(char *path
, char *ftype
, int is_a_dir
)
344 if (is_a_dir
) { /* if there's a directory there already ... */
345 /* ... and this isn't, ... */
346 if ((*ftype
!= 'd') && (*ftype
!= 'x')) {
347 if (rmdir(path
)) { /* try to remove it. */
348 reperr(pkg_gt(ERR_RMDIR
), path
);
354 if (errno
!= ENOENT
) {
355 retcode
= 0; /* It didn't work. */
365 * Description: This function verifies and (if fix > 0) fixes the attributes
366 * of the file at the path provided.
367 * Arguments: fix - 0 - do not fix entries, 1 - fix entries
368 * ftype - single character "type" the entry is supposed to be
369 * path - path to file
370 * ainfo - attribute info structure representing the attributes
371 * the entry is supposed to be
372 * NOTE: attributes are links and permissions
373 * Possible return values:
375 * - VE_EXIST = path name does not exist
376 * - VE_FTYPE = path file type is not recognized, is not supported,
377 * or is not what was expected
378 * - VE_ATTR = path mode/group/user is not what was expected
379 * - VE_CONT = mod time/link target/major/minor/size/file system type/current
380 * directory is not what was expected
381 * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
385 averify(int fix
, char *ftype
, char *path
, struct ainfo
*ainfo
)
387 struct group
*grp
; /* group entry buffer */
395 int targ_is_dir
= 0; /* replacing a directory */
400 char cwd
[MAXPATHLEN
];
404 setval
= (*ftype
== '?');
408 if (get_disable_attribute_check()) {
413 if (stat(path
, &status
) < 0) {
415 reperr(pkg_gt(ERR_EXIST
));
418 my_ino
= status
.st_ino
;
419 my_dev
= status
.st_dev
;
421 /* Get copy of the current working directory */
422 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
423 reperr(pkg_gt(ERR_GETWD
));
428 * Change to the directory in which the hard
429 * link is to be created.
432 c
= strrchr(cd
, '/');
435 if (strcmp(cd
, c
) == 0)
436 (void) strcpy(cd
, "/");
440 if (chdir(cd
) != 0) {
441 reperr(pkg_gt(ERR_CHDIR
), cd
);
447 if (retcode
|| (status
.st_nlink
< 2) ||
448 (stat(ainfo
->local
, &status
) < 0) ||
449 (my_dev
!= status
.st_dev
) || (my_ino
!= status
.st_ino
)) {
452 * Don't want to do a hard link to a
455 if (!isdir(ainfo
->local
)) {
457 reperr(pkg_gt(ERR_LINKISDIR
),
461 /* Now do the link. */
462 if (!clear_target(path
, ftype
, targ_is_dir
))
465 if (link(ainfo
->local
, path
)) {
467 reperr(pkg_gt(ERR_LINKFAIL
),
473 /* Go back to previous working directory */
475 reperr(pkg_gt(ERR_CHDIR
), cwd
);
477 reperr(pkg_gt(ERR_LINK
), ainfo
->local
);
482 /* Go back to previous working directory */
483 if (chdir(cwd
) != 0) {
484 reperr(pkg_gt(ERR_CHDIR
), cwd
);
493 /* If we are to process symlinks the old way then we follow the link */
494 if (nonABI_symlinks()) {
495 if ((*ftype
== 's') ? lstat(path
, &status
) :
496 stat(path
, &status
)) {
497 reperr(pkg_gt(ERR_EXIST
));
502 /* If not then we inspect the target of the link */
504 if ((n
= lstat(path
, &status
)) == -1) {
505 reperr(pkg_gt(ERR_EXIST
));
512 /* determining actual type of existing object */
513 switch (status
.st_mode
& S_IFMT
) {
545 reperr(pkg_gt(ERR_UNKNOWN
));
552 * Check to make sure that a package or an installf that uses
553 * wild cards '?' to assume the ftype of an object on the
554 * system is not assuming a door ftype. Doors are not supported
555 * but should be ignored.
557 if (myftype
== 'D') {
558 reperr(pkg_gt(ERR_FTYPED
), path
);
564 } else if (!retcode
&& (*ftype
!= myftype
) &&
565 ((myftype
!= 'f') || !strchr("ilev", *ftype
)) &&
566 ((myftype
!= 'd') || (*ftype
!= 'x'))) {
567 reperr(pkg_gt(ERR_FTYPE
), *ftype
, myftype
);
571 if (!retcode
&& (*ftype
== 's')) {
572 /* make sure that symbolic link is correct */
573 n
= readlink(path
, buf
, PATH_MAX
);
575 reperr(pkg_gt(ERR_SLINK
), ainfo
->local
);
577 } else if (ainfo
->local
!= NULL
) {
579 if (strcmp(buf
, ainfo
->local
)) {
580 reperr(pkg_gt(ERR_SLINK
), ainfo
->local
);
583 } else if (ainfo
->local
== NULL
) {
585 * Since a sym link target exists, insert it
586 * into the ainfo structure
589 ainfo
->local
= strdup(buf
);
594 /* The path doesn't exist or is different than it should be. */
597 * Clear the way for the write. If it won't clear,
598 * there's nothing we can do.
600 if (!clear_target(path
, ftype
, targ_is_dir
))
603 if ((*ftype
== 'd') || (*ftype
== 'x')) {
606 /* Try to make it the easy way */
607 if (mkdir(path
, ainfo
->mode
)) {
609 * Failing that, walk through the
610 * parent directories creating
611 * whatever is needed.
614 pt
= (*p
== '/') ? p
+1 : p
;
616 if (pt
= strchr(pt
, '/'))
619 mkdir(p
, ainfo
->mode
))
626 if (stat(path
, &status
) < 0) {
627 reperr(pkg_gt(ERR_DIRFAIL
));
630 } else if (*ftype
== 's') {
631 if (symlink(ainfo
->local
, path
)) {
632 reperr(pkg_gt(ERR_SLINKFAIL
),
637 } else if (*ftype
== 'c') {
640 * The next three if's support 2.4 and older
641 * packages that use "?" as device numbers.
642 * This should be considered for removal by
645 if (ainfo
->major
== BADMAJOR
) {
650 if (ainfo
->minor
== BADMINOR
) {
657 logerr(MSG_WLDDEVNO
, path
,
658 ainfo
->major
, ainfo
->minor
);
661 if (mknod(path
, ainfo
->mode
| S_IFCHR
,
662 makedev(ainfo
->major
, ainfo
->minor
)) ||
663 (stat(path
, &status
) < 0)) {
664 reperr(pkg_gt(ERR_CDEVFAIL
));
667 } else if (*ftype
== 'b') {
670 * The next three if's support 2.4 and older
671 * packages that use "?" as device numbers.
672 * This should be considered for removal by
675 if (ainfo
->major
== BADMAJOR
) {
680 if (ainfo
->minor
== BADMINOR
) {
687 logerr(MSG_WLDDEVNO
, path
,
688 ainfo
->major
, ainfo
->minor
);
691 if (mknod(path
, ainfo
->mode
| S_IFBLK
,
692 makedev(ainfo
->major
, ainfo
->minor
)) ||
693 (stat(path
, &status
) < 0)) {
694 reperr(pkg_gt(ERR_BDEVFAIL
));
697 } else if (*ftype
== 'p') {
698 if (mknod(path
, ainfo
->mode
| S_IFIFO
, NULL
) ||
699 (stat(path
, &status
) < 0)) {
700 reperr(pkg_gt(ERR_PIPEFAIL
));
711 return (0); /* don't check anything else */
713 return (0); /* don't check anything else */
716 if ((myftype
== 'c') || (myftype
== 'b')) {
717 if (setval
|| (ainfo
->major
== BADMAJOR
))
718 ainfo
->major
= major(status
.st_rdev
);
719 if (setval
|| (ainfo
->minor
== BADMINOR
))
720 ainfo
->minor
= minor(status
.st_rdev
);
721 /* check major & minor */
722 if (status
.st_rdev
!= makedev(ainfo
->major
, ainfo
->minor
)) {
723 reperr(pkg_gt(ERR_MAJMIN
), ainfo
->major
, ainfo
->minor
,
724 major(status
.st_rdev
), minor(status
.st_rdev
));
729 /* compare specified mode w/ actual mode excluding sticky bit */
730 if (setval
|| (ainfo
->mode
== BADMODE
) || (ainfo
->mode
== WILDCARD
))
731 ainfo
->mode
= status
.st_mode
& 07777;
732 else if ((ainfo
->mode
& 06777) != (status
.st_mode
& 06777)) {
734 if ((ainfo
->mode
== BADMODE
) ||
735 (chmod(path
, ainfo
->mode
) < 0))
738 reperr(pkg_gt(ERR_PERM
), ainfo
->mode
,
739 status
.st_mode
& 07777);
747 /* get group entry for specified group */
748 if (setval
|| strcmp(ainfo
->group
, BADGROUP
) == 0) {
749 grp
= cgrgid(status
.st_gid
);
751 (void) strcpy(ainfo
->group
, grp
->gr_name
);
755 reperr(pkg_gt(ERR_BADGRPID
), status
.st_gid
);
758 } else if ((grp
= cgrnam(ainfo
->group
)) == NULL
) {
759 reperr(pkg_gt(ERR_BADGRPNM
), ainfo
->group
);
762 } else if ((gid
= grp
->gr_gid
) != status
.st_gid
) {
764 /* save specified GID */
768 if ((grp
= cgrgid((int)status
.st_gid
)) ==
769 (struct group
*)NULL
) {
770 reperr(pkg_gt(ERR_GROUP
), ainfo
->group
,
773 reperr(pkg_gt(ERR_GROUP
), ainfo
->group
,
781 /* get password entry for specified owner */
782 if (setval
|| strcmp(ainfo
->owner
, BADOWNER
) == 0) {
783 pwd
= cpwuid((int)status
.st_uid
);
785 (void) strcpy(ainfo
->owner
, pwd
->pw_name
);
789 reperr(pkg_gt(ERR_BADUSRID
), status
.st_uid
);
792 } else if ((pwd
= cpwnam(ainfo
->owner
)) == NULL
) {
793 /* UID does not exist in password file */
794 reperr(pkg_gt(ERR_BADUSRNM
), ainfo
->owner
);
797 } else if ((uid
= pwd
->pw_uid
) != status
.st_uid
) {
798 /* get owner name for actual UID */
803 pwd
= cpwuid((int)status
.st_uid
);
805 reperr(pkg_gt(ERR_BADUSRID
),
808 reperr(pkg_gt(ERR_OWNER
), ainfo
->owner
,
816 if (statvfs(path
, &vfsstatus
) < 0) {
817 reperr(pkg_gt(ERR_EXIST
));
821 /* pcfs doesn't support file ownership */
822 if (strcmp(vfsstatus
.f_basetype
, "pcfs") != 0 &&
823 chown(path
, uid
, gid
) < 0) {
824 retcode
= VE_FAIL
; /* chown failed */
829 if (retcode
== VE_FAIL
)
830 reperr(pkg_gt(ERR_ATTRFAIL
));
835 * This is a special fast verify which basically checks the attributes
836 * and then, if all is OK, checks the size and mod time using the same
837 * stat and statvfs structures.
840 fverify(int fix
, char *ftype
, char *path
, struct ainfo
*ainfo
,
845 /* return success if attribute checks are disabled */
847 if (get_disable_attribute_check()) {
851 if ((retval
= averify(fix
, ftype
, path
, ainfo
)) == 0) {
852 if (*ftype
== 'f' || *ftype
== 'i') {
853 if (cinfo
->size
!= status
.st_size
) {
854 reperr(pkg_gt(WRN_QV_SIZE
), path
);
857 /* pcfs doesn't support modification times */
858 if (strcmp(vfsstatus
.f_basetype
, "pcfs") != 0) {
859 if (cinfo
->modtime
!= status
.st_mtime
) {
860 reperr(pkg_gt(WRN_QV_MTIME
), path
);
871 * This function determines whether or not non-ABI symlinks are supported.
875 nonABI_symlinks(void)
877 return (nonabi_symlinks
);
881 set_nonABI_symlinks(void)
887 * Disable attribute checking. Only disable attribute checking if files
888 * are guaranteed to exist in the FS.
891 disable_attribute_check(void)
893 disable_attributes
= 1;
897 * This function determines whether or not to do attribute checking.
898 * Returns: 0 - Do attribute checking
899 * !0 - Don't do attribute checking
902 get_disable_attribute_check(void)
904 return (disable_attributes
);
908 * This function returns the address of the "global" error buffer that
909 * is populated by the various functions in this module.
919 * This function returns the size of the buffer returned by getErrbufAddr()
925 return (sizeof (theErrBuf
));
929 * This function returns the current global "error string"
939 * This function sets the global "error string"
943 setErrstr(char *a_errstr
)
945 theErrStr
= a_errstr
;
949 * This function enables checksumming
959 * This function disables checksumming