/* * 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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* merg() return codes */ #define MRG_SAME 0 #define MRG_DIFFERENT 1 #define MRG_REPLACE 2 /* typechg() return codes */ #define TYPE_OK 0 #define TYPE_WARNING 1 #define TYPE_IGNORED 2 #define TYPE_REPLACE 3 #define TYPE_FATAL 4 /* message pool */ #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 " #define WRN_NOTSYMLN "WARNING: %s " #define WRN_NOTLINK "WARNING: %s " #define WRN_NOTDIR "WARNING: %s " #define WRN_NOTCHAR "WARNING: %s " #define WRN_NOTBLOCK "WARNING: %s " #define WRN_NOTPIPE "WARNING: %s " #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; /* pkgobjmap.c */ extern int cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent); /* setlist.c */ extern void cl_def_dverify(int idx); char dbst = '\0'; /* usually set by installf() or removef() */ int files_installed(void); /* return number of files installed. */ static int errflg = 0; static int eptnum; static int installed; /* # of files, already properly 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); /* * This scans the extlist (pkgmap) and matches them to the database, copying * out the modified contents to the file at tmpfp. It updates the mergstat * structures and deals with administrative defaults regarding setuid and * conflict. */ int pkgdbmerg(PKGserver server, VFP_T *tmpvfp, struct cfextra **extlist) { static struct cfent cf_ent; /* scratch area */ struct cfextra *el_ent; /* extlist entry under review */ 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 there's an entry in the extlist at this position, * process that entry. */ /* Metafiles don't get merged. */ if ((el_ent->cf_ent.ftype == 'i') || (el_ent->cf_ent.ftype == 'n')) { continue; } /* * Copy cfextra structure for duplicated paths. * This is not just an optimization, it is * necessary for correct operation of algorithm. */ 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; } /* * Normally dbst comes to us from installf() or * removef() in order to specify their special * database status codes. They cannot implement a * quick verify (it just doesn't make sense). For * that reason, we can test to see if we already have * a special database status. If we don't (it's from * pkgadd) then we can test to see if this is calling * for a quick verify wherein we assume the install * will work and fix it if it doesn't. In that case * we set our own dbst to be ENTRY_OK. */ if (dbst == '\0') { if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) == QKVERIFY) { assume_ok = 1; } } else { /* * If we DO end up with an installf/quick * verify combination, we fix that by simply * denying the quick verify for this class. * This forces everything to come out alright * by forcing the standard assumptions as * regards package database for the rest of * the load. */ if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) == QKVERIFY) { logerr(gettext(WRN_ODDVERIFY), cl_nam(el_ent->cf_ent.pkg_class_idx)); /* * Set destination verification to * default. */ cl_def_dverify(el_ent->cf_ent.pkg_class_idx); } } /* * Comply with administrative requirements regarding * setuid/setgid processes. */ 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 setuid/setgid processes are not allowed, reset * those bits. */ if (nosetuid && (el_ent->mstat.setgid || el_ent->mstat.setuid)) { el_ent->cf_ent.ainfo.mode &= ~(S_ISUID | S_ISGID); } /* Search package database for this entry. */ n = srchcfile(&cf_ent, el_ent->cf_ent.path, server); /* * If there was an error, note it and return an error * flag. */ 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); /* * If there was a match, then merge them into a * single entry. */ } else if (n == 1) { /* * If this package is overwriting a setuid or * setgid process, set the status bits so we * can inform the administrator. */ if (is_setuid(&cf_ent)) { el_ent->mstat.osetuid = 1; } if (is_setgid(&cf_ent)) { el_ent->mstat.osetgid = 1; } /* * Detect if a symlink has changed to directory * If so mark all the files/dir supposed to be * iniside this dir, so that they are not miss * understood by do_new_ent later as already * installed. */ 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 { /* * The file doesn't exist in the database. */ if (do_new_ent(tmpvfp, el_ent, assume_ok)) { changed++; } } } return (errflg ? -1 : changed); } /* * Merge a new entry with an installed package object of the same name and * insert that object into the package database. Obey administrative defaults * as regards conflicting files. */ 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; /* * Construct the record defining the current package. If there are * other packages involved, this will be appended to the existing * list. If this is an update of the same package, it will get merged * with the existing record. If this is a preloaded record (like from * a dryrun file), it will keep it's current pinfo pointer and will * pass it on to the record from the contents file - because on the * final continuation, the contents file will be wrong. */ if (el_ent->mstat.preloaded) { struct pinfo *pkginfo; /* Contents file is not to be trusted for this list. */ pkginfo = cf_ent->pinfo; /* Free the potentially bogus list. */ 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 it's marked for erasure, make it official */ if (el_ent->cf_ent.ftype == RM_RDY) { if (!errflg) { pkgpinfo = eptstat(cf_ent, pkginst, RM_RDY); /* * Get copy of status character in case the object is * "shared" by a server, in which case we need to * maintain the shared status after the entry is * written to the package database with RM_RDY * status. This is needed to support the `removef' * command. */ stflag = pkgpinfo->status; pkgpinfo->status = RM_RDY; if (putcvfpfile(cf_ent, vfpo)) { progerr(gettext(ERR_OUTPUT)); quit(99); } /* * If object is provided by a server, allocate an * info block and set the status to indicate this. * This is needed to support the `removef' command. */ 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 there is no package associated with it, there's something * very wrong. */ if (!pkgpinfo) { progerr(gettext(ERR_PINFO), cf_ent->path); quit(99); } /* * Do not allow installation if nocnflct is set and other packages * reference this pathname. The cp_cfent() function below writes the * information from the installed file over the new entry, so the * package database will be unchanged. * * By the way, ftype "e" is often shared and that's OK, so ftype * "e" doesn't count here. */ if ((nocnflct && el_ent->mstat.shared && el_ent->cf_ent.ftype != 'e')) { /* * First set the attrchg and contchg entries for proper * messaging in the install phase. */ set_change(el_ent); /* * Now overwrite the new entry with the entry for the * currently installed object. */ 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: /* * We'll pick one or the other later. For now, cf_ent * will have the fault value and el_ent will retain * the other value. This is the only state that allows * the database and the pkgmap to differ. */ el_ent->mstat.contchg = 1; /* subject to change */ ignore++; break; default: break; } } /* el_ent structure now contains updated entry */ if (!el_ent->mstat.contchg && !ignore) { /* * We know the DB entry matches the pkgmap, so now we need to * see if the actual object matches the pkgmap. */ set_change(el_ent); } if (!errflg) { if (ctrl == 1) { /* quick verify assumes OK */ /* * The pkgpinfo entry is already correctly * constructed. Look into dropping this soon. */ pkgpinfo = eptstat(&(el_ent->cf_ent), pkginst, ENTRY_OK); if (stflag != DUP_ENTRY) { changed++; } /* * We could trust the prior pkginfo entry, but things * could have changed and we need to update the * fs_tab[] anyway. We check for a server object * here. */ 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) { /* * Put the original package database entry back into * the package database for now. */ output(vfpo, cf_ent, pkgpinfo); } else { /* Put the merged entry into the package database. */ output(vfpo, &(el_ent->cf_ent), pkgpinfo); } } if (pkgpinfo->aclass[0] != '\0') { (void) strcpy(el_ent->cf_ent.pkg_class, pkgpinfo->aclass); } /* * If a sym link entry exists in the contents file and * and the destination of the link does not exist on the the system * then the contents file needs to be updated appropriately so a * subsequent invocation of "installf -f" will create the destination. */ if (el_ent->mstat.contchg && pkgpinfo->status == INST_RDY) { changed++; } if (!(el_ent->mstat.preloaded)) el_ent->cf_ent.pinfo = NULL; /* * If no change during the merg and we don't have a case where types * were different in odd ways, count this as installed. */ if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg && !el_ent->mstat.replace) installed++; return (changed); } /* Insert an entirely new entry into the package database. */ 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; /* * Check the file/dir existence only if any of the parent directory * of the file/dir has not changed from symbolic link to directory. * At this time we are only doing a dry run, the symlink is not yet * replaced, so if this is done directly then access will result in * incorrect information in case a file with the same attr and cont * exists in the link target. */ if ((!el_ent->mstat.parentsyml2dir) && (access(tp, F_OK) == 0)) { /* * Path exists, and although its not referenced by any * package we make it look like it is so it appears as a * conflicting file in case the user doesn't want it * installed. We set the rogue flag to distinguish this from * package object conflicts if the administrator is queried * about this later. Note that noconflict means NO conflict * at the file level. Even rogue files count. */ el_ent->mstat.shared = 1; el_ent->mstat.rogue = 1; set_change(el_ent); } else { /* since path doesn't exist, we're changing everything */ 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); /* * Do not allow installation if nocnflct is set and this pathname is * already in place. Since this entry is new (not associated with a * package), we don't issue anything to the database we're building. */ if (nocnflct && el_ent->mstat.shared) { return (0); } if (!errflg) { if (el_ent->mstat.preloaded) { /* Add this package to the already established list. */ 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) { /* quick verify assumes OK */ pinfo->status = dbst ? dbst : ENTRY_OK; /* * The entry won't be verified, but the entry in the * database isn't necessarily ENTRY_OK. If this is * coming from a server, we need to note that * instead. */ 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); } /* * This function determines if there is a difference between the file on * the disk and the file to be laid down. It set's mstat flags attrchg * and contchg accordingly. */ 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", /* type 1, regular files */ "s", /* type 2, symbolic links */ "l", /* type 3, linked files */ "dx", /* type 4, directories */ "c", /* type 5, character special devices */ "b", /* type 6, block special devices */ "p", /* type 7, named pipes */ NULL }; /* * This determines if the ftype of the file on the disk and the file to be * laid down are close enough. If they aren't, this either returns an error * or displays a warning. This returns : * TYPE_OK they're identical or close enough * TYPE_WARNING they're pretty close (probably no problem) * TYPE_IGNORED the type change was not allowed * TYPE_REPLACE to be reviewed later - in endofclass() maybe * TYPE_FATAL something awful happened */ static int typechg(struct cfent *el_ent, struct cfent *cf_ent, struct mergstat *mstat) { int i, etype, itype, retcode; /* If they are identical, return OK */ if (cf_ent->ftype == el_ent->ftype) return (TYPE_OK); /* * If package database entry is ambiguous, set it to the new entity's * ftype */ if (cf_ent->ftype == BADFTYPE) { cf_ent->ftype = el_ent->ftype; return (TYPE_OK); /* do nothing; not really different */ } /* If the new entity is ambiguous, wait for the verify */ if (el_ent->ftype == BADFTYPE) return (TYPE_OK); /* * If we're trying to convert an existing regular directory to an * exclusive directory, this is very dangerous. We will continue, but * we will deny the conversion. */ if (el_ent->ftype == 'x' && cf_ent->ftype == 'd') { logerr(gettext(WRN_TOEXCL), el_ent->path); return (TYPE_IGNORED); } etype = itype = 0; /* Set etype to that of the new entity */ for (i = 0; types[i]; ++i) { if (strchr(types[i], el_ent->ftype)) { etype = i+1; break; } } /* Set itype to that in the package database. */ for (i = 0; types[i]; ++i) { if (strchr(types[i], cf_ent->ftype)) { itype = i+1; break; } } if (itype == etype) { /* same basic object type */ return (TYPE_OK); } retcode = TYPE_WARNING; /* * If a simple object (like a file) is overwriting a directory, mark * it for full inspection during installation. */ if (etype != 4 && itype == 4) { mstat->dir2nondir = 1; retcode = TYPE_REPLACE; } /* allow change, but warn user of possible problems */ 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); } /* * This function takes el_ent (the entry from the pkgmap) and cf_ent (the * entry from the package database) and merge them into el_ent. The rules * are still being figured out, but the comments should make the approach * pretty clear. * * RETURN CODES: * MRG_DIFFERENT The two entries are different and el_ent now contains * the intended new entry to be installed. * MRG_SAME The two entries were identical and the old database * entry will be replaced unchanged. * MRG_REPLACE One or the other entry will be used but the decision * has to be made at install time. */ static int merg(struct cfextra *el_ent, struct cfent *cf_ent) { int n, changed = 0; /* * We need to change the original entry to make it look like the new * entry (the eptstat() routine has already added appropriate package * information, but not about 'aclass' which may represent a change * in class from the previous installation. * * NOTE: elent->cf_ent.pinfo (the list of associated packages) is NULL * upon entry to this function. */ 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; } /* * Evaluate the ftype change. Usually the ftype won't change. If it * does it may be easy (s -> f), not allowed (d -> x), so complex we * can't figure it 'til later (d -> s) or fatal (a hook for later). */ 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; /* This is an allowable change. */ case TYPE_WARNING: el_ent->mstat.contchg = 1; break; /* Not allowed, but leaving it as is is OK. */ case TYPE_IGNORED: logerr(gettext(MSG_TYPIGN)); if (cp_cfent(cf_ent, el_ent) == 0) quit(99); return (MRG_SAME); /* Future analysis will reveal if this is OK. */ case TYPE_REPLACE: el_ent->mstat.replace = 1; return (MRG_REPLACE); /* Kill it before it does any damage. */ case TYPE_FATAL: logerr(gettext(MSG_TYPE_ERR)); quit(99); default: break; } changed++; } /* Evaluate and merge the class. */ if (strcmp(cf_ent->pkg_class, el_ent->cf_ent.pkg_class)) { /* * we always allow a class change as long as we have * consistent ftypes, which at this point we must */ 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); } } /* * Evaluate and merge based upon the ftype of the intended package * database entry. */ if (((el_ent->cf_ent.ftype == 's') || (el_ent->cf_ent.ftype == 'l'))) { /* If both have link sources, then they need to be merged. */ if (cf_ent->ainfo.local && el_ent->cf_ent.ainfo.local) { /* * If both sources are identical, the merge is * already done. */ if (strcmp(cf_ent->ainfo.local, el_ent->cf_ent.ainfo.local) != 0) { changed++; /* * Otherwise, if the pkgmap entry is * ambiguous, it will inherit the database * entry. */ 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') { /* * The contents of edittable files are assumed to be changing * since some class action script will be doing the work and * we have no way of evaluating what it will actually do. */ el_ent->mstat.contchg = 1; changed++; } else if (((el_ent->cf_ent.ftype == 'f') || (el_ent->cf_ent.ftype == 'v'))) { /* * For regular files, Look at content information; a BADCONT * in any el_ent field indicates the contents are unknown -- * since cf_ent is guaranteed to have a valid entry here (bad * assumption?) this function will recognize this as a * change. The ambiguous el_ent values will be evaluated and * set later. */ 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'))) { /* * For devices, if major or minor numbers are identical the * merge is trivial. If the el_ent value is ambiguous (BAD), * the cf_ent value is inherited. Otherwise, the el_ent value * is preserved. */ 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; } } /* * For mode, owner and group follow the same rules as above - if * ambiguous, inherit, otherwise keep the new one. */ if (cf_ent->ainfo.mode != el_ent->cf_ent.ainfo.mode) { changed++; /* attribute info is changing */ 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) { /* * If pkgmap has a '?' set for mode, use the mode from * the pkg DB (contents file). */ 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++; /* attribute info is changing */ 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++; /* attribute info is changing */ 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); } /* * This puts the current entry into the package database in the appropriate * intermediate format for this stage of the installation. This also assures * the correct format for the various package object ftypes, stripping the * link name before storing a regular file and stuff like that. */ static void output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo) { short svvolno; char *svpt; /* output without volume information */ 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 { /* output without local pathname */ svpt = ent->ainfo.local; ent->ainfo.local = NULL; if (putcvfpfile(ent, vfpo)) { progerr(gettext(ERR_OUTPUT)); quit(99); } ent->ainfo.local = svpt; /* * If this entry represents a file which is being edited, we * need to store in memory the fact that it is an edittable * file so that when we audit it after installation we do not * worry about its contents; we do this by resetting the ftype * to 'e' in the memory array which is later used to control * the audit */ if (pinfo->editflag) ent->ftype = 'e'; } /* restore volume information */ ent->volno = svvolno; } static void chgclass(struct cfent *cf_ent, struct pinfo *pinfo) { struct pinfo *pp; char *oldclass, newclass[CLSSIZ+1]; int newcnt, oldcnt; /* * we use this routine to minimize the use of the aclass element by * optimizing the use of the cf_ent->pkg_class element */ (void) strlcpy(newclass, pinfo->aclass, sizeof (newclass)); newcnt = 1; oldclass = cf_ent->pkg_class; oldcnt = 0; /* * count the number of times the newclass will be used and see if it * exceeds the number of times the oldclass is referenced */ 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); } }