pkgdbmerg.c revision 62224350e5355e6834f7deb9d8a7d062a50cb7c2
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#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 <locale.h>
#include <libintl.h>
#include <pkginfo.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <libinst.h>
#include <messages.h>
/* 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 <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;
/* pkgobjmap.c */
/* setlist.c */
extern void cl_def_dverify(int idx);
int files_installed(void); /* return number of files installed. */
static int errflg = 0;
static int eptnum;
static int installed; /* # of files, already properly installed. */
/*
* 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
{
int n;
int changed;
int assume_ok = 0;
errflg = 0;
/*
* If there's an entry in the extlist at this position,
* process that entry.
*/
/* Metafiles don't get merged. */
continue;
}
/*
* Copy cfextra structure for duplicated paths.
* This is not just an optimization, it is
* necessary for correct operation of algorithm.
*/
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') {
QKVERIFY) {
assume_ok = 1;
}
} else {
/*
* 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.
*/
QKVERIFY) {
/*
* Set destination verification to
* default.
*/
}
}
/*
* Comply with administrative requirements regarding
*/
}
}
/*
* those bits.
*/
}
/* Search package database for this entry. */
/*
* If there was an error, note it and return an error
* flag.
*/
if (n < 0) {
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.
*/
}
}
/*
* Detect if a symlink has changed to directory
* iniside this dir, so that they are not miss
* understood by do_new_ent later as already
* installed.
*/
int i;
plen) != 0)
break;
= 1;
}
}
changed++;
}
} else {
/*
* The file doesn't exist in the database.
*/
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
{
/*
* 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.
*/
/* Contents file is not to be trusted for this list. */
/* Free the potentially bogus list. */
while (pkginfo) {
}
}
if (otherstoo)
/* If it's marked for erasure, make it official */
if (!errflg) {
/*
* 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.
*/
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) {
sizeof (struct pinfo));
}
}
return (1);
}
/*
* If there is no package associated with it, there's something
* very wrong.
*/
if (!pkgpinfo) {
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.
*/
/*
* First set the attrchg and contchg entries for proper
* messaging in the install phase.
*/
/*
* Now overwrite the new entry with the entry for the
* currently installed object.
*/
quit(99);
ignore++;
} else {
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.
*/
ignore++;
break;
default:
break;
}
}
/* el_ent structure now contains updated entry */
/*
* We know the DB entry matches the pkgmap, so now we need to
* see if the actual object matches the pkgmap.
*/
}
if (!errflg) {
/*
* The pkgpinfo entry is already correctly
* constructed. Look into dropping this soon.
*/
ENTRY_OK);
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.
*/
&(el_ent->fsys_value)))
} else {
pkgpinfo =
pkgpinfo =
pkgpinfo =
dbst);
changed++;
pkginst, '\0');
changed++;
}
}
}
if (mrg_result == MRG_REPLACE) {
/*
* Put the original package database entry back into
* the package database for now.
*/
} else {
/* Put the merged entry into the package database. */
}
}
}
/*
* 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.
*/
changed++;
}
/*
* If no change during the merg and we don't have a case where types
* were different in odd ways, count this as installed.
*/
installed++;
return (changed);
}
/* Insert an entirely new entry into the package database. */
static int
{
char *tp;
int changed = 0;
return (0);
}
/*
* 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.
*/
/*
* 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.
*/
} else {
/* since path doesn't exist, we're changing everything */
}
} else {
}
}
/*
* 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.
*/
return (0);
}
if (!errflg) {
/* Add this package to the already established list. */
} else {
sizeof (struct pinfo));
if (!pinfo) {
quit(99);
}
}
/*
* 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.
*/
&(el_ent->fsys_value)))
} else {
}
changed++;
}
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
{
int n;
char *tp;
}
if (n == VE_ATTR)
else if (n && (n != VE_EXIST)) {
}
if (n == VE_ATTR)
else if (n && (n == VE_EXIST)) {
}
}
}
static int
{
}
static int
{
}
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 */
};
/*
* 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
{
/* If they are identical, return OK */
return (TYPE_OK);
/*
* If package database entry is ambiguous, set it to the new entity's
* ftype
*/
return (TYPE_OK); /* do nothing; not really different */
}
/* If the new entity is ambiguous, wait for the verify */
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.
*/
return (TYPE_IGNORED);
}
/* Set etype to that of the new entity */
for (i = 0; types[i]; ++i) {
etype = i+1;
break;
}
}
/* Set itype to that in the package database. */
for (i = 0; types[i]; ++i) {
itype = i+1;
break;
}
}
/* same basic object type */
return (TYPE_OK);
}
/*
* If a simple object (like a file) is overwriting a directory, mark
* it for full inspection during installation.
*/
}
/* allow change, but warn user of possible problems */
switch (itype) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
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
{
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.
*/
}
/*
* 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).
*/
switch (n) {
case TYPE_OK:
break;
/* This is an allowable change. */
case TYPE_WARNING:
break;
/* Not allowed, but leaving it as is is OK. */
case TYPE_IGNORED:
quit(99);
return (MRG_SAME);
/* Future analysis will reveal if this is OK. */
case TYPE_REPLACE:
return (MRG_REPLACE);
/* Kill it before it does any damage. */
case TYPE_FATAL:
quit(99);
default:
break;
}
changed++;
}
/* Evaluate and merge the class. */
/*
* we always allow a class change as long as we have
* consistent ftypes, which at this point we must
*/
changed++;
}
}
/*
* Evaluate and merge based upon the ftype of the intended package
* database entry.
*/
/* If both have link sources, then they need to be merged. */
/*
* If both sources are identical, the merge is
* already done.
*/
changed++;
/*
* Otherwise, if the pkgmap entry is
* ambiguous, it will inherit the database
* entry.
*/
"?") == NULL) {
(void) strlcpy(
PATH_MAX);
} else {
}
}
}
/*
* 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.
*/
changed++;
/*
* 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.
*/
/*
* for type f/v files, if the file is in an area that is
* inherited from the global zone, that area is read only
* and the object cannot be changed - ignore any settings
* in the current package database that may be present for
* any existing object because they are irrelevant - since
* the object is in a read-only area shared from the global
* zone, accept that file's actual attributes as being correct.
*/
changed++;
changed++;
changed++;
}
/*
* 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.
*/
changed++;
} else {
}
}
changed++;
else
}
}
/*
* For mode, owner and group follow the same rules as above - if
* ambiguous, inherit, otherwise keep the new one.
*/
changed++; /* attribute info is changing */
/*
* If pkgmap has a '?' set for mode, use the mode from
* the pkg DB (contents file).
*/
} else {
}
}
changed++; /* attribute info is changing */
else
}
changed++; /* attribute info is changing */
else
}
}
/*
* 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
{
short svvolno;
char *svpt;
/* output without volume information */
quit(99);
}
} else {
/* output without local pathname */
quit(99);
}
/*
* 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
*/
}
/* restore volume information */
}
static void
{
/*
* we use this routine to minimize the use of the aclass element by
* optimizing the use of the cf_ent->pkg_class element
*/
newcnt = 1;
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
*/
while (pp) {
newcnt++;
oldcnt++;
}
}
while (pp) {
}
}
}
}