#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <pkgstrct.h>
#include <sys/stat.h>
#include <locale.h>
#include <libintl.h>
#include <pkginfo.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <libinst.h>
#include <messages.h>
#define MRG_SAME 0
#define MRG_DIFFERENT 1
#define MRG_REPLACE 2
#define TYPE_OK 0
#define TYPE_WARNING 1
#define TYPE_IGNORED 2
#define TYPE_REPLACE 3
#define TYPE_FATAL 4
#define ERR_OUTPUT "unable to update package database"
#define ERR_PINFO "missing pinfo structure for <%s>"
#define INFO_PROCESS " %2ld%% of information processed; continuing ..."
#define WRN_NOTFILE "WARNING: %s <no longer a regular file>"
#define WRN_NOTSYMLN "WARNING: %s <no longer a symbolic link>"
#define WRN_NOTLINK "WARNING: %s <no longer a linked file>"
#define WRN_NOTDIR "WARNING: %s <no longer a directory>"
#define WRN_NOTCHAR "WARNING: %s <no longer a character special device>"
#define WRN_NOTBLOCK "WARNING: %s <no longer a block special device>"
#define WRN_NOTPIPE "WARNING: %s <no longer a named pipe>"
#define WRN_TOEXCL "WARNING: cannot convert %s to an exclusive directory."
#define WRN_ODDVERIFY "WARNING: quick verify disabled for class %s."
#define MSG_TYPIGN "Object type change ignored."
#define MSG_TYPE_ERR "Package attempts fatal object type change."
extern char *pkginst;
extern int nosetuid, nocnflct, otherstoo;
extern int cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent);
extern void cl_def_dverify(int idx);
char dbst = '\0';
int files_installed(void);
static int errflg = 0;
static int eptnum;
static int installed;
static struct pinfo *pkgpinfo = (struct pinfo *)0;
static int is_setuid(struct cfent *ent);
static int is_setgid(struct cfent *ent);
static int merg(struct cfextra *el_ent, struct cfent *cf_ent);
static int do_like_ent(VFP_T *vfpo, struct cfextra *el_ent,
struct cfent *cf_ent, int ctrl);
static int do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl);
static int typechg(struct cfent *el_ent, struct cfent *cf_ent,
struct mergstat *mstat);
static void set_change(struct cfextra *el_ent);
static void chgclass(struct cfent *cf_ent, struct pinfo *pinfo);
static void output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo);
int
pkgdbmerg(PKGserver server, VFP_T *tmpvfp, struct cfextra **extlist)
{
static struct cfent cf_ent;
struct cfextra *el_ent;
int n;
int changed;
int assume_ok = 0;
cf_ent.pinfo = (NULL);
errflg = 0;
installed = changed = 0;
vfpRewind(tmpvfp);
for (eptnum = 0; (el_ent = extlist[eptnum]) != NULL; eptnum++) {
if ((el_ent->cf_ent.ftype == 'i') ||
(el_ent->cf_ent.ftype == 'n')) {
continue;
}
if ((eptnum > 0) && (strncmp(el_ent->cf_ent.path,
extlist[eptnum-1]->cf_ent.path, PATH_MAX) == 0)) {
memcpy(extlist[eptnum], extlist[eptnum-1],
sizeof (struct cfextra));
continue;
}
if (dbst == '\0') {
if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
QKVERIFY) {
assume_ok = 1;
}
} else {
if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
QKVERIFY) {
logerr(gettext(WRN_ODDVERIFY),
cl_nam(el_ent->cf_ent.pkg_class_idx));
cl_def_dverify(el_ent->cf_ent.pkg_class_idx);
}
}
if (is_setuid(&(el_ent->cf_ent))) {
el_ent->mstat.setuid = 1;
}
if (is_setgid(&(el_ent->cf_ent))) {
el_ent->mstat.setgid = 1;
}
if (nosetuid && (el_ent->mstat.setgid ||
el_ent->mstat.setuid)) {
el_ent->cf_ent.ainfo.mode &= ~(S_ISUID | S_ISGID);
}
n = srchcfile(&cf_ent, el_ent->cf_ent.path, server);
if (n < 0) {
char *errstr = getErrstr();
progerr(ERR_CFBAD);
logerr(gettext("pathname: %s"),
(cf_ent.path && *cf_ent.path) ?
cf_ent.path : "Unknown");
logerr(gettext("problem: %s"),
(errstr && *errstr) ? errstr : "Unknown");
return (-1);
} else if (n == 1) {
if (is_setuid(&cf_ent)) {
el_ent->mstat.osetuid = 1;
}
if (is_setgid(&cf_ent)) {
el_ent->mstat.osetgid = 1;
}
if ((cf_ent.ftype == 's') &&
(el_ent->cf_ent.ftype == 'd')) {
int i;
int plen = strlen(el_ent->cf_ent.path);
for (i = eptnum + 1; extlist[i]; i++) {
if (strncmp(el_ent->cf_ent.path,
extlist[i]->cf_ent.path,
plen) != 0)
break;
extlist[i]->mstat.parentsyml2dir
= 1;
}
}
if (do_like_ent(tmpvfp, el_ent, &cf_ent, assume_ok)) {
changed++;
}
} else {
if (do_new_ent(tmpvfp, el_ent, assume_ok)) {
changed++;
}
}
}
return (errflg ? -1 : changed);
}
static int
do_like_ent(VFP_T *vfpo, struct cfextra *el_ent, struct cfent *cf_ent, int ctrl)
{
int stflag, ignore, changed, mrg_result = -1;
ignore = changed = 0;
if (el_ent->mstat.preloaded) {
struct pinfo *pkginfo;
pkginfo = cf_ent->pinfo;
while (pkginfo) {
struct pinfo *next;
next = pkginfo->next;
free(pkginfo);
pkginfo = next;
}
cf_ent->pinfo = el_ent->cf_ent.pinfo;
}
pkgpinfo = eptstat(cf_ent, pkginst, DUP_ENTRY);
stflag = pkgpinfo->status;
if (otherstoo)
el_ent->mstat.shared = 1;
if (el_ent->cf_ent.ftype == RM_RDY) {
if (!errflg) {
pkgpinfo = eptstat(cf_ent, pkginst, RM_RDY);
stflag = pkgpinfo->status;
pkgpinfo->status = RM_RDY;
if (putcvfpfile(cf_ent, vfpo)) {
progerr(gettext(ERR_OUTPUT));
quit(99);
}
if (stflag == SERVED_FILE) {
el_ent->cf_ent.pinfo =
(struct pinfo *)calloc(1,
sizeof (struct pinfo));
el_ent->cf_ent.pinfo->next = NULL;
el_ent->cf_ent.pinfo->status = SERVED_FILE;
}
}
return (1);
}
if (!pkgpinfo) {
progerr(gettext(ERR_PINFO), cf_ent->path);
quit(99);
}
if ((nocnflct && el_ent->mstat.shared && el_ent->cf_ent.ftype != 'e')) {
set_change(el_ent);
if (cp_cfent(cf_ent, el_ent) == 0)
quit(99);
ignore++;
} else {
mrg_result = merg(el_ent, cf_ent);
switch (mrg_result) {
case MRG_SAME:
break;
case MRG_DIFFERENT:
changed++;
break;
case MRG_REPLACE:
el_ent->mstat.contchg = 1;
ignore++;
break;
default:
break;
}
}
if (!el_ent->mstat.contchg && !ignore) {
set_change(el_ent);
}
if (!errflg) {
if (ctrl == 1) {
pkgpinfo = eptstat(&(el_ent->cf_ent), pkginst,
ENTRY_OK);
if (stflag != DUP_ENTRY) {
changed++;
}
if (is_served(el_ent->server_path,
&(el_ent->fsys_value)))
pkgpinfo->status = SERVED_FILE;
} else {
if (!ignore && el_ent->mstat.contchg) {
pkgpinfo =
eptstat(&(el_ent->cf_ent), pkginst,
(dbst ? dbst : CONFIRM_CONT));
} else if (!ignore && el_ent->mstat.attrchg) {
pkgpinfo =
eptstat(&(el_ent->cf_ent), pkginst,
(dbst ? dbst : CONFIRM_ATTR));
} else if (!ignore && el_ent->mstat.shared) {
pkgpinfo =
eptstat(&(el_ent->cf_ent), pkginst,
dbst);
changed++;
} else if (stflag != DUP_ENTRY) {
pkgpinfo = eptstat(&(el_ent->cf_ent),
pkginst, '\0');
if (stflag != ENTRY_OK) {
changed++;
}
}
}
if (mrg_result == MRG_REPLACE) {
output(vfpo, cf_ent, pkgpinfo);
} else {
output(vfpo, &(el_ent->cf_ent), pkgpinfo);
}
}
if (pkgpinfo->aclass[0] != '\0') {
(void) strcpy(el_ent->cf_ent.pkg_class, pkgpinfo->aclass);
}
if (el_ent->mstat.contchg && pkgpinfo->status == INST_RDY) {
changed++;
}
if (!(el_ent->mstat.preloaded))
el_ent->cf_ent.pinfo = NULL;
if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg &&
!el_ent->mstat.replace)
installed++;
return (changed);
}
static int
do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl)
{
struct pinfo *pinfo;
char *tp;
int changed = 0;
if (el_ent->cf_ent.ftype == RM_RDY) {
return (0);
}
tp = el_ent->server_path;
if ((!el_ent->mstat.parentsyml2dir) && (access(tp, F_OK) == 0)) {
el_ent->mstat.shared = 1;
el_ent->mstat.rogue = 1;
set_change(el_ent);
} else {
el_ent->mstat.rogue = 0;
el_ent->mstat.contchg = 1;
el_ent->mstat.attrchg = 1;
}
if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
if (el_ent->cf_ent.ftype == 'd') {
el_ent->cf_ent.ainfo.mode = DEFAULT_MODE;
} else {
el_ent->cf_ent.ainfo.mode = DEFAULT_MODE_FILE;
}
logerr(WRN_SET_DEF_MODE, el_ent->cf_ent.path,
(int)el_ent->cf_ent.ainfo.mode);
}
if (strcmp(el_ent->cf_ent.ainfo.owner, DB_UNDEFINED_ENTRY) == 0)
(void) strcpy(el_ent->cf_ent.ainfo.owner, DEFAULT_OWNER);
if (strcmp(el_ent->cf_ent.ainfo.group, DB_UNDEFINED_ENTRY) == 0)
(void) strcpy(el_ent->cf_ent.ainfo.group, DEFAULT_GROUP);
if (nocnflct && el_ent->mstat.shared) {
return (0);
}
if (!errflg) {
if (el_ent->mstat.preloaded) {
pinfo = eptstat(&(el_ent->cf_ent), pkginst, DUP_ENTRY);
} else {
el_ent->cf_ent.npkgs = 1;
pinfo = (struct pinfo *)calloc(1,
sizeof (struct pinfo));
if (!pinfo) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
el_ent->cf_ent.pinfo = pinfo;
(void) strcpy(pinfo->pkg, pkginst);
}
if (ctrl == 1) {
pinfo->status = dbst ? dbst : ENTRY_OK;
if (is_served(el_ent->server_path,
&(el_ent->fsys_value)))
pinfo->status = SERVED_FILE;
} else {
pinfo->status = dbst ? dbst : CONFIRM_CONT;
}
output(vfpo, &(el_ent->cf_ent), pinfo);
changed++;
free(pinfo);
el_ent->cf_ent.pinfo = NULL;
}
if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg) {
installed++;
}
return (changed);
}
int
files_installed(void)
{
return (installed);
}
static void
set_change(struct cfextra *el_ent)
{
int n;
char *tp;
tp = el_ent->server_path;
if ((el_ent->cf_ent.ftype == 'f') || (el_ent->cf_ent.ftype == 'e') ||
(el_ent->cf_ent.ftype == 'v')) {
if (cverify(0, &(el_ent->cf_ent.ftype), tp,
&(el_ent->cf_ent.cinfo), 1)) {
el_ent->mstat.contchg = 1;
} else if (!el_ent->mstat.contchg && !el_ent->mstat.attrchg) {
if (averify(0, &(el_ent->cf_ent.ftype), tp,
&(el_ent->cf_ent.ainfo)))
el_ent->mstat.attrchg = 1;
}
} else if (!el_ent->mstat.attrchg &&
((el_ent->cf_ent.ftype == 'd') ||
(el_ent->cf_ent.ftype == 'x') ||
(el_ent->cf_ent.ftype == 'c') ||
(el_ent->cf_ent.ftype == 'b') ||
(el_ent->cf_ent.ftype == 'p'))) {
n = averify(0, &(el_ent->cf_ent.ftype), tp,
&(el_ent->cf_ent.ainfo));
if (n == VE_ATTR)
el_ent->mstat.attrchg = 1;
else if (n && (n != VE_EXIST)) {
el_ent->mstat.contchg = 1;
}
} else if (!el_ent->mstat.attrchg &&
((el_ent->cf_ent.ftype == 's') ||
(el_ent->cf_ent.ftype == 'l'))) {
n = averify(0, &(el_ent->cf_ent.ftype), tp,
&(el_ent->cf_ent.ainfo));
if (n == VE_ATTR)
el_ent->mstat.attrchg = 1;
else if (n && (n == VE_EXIST)) {
el_ent->mstat.contchg = 1;
}
}
}
static int
is_setuid(struct cfent *ent)
{
return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
(ent->ftype == 'e')) &&
(ent->ainfo.mode != BADMODE) &&
(ent->ainfo.mode != WILDCARD) &&
(ent->ainfo.mode & S_ISUID));
}
static int
is_setgid(struct cfent *ent)
{
return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
(ent->ftype == 'e')) && (ent->ainfo.mode != BADMODE) &&
(ent->ainfo.mode != WILDCARD) &&
(ent->ainfo.mode & S_ISGID) &&
(ent->ainfo.mode & (S_IEXEC|S_IXUSR|S_IXOTH)));
}
char *types[] = {
"fev",
"s",
"l",
"dx",
"c",
"b",
"p",
NULL
};
static int
typechg(struct cfent *el_ent, struct cfent *cf_ent, struct mergstat *mstat)
{
int i, etype, itype, retcode;
if (cf_ent->ftype == el_ent->ftype)
return (TYPE_OK);
if (cf_ent->ftype == BADFTYPE) {
cf_ent->ftype = el_ent->ftype;
return (TYPE_OK);
}
if (el_ent->ftype == BADFTYPE)
return (TYPE_OK);
if (el_ent->ftype == 'x' && cf_ent->ftype == 'd') {
logerr(gettext(WRN_TOEXCL), el_ent->path);
return (TYPE_IGNORED);
}
etype = itype = 0;
for (i = 0; types[i]; ++i) {
if (strchr(types[i], el_ent->ftype)) {
etype = i+1;
break;
}
}
for (i = 0; types[i]; ++i) {
if (strchr(types[i], cf_ent->ftype)) {
itype = i+1;
break;
}
}
if (itype == etype) {
return (TYPE_OK);
}
retcode = TYPE_WARNING;
if (etype != 4 && itype == 4) {
mstat->dir2nondir = 1;
retcode = TYPE_REPLACE;
}
switch (itype) {
case 1:
logerr(gettext(WRN_NOTFILE), el_ent->path);
break;
case 2:
logerr(gettext(WRN_NOTSYMLN), el_ent->path);
break;
case 3:
logerr(gettext(WRN_NOTLINK), el_ent->path);
break;
case 4:
logerr(gettext(WRN_NOTDIR), el_ent->path);
break;
case 5:
logerr(gettext(WRN_NOTCHAR), el_ent->path);
break;
case 6:
logerr(gettext(WRN_NOTBLOCK), el_ent->path);
break;
case 7:
logerr(gettext(WRN_NOTPIPE), el_ent->path);
break;
default:
break;
}
return (retcode);
}
static int
merg(struct cfextra *el_ent, struct cfent *cf_ent)
{
int n, changed = 0;
el_ent->cf_ent.pinfo = cf_ent->pinfo;
if (dbst == INST_RDY && el_ent->cf_ent.ftype == '?') {
el_ent->cf_ent.ftype = cf_ent->ftype;
}
if (cf_ent->ftype != el_ent->cf_ent.ftype) {
n = typechg(&(el_ent->cf_ent), cf_ent, &(el_ent->mstat));
switch (n) {
case TYPE_OK:
break;
case TYPE_WARNING:
el_ent->mstat.contchg = 1;
break;
case TYPE_IGNORED:
logerr(gettext(MSG_TYPIGN));
if (cp_cfent(cf_ent, el_ent) == 0)
quit(99);
return (MRG_SAME);
case TYPE_REPLACE:
el_ent->mstat.replace = 1;
return (MRG_REPLACE);
case TYPE_FATAL:
logerr(gettext(MSG_TYPE_ERR));
quit(99);
default:
break;
}
changed++;
}
if (strcmp(cf_ent->pkg_class, el_ent->cf_ent.pkg_class)) {
changed++;
if (strcmp(cf_ent->pkg_class, "?")) {
(void) strcpy(pkgpinfo->aclass,
el_ent->cf_ent.pkg_class);
(void) strcpy(el_ent->cf_ent.pkg_class,
cf_ent->pkg_class);
chgclass(&(el_ent->cf_ent), pkgpinfo);
}
}
if (((el_ent->cf_ent.ftype == 's') || (el_ent->cf_ent.ftype == 'l'))) {
if (cf_ent->ainfo.local && el_ent->cf_ent.ainfo.local) {
if (strcmp(cf_ent->ainfo.local,
el_ent->cf_ent.ainfo.local) != 0) {
changed++;
if (strcmp(el_ent->cf_ent.ainfo.local,
"?") == 0) {
(void) strlcpy(
el_ent->cf_ent.ainfo.local,
cf_ent->ainfo.local,
PATH_MAX);
} else {
el_ent->mstat.contchg = 1;
}
}
}
return (changed ? MRG_DIFFERENT : MRG_SAME);
} else if (el_ent->cf_ent.ftype == 'e') {
el_ent->mstat.contchg = 1;
changed++;
} else if (((el_ent->cf_ent.ftype == 'f') ||
(el_ent->cf_ent.ftype == 'v'))) {
if (cf_ent->cinfo.size != el_ent->cf_ent.cinfo.size) {
changed++;
el_ent->mstat.contchg = 1;
} else if (cf_ent->cinfo.modtime !=
el_ent->cf_ent.cinfo.modtime) {
changed++;
el_ent->mstat.contchg = 1;
} else if (cf_ent->cinfo.cksum != el_ent->cf_ent.cinfo.cksum) {
changed++;
el_ent->mstat.contchg = 1;
}
} else if (((el_ent->cf_ent.ftype == 'c') ||
(el_ent->cf_ent.ftype == 'b'))) {
if (cf_ent->ainfo.major != el_ent->cf_ent.ainfo.major) {
changed++;
if (el_ent->cf_ent.ainfo.major == BADMAJOR) {
el_ent->cf_ent.ainfo.major =
cf_ent->ainfo.major;
} else {
el_ent->mstat.contchg = 1;
}
}
if (cf_ent->ainfo.minor != el_ent->cf_ent.ainfo.minor) {
changed++;
if (el_ent->cf_ent.ainfo.minor == BADMINOR)
el_ent->cf_ent.ainfo.minor =
cf_ent->ainfo.minor;
else
el_ent->mstat.contchg = 1;
}
}
if (cf_ent->ainfo.mode != el_ent->cf_ent.ainfo.mode) {
changed++;
if (el_ent->cf_ent.ainfo.mode == BADMODE) {
el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
} else if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
el_ent->mstat.attrchg = 0;
} else {
el_ent->mstat.attrchg = 1;
}
}
if (strcmp(cf_ent->ainfo.owner, el_ent->cf_ent.ainfo.owner) != 0) {
changed++;
if (strcmp(el_ent->cf_ent.ainfo.owner, BADOWNER) == 0)
(void) strcpy(el_ent->cf_ent.ainfo.owner,
cf_ent->ainfo.owner);
else
el_ent->mstat.attrchg = 1;
}
if (strcmp(cf_ent->ainfo.group, el_ent->cf_ent.ainfo.group) != 0) {
changed++;
if (strcmp(el_ent->cf_ent.ainfo.group, BADGROUP) == 0)
(void) strcpy(el_ent->cf_ent.ainfo.group,
cf_ent->ainfo.group);
else
el_ent->mstat.attrchg = 1;
}
return (changed ? MRG_DIFFERENT : MRG_SAME);
}
static void
output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo)
{
short svvolno;
char *svpt;
svvolno = ent->volno;
ent->volno = 0;
pinfo->editflag = 0;
if (((ent->ftype == 's') || (ent->ftype == 'l'))) {
if (putcvfpfile(ent, vfpo)) {
progerr(gettext(ERR_OUTPUT));
quit(99);
}
} else {
svpt = ent->ainfo.local;
ent->ainfo.local = NULL;
if (putcvfpfile(ent, vfpo)) {
progerr(gettext(ERR_OUTPUT));
quit(99);
}
ent->ainfo.local = svpt;
if (pinfo->editflag)
ent->ftype = 'e';
}
ent->volno = svvolno;
}
static void
chgclass(struct cfent *cf_ent, struct pinfo *pinfo)
{
struct pinfo *pp;
char *oldclass, newclass[CLSSIZ+1];
int newcnt, oldcnt;
(void) strlcpy(newclass, pinfo->aclass, sizeof (newclass));
newcnt = 1;
oldclass = cf_ent->pkg_class;
oldcnt = 0;
pp = cf_ent->pinfo;
while (pp) {
if (pp->aclass[0] != '\0') {
if (strcmp(pp->aclass, newclass) == 0)
newcnt++;
else if (strcmp(pp->aclass, oldclass) == 0)
oldcnt++;
}
pp = pp->next;
}
if (newcnt > oldcnt) {
pp = cf_ent->pinfo;
while (pp) {
if (pp->aclass[0] == '\0') {
(void) strcpy(pp->aclass, oldclass);
} else if (strcmp(pp->aclass, newclass) == 0) {
pp->aclass[0] = '\0';
}
pp = pp->next;
}
(void) strcpy(cf_ent->pkg_class, newclass);
}
}