#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;
static int enable_checksum = 1;
static int disable_attributes = 0;
static int nonabi_symlinks;
static int clear_target(char *path, char *ftype, int is_a_dir);
unsigned long compute_checksum(int *r_err, char *path);
typedef union hilo {
struct part {
uint16_t hi;
uint16_t lo;
} hl;
uint32_t lg;
} CHECKSUM_T;
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);
}
}
int
cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
int allow_checksum)
{
struct stat status;
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);
}
if (setval || (cinfo->modtime == BADCONT)) {
cinfo->modtime = status.st_mtime;
} else if (status.st_mtime != cinfo->modtime) {
if (fix > 0) {
times.actime = cinfo->modtime;
times.modtime = cinfo->modtime;
if (utime(path, ×)) {
reperr(pkg_gt(ERR_MODFAIL));
retcode = VE_FAIL;
}
} else if (fix < 0) {
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;
if ((allow_checksum == 0) || (enable_checksum == 0)) {
return (retcode);
}
mycksum = compute_checksum(&cksumerr, path);
if (setval || (cinfo->cksum == BADCONT)) {
cinfo->cksum = mycksum;
return (retcode);
}
if ((mycksum != cinfo->cksum) || cksumerr) {
if (!retcode) {
retcode = VE_CONT;
}
if (!cksumerr) {
reperr(pkg_gt(ERR_CKSUM), cinfo->cksum, mycksum);
}
}
return (retcode);
}
unsigned long
compute_checksum(int *r_cksumerr, char *a_path)
{
CHECKSUM_T suma;
CHECKSUM_T tempa;
int fd;
uint32_t lg;
uint32_t buf[CHUNK/4];
uint32_t lsavhi;
uint32_t lsavlo;
int leap = sizeof (uint32_t);
int notyet = 0;
int nread;
struct stat64 sbuf;
*r_cksumerr = 0;
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);
}
lg = 0;
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;
while (notyet--)
lg += (((uint32_t)(*s++)) & WDMSK);
}
(void) close(fd);
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 (lsavhi+lsavlo);
}
static struct stat status;
static struct statvfs vfsstatus;
static int
clear_target(char *path, char *ftype, int is_a_dir)
{
int retcode = 1;
if (is_a_dir) {
if ((*ftype != 'd') && (*ftype != 'x')) {
if (rmdir(path)) {
reperr(pkg_gt(ERR_RMDIR), path);
retcode = 0;
}
}
} else {
if (remove(path)) {
if (errno != ENOENT) {
retcode = 0;
}
}
}
return (retcode);
}
int
averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
{
struct group *grp;
struct passwd *pwd;
int n;
int setval;
int uid, gid;
int dochown;
int retcode;
int statError = 0;
int targ_is_dir = 0;
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;
if (getcwd(cwd, MAXPATHLEN) == NULL) {
reperr(pkg_gt(ERR_GETWD));
return (VE_FAIL);
}
cd = strdup(path);
c = strrchr(cd, '/');
if (c) {
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) {
if (!isdir(ainfo->local)) {
(void) chdir(cwd);
reperr(pkg_gt(ERR_LINKISDIR),
ainfo->local);
return (VE_FAIL);
}
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 {
if (chdir(cwd) != 0)
reperr(pkg_gt(ERR_CHDIR), cwd);
reperr(pkg_gt(ERR_LINK), ainfo->local);
return (VE_CONT);
}
}
if (chdir(cwd) != 0) {
reperr(pkg_gt(ERR_CHDIR), cwd);
return (VE_CONT);
}
return (retcode);
}
retcode = 0;
if (nonABI_symlinks()) {
if ((*ftype == 's') ? lstat(path, &status) :
stat(path, &status)) {
reperr(pkg_gt(ERR_EXIST));
retcode = VE_EXIST;
myftype = '?';
statError++;
}
} else {
if ((n = lstat(path, &status)) == -1) {
reperr(pkg_gt(ERR_EXIST));
retcode = VE_EXIST;
myftype = '?';
statError++;
}
}
if (!statError) {
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) {
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')) {
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) {
buf[n] = '\0';
ainfo->local = strdup(buf);
}
}
if (retcode) {
if (fix) {
if (!clear_target(path, ftype, targ_is_dir))
return (VE_FAIL);
if ((*ftype == 'd') || (*ftype == 'x')) {
char *pt, *p;
if (mkdir(path, ainfo->mode)) {
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;
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;
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);
if (*ftype == 'i')
return (0);
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);
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;
}
}
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;
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) {
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;
}
}
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) {
reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
if (!retcode)
retcode = VE_ATTR;
} else if ((uid = pwd->pw_uid) != status.st_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) {
if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
chown(path, uid, gid) < 0) {
retcode = VE_FAIL;
}
}
}
if (retcode == VE_FAIL)
reperr(pkg_gt(ERR_ATTRFAIL));
return (retcode);
}
int
fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
struct cinfo *cinfo)
{
int retval;
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;
}
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);
}
int
nonABI_symlinks(void)
{
return (nonabi_symlinks);
}
void
set_nonABI_symlinks(void)
{
nonabi_symlinks = 1;
}
void
disable_attribute_check(void)
{
disable_attributes = 1;
}
int
get_disable_attribute_check(void)
{
return (disable_attributes);
}
char *
getErrbufAddr(void)
{
return (theErrBuf);
}
int
getErrbufSize(void)
{
return (sizeof (theErrBuf));
}
char *
getErrstr(void)
{
return (theErrStr);
}
void
setErrstr(char *a_errstr)
{
theErrStr = a_errstr;
}
void
checksum_on(void)
{
enable_checksum = 1;
}
void
checksum_off(void)
{
enable_checksum = 0;
}