diff options
Diffstat (limited to 'lib')
96 files changed, 19018 insertions, 9461 deletions
diff --git a/lib/.gitignore b/lib/.gitignore deleted file mode 100644 index 1fb7263f8..000000000 --- a/lib/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/tagtbl.C diff --git a/lib/Makefile.am b/lib/Makefile.am index 9e725b36d..baf3238ee 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,10 +1,13 @@ # Makefile for rpm library. include $(top_srcdir)/rpm.am +AM_CFLAGS = @RPMCFLAGS@ AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/ +AM_CPPFLAGS += @WITH_BEECRYPT_INCLUDE@ AM_CPPFLAGS += @WITH_NSS_INCLUDE@ AM_CPPFLAGS += @WITH_POPT_INCLUDE@ +AM_CPPFLAGS += $(LMDB_CFLAGS) AM_CPPFLAGS += -I$(top_srcdir)/misc AM_CPPFLAGS += -DLOCALEDIR="\"$(localedir)\"" AM_CPPFLAGS += -DSYSCONFDIR="\"$(sysconfdir)\"" @@ -21,7 +24,8 @@ EXTRA_PROGRAMS = usrlib_LTLIBRARIES = librpm.la librpm_la_SOURCES = \ - backend/dbconfig.c backend/db3.c backend/dbi.h \ + backend/db3.c backend/dbi.c backend/dbi.h \ + backend/dbiset.c backend/dbiset.h \ headerutil.c header.c headerfmt.c header_internal.h \ rpmdb.c rpmdb_internal.h \ fprint.c fprint.h tagname.c rpmtd.c \ @@ -34,17 +38,17 @@ librpm_la_SOURCES = \ rpmlead.c rpmlead.h rpmps.c rpmprob.c rpmrc.c \ rpmte.c rpmte_internal.h rpmts.c rpmfs.h rpmfs.c \ rpmvercmp.c signature.c signature.h transaction.c \ - verify.c rpmlock.c rpmlock.h misc.h \ - rpmscript.h rpmscript.c legacy.c merge.c \ + verify.c rpmlock.c rpmlock.h misc.h relocation.c \ + rpmscript.h rpmscript.c \ rpmchroot.c rpmchroot.h \ - rpmplugins.c rpmplugins.h rpmug.c rpmug.h + rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \ + rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h -librpm_la_LDFLAGS = -version-info 4:0:1 +librpm_la_LDFLAGS = -version-info $(rpm_version_info) librpm_la_LIBADD = \ $(top_builddir)/rpmio/librpmio.la \ @WITH_POPT_LIB@ \ - @WITH_SELINUX_LIB@ \ @WITH_CAP_LIB@ \ @WITH_ACL_LIB@ \ @LIBINTL@ @@ -61,6 +65,24 @@ else librpm_la_LIBADD += @WITH_DB_LIB@ endif +if NDB +librpm_la_SOURCES += \ + backend/ndb/glue.c \ + backend/ndb/rpmpkg.c \ + backend/ndb/rpmpkg.h \ + backend/ndb/rpmidx.c \ + backend/ndb/rpmidx.h \ + backend/ndb/rpmxdb.c \ + backend/ndb/rpmxdb.h +endif + +if LMDB +AM_CPPFLAGS += $(LMDB_CFLAGS) +librpm_la_LIBADD += $(LMDB_LIBS) +librpm_la_SOURCES += \ + backend/lmdb.c +endif + tagtbl.C: Makefile.am $(srcdir)/rpmtag.h gentagtbl.sh @AWK=${AWK} ${SHELL} $(srcdir)/gentagtbl.sh \ $(srcdir)/rpmtag.h > $@.new && \ diff --git a/lib/backend/db3.c b/lib/backend/db3.c index a55a608cf..7a86d3525 100644 --- a/lib/backend/db3.c +++ b/lib/backend/db3.c @@ -8,6 +8,9 @@ static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */ #include <errno.h> #include <sys/wait.h> +#include <popt.h> +#include <db.h> +#include <signal.h> #include <rpm/rpmtypes.h> #include <rpm/rpmmacro.h> @@ -22,19 +25,80 @@ static const char * _errpfx = "rpmdb"; struct dbiCursor_s { dbiIndex dbi; + const void *key; + unsigned int keylen; + int flags; DBC *cursor; }; +static struct dbiConfig_s staticdbicfg; +static struct dbConfig_s staticcfg; + +/** \ingroup dbi + */ +static const struct poptOption rdbOptions[] = { + /* Environment options */ + + { "cdb", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_CDB, + NULL, NULL }, + { "lock", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOCK, + NULL, NULL }, + { "log", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOG, + NULL, NULL }, + { "txn", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_TXN, + NULL, NULL }, + { "recover", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER, + NULL, NULL }, + { "recover_fatal", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER_FATAL, + NULL, NULL }, + { "lockdown", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_LOCKDOWN, + NULL, NULL }, + { "private", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_PRIVATE, + NULL, NULL }, + + { "deadlock", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_DEADLOCK, + NULL, NULL }, + { "recovery", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_RECOVERY, + NULL, NULL }, + { "waitsfor", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_WAITSFOR, + NULL, NULL }, + { "verbose", 0,POPT_ARG_VAL, &staticcfg.db_verbose, -1, + NULL, NULL }, + + { "cachesize", 0,POPT_ARG_INT, &staticcfg.db_cachesize, 0, + NULL, NULL }, + { "mmapsize", 0,POPT_ARG_INT, &staticcfg.db_mmapsize, 0, + NULL, NULL }, + { "mp_mmapsize", 0,POPT_ARG_INT, &staticcfg.db_mmapsize, 0, + NULL, NULL }, + { "mp_size", 0,POPT_ARG_INT, &staticcfg.db_cachesize, 0, + NULL, NULL }, + + { "nofsync", 0,POPT_ARG_NONE, &staticcfg.db_no_fsync, 0, + NULL, NULL }, + + /* Per-dbi options */ + { "nommap", 0,POPT_BIT_SET, &staticdbicfg.dbi_oflags, DB_NOMMAP, + NULL, NULL }, + + { "nodbsync", 0,POPT_ARG_NONE, &staticdbicfg.dbi_no_dbsync, 0, + NULL, NULL }, + { "lockdbfd", 0,POPT_ARG_NONE, &staticdbicfg.dbi_lockdbfd, 0, + NULL, NULL }, + + POPT_TABLEEND +}; + + static int dbapi_err(rpmdb rdb, const char * msg, int error, int printit) { if (printit && error) { - int db_api = rdb->db_ver; if (msg) - rpmlog(RPMLOG_ERR, _("db%d error(%d) from %s: %s\n"), - db_api, error, msg, db_strerror(error)); + rpmlog(RPMLOG_ERR, _("%s error(%d) from %s: %s\n"), + rdb->db_descr, error, msg, db_strerror(error)); else - rpmlog(RPMLOG_ERR, _("db%d error(%d): %s\n"), - db_api, error, db_strerror(error)); + rpmlog(RPMLOG_ERR, _("%s error(%d): %s\n"), + rdb->db_descr, error, db_strerror(error)); } return error; } @@ -49,6 +113,11 @@ static void errlog(const DB_ENV * env, const char *errpfx, const char *msg) rpmlog(RPMLOG_ERR, "%s: %s\n", errpfx, msg); } +static void warnlog(const DB_ENV *env, const char *msg) +{ + rpmlog(RPMLOG_WARNING, "%s: %s\n", _errpfx, msg); +} + static uint32_t db_envflags(DB * db) { DB_ENV * env = db->get_env(db); @@ -57,10 +126,42 @@ static uint32_t db_envflags(DB * db) return eflags; } +/* + * Try to acquire db environment open/close serialization lock. + * Return the open, locked fd on success, -1 on failure. + */ +static int serialize_env(const char *dbhome) +{ + char *lock_path = rstrscat(NULL, dbhome, "/.dbenv.lock", NULL); + mode_t oldmask = umask(022); + int fd = open(lock_path, (O_RDWR|O_CREAT), 0644); + umask(oldmask); + + if (fd >= 0) { + int rc; + struct flock info; + memset(&info, 0, sizeof(info)); + info.l_type = F_WRLCK; + info.l_whence = SEEK_SET; + do { + rc = fcntl(fd, F_SETLKW, &info); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + close(fd); + fd = -1; + } + } + + free(lock_path); + return fd; +} + static int db_fini(rpmdb rdb, const char * dbhome) { DB_ENV * dbenv = rdb->db_dbenv; int rc; + int lockfd = -1; uint32_t eflags = 0; if (dbenv == NULL) @@ -72,6 +173,9 @@ static int db_fini(rpmdb rdb, const char * dbhome) } (void) dbenv->get_open_flags(dbenv, &eflags); + if (!(eflags & DB_PRIVATE)) + lockfd = serialize_env(dbhome); + rc = dbenv->close(dbenv, 0); rc = dbapi_err(rdb, "dbenv->close", rc, _debug); @@ -89,6 +193,10 @@ static int db_fini(rpmdb rdb, const char * dbhome) rpmlog(RPMLOG_DEBUG, "removed db environment %s\n", dbhome); } + + if (lockfd >= 0) + close(lockfd); + return rc; } @@ -117,11 +225,189 @@ static int isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, uint32_t flags) return alive; } + +static void dbConfigure(rpmDbiTagVal rpmtag, struct dbConfig_s *cfg, struct dbiConfig_s *dbicfg) +{ + char *dbOpts; + + dbOpts = rpmExpand("%{_dbi_config_", rpmTagGetName(rpmtag), "}", NULL); + + if (!(dbOpts && *dbOpts && *dbOpts != '%')) { + dbOpts = _free(dbOpts); + dbOpts = rpmExpand("%{_dbi_config}", NULL); + if (!(dbOpts && *dbOpts && *dbOpts != '%')) { + dbOpts = _free(dbOpts); + } + } + + /* Parse the options for the database element(s). */ + if (dbOpts && *dbOpts && *dbOpts != '%') { + char *o, *oe; + char *p, *pe; + + memset(&staticdbicfg, 0, sizeof(staticdbicfg)); +/*=========*/ + for (o = dbOpts; o && *o; o = oe) { + const struct poptOption *opt; + const char * tok; + unsigned int argInfo; + + /* Skip leading white space. */ + while (*o && risspace(*o)) + o++; + + /* Find and terminate next key=value pair. Save next start point. */ + for (oe = o; oe && *oe; oe++) { + if (risspace(*oe)) + break; + if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/')) + break; + } + if (oe && *oe) + *oe++ = '\0'; + if (*o == '\0') + continue; + + /* Separate key from value, save value start (if any). */ + for (pe = o; pe && *pe && *pe != '='; pe++) + {}; + p = (pe ? *pe++ = '\0', pe : NULL); + + /* Skip over negation at start of token. */ + for (tok = o; *tok == '!'; tok++) + {}; + + /* Find key in option table. */ + for (opt = rdbOptions; opt->longName != NULL; opt++) { + if (!rstreq(tok, opt->longName)) + continue; + break; + } + if (opt->longName == NULL) { + rpmlog(RPMLOG_ERR, + _("unrecognized db option: \"%s\" ignored.\n"), o); + continue; + } + + /* Toggle the flags for negated tokens, if necessary. */ + argInfo = opt->argInfo; + if (argInfo == POPT_BIT_SET && *o == '!' && ((tok - o) % 2)) + argInfo = POPT_BIT_CLR; + + /* Save value in template as appropriate. */ + switch (argInfo & POPT_ARG_MASK) { + + case POPT_ARG_NONE: + (void) poptSaveInt((int *)opt->arg, argInfo, 1L); + break; + case POPT_ARG_VAL: + (void) poptSaveInt((int *)opt->arg, argInfo, (long)opt->val); + break; + case POPT_ARG_STRING: + { char ** t = opt->arg; + if (t) { +/* FIX: opt->arg annotation in popt.h */ + *t = _free(*t); + *t = xstrdup( (p ? p : "") ); + } + } break; + + case POPT_ARG_INT: + case POPT_ARG_LONG: + { long aLong = strtol(p, &pe, 0); + if (pe) { + if (!rstrncasecmp(pe, "Mb", 2)) + aLong *= 1024 * 1024; + else if (!rstrncasecmp(pe, "Kb", 2)) + aLong *= 1024; + else if (*pe != '\0') { + rpmlog(RPMLOG_ERR, + _("%s has invalid numeric value, skipped\n"), + opt->longName); + continue; + } + } + + if ((argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) { + if (aLong == LONG_MIN || aLong == LONG_MAX) { + rpmlog(RPMLOG_ERR, + _("%s has too large or too small long value, skipped\n"), + opt->longName); + continue; + } + (void) poptSaveLong((long *)opt->arg, argInfo, aLong); + break; + } else { + if (aLong > INT_MAX || aLong < INT_MIN) { + rpmlog(RPMLOG_ERR, + _("%s has too large or too small integer value, skipped\n"), + opt->longName); + continue; + } + (void) poptSaveInt((int *)opt->arg, argInfo, aLong); + } + } break; + default: + break; + } + } +/*=========*/ + } + + dbOpts = _free(dbOpts); + if (cfg) { + *cfg = staticcfg; /* structure assignment */ + /* Throw in some defaults if configuration didn't set any */ + if (!cfg->db_mmapsize) + cfg->db_mmapsize = 16 * 1024 * 1024; + if (!cfg->db_cachesize) + cfg->db_cachesize = 8 * 1024 * 1024; + } + if (dbicfg) { + *dbicfg = staticdbicfg; + } +} + +static char * prDbiOpenFlags(int dbflags, int print_dbenv_flags) +{ + ARGV_t flags = NULL; + const struct poptOption *opt; + char *buf; + + for (opt = rdbOptions; opt->longName != NULL; opt++) { + if (opt->argInfo != POPT_BIT_SET) + continue; + if (print_dbenv_flags) { + if (!(opt->arg == &staticcfg.db_eflags)) + continue; + } else { + if (!(opt->arg == &staticdbicfg.dbi_oflags)) + continue; + } + if ((dbflags & opt->val) != opt->val) + continue; + argvAdd(&flags, opt->longName); + dbflags &= ~opt->val; + } + if (dbflags) { + char *df = NULL; + rasprintf(&df, "0x%x", (unsigned)dbflags); + argvAdd(&flags, df); + free(df); + } + buf = argvJoin(flags, ":"); + argvFree(flags); + + return buf ? buf : xstrdup("(none)"); +} + static int db_init(rpmdb rdb, const char * dbhome) { DB_ENV *dbenv = NULL; int rc, xx; int retry_open = 2; + int lockfd = -1; + int rdonly = ((rdb->db_mode & O_ACCMODE) == O_RDONLY); struct dbConfig_s * cfg = &rdb->cfg; /* This is our setup, thou shall not have other setups before us */ uint32_t eflags = (DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB); @@ -129,6 +415,10 @@ static int db_init(rpmdb rdb, const char * dbhome) if (rdb->db_dbenv != NULL) { rdb->db_opens++; return 0; + } else { + /* On first call, set backend description to something... */ + free(rdb->db_descr); + rasprintf(&rdb->db_descr, "db%u", DB_VERSION_MAJOR); } /* @@ -150,6 +440,7 @@ static int db_init(rpmdb rdb, const char * dbhome) dbenv->set_alloc(dbenv, rmalloc, rrealloc, NULL); dbenv->set_errcall(dbenv, NULL); dbenv->set_errpfx(dbenv, _errpfx); + dbenv->set_msgcall(dbenv, warnlog); /* * These enable automatic stale lock removal. @@ -176,6 +467,24 @@ static int db_init(rpmdb rdb, const char * dbhome) } /* + * Serialize shared environment open (and clock) via fcntl() lock. + * Otherwise we can end up calling dbenv->failchk() while another + * process is joining the environment, leading to transient + * DB_RUNRECOVER errors. Also prevents races wrt removing the + * environment (eg chrooted operation). Silently fall back to + * private environment on failure to allow non-privileged queries + * to "work", broken as it might be. + */ + if (!(eflags & DB_PRIVATE)) { + lockfd = serialize_env(dbhome); + if (lockfd < 0 && rdonly) { + eflags |= DB_PRIVATE; + retry_open--; + rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n"); + } + } + + /* * Actually open the environment. Fall back to private environment * if we dont have permission to join/create shared environment or * system doesn't support it.. @@ -186,7 +495,10 @@ static int db_init(rpmdb rdb, const char * dbhome) free(fstr); rc = (dbenv->open)(dbenv, dbhome, eflags, rdb->db_perms); - if ((rc == EACCES || rc == EROFS || rc == EINVAL) && errno == rc) { + if (rc == EINVAL && errno == rc) { + eflags |= DB_PRIVATE; + retry_open--; + } else if (rdonly && (rc == EACCES || rc == EROFS || rc == DB_VERSION_MISMATCH)) { eflags |= DB_PRIVATE; retry_open--; } else { @@ -208,6 +520,8 @@ static int db_init(rpmdb rdb, const char * dbhome) rdb->db_dbenv = dbenv; rdb->db_opens = 1; + if (lockfd >= 0) + close(lockfd); return 0; errxit: @@ -216,10 +530,12 @@ errxit: xx = dbenv->close(dbenv, 0); xx = dbapi_err(rdb, "dbenv->close", xx, _debug); } + if (lockfd >= 0) + close(lockfd); return rc; } -void dbSetFSync(void *dbenv, int enable) +static void db3_dbSetFSync(rpmdb rdb, int enable) { #ifdef HAVE_FDATASYNC db_env_set_func_fsync(enable ? fdatasync : fsync_disable); @@ -228,19 +544,24 @@ void dbSetFSync(void *dbenv, int enable) #endif } -int dbiSync(dbiIndex dbi, unsigned int flags) +static int db3_Ctrl(rpmdb rdb, dbCtrlOp ctrl) +{ + return 0; +} + +static int dbiSync(dbiIndex dbi, unsigned int flags) { DB * db = dbi->dbi_db; int rc = 0; - if (db != NULL && !dbi->dbi_no_dbsync) { + if (db != NULL && !dbi->cfg.dbi_no_dbsync) { rc = db->sync(db, flags); rc = cvtdberr(dbi, "db->sync", rc, _debug); } return rc; } -dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags) +static dbiCursor db3_dbiCursorInit(dbiIndex dbi, unsigned int flags) { dbiCursor dbc = NULL; @@ -248,33 +569,52 @@ dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags) DB * db = dbi->dbi_db; DBC * cursor; int cflags; - int rc; + int rc = 0; uint32_t eflags = db_envflags(db); /* DB_WRITECURSOR requires CDB and writable db */ - if ((flags & DB_WRITECURSOR) && - (eflags & DB_INIT_CDB) && !(dbi->dbi_oflags & DB_RDONLY)) + if ((flags & DBC_WRITE) && + (eflags & DB_INIT_CDB) && !(dbi->dbi_flags & DBI_RDONLY)) { cflags = DB_WRITECURSOR; } else cflags = 0; - rc = db->cursor(db, NULL, &cursor, cflags); - rc = cvtdberr(dbi, "db->cursor", rc, _debug); + /* + * Check for stale locks which could block writes "forever". + * XXX: Should we also do this on reads? Reads are less likely + * to get blocked so it seems excessive... + * XXX: On DB_RUNRECOVER, we should abort everything. Now + * we'll just fail to open a cursor again and again and again. + */ + if (cflags & DB_WRITECURSOR) { + DB_ENV *dbenv = db->get_env(db); + rc = dbenv->failchk(dbenv, 0); + rc = cvtdberr(dbi, "dbenv->failchk", rc, _debug); + } + + if (rc == 0) { + rc = db->cursor(db, NULL, &cursor, cflags); + rc = cvtdberr(dbi, "db->cursor", rc, _debug); + } if (rc == 0) { dbc = xcalloc(1, sizeof(*dbc)); dbc->cursor = cursor; dbc->dbi = dbi; + dbc->flags = flags; } } return dbc; } -dbiCursor dbiCursorFree(dbiCursor dbc) +static dbiCursor db3_dbiCursorFree(dbiIndex dbi, dbiCursor dbc) { if (dbc) { + /* Automatically sync on write-cursor close */ + if (dbc->flags & DBC_WRITE) + dbiSync(dbc->dbi, 0); DBC * cursor = dbc->cursor; int rc = cursor->c_close(cursor); cvtdberr(dbc->dbi, "dbcursor->c_close", rc, _debug); @@ -283,7 +623,7 @@ dbiCursor dbiCursorFree(dbiCursor dbc) return NULL; } -int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) +static int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) { int rc = EINVAL; int sane = (key->data != NULL && key->size > 0 && @@ -302,7 +642,7 @@ int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) return rc; } -int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) +static int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) { int rc = EINVAL; int sane = ((flags == DB_NEXT) || (key->data != NULL && key->size > 0)); @@ -319,12 +659,21 @@ int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) _printit = (rc == DB_NOTFOUND ? 0 : _debug); rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit); + /* Remember the last key fetched */ + if (rc == 0) { + dbc->key = key->data; + dbc->keylen = key->size; + } else { + dbc->key = NULL; + dbc->keylen = 0; + } + rpmswExit(&rdb->db_getops, data->size); } return rc; } -int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) +static int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) { int rc = EINVAL; int sane = (key->data != NULL && key->size > 0); @@ -350,23 +699,7 @@ int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) return rc; } -unsigned int dbiCursorCount(dbiCursor dbc) -{ - db_recno_t count = 0; - if (dbc) { - DBC * cursor = dbc->cursor; - int rc = cursor->c_count(cursor, &count, 0); - cvtdberr(dbc->dbi, "dbcursor->c_count", rc, _debug); - } - return count; -} - -dbiIndex dbiCursorIndex(dbiCursor dbc) -{ - return (dbc != NULL) ? dbc->dbi : NULL; -} - -int dbiByteSwapped(dbiIndex dbi) +static int dbiByteSwapped(dbiIndex dbi) { DB * db = dbi->dbi_db; int rc = 0; @@ -383,32 +716,7 @@ int dbiByteSwapped(dbiIndex dbi) return rc; } -dbiIndexType dbiType(dbiIndex dbi) -{ - return dbi->dbi_type; -} - -int dbiFlags(dbiIndex dbi) -{ - DB *db = dbi->dbi_db; - int flags = DBI_NONE; - uint32_t oflags = 0; - - if (db && db->get_open_flags(db, &oflags) == 0) { - if (oflags & DB_CREATE) - flags |= DBI_CREATED; - if (oflags & DB_RDONLY) - flags |= DBI_RDONLY; - } - return flags; -} - -const char * dbiName(dbiIndex dbi) -{ - return dbi->dbi_file; -} - -int dbiVerify(dbiIndex dbi, unsigned int flags) +static int db3_dbiVerify(dbiIndex dbi, unsigned int flags) { int rc = 0; @@ -428,7 +736,7 @@ int dbiVerify(dbiIndex dbi, unsigned int flags) static int _lockdbfd = 0; -int dbiClose(dbiIndex dbi, unsigned int flags) +static int db3_dbiClose(dbiIndex dbi, unsigned int flags) { rpmdb rdb = dbi->dbi_rpmdb; const char * dbhome = rpmdbHome(rdb); @@ -445,7 +753,7 @@ int dbiClose(dbiIndex dbi, unsigned int flags) rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n", dbhome, dbi->dbi_file); - if (dbi->dbi_lockdbfd && _lockdbfd) + if (dbi->cfg.dbi_lockdbfd && _lockdbfd) _lockdbfd--; } @@ -533,7 +841,7 @@ static int dbiFlock(dbiIndex dbi, int mode) return rc; } -int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) +static int db3_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) { const char *dbhome = rpmdbHome(rdb); dbiIndex dbi = NULL; @@ -548,18 +856,20 @@ int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) if (dbip) *dbip = NULL; - /* - * Parse db configuration parameters. - */ if ((dbi = dbiNew(rdb, rpmtag)) == NULL) return 1; - oflags = dbi->dbi_oflags; + /* + * Parse db configuration parameters. + */ + dbConfigure(rpmtag, rdb->db_dbenv == NULL ? &rdb->cfg : NULL, &dbi->cfg); /* * Map open mode flags onto configured database/environment flags. */ - if ((rdb->db_mode & O_ACCMODE) == O_RDONLY) oflags |= DB_RDONLY; + oflags = dbi->cfg.dbi_oflags; + if ((rdb->db_mode & O_ACCMODE) == O_RDONLY) + oflags |= DB_RDONLY; rc = db_init(rdb, dbhome); @@ -587,7 +897,7 @@ int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) if (rc == ENOENT) { oflags |= DB_CREATE; oflags &= ~DB_RDONLY; - dbtype = (dbiType(dbi) == DBI_PRIMARY) ? DB_HASH : DB_BTREE; + dbtype = (rpmtag == RPMDBI_PACKAGES) ? DB_HASH : DB_BTREE; retry_open--; } else { retry_open = 0; @@ -615,12 +925,14 @@ int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) } dbi->dbi_db = db; - dbi->dbi_oflags = oflags; - if (verifyonly) - dbi->dbi_lockdbfd = 0; /* disable locking in verify mode */ + dbi->dbi_flags = 0; + if (oflags & DB_CREATE) + dbi->dbi_flags |= DBI_CREATED; + if (oflags & DB_RDONLY) + dbi->dbi_flags |= DBI_RDONLY; - if (rc == 0 && dbi->dbi_lockdbfd && _lockdbfd++ == 0) { + if (!verifyonly && rc == 0 && dbi->cfg.dbi_lockdbfd && _lockdbfd++ == 0) { rc = dbiFlock(dbi, rdb->db_mode); } @@ -632,59 +944,456 @@ int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) return rc; } - -int dbiSuspendDBLock(dbiIndex dbi, unsigned int flags) +/** + * Convert retrieved data to index set. + * @param dbi index database handle + * @param data retrieved data + * @retval setp (malloc'ed) index set + * @return 0 on success + */ +static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp) { - struct flock l; - int rc = 0; - int fdno = -1; + int _dbbyteswapped = dbiByteSwapped(dbi); + const char * sdbir; + dbiIndexSet set; + unsigned int i; - if (!dbi->dbi_lockdbfd) - return 0; - if (!(dbi->dbi_rpmdb->db_mode & (O_RDWR|O_WRONLY))) + if (dbi == NULL || data == NULL || setp == NULL) + return -1; + + if ((sdbir = data->data) == NULL) { + *setp = NULL; return 0; - if (_lockdbfd == 0) + } + + set = dbiIndexSetNew(data->size / (2 * sizeof(int32_t))); + set->count = data->size / (2 * sizeof(int32_t)); + + for (i = 0; i < set->count; i++) { + union _dbswap hdrNum, tagNum; + + memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); + sdbir += sizeof(hdrNum.ui); + memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui)); + sdbir += sizeof(tagNum.ui); + if (_dbbyteswapped) { + _DBSWAP(hdrNum); + _DBSWAP(tagNum); + } + set->recs[i].hdrNum = hdrNum.ui; + set->recs[i].tagNum = tagNum.ui; + } + *setp = set; + return 0; +} + +/** + * Convert index set to database representation. + * @param dbi index database handle + * @param data retrieved data + * @param set index set + * @return 0 on success + */ +static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set) +{ + int _dbbyteswapped = dbiByteSwapped(dbi); + char * tdbir; + unsigned int i; + + if (dbi == NULL || data == NULL || set == NULL) + return -1; + + data->size = set->count * (2 * sizeof(int32_t)); + if (data->size == 0) { + data->data = NULL; return 0; - if (!(dbi->dbi_db->fd(dbi->dbi_db, &fdno) == 0 && fdno >= 0)) - return 1; - memset(&l, 0, sizeof(l)); - l.l_whence = 0; - l.l_start = 0; - l.l_len = 0; - l.l_type = F_RDLCK; - rc = fcntl(fdno, F_SETLK, (void *)&l); + } + tdbir = data->data = xmalloc(data->size); + + for (i = 0; i < set->count; i++) { + union _dbswap hdrNum, tagNum; + + memset(&hdrNum, 0, sizeof(hdrNum)); + memset(&tagNum, 0, sizeof(tagNum)); + hdrNum.ui = set->recs[i].hdrNum; + tagNum.ui = set->recs[i].tagNum; + if (_dbbyteswapped) { + _DBSWAP(hdrNum); + _DBSWAP(tagNum); + } + memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); + tdbir += sizeof(hdrNum.ui); + memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui)); + tdbir += sizeof(tagNum.ui); + } + return 0; +} + +static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexSet *set, int searchType) +{ + rpmRC rc = RPMRC_FAIL; /* assume failure */ + if (dbi != NULL && dbc != NULL && set != NULL) { + int cflags = DB_NEXT; + int dbrc; + DBT data, key; + memset(&data, 0, sizeof(data)); + memset(&key, 0, sizeof(key)); + + if (keyp) { + if (keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen = 1; + } + key.data = (void *) keyp; /* discards const */ + key.size = keylen; + cflags = searchType == DBC_PREFIX_SEARCH ? DB_SET_RANGE : DB_SET; + } + + for (;;) { + dbiIndexSet newset = NULL; + dbrc = dbiCursorGet(dbc, &key, &data, cflags); + if (dbrc != 0) + break; + if (searchType == DBC_PREFIX_SEARCH && + (key.size < keylen || memcmp(key.data, keyp, keylen) != 0)) + break; + dbt2set(dbi, &data, &newset); + if (*set == NULL) { + *set = newset; + } else { + dbiIndexSetAppendSet(*set, newset, 0); + dbiIndexSetFree(newset); + } + if (searchType != DBC_PREFIX_SEARCH) + break; + key.data = NULL; + key.size = 0; + cflags = DB_NEXT; + } + + /* fixup result status for prefix search */ + if (searchType == DBC_PREFIX_SEARCH) { + if (dbrc == DB_NOTFOUND && *set != NULL && (*set)->count > 0) + dbrc = 0; + else if (dbrc == 0 && (*set == NULL || (*set)->count == 0)) + dbrc = DB_NOTFOUND; + } + + if (dbrc == 0) { + rc = RPMRC_OK; + } else if (dbrc == DB_NOTFOUND) { + rc = RPMRC_NOTFOUND; + } else { + rpmlog(RPMLOG_ERR, + _("error(%d) getting \"%s\" records from %s index: %s\n"), + dbrc, keyp ? keyp : "???", dbiName(dbi), db_strerror(dbrc)); + } + } + return rc; +} + +/* Update secondary index. NULL set deletes the key */ +static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen, + dbiIndexSet set) +{ + rpmRC rc = RPMRC_FAIL; + + if (dbc && keyp) { + dbiIndex dbi = dbc->dbi; + int dbrc; + DBT data, key; + memset(&key, 0, sizeof(data)); + memset(&data, 0, sizeof(data)); + + key.data = (void *) keyp; /* discards const */ + key.size = keylen; + + if (set) + set2dbt(dbi, &data, set); + + if (dbiIndexSetCount(set) > 0) { + dbrc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("error(%d) storing record \"%s\" into %s\n"), + dbrc, (char*)key.data, dbiName(dbi)); + } + free(data.data); + } else { + dbrc = dbiCursorDel(dbc, &key, &data, 0); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("error(%d) removing record \"%s\" from %s\n"), + dbrc, (char*)key.data, dbiName(dbi)); + } + } + + if (dbrc == 0) + rc = RPMRC_OK; + } + + return rc; +} + +static rpmRC db3_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) +{ + dbiIndexSet set = NULL; + rpmRC rc; + + if (keyp && keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen++; + } + rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); + + /* Not found means a new key and is not an error. */ + if (rc && rc != RPMRC_NOTFOUND) + return rc; + + if (set == NULL) + set = dbiIndexSetNew(1); + dbiIndexSetAppend(set, rec, 1, 0); + + rc = updateIndex(dbc, keyp, keylen, set); + + dbiIndexSetFree(set); + return rc; +} + +static rpmRC db3_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) +{ + dbiIndexSet set = NULL; + rpmRC rc; + + if (keyp && keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen++; + } + rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); if (rc) - rpmlog(RPMLOG_WARNING, _("could not suspend database lock\n")); + return rc; + + if (dbiIndexSetPrune(set, rec, 1, 1)) { + /* Nothing was pruned. XXX: Can this actually happen? */ + rc = RPMRC_OK; + } else { + /* If there's data left, update data. Otherwise delete the key. */ + if (dbiIndexSetCount(set) > 0) { + rc = updateIndex(dbc, keyp, keylen, set); + } else { + rc = updateIndex(dbc, keyp, keylen, NULL); + } + }; + dbiIndexSetFree(set); + return rc; } -int dbiResumeDBLock(dbiIndex dbi, unsigned int flags) +static const void * db3_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) { - struct flock l; + const void *key = NULL; + if (dbc) { + key = dbc->key; + if (key && keylen) + *keylen = dbc->keylen; + } + return key; +} + + +/* Update primary Packages index. NULL hdr means remove */ +static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, DBT *hdr) +{ + union _dbswap mi_offset; int rc = 0; - int tries; - int fdno = -1; + DBT key; - if (!dbi->dbi_lockdbfd) - return 0; - if (!(dbi->dbi_rpmdb->db_mode & (O_RDWR|O_WRONLY))) - return 0; - if (_lockdbfd == 0) - return 0; - if (!(dbi->dbi_db->fd(dbi->dbi_db, &fdno) == 0 && fdno >= 0)) - return 1; - for (tries = 0; tries < 2; tries++) { - memset(&l, 0, sizeof(l)); - l.l_whence = 0; - l.l_start = 0; - l.l_len = 0; - l.l_type = F_WRLCK; - rc = fcntl(fdno, tries ? F_SETLKW : F_SETLK, (void *)&l); - if (!rc) - break; - if (tries == 0) - rpmlog(RPMLOG_WARNING, _("waiting to reestablish exclusive database lock\n")); + if (dbc == NULL || hdrNum == 0) + return RPMRC_FAIL; + + memset(&key, 0, sizeof(key)); + + mi_offset.ui = hdrNum; + if (dbiByteSwapped(dbc->dbi) == 1) + _DBSWAP(mi_offset); + key.data = (void *) &mi_offset; + key.size = sizeof(mi_offset.ui); + + if (hdr) { + rc = dbiCursorPut(dbc, &key, hdr, DB_KEYLAST); + if (rc) { + rpmlog(RPMLOG_ERR, + _("error(%d) adding header #%d record\n"), rc, hdrNum); + } + } else { + DBT data; + + memset(&data, 0, sizeof(data)); + rc = dbiCursorGet(dbc, &key, &data, DB_SET); + if (rc) { + rpmlog(RPMLOG_ERR, + _("error(%d) removing header #%d record\n"), rc, hdrNum); + } else + rc = dbiCursorDel(dbc, &key, &data, 0); } - return rc; + + return rc == 0 ? RPMRC_OK : RPMRC_FAIL; +} + +/* Get current header instance number or try to allocate a new one */ +static unsigned int pkgInstance(dbiIndex dbi, int alloc) +{ + unsigned int hdrNum = 0; + + if (dbi != NULL && dbi->dbi_type == DBI_PRIMARY) { + dbiCursor dbc; + DBT key, data; + unsigned int firstkey = 0; + union _dbswap mi_offset; + int ret; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + dbc = dbiCursorInit(dbi, alloc ? DBC_WRITE : 0); + + /* Key 0 holds the current largest instance, fetch it */ + key.data = &firstkey; + key.size = sizeof(firstkey); + ret = dbiCursorGet(dbc, &key, &data, DB_SET); + + if (ret == 0 && data.data) { + memcpy(&mi_offset, data.data, sizeof(mi_offset.ui)); + if (dbiByteSwapped(dbi) == 1) + _DBSWAP(mi_offset); + hdrNum = mi_offset.ui; + } + + if (alloc) { + /* Rather complicated "increment by one", bswapping as needed */ + ++hdrNum; + mi_offset.ui = hdrNum; + if (dbiByteSwapped(dbi) == 1) + _DBSWAP(mi_offset); + if (ret == 0 && data.data) { + memcpy(data.data, &mi_offset, sizeof(mi_offset.ui)); + } else { + data.data = &mi_offset; + data.size = sizeof(mi_offset.ui); + } + + /* Unless we manage to insert the new instance number, we failed */ + ret = dbiCursorPut(dbc, &key, &data, DB_KEYLAST); + if (ret) { + hdrNum = 0; + rpmlog(RPMLOG_ERR, + _("error(%d) allocating new package instance\n"), ret); + } + } + dbiCursorFree(dbi, dbc); + } + + return hdrNum; } +static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char *hdrBlob, unsigned int hdrLen) +{ + DBT hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.data = hdrBlob; + hdr.size = hdrLen; + return updatePackages(dbc, hdrNum, &hdr); +} + +static rpmRC db3_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) +{ + return updatePackages(dbc, hdrNum, NULL); +} + +static rpmRC db3_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char **hdrBlob, unsigned int *hdrLen) +{ + DBT key, data; + union _dbswap mi_offset; + int rc; + + if (dbc == NULL) + return RPMRC_FAIL; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + if (hdrNum) { + mi_offset.ui = hdrNum; + if (dbiByteSwapped(dbc->dbi) == 1) + _DBSWAP(mi_offset); + key.data = (void *) &mi_offset; + key.size = sizeof(mi_offset.ui); + } + +#if !defined(_USE_COPY_LOAD) + data.flags |= DB_DBT_MALLOC; +#endif + rc = dbiCursorGet(dbc, &key, &data, hdrNum ? DB_SET : DB_NEXT); + if (rc == 0) { + if (hdrBlob) + *hdrBlob = data.data; + if (hdrLen) + *hdrLen = data.size; + return RPMRC_OK; + } else if (rc == DB_NOTFOUND) + return RPMRC_NOTFOUND; + else + return RPMRC_FAIL; +} + +static unsigned int db3_pkgdbKey(dbiIndex dbi, dbiCursor dbc) +{ + union _dbswap mi_offset; + + if (dbc == NULL || dbc->key == NULL) + return 0; + memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui)); + if (dbiByteSwapped(dbc->dbi) == 1) + _DBSWAP(mi_offset); + return mi_offset.ui; +} + +static rpmRC db3_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +{ + unsigned int num; + if (dbc == NULL) + return RPMRC_FAIL; + num = pkgInstance(dbc->dbi, 1); + if (!num) + return RPMRC_FAIL; + *hdrNum = num; + return RPMRC_OK; +} + +struct rpmdbOps_s db3_dbops = { + .open = db3_dbiOpen, + .close = db3_dbiClose, + .verify = db3_dbiVerify, + + .setFSync = db3_dbSetFSync, + .ctrl = db3_Ctrl, + + .cursorInit = db3_dbiCursorInit, + .cursorFree = db3_dbiCursorFree, + + .pkgdbGet = db3_pkgdbGet, + .pkgdbPut = db3_pkgdbPut, + .pkgdbDel = db3_pkgdbDel, + .pkgdbNew = db3_pkgdbNew, + .pkgdbKey = db3_pkgdbKey, + + .idxdbGet = db3_idxdbGet, + .idxdbPut = db3_idxdbPut, + .idxdbDel = db3_idxdbDel, + .idxdbKey = db3_idxdbKey +}; diff --git a/lib/backend/dbi.c b/lib/backend/dbi.c new file mode 100644 index 000000000..e99a5f2b2 --- /dev/null +++ b/lib/backend/dbi.c @@ -0,0 +1,184 @@ +/** \ingroup rpmdb + * \file lib/dbi.c + */ + +#include "system.h" + +#include <stdlib.h> +#include <rpm/rpmtypes.h> +#include <rpm/rpmstring.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> +#include "lib/rpmdb_internal.h" +#include "debug.h" + + +dbiIndex dbiFree(dbiIndex dbi) +{ + if (dbi) { + free(dbi); + } + return NULL; +} + +dbiIndex dbiNew(rpmdb rdb, rpmDbiTagVal rpmtag) +{ + dbiIndex dbi = xcalloc(1, sizeof(*dbi)); + /* FIX: figger lib/dbi refcounts */ + dbi->dbi_rpmdb = rdb; + dbi->dbi_file = rpmTagGetName(rpmtag); + dbi->dbi_type = (rpmtag == RPMDBI_PACKAGES) ? DBI_PRIMARY : DBI_SECONDARY; + dbi->dbi_byteswapped = -1; /* -1 unknown, 0 native order, 1 alien order */ + return dbi; +} + +static void +dbDetectBackend(rpmdb rdb) +{ + const char *dbhome = rpmdbHome(rdb); + char *db_backend = rpmExpand("%{?_db_backend}", NULL); + char *path = NULL; + +#if defined(WITH_LMDB) + if (!strcmp(db_backend, "lmdb")) { + rdb->db_ops = &lmdb_dbops; + } else +#endif +#ifdef ENABLE_NDB + if (!strcmp(db_backend, "ndb")) { + rdb->db_ops = &ndb_dbops; + } else +#endif + { + rdb->db_ops = &db3_dbops; + if (*db_backend == '\0') { + free(db_backend); + db_backend = xstrdup("bdb"); + } + } + +#if defined(WITH_LMDB) + path = rstrscat(NULL, dbhome, "/data.mdb", NULL); + if (access(path, F_OK) == 0 && rdb->db_ops != &lmdb_dbops) { + rdb->db_ops = &lmdb_dbops; + rpmlog(RPMLOG_WARNING, _("Found LMDB data.mdb database while attempting %s backend: using lmdb backend.\n"), db_backend); + } + free(path); +#endif + +#ifdef ENABLE_NDB + path = rstrscat(NULL, dbhome, "/Packages.db", NULL); + if (access(path, F_OK) == 0 && rdb->db_ops != &ndb_dbops) { + rdb->db_ops = &ndb_dbops; + rpmlog(RPMLOG_WARNING, _("Found NDB Packages.db database while attempting %s backend: using ndb backend.\n"), db_backend); + } + free(path); +#endif + + path = rstrscat(NULL, dbhome, "/Packages", NULL); + if (access(path, F_OK) == 0 && rdb->db_ops != &db3_dbops) { + rdb->db_ops = &db3_dbops; + rpmlog(RPMLOG_WARNING, _("Found BDB Packages database while attempting %s backend: using bdb backend.\n"), db_backend); + } + free(path); + + if (db_backend) + free(db_backend); +} + +const char * dbiName(dbiIndex dbi) +{ + return dbi->dbi_file; +} + +int dbiFlags(dbiIndex dbi) +{ + return dbi->dbi_flags; +} + +void dbSetFSync(rpmdb rdb, int enable) +{ + if (!rdb->db_ops) + dbDetectBackend(rdb); + rdb->db_ops->setFSync(rdb, enable); +} + +int dbCtrl(rpmdb rdb, dbCtrlOp ctrl) +{ + if (!rdb->db_ops) + dbDetectBackend(rdb); + return rdb->db_ops->ctrl(rdb, ctrl); +} + +int dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) +{ + if (!rdb->db_ops) + dbDetectBackend(rdb); + return rdb->db_ops->open(rdb, rpmtag, dbip, flags); +} + +int dbiClose(dbiIndex dbi, unsigned int flags) +{ + return dbi ? dbi->dbi_rpmdb->db_ops->close(dbi, flags) : 0; +} + +int dbiVerify(dbiIndex dbi, unsigned int flags) +{ + return dbi->dbi_rpmdb->db_ops->verify(dbi, flags); +} + +dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags) +{ + return dbi->dbi_rpmdb->db_ops->cursorInit(dbi, flags); +} + +dbiCursor dbiCursorFree(dbiIndex dbi, dbiCursor dbc) +{ + return dbi->dbi_rpmdb->db_ops->cursorFree(dbi, dbc); +} + +rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) +{ + return dbi->dbi_rpmdb->db_ops->pkgdbPut(dbi, dbc, hdrNum, hdrBlob, hdrLen); +} + +rpmRC pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) +{ + return dbi->dbi_rpmdb->db_ops->pkgdbDel(dbi, dbc, hdrNum); +} + +rpmRC pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen) +{ + return dbi->dbi_rpmdb->db_ops->pkgdbGet(dbi, dbc, hdrNum, hdrBlob, hdrLen); +} + +rpmRC pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +{ + return dbi->dbi_rpmdb->db_ops->pkgdbNew(dbi, dbc, hdrNum); +} + +unsigned int pkgdbKey(dbiIndex dbi, dbiCursor dbc) +{ + return dbi->dbi_rpmdb->db_ops->pkgdbKey(dbi, dbc); +} + +rpmRC idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int curFlags) +{ + return dbi->dbi_rpmdb->db_ops->idxdbGet(dbi, dbc, keyp, keylen, set, curFlags); +} + +rpmRC idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) +{ + return dbi->dbi_rpmdb->db_ops->idxdbPut(dbi, dbc, keyp, keylen, rec); +} + +rpmRC idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) +{ + return dbi->dbi_rpmdb->db_ops->idxdbDel(dbi, dbc, keyp, keylen, rec); +} + +const void * idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) +{ + return dbi->dbi_rpmdb->db_ops->idxdbKey(dbi, dbc, keylen); +} + diff --git a/lib/backend/dbi.h b/lib/backend/dbi.h index 3d848a8a0..1833da263 100644 --- a/lib/backend/dbi.h +++ b/lib/backend/dbi.h @@ -1,12 +1,25 @@ #ifndef _DBI_H #define _DBI_H +#include "dbiset.h" + +/* XXX: make this backend-specific, eliminate or something... */ +#define _USE_COPY_LOAD + enum rpmdbFlags { RPMDB_FLAG_JUSTCHECK = (1 << 0), RPMDB_FLAG_REBUILD = (1 << 1), RPMDB_FLAG_VERIFYONLY = (1 << 2), }; +typedef enum dbCtrlOp_e { + DB_CTRL_LOCK_RO = 1, + DB_CTRL_UNLOCK_RO = 2, + DB_CTRL_LOCK_RW = 3, + DB_CTRL_UNLOCK_RW = 4, + DB_CTRL_INDEXSYNC = 5 +} dbCtrlOp; + typedef struct dbiIndex_s * dbiIndex; typedef struct dbiCursor_s * dbiCursor; @@ -15,8 +28,17 @@ struct dbConfig_s { int db_cachesize; /*!< (128Kb) */ int db_verbose; int db_no_fsync; /*!< no-op fsync for db */ + int db_eflags; /*!< obsolete */ +}; + +struct dbiConfig_s { + int dbi_oflags; /*!< open flags */ + int dbi_no_dbsync; /*!< don't call dbiSync */ + int dbi_lockdbfd; /*!< do fcntl lock on db fd */ }; +struct rpmdbOps_s; + /** \ingroup rpmdb * Describes the collection of index databases used by rpm. */ @@ -27,16 +49,20 @@ struct rpmdb_s { int db_flags; int db_mode; /*!< open mode */ int db_perms; /*!< open permissions */ - int db_ver; /*!< Berkeley DB version */ + char * db_descr; /*!< db backend description (for error msgs) */ struct dbChk_s * db_checked;/*!< headerCheck()'ed package instances */ rpmdb db_next; int db_opens; + dbiIndex db_pkgs; /*!< Package db */ + const rpmDbiTag * db_tags; int db_ndbi; /*!< No. of tag indices. */ - dbiIndex * _dbi; /*!< Tag indices. */ + dbiIndex * db_indexes; /*!< Tag indices. */ int db_buildindex; /*!< Index rebuild indicator */ + struct rpmdbOps_s * db_ops; /*!< backend ops */ + /* dbenv and related parameters */ - void * db_dbenv; /*!< Berkeley DB_ENV handle. */ + void * db_dbenv; /*!< Backend private handle */ struct dbConfig_s cfg; int db_remove_env; @@ -59,24 +85,44 @@ enum dbiFlags_e { DBI_RDONLY = (1 << 1), }; +enum dbcFlags_e { + DBC_READ = 0, + DBC_WRITE = (1 << 0), +}; + +enum dbcSearchType_e { + DBC_NORMAL_SEARCH = 0, + DBC_PREFIX_SEARCH = (1 << 0), +}; + /** \ingroup dbi * Describes an index database (implemented on Berkeley db functionality). */ struct dbiIndex_s { + rpmdb dbi_rpmdb; /*!< the parent rpm database */ + dbiIndexType dbi_type; /*! Type of dbi (primary / index) */ const char * dbi_file; /*!< file component of path */ - - int dbi_oflags; /*!< db->open flags */ - int dbi_permit_dups; /*!< permit duplicate entries? */ - int dbi_no_dbsync; /*!< don't call dbiSync */ - int dbi_lockdbfd; /*!< do fcntl lock on db fd */ + int dbi_flags; int dbi_byteswapped; - rpmdb dbi_rpmdb; /*!< the parent rpm database */ - dbiIndexType dbi_type; /*! Type of dbi (primary / index) */ + struct dbiConfig_s cfg; + + void * dbi_db; /*!< Backend private handle */ +}; - DB * dbi_db; /*!< Berkeley DB * handle */ +union _dbswap { + unsigned int ui; + unsigned char uc[4]; }; +#define _DBSWAP(_a) \ +\ + { unsigned char _b, *_c = (_a).uc; \ + _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \ + _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \ +\ + } + #ifdef __cplusplus extern "C" { #endif @@ -102,7 +148,10 @@ int dbiResumeDBLock(dbiIndex dbi, unsigned int flags); RPM_GNUC_INTERNAL /* Globally enable/disable fsync in the backend */ -void dbSetFSync(void *dbenv, int enable); +void dbSetFSync(rpmdb rdb, int enable); + +RPM_GNUC_INTERNAL +int dbCtrl(rpmdb rdb, dbCtrlOp ctrl); /** \ingroup dbi * Return new configured index database handle instance. @@ -122,15 +171,6 @@ RPM_GNUC_INTERNAL dbiIndex dbiFree( dbiIndex dbi); /** \ingroup dbi - * Format dbi open flags for debugging print. - * @param dbflags db open flags - * @param print_dbenv_flags format db env flags instead? - * @return formatted flags (malloced) - */ -RPM_GNUC_INTERNAL -char * prDbiOpenFlags(int dbflags, int print_dbenv_flags); - -/** \ingroup dbi * Actually open the database of the index. * @param db rpm database * @param rpmtag database index tag @@ -151,15 +191,6 @@ RPM_GNUC_INTERNAL int dbiClose(dbiIndex dbi, unsigned int flags); /** \ingroup dbi - * Flush pending operations to disk. - * @param dbi index database handle - * @param flags (unused) - * @return 0 on success - */ -RPM_GNUC_INTERNAL -int dbiSync (dbiIndex dbi, unsigned int flags); - -/** \ingroup dbi * Verify (and close) index database. * @param dbi index database handle * @param flags (unused) @@ -169,22 +200,6 @@ RPM_GNUC_INTERNAL int dbiVerify(dbiIndex dbi, unsigned int flags); /** \ingroup dbi - * Is database byte swapped? - * @param dbi index database handle - * @return 0 same order, 1 swapped order - */ -RPM_GNUC_INTERNAL -int dbiByteSwapped(dbiIndex dbi); - -/** \ingroup dbi - * Type of dbi (primary data / index) - * @param dbi index database handle - * @return type of dbi - */ -RPM_GNUC_INTERNAL -dbiIndexType dbiType(dbiIndex dbi); - -/** \ingroup dbi * Retrieve index control flags (new/existing, read-only etc) * @param dbi index database handle * @return dbi control flags @@ -203,7 +218,7 @@ const char * dbiName(dbiIndex dbi); /** \ingroup dbi * Open a database cursor. * @param dbi index database handle - * @param flags DB_WRITECURSOR if writing, or 0 + * @param flags DBC_WRITE if writing, or 0 (DBC_READ) for reading * @return database cursor handle */ RPM_GNUC_INTERNAL @@ -215,56 +230,69 @@ dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags); * @return NULL always */ RPM_GNUC_INTERNAL -dbiCursor dbiCursorFree(dbiCursor dbc); +dbiCursor dbiCursorFree(dbiIndex dbi, dbiCursor dbc); -/** \ingroup dbi - * Store (key,data) pair in index database. - * @param dbcursor database cursor handle - * @param key store key value/length/flags - * @param data store data value/length/flags - * @param flags flags - * @return 0 on success - */ + +RPM_GNUC_INTERNAL +rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char *hdrBlob, unsigned int hdrLen); +RPM_GNUC_INTERNAL +rpmRC pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum); RPM_GNUC_INTERNAL -int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags); +rpmRC pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char **hdrBlob, unsigned int *hdrLen); +RPM_GNUC_INTERNAL +rpmRC pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum); +RPM_GNUC_INTERNAL +unsigned int pkgdbKey(dbiIndex dbi, dbiCursor dbc); -/** \ingroup dbi - * Retrieve (key,data) pair from index database. - * @param dbc database cursor handle - * @param key retrieve key value/length/flags - * @param data retrieve data value/length/flags - * @param flags flags - * @return 0 on success - */ RPM_GNUC_INTERNAL -int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags); +rpmRC idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexSet *set, int curFlags); +RPM_GNUC_INTERNAL +rpmRC idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec); +RPM_GNUC_INTERNAL +rpmRC idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec); +RPM_GNUC_INTERNAL +const void * idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen); + +struct rpmdbOps_s { + int (*open)(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags); + int (*close)(dbiIndex dbi, unsigned int flags); + int (*verify)(dbiIndex dbi, unsigned int flags); + void (*setFSync)(rpmdb rdb, int enable); + int (*ctrl)(rpmdb rdb, dbCtrlOp ctrl); + + dbiCursor (*cursorInit)(dbiIndex dbi, unsigned int flags); + dbiCursor (*cursorFree)(dbiIndex dbi, dbiCursor dbc); + + rpmRC (*pkgdbGet)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen); + rpmRC (*pkgdbPut)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen); + rpmRC (*pkgdbDel)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum); + rpmRC (*pkgdbNew)(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum); + unsigned int (*pkgdbKey)(dbiIndex dbi, dbiCursor dbc); + + rpmRC (*idxdbGet)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int curFlags); + rpmRC (*idxdbPut)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec); + rpmRC (*idxdbDel)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec); + const void * (*idxdbKey)(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen); +}; -/** \ingroup dbi - * Delete (key,data) pair(s) from index database. - * @param dbc database cursor handle - * @param key delete key value/length/flags - * @param data delete data value/length/flags - * @param flags flags - * @return 0 on success - */ RPM_GNUC_INTERNAL -int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags); +extern struct rpmdbOps_s db3_dbops; -/** \ingroup dbi - * Retrieve count of (possible) duplicate items. - * @param dbcursor database cursor - * @return number of duplicates - */ +#ifdef ENABLE_NDB RPM_GNUC_INTERNAL -unsigned int dbiCursorCount(dbiCursor dbc); +extern struct rpmdbOps_s ndb_dbops; +#endif -/** \ingroup dbi - * Retrieve underlying index database handle. - * @param dbcursor database cursor - * @return index database handle - */ +#if defined(WITH_LMDB) RPM_GNUC_INTERNAL -dbiIndex dbiCursorIndex(dbiCursor dbc); +extern struct rpmdbOps_s lmdb_dbops; +#endif + #ifdef __cplusplus } #endif diff --git a/lib/backend/dbiset.c b/lib/backend/dbiset.c new file mode 100644 index 000000000..8fb922ef7 --- /dev/null +++ b/lib/backend/dbiset.c @@ -0,0 +1,214 @@ +#include "system.h" +#include <string.h> +#include <stdlib.h> +#include "dbiset.h" +#include "debug.h" + +dbiIndexSet dbiIndexSetNew(unsigned int sizehint) +{ + dbiIndexSet set = xcalloc(1, sizeof(*set)); + if (sizehint > 0) + dbiIndexSetGrow(set, sizehint); + return set; +} + +/* + * Ensure sufficient memory for nrecs of new records in dbiIndexSet. + * Allocate in power of two sizes to avoid memory fragmentation, so + * realloc is not always needed. + */ +void dbiIndexSetGrow(dbiIndexSet set, unsigned int nrecs) +{ + size_t need = (set->count + nrecs) * sizeof(*(set->recs)); + size_t alloced = set->alloced ? set->alloced : 1 << 4; + + while (alloced < need) + alloced <<= 1; + + if (alloced != set->alloced) { + set->recs = xrealloc(set->recs, alloced); + set->alloced = alloced; + } +} + +static int hdrNumCmp(const void * one, const void * two) +{ + const struct dbiIndexItem_s *a = one, *b = two; + if (a->hdrNum - b->hdrNum != 0) + return a->hdrNum - b->hdrNum; + return a->tagNum - b->tagNum; +} + +void dbiIndexSetSort(dbiIndexSet set) +{ + /* + * mergesort is much (~10x with lots of identical basenames) faster + * than pure quicksort, but glibc uses msort_with_tmp() on stack. + */ + if (set && set->recs && set->count > 1) { +#if HAVE_MERGESORT + mergesort(set->recs, set->count, sizeof(*set->recs), hdrNumCmp); +#else + qsort(set->recs, set->count, sizeof(*set->recs), hdrNumCmp); +#endif + } +} + +void dbiIndexSetUniq(dbiIndexSet set, int sorted) +{ + unsigned int from; + unsigned int to = 0; + unsigned int num = set->count; + + if (set->count < 2) + return; + + if (!sorted) + dbiIndexSetSort(set); + + for (from = 0; from < num; from++) { + if (from > 0 && set->recs[from - 1].hdrNum == set->recs[from].hdrNum) { + set->count--; + continue; + } + if (from != to) + set->recs[to] = set->recs[from]; /* structure assignment */ + to++; + } +} + +int dbiIndexSetAppend(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sortset) +{ + if (set == NULL || recs == NULL) + return 1; + + if (nrecs) { + dbiIndexSetGrow(set, nrecs); + memcpy(set->recs + set->count, recs, nrecs * sizeof(*(set->recs))); + set->count += nrecs; + } + + if (sortset && set->count > 1) + qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp); + + return 0; +} + +int dbiIndexSetAppendSet(dbiIndexSet set, dbiIndexSet oset, int sortset) +{ + if (oset == NULL) + return 1; + return dbiIndexSetAppend(set, oset->recs, oset->count, sortset); +} + +int dbiIndexSetAppendOne(dbiIndexSet set, unsigned int hdrNum, + unsigned int tagNum, int sortset) +{ + if (set == NULL) + return 1; + dbiIndexSetGrow(set, 1); + + set->recs[set->count].hdrNum = hdrNum; + set->recs[set->count].tagNum = tagNum; + set->count += 1; + + if (sortset && set->count > 1) + qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp); + + return 0; +} + +int dbiIndexSetPrune(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sorted) +{ + unsigned int from; + unsigned int to = 0; + unsigned int num = set->count; + unsigned int numCopied = 0; + size_t recsize = sizeof(*recs); + + if (num == 0 || nrecs == 0) + return 1; + + if (nrecs > 1 && !sorted) + qsort(recs, nrecs, recsize, hdrNumCmp); + + for (from = 0; from < num; from++) { + if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) { + set->count--; + continue; + } + if (from != to) + set->recs[to] = set->recs[from]; /* structure assignment */ + to++; + numCopied++; + } + return (numCopied == num); +} + +int dbiIndexSetPruneSet(dbiIndexSet set, dbiIndexSet oset, int sortset) +{ + if (oset == NULL) + return 1; + return dbiIndexSetPrune(set, oset->recs, oset->count, sortset); +} + +int dbiIndexSetFilter(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sorted) +{ + unsigned int from; + unsigned int to = 0; + unsigned int num = set->count; + unsigned int numCopied = 0; + size_t recsize = sizeof(*recs); + + if (num == 0 || nrecs == 0) { + set->count = 0; + return num ? 0 : 1; + } + if (nrecs > 1 && !sorted) + qsort(recs, nrecs, recsize, hdrNumCmp); + for (from = 0; from < num; from++) { + if (!bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) { + set->count--; + continue; + } + if (from != to) + set->recs[to] = set->recs[from]; /* structure assignment */ + to++; + numCopied++; + } + return (numCopied == num); +} + +int dbiIndexSetFilterSet(dbiIndexSet set, dbiIndexSet oset, int sorted) +{ + return dbiIndexSetFilter(set, oset->recs, oset->count, sorted); +} + +unsigned int dbiIndexSetCount(dbiIndexSet set) +{ + return (set != NULL) ? set->count : 0; +} + +unsigned int dbiIndexRecordOffset(dbiIndexSet set, unsigned int recno) +{ + return set->recs[recno].hdrNum; +} + +unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, unsigned int recno) +{ + return set->recs[recno].tagNum; +} + +dbiIndexSet dbiIndexSetFree(dbiIndexSet set) +{ + if (set) { + free(set->recs); + memset(set, 0, sizeof(*set)); /* trash and burn */ + free(set); + } + return NULL; +} + diff --git a/lib/backend/dbiset.h b/lib/backend/dbiset.h new file mode 100644 index 000000000..da196c865 --- /dev/null +++ b/lib/backend/dbiset.h @@ -0,0 +1,130 @@ +#ifndef _DBISET_H +#define _DBISET_H + +#include <rpm/rpmutil.h> + +/* A single item from an index database (i.e. the "data returned"). */ +typedef struct dbiIndexItem_s { + unsigned int hdrNum; /*!< header instance in db */ + unsigned int tagNum; /*!< tag index in header */ +} * dbiIndexItem; + +/* Items retrieved from the index database.*/ +typedef struct dbiIndexSet_s { + dbiIndexItem recs; /*!< array of records */ + unsigned int count; /*!< number of records */ + size_t alloced; /*!< alloced size */ +} * dbiIndexSet; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create an empty index set, optionally with sizehint reservation for recs */ +RPM_GNUC_INTERNAL +dbiIndexSet dbiIndexSetNew(unsigned int sizehint); + +/* Reserve space for at least nrecs new records */ +RPM_GNUC_INTERNAL +void dbiIndexSetGrow(dbiIndexSet set, unsigned int nrecs); + +/* Sort an index set */ +RPM_GNUC_INTERNAL +void dbiIndexSetSort(dbiIndexSet set); + +/* Uniq an index set */ +RPM_GNUC_INTERNAL +void dbiIndexSetUniq(dbiIndexSet set, int sorted); + +/* Append an index set to another */ +RPM_GNUC_INTERNAL +int dbiIndexSetAppendSet(dbiIndexSet set, dbiIndexSet oset, int sortset); + +/** + * Append element(s) to set of index database items. + * @param set set of index database items + * @param recs array of items to append to set + * @param nrecs number of items + * @param sortset should resulting set be sorted? + * @return 0 success, 1 failure (bad args) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetAppend(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sortset); + +/** + * Append a single element to a set of index database items. + * @param set set of index database items + * @param hdrNum header instance in db + * @param tagNum tag index in header + * @param sortset should resulting set be sorted? + * @return 0 success, 1 failure (bad args) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetAppendOne(dbiIndexSet set, unsigned int hdrNum, + unsigned int tagNum, int sortset); + +/** + * Remove element(s) from set of index database items. + * @param set set of index database items + * @param recs array of items to remove from set + * @param nrecs number of items + * @param sorted array is already sorted? + * @return 0 success, 1 failure (no items found) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetPrune(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sorted); + +/** + * Remove an index set from another. + * @param set set of index database items + * @param oset set of entries that should be removed + * @param sorted oset is already sorted? + * @return 0 success, 1 failure (no items found) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetPruneSet(dbiIndexSet set, dbiIndexSet oset, int sorted); + +/** + * Filter element(s) from set of index database items. + * @param set set of index database items + * @param recs array of items to remove from set + * @param nrecs number of items + * @param sorted recs array is already sorted? + * @return 0 success, 1 failure (no items removed) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetFilter(dbiIndexSet set, dbiIndexItem recs, + unsigned int nrecs, int sorted); + +/** + * Filter (intersect) an index set with another. + * @param set set of index database items + * @param oset set of entries that should be intersected + * @param sorted oset is already sorted? + * @return 0 success, 1 failure (no items removed) + */ +RPM_GNUC_INTERNAL +int dbiIndexSetFilterSet(dbiIndexSet set, dbiIndexSet oset, int sorted); + +/* Count items in index database set. */ +RPM_GNUC_INTERNAL +unsigned int dbiIndexSetCount(dbiIndexSet set); + +/* Return record offset of header from element in index database set. */ +RPM_GNUC_INTERNAL +unsigned int dbiIndexRecordOffset(dbiIndexSet set, unsigned int recno); + +/* Return file index from element in index database set. */ +RPM_GNUC_INTERNAL +unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, unsigned int recno); + +/* Destroy set of index database items */ +RPM_GNUC_INTERNAL +dbiIndexSet dbiIndexSetFree(dbiIndexSet set); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/backend/lmdb.c b/lib/backend/lmdb.c new file mode 100644 index 000000000..db1270ebc --- /dev/null +++ b/lib/backend/lmdb.c @@ -0,0 +1,939 @@ +/** \ingroup rpmdb + * \file lib/lmdb.c + */ + +#include "system.h" + +#include <ctype.h> +#include <errno.h> +#include <sys/wait.h> +#include <popt.h> +#include <lmdb.h> +#include <signal.h> + +#include <rpm/rpmtypes.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmlog.h> + +#include "lib/rpmdb_internal.h" + +#include "debug.h" + +static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */ + +struct dbiCursor_s { + dbiIndex dbi; + const void *key; + unsigned int keylen; + int flags; + MDB_cursor * cursor; + MDB_txn * txn; +}; + +static const char * _EnvF(unsigned eflags) +{ + static char t[256]; + char *te = t; + + *te = '\0'; +#define _EF(_v) if (eflags & MDB_##_v) te = stpcpy(stpcpy(te,"|"),#_v) + _EF(FIXEDMAP); + _EF(NOSUBDIR); + _EF(NOSYNC); + _EF(RDONLY); + _EF(NOMETASYNC); + _EF(WRITEMAP); + _EF(MAPASYNC); + _EF(NOTLS); + _EF(NOLOCK); + _EF(NORDAHEAD); + _EF(NOMEMINIT); +#undef _EF + if (t[0] == '\0') te += sprintf(te, "|0x%x", eflags); + *te = '\0'; + return t+1; +} + +static const char * _OpenF(unsigned oflags) +{ + static char t[256]; + char *te = t; + + *te = '\0'; +#define _OF(_v) if (oflags & MDB_##_v) te = stpcpy(stpcpy(te,"|"),#_v) + _OF(REVERSEKEY); + _OF(DUPSORT); + _OF(INTEGERKEY); + _OF(DUPFIXED); + _OF(INTEGERDUP); + _OF(REVERSEDUP); + _OF(CREATE); +#undef _OF + if (t[0] == '\0') te += sprintf(te, "|0x%x", oflags); + *te = '\0'; + return t+1; +} + +static int dbapi_err(rpmdb rdb, const char * msg, int rc, int printit) +{ + if (printit && rc) { + int lvl = RPMLOG_ERR; + if (msg) + rpmlog(lvl, _("%s:\trc(%d) = %s(): %s\n"), + rdb->db_descr, rc, msg, (rc ? mdb_strerror(rc) : "")); + else + rpmlog(lvl, _("%s:\trc(%d) = %s()\n"), + rdb->db_descr, rc, (rc ? mdb_strerror(rc) : "")); + } + return rc; +} + +static int cvtdberr(dbiIndex dbi, const char * msg, int rc, int printit) +{ + return dbapi_err(dbi->dbi_rpmdb, msg, rc, printit); +} + +static void lmdb_assert(MDB_env *env, const char *msg) +{ + rpmlog(RPMLOG_ERR, "%s: %s\n", __FUNCTION__, msg); +} + +static void lmdb_dbSetFSync(rpmdb rdb, int enable) +{ +} + +static int lmdb_Ctrl(rpmdb rdb, dbCtrlOp ctrl) +{ + return 0; +} + +static int db_fini(rpmdb rdb, const char * dbhome) +{ + int rc = 0; + MDB_env * env = rdb->db_dbenv; + + if (env == NULL) + goto exit; + if (--rdb->db_opens > 0) + goto exit; + + mdb_env_close(env); + rdb->db_dbenv = env = NULL; + + rpmlog(RPMLOG_DEBUG, "closed db environment %s\n", dbhome); + +exit: + return rc; +} + +static int db_init(rpmdb rdb, const char * dbhome) +{ + int rc = EINVAL; + MDB_env * env = NULL; + int retry_open = 2; + uint32_t eflags = 0; + + if (rdb->db_dbenv != NULL) { + rdb->db_opens++; + return 0; + } else { + /* On first call, set backend description to something... */ + free(rdb->db_descr); + rdb->db_descr = xstrdup("lmdb"); + } + + MDB_dbi maxdbs = 32; + unsigned int maxreaders = 16; + size_t mapsize = 256 * 1024 * 1024; + + if ((rc = mdb_env_create(&env)) + || (rc = mdb_env_set_maxreaders(env, maxreaders)) + || (rc = mdb_env_set_mapsize(env, mapsize)) + || (rc = mdb_env_set_maxdbs(env, maxdbs)) + || (rc = mdb_env_set_assert(env, lmdb_assert)) + || (rc = mdb_env_set_userctx(env, rdb)) + ) { + rc = dbapi_err(rdb, "mdb_env_create", rc, _debug); + goto exit; + } + + /* + * Actually open the environment. Fall back to private environment + * if we dont have permission to join/create shared environment or + * system doesn't support it.. + */ + while (retry_open) { + rpmlog(RPMLOG_DEBUG, "opening db environment %s eflags=%s perms=0%o\n", dbhome, _EnvF(eflags), rdb->db_perms); + + eflags = 0; + eflags |= MDB_WRITEMAP; + eflags |= MDB_MAPASYNC; + eflags |= MDB_NOTLS; + + if (access(dbhome, W_OK) && (rdb->db_mode & O_ACCMODE) == O_RDONLY) + eflags |= MDB_RDONLY; + + rc = mdb_env_open(env, dbhome, eflags, rdb->db_perms); + if (rc) { + rc = dbapi_err(rdb, "mdb_env_open", rc, _debug); + if (rc == EPERM) + rpmlog(RPMLOG_ERR, "lmdb: %s(%s/lock.mdb): %s\n", __FUNCTION__, dbhome, mdb_strerror(rc)); + } + retry_open = 0; /* XXX EAGAIN might need a retry */ + } + if (rc) + goto exit; + + rdb->db_dbenv = env; + rdb->db_opens = 1; + +exit: + if (rc && env) { + mdb_env_close(env); + rdb->db_dbenv = env = NULL; + } + return rc; +} + +static int dbiSync(dbiIndex dbi, unsigned int flags) +{ + int rc = 0; + MDB_dbi db = (unsigned long) dbi->dbi_db; + + if (db != 0xdeadbeef && !dbi->cfg.dbi_no_dbsync) { + MDB_env * env = dbi->dbi_rpmdb->db_dbenv; + unsigned eflags = 0; + rc = mdb_env_get_flags(env, &eflags); + if (rc) { + rc = cvtdberr(dbi, "mdb_env_get_flags", rc, _debug); + eflags |= MDB_RDONLY; + } + if (!(eflags & MDB_RDONLY)) { + int force = 0; + rc = mdb_env_sync(env, force); + if (rc) + rc = cvtdberr(dbi, "mdb_env_sync", rc, _debug); + } + } + return rc; +} + +static dbiCursor lmdb_dbiCursorInit(dbiIndex dbi, unsigned int flags) +{ + dbiCursor dbc = NULL; + + if (dbi && dbi->dbi_db != (void *)0xdeadbeefUL) { + MDB_env * env = dbi->dbi_rpmdb->db_dbenv; + MDB_txn * parent = NULL; + unsigned tflags = !(flags & DBC_WRITE) ? MDB_RDONLY : 0; + MDB_txn * txn = NULL; + MDB_cursor * cursor = NULL; + int rc = EINVAL; + + rc = mdb_txn_begin(env, parent, tflags, &txn); + if (rc) + rc = cvtdberr(dbi, "mdb_txn_begin", rc, _debug); + + if (rc == 0) { + MDB_dbi db = (unsigned long) dbi->dbi_db; + rc = mdb_cursor_open(txn, db, &cursor); + if (rc) + rc = cvtdberr(dbi, "mdb_cursor_open", rc, _debug); + } + + if (rc == 0) { + dbc = xcalloc(1, sizeof(*dbc)); + dbc->dbi = dbi; + dbc->flags = flags; + dbc->cursor = cursor; + dbc->txn = txn; + } + } + + return dbc; +} + +static dbiCursor lmdb_dbiCursorFree(dbiIndex dbi, dbiCursor dbc) +{ + if (dbc) { + int rc = 0; + MDB_cursor * cursor = dbc->cursor; + MDB_txn * txn = dbc->txn; + dbiIndex dbi = dbc->dbi; + unsigned flags = dbc->flags; + + mdb_cursor_close(cursor); + dbc->cursor = cursor = NULL; + if (rc) + cvtdberr(dbc->dbi, "mdb_cursor_close", rc, _debug); + + /* Automatically commit close */ + if (txn) { + rc = mdb_txn_commit(txn); + dbc->txn = txn = NULL; + if (rc) + rc = cvtdberr(dbc->dbi, "mdb_txn_commit", rc, _debug); + } + + /* Automatically sync on write-cursor close */ + if (flags & DBC_WRITE) + dbiSync(dbi, 0); + + free(dbc); + rc = 0; + } + return NULL; +} + +static int dbiCursorPut(dbiCursor dbc, MDB_val * key, MDB_val * data, unsigned flags) +{ + int rc = EINVAL; + int sane = (key->mv_data != NULL && key->mv_size > 0 && + data->mv_data != NULL && data->mv_size > 0); + + if (dbc && sane) { + MDB_cursor * cursor = dbc->cursor; + rpmdb rdb = dbc->dbi->dbi_rpmdb; + rpmswEnter(&rdb->db_putops, (ssize_t) 0); + + rc = mdb_cursor_put(cursor, key, data, flags); + if (rc) { + rc = cvtdberr(dbc->dbi, "mdb_cursor_put", rc, _debug); + if (dbc->txn) { + mdb_txn_abort(dbc->txn); + dbc->txn = NULL; + } + } + + rpmswExit(&rdb->db_putops, (ssize_t) data->mv_size); + } + return rc; +} + +static int dbiCursorGet(dbiCursor dbc, MDB_val *key, MDB_val *data, unsigned op) +{ + int rc = EINVAL; + int sane = ((op == MDB_NEXT) || (key->mv_data != NULL && key->mv_size > 0)); + + if (dbc && sane) { + MDB_cursor * cursor = dbc->cursor; + rpmdb rdb = dbc->dbi->dbi_rpmdb; + + rpmswEnter(&rdb->db_getops, 0); + + /* XXX db4 does DB_FIRST on uninitialized cursor */ + rc = mdb_cursor_get(cursor, key, data, op); + if (rc && rc != MDB_NOTFOUND) { + rc = cvtdberr(dbc->dbi, "mdb_cursor_get", rc, _debug); + if (dbc->txn) { + mdb_txn_abort(dbc->txn); + dbc->txn = NULL; + } + } + + /* Remember the last key fetched */ + if (rc == 0) { + dbc->key = key->mv_data; + dbc->keylen = key->mv_size; + } else { + dbc->key = NULL; + dbc->keylen = 0; + } + + rpmswExit(&rdb->db_getops, data->mv_size); + } + return rc; +} + +static int dbiCursorDel(dbiCursor dbc, MDB_val *key, MDB_val *data, unsigned int flags) +{ + int rc = EINVAL; + int sane = (key->mv_data != NULL && key->mv_size > 0); + + if (dbc && sane) { + MDB_cursor * cursor = dbc->cursor; + rpmdb rdb = dbc->dbi->dbi_rpmdb; + rpmswEnter(&rdb->db_delops, 0); + + /* XXX TODO: ensure that cursor is positioned with duplicates */ + rc = mdb_cursor_get(cursor, key, data, MDB_SET); + if (rc && rc != MDB_NOTFOUND) { + rc = cvtdberr(dbc->dbi, "mdb_cursor_get", rc, _debug); + if (dbc->txn) + dbc->txn = NULL; + } + + if (rc == 0) { + rc = mdb_cursor_del(cursor, flags); + if (rc) + rc = cvtdberr(dbc->dbi, "mdb_cursor_del", rc, _debug); + } + rpmswExit(&rdb->db_delops, data->mv_size); + } + return rc; +} + +static int lmdb_dbiVerify(dbiIndex dbi, unsigned int flags) +{ + return 0; +} + +static int lmdb_dbiClose(dbiIndex dbi, unsigned int flags) +{ + int rc = 0; + rpmdb rdb = dbi->dbi_rpmdb; + const char * dbhome = rpmdbHome(rdb); + MDB_dbi db = (unsigned long) dbi->dbi_db; + + if (db != 0xdeadbeef) { + MDB_env * env = dbi->dbi_rpmdb->db_dbenv; + mdb_dbi_close(env, db); + dbi->dbi_db = (void *) 0xdeadbeefUL; + + rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n", + dbhome, dbi->dbi_file); + } + + db_fini(rdb, dbhome ? dbhome : ""); + + dbi = dbiFree(dbi); + + return rc; +} + +static int lmdb_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) +{ + int rc = 1; + const char *dbhome = rpmdbHome(rdb); + dbiIndex dbi = NULL; + int retry_open; + + MDB_dbi db = 0; + uint32_t oflags; + + if (dbip) + *dbip = NULL; + + if ((dbi = dbiNew(rdb, rpmtag)) == NULL) + goto exit; + dbi->dbi_flags = 0; + dbi->dbi_db = (void *) 0xdeadbeefUL; + + rc = db_init(rdb, dbhome); + + retry_open = (rc == 0) ? 2 : 0; + + do { + MDB_env * env = rdb->db_dbenv; + MDB_txn * parent = NULL; + unsigned tflags = access(dbhome, W_OK) ? MDB_RDONLY : 0; + MDB_txn * txn = NULL; + + if (tflags & MDB_RDONLY) + dbi->dbi_flags |= DBI_RDONLY; + + rc = mdb_txn_begin(env, parent, tflags, &txn); + if (rc) + rc = cvtdberr(dbi, "mdb_txn_begin", rc, _debug); + + const char * name = dbi->dbi_file; + oflags = 0; + if (!(tflags & MDB_RDONLY)) + oflags |= MDB_CREATE; + if (!strcmp(dbi->dbi_file, "Packages")) + oflags |= MDB_INTEGERKEY; + + rpmlog(RPMLOG_DEBUG, "opening db index %s/%s oflags=%s\n", + dbhome, dbi->dbi_file, _OpenF(oflags)); + + db = 0xdeadbeef; + rc = mdb_dbi_open(txn, name, oflags, &db); + if (rc && rc != MDB_NOTFOUND) { + rc = cvtdberr(dbi, "mdb_dbi_open", rc, _debug); + if (txn) { + mdb_txn_abort(txn); + txn = NULL; + db = 0xdeadbeef; + } + } + + if (txn) { + rc = mdb_txn_commit(txn); + if (rc) + rc = cvtdberr(dbi, "mdb_txn_commit", rc, _debug); + } + retry_open = 0; + } while (--retry_open > 0); + + dbi->dbi_db = (void *) ((unsigned long)db); + + if (!rc && dbip) + *dbip = dbi; + else + (void) dbiClose(dbi, 0); + +exit: + return rc; +} + +/* The LMDB btree implementation needs BIGENDIAN primary keys. */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static int _dbibyteswapped = 1; +#else +static int _dbibyteswapped = 0; +#endif + +/** + * Convert retrieved data to index set. + * @param dbi index database handle + * @param data retrieved data + * @retval setp (malloc'ed) index set + * @return 0 on success + */ +static rpmRC dbt2set(dbiIndex dbi, MDB_val * data, dbiIndexSet * setp) +{ + rpmRC rc = RPMRC_FAIL; + const char * sdbir; + dbiIndexSet set = NULL; + unsigned int i; + + if (dbi == NULL || data == NULL || setp == NULL) + goto exit; + + rc = RPMRC_OK; + if ((sdbir = data->mv_data) == NULL) { + *setp = NULL; + goto exit; + } + + set = dbiIndexSetNew(data->mv_size / (2 * sizeof(int32_t))); + set->count = data->mv_size / (2 * sizeof(int32_t)); + + for (i = 0; i < set->count; i++) { + union _dbswap hdrNum, tagNum; + + memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); + sdbir += sizeof(hdrNum.ui); + memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui)); + sdbir += sizeof(tagNum.ui); + if (_dbibyteswapped) { + _DBSWAP(hdrNum); + _DBSWAP(tagNum); + } + set->recs[i].hdrNum = hdrNum.ui; + set->recs[i].tagNum = tagNum.ui; + } + *setp = set; + +exit: + return rc; +} + +/** + * Convert index set to database representation. + * @param dbi index database handle + * @param data retrieved data + * @param set index set + * @return 0 on success + */ +static rpmRC set2dbt(dbiIndex dbi, MDB_val * data, dbiIndexSet set) +{ + rpmRC rc = RPMRC_FAIL; + char * tdbir; + unsigned int i; + + if (dbi == NULL || data == NULL || set == NULL) + goto exit; + + rc = RPMRC_OK; + data->mv_size = set->count * (2 * sizeof(int32_t)); + if (data->mv_size == 0) { + data->mv_data = NULL; + goto exit; + } + tdbir = data->mv_data = xmalloc(data->mv_size); + + for (i = 0; i < set->count; i++) { + union _dbswap hdrNum, tagNum; + + memset(&hdrNum, 0, sizeof(hdrNum)); + memset(&tagNum, 0, sizeof(tagNum)); + hdrNum.ui = set->recs[i].hdrNum; + tagNum.ui = set->recs[i].tagNum; + if (_dbibyteswapped) { + _DBSWAP(hdrNum); + _DBSWAP(tagNum); + } + memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); + tdbir += sizeof(hdrNum.ui); + memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui)); + tdbir += sizeof(tagNum.ui); + } +exit: + return rc; +} + +static rpmRC lmdb_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexSet *set, int searchType) +{ + rpmRC rc = RPMRC_FAIL; /* assume failure */ + if (dbi != NULL && dbc != NULL && set != NULL) { + int cflags = MDB_NEXT; + int dbrc; + MDB_val key = { 0, NULL }; + MDB_val data = { 0, NULL }; + + if (keyp) { + if (keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen = 1; + } + key.mv_data = (void *) keyp; /* discards const */ + key.mv_size = keylen; + cflags = searchType == DBC_PREFIX_SEARCH ? MDB_SET_RANGE : MDB_SET; + } + + for (;;) { + dbiIndexSet newset = NULL; + dbrc = dbiCursorGet(dbc, &key, &data, cflags); + if (dbrc != 0) + break; + if (searchType == DBC_PREFIX_SEARCH && + (key.mv_size < keylen || memcmp(key.mv_data, keyp, keylen) != 0)) + break; + dbt2set(dbi, &data, &newset); + if (*set == NULL) { + *set = newset; + } else { + dbiIndexSetAppendSet(*set, newset, 0); + dbiIndexSetFree(newset); + } + if (searchType != DBC_PREFIX_SEARCH) + break; + key.mv_data = NULL; + key.mv_size = 0; + cflags = MDB_NEXT; + } + + /* fixup result status for prefix search */ + if (searchType == DBC_PREFIX_SEARCH) { + if (dbrc == MDB_NOTFOUND && *set != NULL && (*set)->count > 0) + dbrc = 0; + else if (dbrc == 0 && (*set == NULL || (*set)->count == 0)) + dbrc = MDB_NOTFOUND; + } + + if (dbrc == 0) { + rc = RPMRC_OK; + } else if (dbrc == MDB_NOTFOUND) { + rc = RPMRC_NOTFOUND; + } else { + rpmlog(RPMLOG_ERR, + _("rc(%d) getting \"%s\" records from %s index: %s\n"), + dbrc, keyp ? keyp : "???", dbiName(dbi), mdb_strerror(dbrc)); + } + } + return rc; +} + +/* Update secondary index. NULL set deletes the key */ +static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen, + dbiIndexSet set) +{ + rpmRC rc = RPMRC_FAIL; + + if (dbc && keyp) { + dbiIndex dbi = dbc->dbi; + int dbrc; + MDB_val key = { 0, NULL }; + MDB_val data = { 0, NULL }; + + key.mv_data = (void *) keyp; /* discards const */ + key.mv_size = keylen; + + if (set) + set2dbt(dbi, &data, set); + + if (dbiIndexSetCount(set) > 0) { + dbrc = dbiCursorPut(dbc, &key, &data, 0); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("rc(%d) storing record \"%s\" into %s index: %s\n"), + dbrc, (char*)key.mv_data, dbiName(dbi), mdb_strerror(dbrc)); + } + free(data.mv_data); + } else { + dbrc = dbiCursorDel(dbc, &key, &data, 0); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("rc(%d) removing record \"%s\" from %s index: %s\n"), + dbrc, (char*)key.mv_data, dbiName(dbi), mdb_strerror(dbrc)); + } + } + + if (dbrc == 0) + rc = RPMRC_OK; + } + + return rc; +} + +static rpmRC lmdb_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) +{ + dbiIndexSet set = NULL; + rpmRC rc; + + if (keyp && keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen++; + } + rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); + + /* Not found means a new key and is not an error. */ + if (rc && rc != RPMRC_NOTFOUND) + goto exit; + + if (set == NULL) + set = dbiIndexSetNew(1); + dbiIndexSetAppend(set, rec, 1, 0); + + rc = updateIndex(dbc, keyp, keylen, set); + + dbiIndexSetFree(set); + +exit: + return rc; +} + +static rpmRC lmdb_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) +{ + rpmRC rc = RPMRC_FAIL; + dbiIndexSet set = NULL; + + if (keyp && keylen == 0) { /* XXX "/" fixup */ + keyp = ""; + keylen++; + } + rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); + if (rc) + goto exit; + + if (dbiIndexSetPrune(set, rec, 1, 1)) { + /* Nothing was pruned. XXX: Can this actually happen? */ + rc = RPMRC_OK; + } else { + /* If there's data left, update data. Otherwise delete the key. */ + if (dbiIndexSetCount(set) > 0) { + rc = updateIndex(dbc, keyp, keylen, set); + } else { + rc = updateIndex(dbc, keyp, keylen, NULL); + } + }; + dbiIndexSetFree(set); + +exit: + return rc; +} + +static const void * lmdb_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) +{ + const void *key = NULL; + if (dbc) { + key = dbc->key; + if (key && keylen) + *keylen = dbc->keylen; + } + return key; +} + +/* Update primary Packages index. NULL hdr means remove */ +static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, MDB_val *hdr) +{ + int rc = RPMRC_FAIL; + int dbrc = EINVAL; + + if (dbc == NULL || hdrNum == 0) + goto exit; + + union _dbswap mi_offset; + mi_offset.ui = hdrNum; + if (_dbibyteswapped) + _DBSWAP(mi_offset); + + MDB_val key = { 0, NULL }; + key.mv_data = (void *) &mi_offset; + key.mv_size = sizeof(mi_offset.ui); + + MDB_val data = { 0, NULL }; + + dbrc = dbiCursorGet(dbc, &key, &data, MDB_SET); + if (dbrc && dbrc != MDB_NOTFOUND) { + rpmlog(RPMLOG_ERR, + _("rc(%d) positioning header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc)); + goto exit; + } + + if (hdr) { + dbrc = dbiCursorPut(dbc, &key, hdr, 0); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("rc(%d) adding header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc)); + } + } else { + dbrc = dbiCursorDel(dbc, &key, &data, 0); + if (dbrc) { + rpmlog(RPMLOG_ERR, + _("rc(%d) deleting header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc)); + } + } + +exit: + rc = dbrc == 0 ? RPMRC_OK : RPMRC_FAIL; + return rc; +} + +/* Get current header instance number or try to allocate a new one */ +static unsigned int pkgInstance(dbiCursor dbc, int alloc) +{ + unsigned int hdrNum = 0; + + MDB_val key = { 0, NULL }; + MDB_val data = { 0, NULL }; + unsigned int firstkey = 0; + union _dbswap mi_offset; + int rc; + + /* Key 0 holds the current largest instance, fetch it */ + key.mv_data = &firstkey; + key.mv_size = sizeof(firstkey); + rc = dbiCursorGet(dbc, &key, &data, MDB_SET); + + if (!rc && data.mv_data) { + memcpy(&mi_offset, data.mv_data, sizeof(mi_offset.ui)); + if (_dbibyteswapped) + _DBSWAP(mi_offset); + hdrNum = mi_offset.ui; + } + + if (alloc) { + /* Rather complicated "increment by one", bswapping as needed */ + ++hdrNum; + mi_offset.ui = hdrNum; + if (_dbibyteswapped) + _DBSWAP(mi_offset); + data.mv_data = &mi_offset; + data.mv_size = sizeof(mi_offset.ui); + + /* Unless we manage to insert the new instance number, we failed */ + rc = dbiCursorPut(dbc, &key, &data, 0); + if (rc) { + hdrNum = 0; + rpmlog(RPMLOG_ERR, + _("rc(%d) allocating new package instance: %s\n"), rc, mdb_strerror(rc)); + } + } + + return hdrNum; +} + +static rpmRC lmdb_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char *hdrBlob, unsigned int hdrLen) +{ + MDB_val hdr; + hdr.mv_data = hdrBlob; + hdr.mv_size = hdrLen; + return updatePackages(dbc, hdrNum, &hdr); +} + +static rpmRC lmdb_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) +{ + return updatePackages(dbc, hdrNum, NULL); +} + +static rpmRC lmdb_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char **hdrBlob, unsigned int *hdrLen) +{ + union _dbswap mi_offset; + MDB_val key = { 0, NULL }; + MDB_val data = { 0, NULL }; + rpmRC rc = RPMRC_FAIL; + + if (dbc == NULL) + goto exit; + + if (hdrNum) { + mi_offset.ui = hdrNum; + if (_dbibyteswapped) + _DBSWAP(mi_offset); + key.mv_data = (void *) &mi_offset; + key.mv_size = sizeof(mi_offset.ui); + } + + rc = dbiCursorGet(dbc, &key, &data, hdrNum ? MDB_SET : MDB_NEXT); + if (rc == 0) { + if (hdrBlob) + *hdrBlob = data.mv_data; + if (hdrLen) + *hdrLen = data.mv_size; + rc = RPMRC_OK; + } else if (rc == MDB_NOTFOUND) + rc = RPMRC_NOTFOUND; + else + rc = RPMRC_FAIL; +exit: + return rc; +} + +static unsigned int lmdb_pkgdbKey(dbiIndex dbi, dbiCursor dbc) +{ + union _dbswap mi_offset; + + if (dbc == NULL || dbc->key == NULL) + return 0; + memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui)); + if (_dbibyteswapped) + _DBSWAP(mi_offset); + return mi_offset.ui; +} + +static rpmRC lmdb_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +{ + unsigned int num; + rpmRC rc = RPMRC_FAIL; + + if (dbc == NULL) + goto exit; + num = pkgInstance(dbc, 1); + if (num) { + *hdrNum = num; + rc = RPMRC_OK; + } +exit: + return rc; +} + +struct rpmdbOps_s lmdb_dbops = { + .open = lmdb_dbiOpen, + .close = lmdb_dbiClose, + .verify = lmdb_dbiVerify, + + .setFSync = lmdb_dbSetFSync, + .ctrl = lmdb_Ctrl, + + .cursorInit = lmdb_dbiCursorInit, + .cursorFree = lmdb_dbiCursorFree, + + .pkgdbGet = lmdb_pkgdbGet, + .pkgdbPut = lmdb_pkgdbPut, + .pkgdbDel = lmdb_pkgdbDel, + .pkgdbNew = lmdb_pkgdbNew, + .pkgdbKey = lmdb_pkgdbKey, + + .idxdbGet = lmdb_idxdbGet, + .idxdbPut = lmdb_idxdbPut, + .idxdbDel = lmdb_idxdbDel, + .idxdbKey = lmdb_idxdbKey +}; diff --git a/lib/backend/ndb/glue.c b/lib/backend/ndb/glue.c new file mode 100644 index 000000000..144ada0e4 --- /dev/null +++ b/lib/backend/ndb/glue.c @@ -0,0 +1,492 @@ +#include "system.h" + +#include <errno.h> +#include <stdlib.h> + +#include "lib/rpmdb_internal.h" +#include <rpm/rpmstring.h> +#include <rpm/rpmlog.h> + +#include "lib/backend/ndb/rpmpkg.h" +#include "lib/backend/ndb/rpmxdb.h" +#include "lib/backend/ndb/rpmidx.h" + +#include "debug.h" + +struct dbiCursor_s { + dbiIndex dbi; + const void *key; + unsigned int keylen; + unsigned int hdrNum; + int flags; + + unsigned int *list; + unsigned int nlist; + unsigned int ilist; + unsigned char *listdata; +}; + +struct ndbEnv_s { + rpmpkgdb pkgdb; + rpmxdb xdb; + int refs; + + unsigned int hdrNum; + void *data; + unsigned int datalen; +}; + +static void closeEnv(rpmdb rdb) +{ + struct ndbEnv_s *ndbenv = rdb->db_dbenv; + if (--ndbenv->refs == 0) { + if (ndbenv->xdb) { + rpmxdbClose(ndbenv->xdb); + rpmlog(RPMLOG_DEBUG, "closed db index %s/Index.db\n", rpmdbHome(rdb)); + } + if (ndbenv->pkgdb) { + rpmpkgClose(ndbenv->pkgdb); + rpmlog(RPMLOG_DEBUG, "closed db index %s/Packages.db\n", rpmdbHome(rdb)); + } + if (ndbenv->data) + free(ndbenv->data); + free(ndbenv); + rdb->db_dbenv = 0; + } +} + +static struct ndbEnv_s *openEnv(rpmdb rdb) +{ + struct ndbEnv_s *ndbenv = rdb->db_dbenv; + if (!ndbenv) + rdb->db_dbenv = ndbenv = xcalloc(1, sizeof(struct ndbEnv_s)); + ndbenv->refs++; + return ndbenv; +} + +static int ndb_Close(dbiIndex dbi, unsigned int flags) +{ + rpmdb rdb = dbi->dbi_rpmdb; + if (dbi->dbi_type != DBI_PRIMARY && dbi->dbi_db) { + rpmidxClose(dbi->dbi_db); + rpmlog(RPMLOG_DEBUG, "closed db index %s\n", dbi->dbi_file); + } + if (rdb->db_dbenv) + closeEnv(rdb); + dbi->dbi_db = 0; + return 0; +} + +static int ndb_Open(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) +{ + const char *dbhome = rpmdbHome(rdb); + struct ndbEnv_s *ndbenv; + dbiIndex dbi; + int rc, oflags, ioflags; + + if (dbip) + *dbip = NULL; + + if ((dbi = dbiNew(rdb, rpmtag)) == NULL) + return 1; + + ndbenv = openEnv(rdb); + + oflags = O_RDWR; + if ((rdb->db_mode & O_ACCMODE) == O_RDONLY) + oflags = O_RDONLY; + + if (dbi->dbi_type == DBI_PRIMARY) { + rpmpkgdb pkgdb = 0; + char *path = rstrscat(NULL, dbhome, "/Packages.db", NULL); + rpmlog(RPMLOG_DEBUG, "opening db index %s mode=0x%x\n", path, rdb->db_mode); + rc = rpmpkgOpen(&pkgdb, path, oflags, 0666); + if (rc && errno == ENOENT) { + oflags = O_RDWR|O_CREAT; + dbi->dbi_flags |= DBI_CREATED; + rc = rpmpkgOpen(&pkgdb, path, oflags, 0666); + } + if (rc) { + perror("rpmpkgOpen"); + free(path); + ndb_Close(dbi, 0); + return 1; + } + free(path); + dbi->dbi_db = ndbenv->pkgdb = pkgdb; + + if ((oflags & (O_RDWR | O_RDONLY)) == O_RDONLY) + dbi->dbi_flags |= DBI_RDONLY; + } else { + unsigned int id; + rpmidxdb idxdb = 0; + if (!ndbenv->pkgdb) { + ndb_Close(dbi, 0); + return 1; /* please open primary first */ + } + if (!ndbenv->xdb) { + char *path = rstrscat(NULL, dbhome, "/Index.db", NULL); + rpmlog(RPMLOG_DEBUG, "opening db index %s mode=0x%x\n", path, rdb->db_mode); + + /* Open indexes readwrite if possible */ + ioflags = O_RDWR; + rc = rpmxdbOpen(&ndbenv->xdb, rdb->db_pkgs->dbi_db, path, ioflags, 0666); + if (rc && errno == EACCES) { + /* If it is not asked for rw explicitly, try to open ro */ + if (!(oflags & O_RDWR)) { + ioflags = O_RDONLY; + rc = rpmxdbOpen(&ndbenv->xdb, rdb->db_pkgs->dbi_db, path, ioflags, 0666); + } + } else if (rc && errno == ENOENT) { + ioflags = O_CREAT|O_RDWR; + rc = rpmxdbOpen(&ndbenv->xdb, rdb->db_pkgs->dbi_db, path, ioflags, 0666); + } + if (rc) { + perror("rpmxdbOpen"); + free(path); + ndb_Close(dbi, 0); + return 1; + } + free(path); + } + if (rpmxdbLookupBlob(ndbenv->xdb, &id, rpmtag, 0, 0) == RPMRC_NOTFOUND) { + dbi->dbi_flags |= DBI_CREATED; + } + rpmlog(RPMLOG_DEBUG, "opening db index %s tag=%d\n", dbiName(dbi), rpmtag); + if (rpmidxOpenXdb(&idxdb, rdb->db_pkgs->dbi_db, ndbenv->xdb, rpmtag)) { + perror("rpmidxOpenXdb"); + ndb_Close(dbi, 0); + return 1; + } + dbi->dbi_db = idxdb; + + if (rpmxdbIsRdonly(ndbenv->xdb)) + dbi->dbi_flags |= DBI_RDONLY; + } + + + if (dbip != NULL) + *dbip = dbi; + else + ndb_Close(dbi, 0); + return 0; +} + +static int ndb_Verify(dbiIndex dbi, unsigned int flags) +{ + return 1; +} + +static void ndb_SetFSync(rpmdb rdb, int enable) +{ +} + +static int indexSync(rpmpkgdb pkgdb, rpmxdb xdb) +{ + unsigned int generation; + int rc; + if (!pkgdb || !xdb) + return 1; + if (rpmpkgLock(pkgdb, 1)) + return 1; + if (rpmpkgGeneration(pkgdb, &generation)) { + rpmpkgUnlock(pkgdb, 1); + return 1; + } + rc = rpmxdbSetUserGeneration(xdb, generation); + rpmpkgUnlock(pkgdb, 1); + return rc; +} + +static int ndb_Ctrl(rpmdb rdb, dbCtrlOp ctrl) +{ + struct ndbEnv_s *ndbenv = rdb->db_dbenv; + + switch (ctrl) { + case DB_CTRL_LOCK_RO: + if (!rdb->db_pkgs) + return 1; + return rpmpkgLock(rdb->db_pkgs->dbi_db, 0); + case DB_CTRL_LOCK_RW: + if (!rdb->db_pkgs) + return 1; + return rpmpkgLock(rdb->db_pkgs->dbi_db, 1); + case DB_CTRL_UNLOCK_RO: + if (!rdb->db_pkgs) + return 1; + return rpmpkgUnlock(rdb->db_pkgs->dbi_db, 0); + case DB_CTRL_UNLOCK_RW: + if (!rdb->db_pkgs) + return 1; + return rpmpkgUnlock(rdb->db_pkgs->dbi_db, 1); + case DB_CTRL_INDEXSYNC: + if (!ndbenv) + return 1; + return indexSync(ndbenv->pkgdb, ndbenv->xdb); + default: + break; + } + return 0; +} + +static dbiCursor ndb_CursorInit(dbiIndex dbi, unsigned int flags) +{ + dbiCursor dbc = xcalloc(1, sizeof(*dbc)); + dbc->dbi = dbi; + dbc->flags = flags; + return dbc; +} + +static dbiCursor ndb_CursorFree(dbiIndex dbi, dbiCursor dbc) +{ + if (dbc) { + if (dbc->list) + free(dbc->list); + if (dbc->listdata) + free(dbc->listdata); + free(dbc); + } + return NULL; +} + + +static void setdata(dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) +{ + struct ndbEnv_s *ndbenv = dbc->dbi->dbi_rpmdb->db_dbenv; + if (ndbenv->data) + free(ndbenv->data); + ndbenv->hdrNum = hdrNum; + ndbenv->data = hdrBlob; + ndbenv->datalen = hdrLen; +} + +static rpmRC ndb_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +{ + int rc = rpmpkgNextPkgIdx(dbc->dbi->dbi_db, hdrNum); + if (!rc) + setdata(dbc, *hdrNum, 0, 0); + return rc; +} + +static rpmRC ndb_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) +{ + int rc = rpmpkgPut(dbc->dbi->dbi_db, hdrNum, hdrBlob, hdrLen); + if (!rc) { + dbc->hdrNum = hdrNum; + setdata(dbc, hdrNum, 0, 0); + } + return rc; +} + +static rpmRC ndb_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) +{ + dbc->hdrNum = 0; + setdata(dbc, 0, 0, 0); + return rpmpkgDel(dbc->dbi->dbi_db, hdrNum); +} + +/* iterate over all packages */ +static rpmRC ndb_pkgdbIter(dbiIndex dbi, dbiCursor dbc, unsigned char **hdrBlob, unsigned int *hdrLen) +{ + int rc; + unsigned int hdrNum; + + if (!dbc->list) { + rc = rpmpkgList(dbc->dbi->dbi_db, &dbc->list, &dbc->nlist); + if (rc) + return rc; + dbc->ilist = 0; + } + for (;;) { + if (dbc->ilist >= dbc->nlist) { + rc = RPMRC_NOTFOUND; + break; + } + *hdrBlob = 0; + hdrNum = dbc->list[dbc->ilist]; + rc = rpmpkgGet(dbc->dbi->dbi_db, hdrNum, hdrBlob, hdrLen); + if (rc && rc != RPMRC_NOTFOUND) + break; + dbc->ilist++; + if (!rc) { + dbc->hdrNum = hdrNum; + setdata(dbc, hdrNum, *hdrBlob, *hdrLen); + break; + } + } + return rc; +} + +static rpmRC ndb_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen) +{ + int rc; + struct ndbEnv_s *ndbenv = dbc->dbi->dbi_rpmdb->db_dbenv; + + if (!hdrNum) + return ndb_pkgdbIter(dbi, dbc, hdrBlob, hdrLen); + if (hdrNum == ndbenv->hdrNum && ndbenv->data) { + *hdrBlob = ndbenv->data; + *hdrLen = ndbenv->datalen; + return RPMRC_OK; + } + rc = rpmpkgGet(dbc->dbi->dbi_db, hdrNum, hdrBlob, hdrLen); + if (!rc) { + dbc->hdrNum = hdrNum; + setdata(dbc, hdrNum, *hdrBlob, *hdrLen); + } + return rc; +} + +static unsigned int ndb_pkgdbKey(dbiIndex dbi, dbiCursor dbc) +{ + return dbc->hdrNum; +} + + +static void addtoset(dbiIndexSet *set, unsigned int *pkglist, unsigned int pkglistn) +{ + unsigned int i, j; + dbiIndexSet newset = dbiIndexSetNew(pkglistn / 2); + for (i = j = 0; i < pkglistn; i += 2) { + newset->recs[j].hdrNum = pkglist[i]; + newset->recs[j].tagNum = pkglist[i + 1]; + j++; + } + newset->count = j; + if (pkglist) + free(pkglist); + if (*set) { + dbiIndexSetAppendSet(*set, newset, 0); + dbiIndexSetFree(newset); + } else + *set = newset; +} + +/* Iterate over all index entries */ +static rpmRC ndb_idxdbIter(dbiIndex dbi, dbiCursor dbc, dbiIndexSet *set) +{ + int rc; + if (!dbc->list) { + /* setup iteration list on first call */ + rc = rpmidxList(dbc->dbi->dbi_db, &dbc->list, &dbc->nlist, &dbc->listdata); + if (rc) + return rc; + dbc->ilist = 0; + } + for (;;) { + unsigned char *k; + unsigned int kl; + unsigned int *pkglist, pkglistn; + if (dbc->ilist >= dbc->nlist) { + rc = RPMRC_NOTFOUND; + break; + } + k = dbc->listdata + dbc->list[dbc->ilist]; + kl = dbc->list[dbc->ilist + 1]; +#if 0 + if (searchType == DBC_KEY_SEARCH) { + dbc->ilist += 2; + dbc->key = k; + dbc->keylen = kl; + rc = RPMRC_OK; + break; + } +#endif + pkglist = 0; + pkglistn = 0; + rc = rpmidxGet(dbc->dbi->dbi_db, k, kl, &pkglist, &pkglistn); + if (rc && rc != RPMRC_NOTFOUND) + break; + dbc->ilist += 2; + if (!rc && pkglistn) { + addtoset(set, pkglist, pkglistn); + dbc->key = k; + dbc->keylen = kl; + break; + } + if (pkglist) + free(pkglist); + } + return rc; +} + +static rpmRC ndb_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int searchType) +{ + int rc; + unsigned int *pkglist = 0, pkglistn = 0; + + if (!keyp) + return ndb_idxdbIter(dbi, dbc, set); + + if (searchType == DBC_PREFIX_SEARCH) { + unsigned int *list = 0, nlist = 0, i = 0; + unsigned char *listdata = 0; + int rrc = RPMRC_NOTFOUND; + rc = rpmidxList(dbc->dbi->dbi_db, &list, &nlist, &listdata); + if (rc) + return rc; + for (i = 0; i < nlist && !rc; i += 2) { + unsigned char *k = listdata + list[i]; + unsigned int kl = list[i + 1]; + if (kl < keylen || memcmp(k, keyp, keylen) != 0) + continue; + rc = ndb_idxdbGet(dbi, dbc, (char *)k, kl, set, DBC_NORMAL_SEARCH); + if (rc == RPMRC_NOTFOUND) + rc = 0; + else + rrc = rc; + } + if (list) + free(list); + if (listdata) + free(listdata); + return rc ? rc : rrc; + } + + rc = rpmidxGet(dbc->dbi->dbi_db, (const unsigned char *)keyp, keylen, &pkglist, &pkglistn); + if (!rc) + addtoset(set, pkglist, pkglistn); + return rc; +} + +static rpmRC ndb_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) +{ + return rpmidxPut(dbc->dbi->dbi_db, (const unsigned char *)keyp, keylen, rec->hdrNum, rec->tagNum); +} + +static rpmRC ndb_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) +{ + return rpmidxDel(dbc->dbi->dbi_db, (const unsigned char *)keyp, keylen, rec->hdrNum, rec->tagNum); +} + +static const void * ndb_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) +{ + if (dbc->key && keylen) + *keylen = dbc->keylen; + return dbc->key; +} + + + +struct rpmdbOps_s ndb_dbops = { + .open = ndb_Open, + .close = ndb_Close, + .verify = ndb_Verify, + .setFSync = ndb_SetFSync, + .ctrl = ndb_Ctrl, + + .cursorInit = ndb_CursorInit, + .cursorFree = ndb_CursorFree, + + .pkgdbNew = ndb_pkgdbNew, + .pkgdbPut = ndb_pkgdbPut, + .pkgdbDel = ndb_pkgdbDel, + .pkgdbGet = ndb_pkgdbGet, + .pkgdbKey = ndb_pkgdbKey, + + .idxdbGet = ndb_idxdbGet, + .idxdbPut = ndb_idxdbPut, + .idxdbDel = ndb_idxdbDel, + .idxdbKey = ndb_idxdbKey +}; + diff --git a/lib/backend/ndb/rpmidx.c b/lib/backend/ndb/rpmidx.c new file mode 100644 index 000000000..313d2e0fb --- /dev/null +++ b/lib/backend/ndb/rpmidx.c @@ -0,0 +1,1280 @@ +#define _GNU_SOURCE + +#include "system.h" + +#include <rpm/rpmlog.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <errno.h> + +#include <endian.h> + +#include "rpmidx.h" +#include "rpmxdb.h" + +#define RPMRC_OK 0 +#define RPMRC_NOTFOUND 1 +#define RPMRC_FAIL 2 + +/* Index database + * + * + * Layout: + * Header + * Slots + * Keys + * + * Each slot contains 12 bytes, they are split into a 8 byte + * and a 4 byte part: + * 4 bytes key offset + extra tag bits + * 4 bytes data + * 4 bytes data overflow + * The slot space first contains all 8 byte parts followed by all of + * the 4 byte overflow parts. This is done because most of the time we + * do not need the latter. + * + * If a new (key, pkgidx, datidx) tupel is added, the key is hashed with + * the popular murmur hash. The lower bits of the hash determine the start + * slot, parts of the higher bits are used as extra key equality check. + * The (pkgidx, datidx) pair is encoded in a (data, dataovl) pair, so that + * most of the time dataovl is zero. + * + * The code then checks the current entry at the start slot. If the key + * does not match, it advances to the next slot. If it matches, it also + * checks the data part for a match but it remembers the key offset. + * If the code found a (key, data, dataovl) match, nothing needs to be done. + * + * Otherwise, the code arrived at an empty slot. It then adds the key + * to the key space if it did not find a matching key, and then puts + * the encoded (key, data, dataovl) pair into the slot. + * + * Deleting a (key, data) pair is done by replacing the slot with a + * (-1, -1, 0) dummy entry. + * + */ + + +typedef struct rpmidxdb_s { + rpmpkgdb pkgdb; /* master database */ + + char *filename; + int fd; /* our file descriptor */ + int flags; + int mode; + + int rdonly; + + /* xdb support */ + rpmxdb xdb; + unsigned int xdbtag; + unsigned int xdbid; + + unsigned char *head_mapped; + unsigned char *slot_mapped; + unsigned char *key_mapped; + unsigned int key_size; + unsigned int file_size; + + unsigned int generation; + unsigned int nslots; + unsigned int usedslots; + unsigned int dummyslots; + + unsigned int keyend; + unsigned int keyexcess; + + unsigned int hmask; + unsigned int xmask; + + unsigned int pagesize; +} * rpmidxdb; + +static inline unsigned int le2h(unsigned char *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline void h2le(unsigned int x, unsigned char *p) +{ + p[0] = x; + p[1] = x >> 8; + p[2] = x >> 16; + p[3] = x >> 24; +} + +/* aligned versions */ +static inline unsigned int le2ha(unsigned char *p) +{ + unsigned int x = *(unsigned int *)p; + return le32toh(x); +} + +static inline void h2lea(unsigned int x, unsigned char *p) +{ + *(unsigned int *)p = htole32(x); +} + +/*** Header management ***/ + +#define IDXDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'I' << 24) +#define IDXDB_VERSION 0 + +#define IDXDB_OFFSET_MAGIC 0 +#define IDXDB_OFFSET_VERSION 4 +#define IDXDB_OFFSET_GENERATION 8 +#define IDXDB_OFFSET_NSLOTS 12 +#define IDXDB_OFFSET_USEDSLOTS 16 +#define IDXDB_OFFSET_DUMMYSLOTS 20 +#define IDXDB_OFFSET_XMASK 24 +#define IDXDB_OFFSET_KEYEND 28 +#define IDXDB_OFFSET_KEYEXCESS 32 +#define IDXDB_OFFSET_OBSOLETE 36 + +#define IDXDB_SLOT_OFFSET 64 +#define IDXDB_KEY_CHUNKSIZE 4096 + +/* XDB subids */ + +#define IDXDB_XDB_SUBTAG 0 +#define IDXDB_XDB_SUBTAG_REBUILD 1 + +static void set_mapped(rpmidxdb idxdb, unsigned char *addr, unsigned int size) +{ + if (addr) { + idxdb->head_mapped = addr; + idxdb->slot_mapped = addr + IDXDB_SLOT_OFFSET; + idxdb->key_mapped = addr + IDXDB_SLOT_OFFSET + idxdb->nslots * 12; + idxdb->key_size = size - (IDXDB_SLOT_OFFSET + idxdb->nslots * 12); + idxdb->file_size = size; + } else { + idxdb->head_mapped = idxdb->slot_mapped = idxdb->key_mapped = 0; + idxdb->file_size = idxdb->key_size = 0; + } +} + +/* XDB callbacks */ +static void mapcb(rpmxdb xdb, void *data, void *newaddr, size_t newsize) { + set_mapped((rpmidxdb)data, newaddr, (unsigned int)newsize); +} + +static int rpmidxReadHeader(rpmidxdb idxdb); + +static int rpmidxMap(rpmidxdb idxdb) +{ + if (idxdb->xdb) { + if (rpmxdbMapBlob(idxdb->xdb, idxdb->xdbid, idxdb->rdonly ? O_RDONLY : O_RDWR, mapcb, idxdb)) + return RPMRC_FAIL; + if (idxdb->file_size < 4096) { + rpmxdbUnmapBlob(idxdb->xdb, idxdb->xdbid); + return RPMRC_FAIL; + } + } else { +#ifdef IDXDB_FILESUPPORT + struct stat stb; + size_t size; + void *mapped; + if (fstat(idxdb->fd, &stb)) + return RPMRC_FAIL; + size = stb.st_size; + if (size < 4096) + return RPMRC_FAIL; + /* round up for mmap */ + size = (size + idxdb->pagesize - 1) & ~(idxdb->pagesize - 1); + mapped = mmap(0, size, idxdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, idxdb->fd, 0); + if (mapped == MAP_FAILED) + return RPMRC_FAIL; + set_mapped(idxdb, mapped, (unsigned int)stb.st_size); +#else + return RPMRC_FAIL; +#endif + } + return RPMRC_OK; +} + +static void rpmidxUnmap(rpmidxdb idxdb) +{ + if (!idxdb->head_mapped) + return; + if (idxdb->xdb) { + rpmxdbUnmapBlob(idxdb->xdb, idxdb->xdbid); + } else { +#ifdef IDXDB_FILESUPPORT + size_t size = idxdb->file_size; + /* round up for munmap */ + size = (size + idxdb->pagesize - 1) & ~(idxdb->pagesize - 1); + munmap(idxdb->head_mapped, size); + set_mapped(idxdb, 0, 0); +#else + return; +#endif + } +} + +#ifdef IDXDB_FILESUPPORT +static int rpmidxReadHeader(rpmidxdb idxdb); + +/* re-open file to get the new version */ +static int rpmidxHandleObsolete(rpmidxdb idxdb) +{ + int nfd; + struct stat stb1, stb2; + + if (fstat(idxdb->fd, &stb1)) + return RPMRC_FAIL; + nfd = open(idxdb->filename, idxdb->rdonly ? O_RDONLY : O_RDWR, 0); + if (nfd == -1) + return RPMRC_FAIL; + if (fstat(nfd, &stb2)) { + close(nfd); + return RPMRC_FAIL; + } + if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino) + return RPMRC_FAIL; /* openend the same obsolete file */ + rpmidxUnmap(idxdb); + close(idxdb->fd); + idxdb->fd = nfd; + return rpmidxReadHeader(idxdb); /* re-try with new file */ +} +#endif + +static int rpmidxReadHeader(rpmidxdb idxdb) +{ + unsigned int version; + + if (idxdb->head_mapped) { + if (le2ha(idxdb->head_mapped + IDXDB_OFFSET_GENERATION) == idxdb->generation) + return RPMRC_OK; + rpmidxUnmap(idxdb); + } + idxdb->nslots = 0; + if (rpmidxMap(idxdb)) + return RPMRC_FAIL; + + if (le2ha(idxdb->head_mapped + IDXDB_OFFSET_MAGIC) != IDXDB_MAGIC) { + rpmidxUnmap(idxdb); + return RPMRC_FAIL; + } + version = le2ha(idxdb->head_mapped + IDXDB_OFFSET_VERSION); + if (version != IDXDB_VERSION) { + rpmlog(RPMLOG_ERR, _("rpmidx: Version mismatch. Expected version: %u. " + "Found version: %u\n"), IDXDB_VERSION, version); + rpmidxUnmap(idxdb); + return RPMRC_FAIL; + } +#ifdef IDXDB_FILESUPPORT + if (!idxdb->xdb && le2ha(idxdb->head_mapped + IDXDB_OFFSET_OBSOLETE)) + return rpmidxHandleObsolete(idxdb); +#endif + idxdb->generation = le2ha(idxdb->head_mapped + IDXDB_OFFSET_GENERATION); + idxdb->nslots = le2ha(idxdb->head_mapped + IDXDB_OFFSET_NSLOTS); + idxdb->usedslots = le2ha(idxdb->head_mapped + IDXDB_OFFSET_USEDSLOTS); + idxdb->dummyslots = le2ha(idxdb->head_mapped + IDXDB_OFFSET_DUMMYSLOTS); + idxdb->xmask = le2ha(idxdb->head_mapped + IDXDB_OFFSET_XMASK); + idxdb->keyend = le2ha(idxdb->head_mapped + IDXDB_OFFSET_KEYEND); + idxdb->keyexcess = le2ha(idxdb->head_mapped + IDXDB_OFFSET_KEYEXCESS); + + idxdb->hmask = idxdb->nslots - 1; + + /* now that we know nslots we can split between slots and keys */ + if (idxdb->file_size <= IDXDB_SLOT_OFFSET + idxdb->nslots * 12) { + rpmidxUnmap(idxdb); /* too small, somthing is wrong */ + return RPMRC_FAIL; + } + idxdb->key_mapped = idxdb->slot_mapped + idxdb->nslots * 12; + idxdb->key_size = idxdb->file_size - (IDXDB_SLOT_OFFSET + idxdb->nslots * 12); + return RPMRC_OK; +} + +static int rpmidxWriteHeader(rpmidxdb idxdb) +{ + if (!idxdb->head_mapped) + return RPMRC_FAIL; + h2lea(IDXDB_MAGIC, idxdb->head_mapped + IDXDB_OFFSET_MAGIC); + h2lea(IDXDB_VERSION, idxdb->head_mapped + IDXDB_OFFSET_VERSION); + h2lea(idxdb->generation, idxdb->head_mapped + IDXDB_OFFSET_GENERATION); + h2lea(idxdb->nslots, idxdb->head_mapped + IDXDB_OFFSET_NSLOTS); + h2lea(idxdb->usedslots, idxdb->head_mapped + IDXDB_OFFSET_USEDSLOTS); + h2lea(idxdb->dummyslots, idxdb->head_mapped + IDXDB_OFFSET_DUMMYSLOTS); + h2lea(idxdb->xmask, idxdb->head_mapped + IDXDB_OFFSET_XMASK); + h2lea(idxdb->keyend, idxdb->head_mapped + IDXDB_OFFSET_KEYEND); + h2lea(idxdb->keyexcess, idxdb->head_mapped + IDXDB_OFFSET_KEYEXCESS); + return RPMRC_OK; +} + +static inline void updateUsedslots(rpmidxdb idxdb) +{ + h2lea(idxdb->usedslots, idxdb->head_mapped + IDXDB_OFFSET_USEDSLOTS); +} + +static inline void updateDummyslots(rpmidxdb idxdb) +{ + h2lea(idxdb->dummyslots, idxdb->head_mapped + IDXDB_OFFSET_DUMMYSLOTS); +} + +static inline void updateKeyend(rpmidxdb idxdb) +{ + h2lea(idxdb->keyend, idxdb->head_mapped + IDXDB_OFFSET_KEYEND); +} + +static inline void updateKeyexcess(rpmidxdb idxdb) +{ + h2lea(idxdb->keyexcess, idxdb->head_mapped + IDXDB_OFFSET_KEYEXCESS); +} + +static inline void bumpGeneration(rpmidxdb idxdb) +{ + idxdb->generation++; + h2lea(idxdb->generation, idxdb->head_mapped + IDXDB_OFFSET_GENERATION); +} + +#ifdef IDXDB_FILESUPPORT +static int createempty(rpmidxdb idxdb, off_t off, size_t size) +{ + char buf[4096]; + memset(buf, 0, sizeof(buf)); + while (size >= 4096) { + if (pwrite(idxdb->fd, buf, 4096, off) != 4096) + return RPMRC_FAIL; + off += 4096; + size -= 4096; + } + if (size > 0 && pwrite(idxdb->fd, buf, size , off) != size) + return RPMRC_FAIL; + return RPMRC_OK; +} +#endif + +/*** Key management ***/ + +#define MURMUR_M 0x5bd1e995 + +static unsigned int murmurhash(const unsigned char *s, unsigned int l) +{ + unsigned int h = l * MURMUR_M; + + while (l >= 4) { + h += s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + h *= MURMUR_M; + h ^= h >> 16; + s += 4; + l -= 4; + } + switch (l) { + case 3: + h += s[2] << 16; + case 2: + h += s[1] << 8; + case 1: + h += s[0]; + h *= MURMUR_M; + h ^= h >> 16; + default: + break; + } + h *= MURMUR_M; + h ^= h >> 10; + h *= MURMUR_M; + h ^= h >> 17; + return h; +} + +static inline unsigned int decodekeyl(unsigned char *p, unsigned int *hl) +{ + if (*p != 255) { + *hl = 1; + return *p; + } else if (p[1] != 255 || p[2] != 255) { + *hl = 3; + return p[1] | p[2] << 8; + } else { + *hl = 7; + return p[3] | p[4] << 8 | p[5] << 16 | p[6] << 24; + } +} + +static inline void encodekeyl(unsigned char *p, unsigned int keyl) +{ + if (keyl && keyl < 255) { + p[0] = keyl; + } else if (keyl < 65535) { + p[0] = 255; + p[1] = keyl; + p[2] = keyl >> 8; + } else { + p[0] = 255; + p[1] = 255; + p[2] = 255; + p[3] = keyl; + p[4] = keyl >> 8; + p[5] = keyl >> 16; + p[6] = keyl >> 24; + } +} + +static inline unsigned int keylsize(unsigned int keyl) +{ + return keyl && keyl < 255 ? 1 : keyl < 65535 ? 3 : 7; +} + +static inline int equalkey(rpmidxdb idxdb, unsigned int off, const unsigned char *key, unsigned int keyl) +{ + unsigned char *p; + if (off + keyl + 1 > idxdb->keyend) + return 0; + p = idxdb->key_mapped + off; + if (keyl && keyl < 255) { + if (*p != keyl) + return 0; + p += 1; + } else if (keyl < 65535) { + if (p[0] != 255 || (p[1] | p[2] << 8) != keyl) + return 0; + p += 3; + } else { + if (p[0] != 255 || p[1] != 255 || p[2] != 255 || (p[3] | p[4] << 8 | p[5] << 16 | p[6] << 24) != keyl) + return 0; + p += 7; + } + if (keyl && memcmp(key, p, keyl)) + return 0; + return 1; +} + +static int addkeypage(rpmidxdb idxdb) { + unsigned int addsize = idxdb->pagesize > IDXDB_KEY_CHUNKSIZE ? idxdb->pagesize : IDXDB_KEY_CHUNKSIZE; + + if (idxdb->xdb) { + if (rpmxdbResizeBlob(idxdb->xdb, idxdb->xdbid, idxdb->file_size + addsize)) + return RPMRC_FAIL; + } else { +#ifdef IDXDB_FILESUPPORT + /* don't use ftruncate because we want to create a "backed" page */ + void *newaddr; + size_t oldsize, newsize; + if (createempty(idxdb, idxdb->file_size, addsize)) + return RPMRC_FAIL; + oldsize = idxdb->file_size; + newsize = idxdb->file_size + addsize; + /* round up for mremap */ + oldsize = (oldsize + idxdb->pagesize - 1) & ~(idxdb->pagesize - 1); + newsize = (newsize + idxdb->pagesize - 1) & ~(idxdb->pagesize - 1); + newaddr = mremap(idxdb->head_mapped, oldsize, newsize, MREMAP_MAYMOVE); + if (newaddr == MAP_FAILED) + return RPMRC_FAIL; + set_mapped(idxdb, newaddr, idxdb->file_size + addsize); +#else + return RPMRC_FAIL; +#endif + } + return RPMRC_OK; +} + +static int addnewkey(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int *keyoffp) +{ + int hl = keylsize(keyl); + while (idxdb->key_size - idxdb->keyend < hl + keyl) { + if (addkeypage(idxdb)) + return RPMRC_FAIL; + } + encodekeyl(idxdb->key_mapped + idxdb->keyend, keyl); + if (keyl) + memcpy(idxdb->key_mapped + idxdb->keyend + hl, key, keyl); + *keyoffp = idxdb->keyend; + idxdb->keyend += hl + keyl; + updateKeyend(idxdb); + return RPMRC_OK; +} + + +/*** Data encoding/decoding ***/ + +/* Encode a (pkgidx, datidx) tuple into a (data, ovldata) tuple in a way + * that most of the time ovldata will be zero. */ +static inline unsigned int encodedata(rpmidxdb idxdb, unsigned int pkgidx, unsigned int datidx, unsigned int *ovldatap) +{ + if (pkgidx < 0x100000 && datidx < 0x400) { + *ovldatap = 0; + return pkgidx | datidx << 20; + } else if (pkgidx < 0x1000000 && datidx < 0x40) { + *ovldatap = 0; + return pkgidx | datidx << 24 | 0x40000000; + } else { + *ovldatap = pkgidx; + return datidx | 0x80000000; + } +} + +/* Decode (data, ovldata) back into (pkgidx, datidx) */ +static inline unsigned int decodedata(rpmidxdb idxdb, unsigned int data, unsigned int ovldata, unsigned int *datidxp) +{ + if (data & 0x80000000) { + *datidxp = data ^ 0x80000000; + return ovldata; + } else if (data & 0x40000000) { + *datidxp = (data ^ 0x40000000) >> 24; + return data & 0xffffff; + } else { + *datidxp = data >> 20; + return data & 0xfffff; + } +} + + +/*** Rebuild helpers ***/ + +/* copy a single data entry into the new database */ +static inline void copyentry(rpmidxdb idxdb, unsigned int keyh, unsigned int newkeyoff, unsigned int data, unsigned int ovldata) +{ + unsigned int h, hh = 7; + unsigned char *ent; + unsigned int hmask = idxdb->hmask; + unsigned int x; + + /* find an empty slot */ + for (h = keyh & hmask;; h = (h + hh++) & hmask) { + ent = idxdb->slot_mapped + 8 * h; + x = le2ha(ent); + if (x == 0) + break; + } + /* write data */ + h2lea(newkeyoff, ent); + h2lea(data, ent + 4); + if (ovldata) + h2lea(ovldata, idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h); + idxdb->usedslots++; +} + +/* copy all entries belonging to a single key from the old database into the new database */ +static inline void copykeyentries(const unsigned char *key, unsigned int keyl, rpmidxdb idxdb, unsigned int oldkeyoff, rpmidxdb nidxdb, unsigned int newkeyoff, unsigned char *done) +{ + unsigned int h, hh; + unsigned int keyh = murmurhash(key, keyl); + unsigned int hmask = idxdb->hmask; + + oldkeyoff |= keyh & idxdb->xmask; + newkeyoff |= keyh & nidxdb->xmask; + for (h = keyh & hmask, hh = 7; ; h = (h + hh++) & hmask) { + unsigned char *ent = idxdb->slot_mapped + 8 * h; + unsigned int data, ovldata; + unsigned int x = le2ha(ent); + if (x == 0) + break; + if (x != oldkeyoff) + continue; + data = le2ha(ent + 4); + ovldata = (data & 0x80000000) ? le2ha(idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h) : 0; + copyentry(nidxdb, keyh, newkeyoff, data, ovldata); + done[h >> 3] |= 1 << (h & 7); + } +} + +static int rpmidxRebuildInternal(rpmidxdb idxdb) +{ + struct rpmidxdb_s nidxdb_s, *nidxdb; + unsigned int i, nslots; + unsigned int keyend, keyoff, xmask; + unsigned char *done; + unsigned char *ent; + unsigned int file_size, key_size, xfile_size; + + nidxdb = &nidxdb_s; + memset(nidxdb, 0, sizeof(*nidxdb)); + nidxdb->pagesize = sysconf(_SC_PAGE_SIZE); + + /* calculate nslots the hard way, don't trust usedslots */ + nslots = 0; + for (i = 0, ent = idxdb->slot_mapped; i < idxdb->nslots; i++, ent += 8) { + unsigned int x = le2ha(ent); + if (x != 0 && x != -1) + nslots++; + } + if (nslots < 256) + nslots = 256; + while (nslots & (nslots - 1)) + nslots = nslots & (nslots - 1); + nslots *= 4; + + nidxdb->nslots = nslots; + nidxdb->hmask = nslots - 1; + + /* calculate the new key space size */ + key_size = idxdb->keyend; + if (key_size < IDXDB_KEY_CHUNKSIZE) + key_size = IDXDB_KEY_CHUNKSIZE; + file_size = IDXDB_SLOT_OFFSET + nslots * 12 + key_size; + + /* round file size to multiple of the page size */ + if (file_size & (nidxdb->pagesize - 1)) { + unsigned int add = nidxdb->pagesize - (file_size & (nidxdb->pagesize - 1)); + file_size += add; + key_size += add; + } + + /* calculate xmask, leave at least 8192 bytes headroom for key space */ + for (xmask = 0x00010000; xmask && xmask < key_size + 8192; xmask <<= 1) + ; + xmask = xmask ? ~(xmask - 1) : 0; + nidxdb->xmask = xmask; + + /* create new database */ + if (idxdb->xdb) { + nidxdb->xdb = idxdb->xdb; + nidxdb->xdbtag = idxdb->xdbtag; + if (rpmxdbLookupBlob(nidxdb->xdb, &nidxdb->xdbid, idxdb->xdbtag, IDXDB_XDB_SUBTAG_REBUILD, O_CREAT|O_TRUNC)) { + return RPMRC_FAIL; + } + if (rpmxdbResizeBlob(nidxdb->xdb, nidxdb->xdbid, file_size)) { + return RPMRC_FAIL; + } + if (rpmidxMap(nidxdb)) { + return RPMRC_FAIL; + } + } else { +#ifdef IDXDB_FILESUPPORT + void *mapped; + nidxdb->filename = malloc(strlen(idxdb->filename) + 8); + if (!nidxdb->filename) + return RPMRC_FAIL; + sprintf(nidxdb->filename, "%s-XXXXXX", idxdb->filename); + nidxdb->fd = mkstemp(nidxdb->filename); + if (nidxdb->fd == -1) { + free(nidxdb->filename); + return RPMRC_FAIL; + } + if (createempty(nidxdb, 0, file_size)) { + close(nidxdb->fd); + unlink(nidxdb->filename); + free(nidxdb->filename); + return RPMRC_FAIL; + } + mapped = mmap(0, file_size, idxdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, nidxdb->fd, 0); + if (mapped == MAP_FAILED) { + close(nidxdb->fd); + unlink(nidxdb->filename); + free(nidxdb->filename); + return RPMRC_FAIL; + } + set_mapped(nidxdb, mapped, file_size); +#else + return RPMRC_FAIL; +#endif + } + + /* copy all entries */ + done = calloc(idxdb->nslots / 8 + 1, 1); + if (!done) { + rpmidxUnmap(nidxdb); + if (!idxdb->xdb) { + close(nidxdb->fd); + unlink(nidxdb->filename); + free(nidxdb->filename); + } + return RPMRC_FAIL; + } + keyend = 1; + for (i = 0, ent = idxdb->slot_mapped; i < idxdb->nslots; i++, ent += 8) { + unsigned int x = le2ha(ent); + unsigned char *key; + unsigned int keyl, hl; + + if (x == 0 || x == -1) + continue; + if (done[i >> 3] & (1 << (i & 7))) { + continue; /* we already did that one */ + } + x &= ~idxdb->xmask; + key = idxdb->key_mapped + x; + keyl = decodekeyl(key, &hl); + keyoff = keyend; + keyend += hl + keyl; + memcpy(nidxdb->key_mapped + keyoff, key, hl + keyl); + copykeyentries(key + hl, keyl, idxdb, x, nidxdb, keyoff, done); + } + free(done); + nidxdb->keyend = keyend; + nidxdb->generation = idxdb->generation + 1; + rpmidxWriteHeader(nidxdb); + rpmidxUnmap(nidxdb); + + /* shrink if we have allocated excessive key space */ + xfile_size = file_size - key_size + keyend + IDXDB_KEY_CHUNKSIZE; + xfile_size = (xfile_size + nidxdb->pagesize - 1) & ~(nidxdb->pagesize - 1); + if (xfile_size < file_size) { + if (nidxdb->xdb) { + rpmxdbResizeBlob(nidxdb->xdb, nidxdb->xdbid, xfile_size); + } else { + if (ftruncate(nidxdb->fd, xfile_size)) { + rpmlog(RPMLOG_WARNING, _("truncate failed: %s\n"), strerror(errno)); + } + } + } + + /* now switch over to new database */ + if (idxdb->xdb) { + rpmidxUnmap(idxdb); + if (rpmxdbRenameBlob(nidxdb->xdb, &nidxdb->xdbid, idxdb->xdbtag, IDXDB_XDB_SUBTAG)) + return RPMRC_FAIL; + idxdb->xdbid = nidxdb->xdbid; + } else { +#ifdef IDXDB_FILESUPPORT + if (rename(nidxdb->filename, idxdb->filename)) { + close(nidxdb->fd); + unlink(nidxdb->filename); + free(nidxdb->filename); + return RPMRC_FAIL; + } + if (idxdb->head_mapped) { + h2lea(1, idxdb->head_mapped + IDXDB_OFFSET_OBSOLETE); + bumpGeneration(idxdb); + rpmidxUnmap(idxdb); + } + free(nidxdb->filename); + close(idxdb->fd); + idxdb->fd = nidxdb->fd; +#else + return RPMRC_FAIL; +#endif + } + if (rpmidxReadHeader(idxdb)) + return RPMRC_FAIL; + return RPMRC_OK; +} + +/* check if we need to rebuild the index. We need to do this if + * - there are too many used slot, so hashing is inefficient + * - there is too much key excess (i.e. holes in the keys) + * - our keys grew so much that they need more bits + */ +static int rpmidxCheck(rpmidxdb idxdb) +{ + if (idxdb->usedslots * 2 > idxdb->nslots || + (idxdb->keyexcess > 4096 && idxdb->keyexcess * 4 > idxdb->keyend) || + idxdb->keyend >= ~idxdb->xmask) { + if (rpmidxRebuildInternal(idxdb)) + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmidxPutInternal(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx) +{ + unsigned int keyh = murmurhash(key, keyl); + unsigned int keyoff = 0; + unsigned int freeh = -1; + unsigned int x, h, hh = 7; + unsigned int hmask; + unsigned int xmask; + unsigned char *ent; + unsigned int data, ovldata; + + if (datidx >= 0x80000000) + return RPMRC_FAIL; + if (rpmidxCheck(idxdb)) + return RPMRC_FAIL; + data = encodedata(idxdb, pkgidx, datidx, &ovldata); + hmask = idxdb->hmask; + xmask = idxdb->xmask; + for (h = keyh & hmask; ; h = (h + hh++) & hmask) { + ent = idxdb->slot_mapped + 8 * h; + x = le2ha(ent); + if (x == 0) /* reached an empty slot */ + break; + if (x == -1) { + freeh = h; /* found a dummy slot, remember the position */ + continue; + } + if (!keyoff) { + if (((x ^ keyh) & xmask) != 0) + continue; + if (!equalkey(idxdb, x & ~xmask, key, keyl)) + continue; + keyoff = x; + } + if (keyoff != x) + continue; + /* string matches, check data/ovldata */ + if (le2ha(ent + 4) == data) { + if (!ovldata || le2ha(idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h) == ovldata) + return RPMRC_OK; /* already in database */ + } + /* continue searching */ + } + if (!keyoff) { + /* we did not find this key. add it */ + if (addnewkey(idxdb, key, keyl, &keyoff)) + return RPMRC_FAIL; + keyoff |= keyh & xmask; /* tag it with the extra bits */ + /* re-calculate ent, addnewkey may have changed the mapping! */ + ent = idxdb->slot_mapped + 8 * h; + } + if (freeh == -1) { + /* did not find a dummy slot, so use the current empty slot */ + idxdb->usedslots++; + updateUsedslots(idxdb); + } else { + /* re-use dummy slot */ + h = freeh; + ent = idxdb->slot_mapped + 8 * h; + if (idxdb->dummyslots) { + idxdb->dummyslots--; + updateDummyslots(idxdb); + } + } + h2lea(keyoff, ent); + h2lea(data, ent + 4); + if (ovldata) + h2lea(ovldata, idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h); + bumpGeneration(idxdb); + return RPMRC_OK; +} + +static int rpmidxDelInternal(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx) +{ + unsigned int keyoff = 0; + unsigned int keyh = murmurhash(key, keyl); + unsigned int hmask; + unsigned int xmask; + unsigned int x, h, hh = 7; + int otherusers = 0; + unsigned int data, ovldata; + + if (datidx >= 0x80000000) + return RPMRC_FAIL; + if (rpmidxCheck(idxdb)) + return RPMRC_FAIL; + data = encodedata(idxdb, pkgidx, datidx, &ovldata); + hmask = idxdb->hmask; + xmask = idxdb->xmask; + for (h = keyh & hmask; ; h = (h + hh++) & hmask) { + unsigned char *ent = idxdb->slot_mapped + 8 * h; + x = le2ha(ent); + if (x == 0) + break; + if (x == -1) + continue; + if (!keyoff) { + if (((x ^ keyh) & xmask) != 0) + continue; + if (!equalkey(idxdb, x & ~xmask, key, keyl)) + continue; + keyoff = x; + } + if (keyoff != x) + continue; + /* key matches, check data/ovldata */ + if (le2ha(ent + 4) != data) { + otherusers = 1; + continue; + } + if (ovldata && le2ha(idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h) != ovldata) { + otherusers = 1; + continue; + } + /* found a match. convert entry to a dummy slot */ + h2lea(-1, ent); + h2lea(-1, ent + 4); + if (ovldata) + h2lea(0, idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h); + idxdb->dummyslots++; + updateDummyslots(idxdb); + /* continue searching (so that we find other users of the key...) */ + } + if (keyoff && !otherusers) { + /* key is no longer in use. free it */ + int hl = keylsize(keyl); + memset(idxdb->key_mapped + (keyoff & ~xmask), 0, hl + keyl); + idxdb->keyexcess += hl + keyl; + updateKeyexcess(idxdb); + } + if (keyoff) + bumpGeneration(idxdb); + return RPMRC_OK; +} + +static int rpmidxGetInternal(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int **pkgidxlistp, unsigned int *pkgidxnump) +{ + unsigned int keyoff = 0; + unsigned int keyh = murmurhash(key, keyl); + unsigned int hmask = idxdb->hmask; + unsigned int xmask = idxdb->xmask; + unsigned int x, h, hh = 7; + unsigned int data, ovldata, datidx; + unsigned int nhits = 0; + unsigned int *hits = 0; + for (h = keyh & hmask; ; h = (h + hh++) & hmask) { + unsigned char *ent = idxdb->slot_mapped + 8 * h; + x = le2ha(ent); + if (x == 0) + break; + if (x == -1) + continue; + if (!keyoff) { + if (((x ^ keyh) & xmask) != 0) + continue; + if (!equalkey(idxdb, x & ~xmask, key, keyl)) + continue; + keyoff = x; + } + if (keyoff != x) + continue; + if ((nhits & 15) == 0) { + if (!hits) { + hits = malloc(16 * sizeof(unsigned int)); + } else { + hits = realloc(hits, (nhits + 16) * sizeof(unsigned int)); + } + if (!hits) + return RPMRC_FAIL; + } + data = le2ha(ent + 4); + ovldata = (data & 0x80000000) ? le2ha(idxdb->slot_mapped + idxdb->nslots * 8 + 4 * h) : 0; + hits[nhits++] = decodedata(idxdb, data, ovldata, &datidx); + hits[nhits++] = datidx; + } + *pkgidxlistp = hits; + *pkgidxnump = nhits; + return nhits ? RPMRC_OK : RPMRC_NOTFOUND; +} + +static int rpmidxListSort_cmp(const void *a, const void *b) +{ + return ((unsigned int *)a)[1] - ((unsigned int *)b)[1]; +} + +/* sort in hash offset order, so that we get sequential acceess */ +static void rpmidxListSort(rpmidxdb idxdb, unsigned int *keylist, unsigned int nkeylist, unsigned char *data) +{ + unsigned int i, *arr; + if (nkeylist < 2 * 2) + return; + arr = malloc(nkeylist * sizeof(unsigned int)); + if (!arr) + return; + for (i = 0; i < nkeylist; i += 2) { + arr[i] = i; + arr[i + 1] = murmurhash(data + keylist[i], keylist[i + 1]) & idxdb->hmask; + } + qsort(arr, nkeylist / 2, 2 * sizeof(unsigned int), rpmidxListSort_cmp); + for (i = 0; i < nkeylist; i += 2) { + unsigned int ai = arr[i]; + arr[i] = keylist[ai]; + arr[i + 1] = keylist[ai + 1]; + } + memcpy(keylist, arr, nkeylist * sizeof(unsigned int)); + free(arr); +} + +static int rpmidxListInternal(rpmidxdb idxdb, unsigned int **keylistp, unsigned int *nkeylistp, unsigned char **datap) +{ + unsigned int *keylist = 0; + unsigned int nkeylist = 0; + unsigned char *data, *terminate, *key, *keyendp; + + data = malloc(idxdb->keyend + 1); /* +1 so we can terminate the last key */ + if (!data) + return RPMRC_FAIL; + memcpy(data, idxdb->key_mapped, idxdb->keyend); + keylist = malloc(16 * sizeof(*keylist)); + if (!keylist) { + free(data); + return RPMRC_FAIL; + } + terminate = 0; + for (key = data + 1, keyendp = data + idxdb->keyend; key < keyendp; ) { + unsigned int hl, keyl; + if (!*key) { + key++; + continue; + } + if ((nkeylist & 15) == 0) { + unsigned int *kl = realloc(keylist, (nkeylist + 16) * sizeof(*keylist)); + if (!kl) { + free(keylist); + free(data); + return RPMRC_FAIL; + } + keylist = kl; + } + keyl = decodekeyl(key, &hl); + keylist[nkeylist++] = key + hl - data; + keylist[nkeylist++] = keyl; + key += hl + keyl; + if (terminate) + *terminate = 0; + terminate = key; + } + if (terminate) + *terminate = 0; + rpmidxListSort(idxdb, keylist, nkeylist, data); + *keylistp = keylist; + *nkeylistp = nkeylist; + *datap = data; + return RPMRC_OK; +} + + +static int rpmidxInitInternal(rpmidxdb idxdb) +{ + if (idxdb->xdb) { + unsigned int id; + int rc = rpmxdbLookupBlob(idxdb->xdb, &id, idxdb->xdbtag, IDXDB_XDB_SUBTAG, 0); + if (rc == RPMRC_OK && id) { + idxdb->xdbid = id; + return RPMRC_OK; /* somebody else was faster */ + } + if (rc && rc != RPMRC_NOTFOUND) + return rc; + } else { +#ifdef IDXDB_FILESUPPORT + struct stat stb; + if (stat(idxdb->filename, &stb)) + return RPMRC_FAIL; + if (stb.st_size) /* somebody else was faster */ + return rpmidxHandleObsolete(idxdb); +#else + return RPMRC_FAIL; +#endif + } + return rpmidxRebuildInternal(idxdb); +} + +static int rpmidxLock(rpmidxdb idxdb, int excl) +{ + if (excl && idxdb->rdonly) + return RPMRC_FAIL; + if (idxdb->xdb) + return rpmxdbLock(idxdb->xdb, excl); + else + return rpmpkgLock(idxdb->pkgdb, excl); +} + +static int rpmidxUnlock(rpmidxdb idxdb, int excl) +{ + if (idxdb->xdb) + return rpmxdbUnlock(idxdb->xdb, excl); + else + return rpmpkgUnlock(idxdb->pkgdb, excl); +} + +static int rpmidxLockReadHeader(rpmidxdb idxdb, int excl) +{ + if (rpmidxLock(idxdb, excl)) + return RPMRC_FAIL; + if (rpmidxReadHeader(idxdb)) { + rpmidxUnlock(idxdb, excl); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmidxInit(rpmidxdb idxdb) +{ + int rc; + if (rpmidxLock(idxdb, 1)) + return RPMRC_FAIL; + rc = rpmidxInitInternal(idxdb); + rpmidxUnlock(idxdb, 1); + return rc; +} + +int rpmidxOpen(rpmidxdb *idxdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode) +{ +#ifdef IDXDB_FILESUPPORT + struct stat stb; + rpmidxdb idxdb; + + *idxdbp = 0; + idxdb = calloc(1, sizeof(*idxdb)); + if (!idxdb) + return RPMRC_FAIL; + idxdb->filename = strdup(filename); + if (!idxdb->filename) { + free(idxdb); + return RPMRC_FAIL; + } + if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY) + idxdb->rdonly = 1; + if ((idxdb->fd = open(filename, flags, mode)) == -1) { + free(idxdb->filename); + free(idxdb); + return RPMRC_FAIL; + } + if (fstat(idxdb->fd, &stb)) { + close(idxdb->fd); + free(idxdb->filename); + free(idxdb); + return RPMRC_FAIL; + } + idxdb->pkgdb = pkgdb; + idxdb->flags = flags; + idxdb->mode = mode; + idxdb->pagesize = sysconf(_SC_PAGE_SIZE); + if (stb.st_size == 0) { + if (rpmidxInit(idxdb)) { + close(idxdb->fd); + free(idxdb->filename); + free(idxdb); + return RPMRC_FAIL; + } + } + *idxdbp = idxdb; + return RPMRC_OK; +#else + return RPMRC_FAIL; +#endif +} + +int rpmidxOpenXdb(rpmidxdb *idxdbp, rpmpkgdb pkgdb, rpmxdb xdb, unsigned int xdbtag) +{ + rpmidxdb idxdb; + unsigned int id; + *idxdbp = 0; + int rc; + + if (rpmxdbLock(xdb, 0)) + return RPMRC_FAIL; + rc = rpmxdbLookupBlob(xdb, &id, xdbtag, IDXDB_XDB_SUBTAG, 0); + if (rc == RPMRC_NOTFOUND) + id = 0; + else if (rc) { + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + idxdb = calloc(1, sizeof(*idxdb)); + if (!idxdb) { + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + idxdb->fd = -1; + idxdb->xdb = xdb; + idxdb->xdbtag = xdbtag; + idxdb->xdbid = id; + idxdb->pkgdb = pkgdb; + idxdb->pagesize = sysconf(_SC_PAGE_SIZE); + if (rpmxdbIsRdonly(xdb)) + idxdb->rdonly = 1; + if (!id) { + if (rpmidxInit(idxdb)) { + free(idxdb); + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + } + *idxdbp = idxdb; + rpmxdbUnlock(xdb, 0); + return RPMRC_OK; +} + +int rpmidxDelXdb(rpmpkgdb pkgdb, rpmxdb xdb, unsigned int xdbtag) +{ + unsigned int id; + int rc; + if (rpmxdbLock(xdb, 1)) + return RPMRC_FAIL; + rc = rpmxdbLookupBlob(xdb, &id, xdbtag, IDXDB_XDB_SUBTAG, 0); + if (rc == RPMRC_NOTFOUND) + id = 0; + else if (rc) { + rpmxdbUnlock(xdb, 1); + return rc; + } + if (id && rpmxdbDelBlob(xdb, id)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; +} + +void rpmidxClose(rpmidxdb idxdb) +{ + rpmidxUnmap(idxdb); + if (idxdb->fd >= 0) { + close(idxdb->fd); + idxdb->fd = -1; + } + if (idxdb->filename) + free(idxdb->filename); + free(idxdb); +} + +int rpmidxPut(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx) +{ + int rc; + if (!pkgidx || datidx >= 0x80000000) { + return RPMRC_FAIL; + } + if (rpmidxLockReadHeader(idxdb, 1)) + return RPMRC_FAIL; + rc = rpmidxPutInternal(idxdb, key, keyl, pkgidx, datidx); + rpmidxUnlock(idxdb, 1); + return rc; +} + +int rpmidxDel(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx) +{ + int rc; + if (!pkgidx || datidx >= 0x80000000) { + return RPMRC_FAIL; + } + if (rpmidxLockReadHeader(idxdb, 1)) + return RPMRC_FAIL; + rc = rpmidxDelInternal(idxdb, key, keyl, pkgidx, datidx); + rpmidxUnlock(idxdb, 1); + return rc; +} + +int rpmidxGet(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int **pkgidxlistp, unsigned int *pkgidxnump) +{ + int rc; + *pkgidxlistp = 0; + *pkgidxnump = 0; + if (rpmidxLockReadHeader(idxdb, 0)) + return RPMRC_FAIL; + rc = rpmidxGetInternal(idxdb, key, keyl, pkgidxlistp, pkgidxnump); + rpmidxUnlock(idxdb, 0); + return rc; +} + +int rpmidxList(rpmidxdb idxdb, unsigned int **keylistp, unsigned int *nkeylistp, unsigned char **datap) +{ + int rc; + *keylistp = 0; + *nkeylistp = 0; + if (rpmidxLockReadHeader(idxdb, 0)) + return RPMRC_FAIL; + rc = rpmidxListInternal(idxdb, keylistp, nkeylistp, datap); + rpmidxUnlock(idxdb, 0); + return rc; +} + +int rpmidxStats(rpmidxdb idxdb) +{ + if (rpmidxLockReadHeader(idxdb, 0)) + return RPMRC_FAIL; + printf("--- IndexDB Stats\n"); + if (idxdb->xdb) { + printf("Xdb tag: %d, id: %d\n", idxdb->xdbtag, idxdb->xdbid); + } else { + printf("Filename: %s\n", idxdb->filename); + } + printf("Generation: %u\n", idxdb->generation); + printf("Slots: %u\n", idxdb->nslots); + printf("Used slots: %u\n", idxdb->usedslots); + printf("Dummy slots: %u\n", idxdb->dummyslots); + printf("Key data size: %u, left %u\n", idxdb->keyend, idxdb->key_size - idxdb->keyend); + printf("Key excess: %u\n", idxdb->keyexcess); + printf("XMask: 0x%08x\n", idxdb->xmask); + rpmidxUnlock(idxdb, 0); + return RPMRC_OK; +} diff --git a/lib/backend/ndb/rpmidx.h b/lib/backend/ndb/rpmidx.h new file mode 100644 index 000000000..e89bd82f1 --- /dev/null +++ b/lib/backend/ndb/rpmidx.h @@ -0,0 +1,18 @@ +#include "rpmpkg.h" +#include "rpmxdb.h" + +struct rpmidxdb_s; +typedef struct rpmidxdb_s *rpmidxdb; + +int rpmidxOpen(rpmidxdb *idxdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode); +int rpmidxOpenXdb(rpmidxdb *idxdbp, rpmpkgdb pkgdb, rpmxdb xdb, unsigned int xdbtag); +int rpmidxDelXdb(rpmpkgdb pkgdb, rpmxdb xdb, unsigned int xdbtag); +void rpmidxClose(rpmidxdb idxdbp); + +int rpmidxGet(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int **pkgidxlist, unsigned int *pkgidxnum); +int rpmidxPut(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx); +int rpmidxDel(rpmidxdb idxdb, const unsigned char *key, unsigned int keyl, unsigned int pkgidx, unsigned int datidx); +int rpmidxList(rpmidxdb idxdb, unsigned int **keylistp, unsigned int *nkeylistp, unsigned char **datap); + +int rpmidxStats(rpmidxdb idxdb); + diff --git a/lib/backend/ndb/rpmpkg.c b/lib/backend/ndb/rpmpkg.c new file mode 100644 index 000000000..68b03eeb7 --- /dev/null +++ b/lib/backend/ndb/rpmpkg.c @@ -0,0 +1,1312 @@ +#include "system.h" + +#include <rpm/rpmlog.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <libgen.h> + +#include "rpmpkg.h" + +#define RPMRC_FAIL 2 +#define RPMRC_NOTFOUND 1 +#define RPMRC_OK 0 + +#ifdef RPMPKG_LZO +static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp); +static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp); +#endif + +static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt); + +typedef struct pkgslot_s { + unsigned int pkgidx; + unsigned int blkoff; + unsigned int blkcnt; + unsigned int slotno; +} pkgslot; + +typedef struct rpmpkgdb_s { + int fd; /* our file descriptor */ + int flags; + int mode; + + int rdonly; + + unsigned int locked_shared; + unsigned int locked_excl; + + int header_ok; /* header data (e.g. generation) is valid */ + unsigned int generation; + unsigned int slotnpages; + unsigned int nextpkgidx; + + struct pkgslot_s *slots; + unsigned int aslots; /* allocated slots */ + unsigned int nslots; /* used slots */ + + unsigned int *slothash; + unsigned int nslothash; + + unsigned int freeslot; /* first free slot */ + int slotorder; + + char *filename; + unsigned int fileblks; /* file size in blks */ + int dofsync; +} * rpmpkgdb; + +#define SLOTORDER_UNORDERED 0 +#define SLOTORDER_BLKOFF 1 + + +static inline unsigned int le2h(unsigned char *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline void h2le(unsigned int x, unsigned char *p) +{ + p[0] = x; + p[1] = x >> 8; + p[2] = x >> 16; + p[3] = x >> 24; +} + +/* adler 32 algorithm taken from RFC 1950 */ +#define ADLER32_INIT 1 +static unsigned int update_adler32(unsigned int adler, unsigned char *buf, unsigned int len) +{ + unsigned int s1 = adler & 0xffff; + unsigned int s2 = (adler >> 16) & 0xffff; + int n; + + for (; len >= 5552; len -= 5552) { + for (n = 0; n < 5552; n++) { + s1 += *buf++; + s2 += s1; + } + s1 %= 65521; + s2 %= 65521; + } + for (n = 0; n < len; n++) { + s1 += *buf++; + s2 += s1; + } + return ((s2 % 65521) << 16) + (s1 % 65521); +} + +/*** Header management ***/ + +#define PKGDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'P' << 24) +#define PKGDB_VERSION 0 + +/* must be a multiple of SLOT_SIZE! */ +#define PKGDB_HEADER_SIZE 32 + +#define PKGDB_OFFSET_MAGIC 0 +#define PKGDB_OFFSET_VERSION 4 +#define PKGDB_OFFSET_GENERATION 8 +#define PKGDB_OFFSET_SLOTNPAGES 12 +#define PKGDB_OFFSET_NEXTPKGIDX 16 + +static int rpmpkgReadHeader(rpmpkgdb pkgdb) +{ + unsigned int generation, slotnpages, nextpkgidx, version; + unsigned char header[PKGDB_HEADER_SIZE]; + + /* if we always head the write lock then our data matches */ + if (pkgdb->header_ok) + return RPMRC_OK; + if (pread(pkgdb->fd, header, PKGDB_HEADER_SIZE, 0) != PKGDB_HEADER_SIZE) { + return RPMRC_FAIL; + } + if (le2h(header + PKGDB_OFFSET_MAGIC) != PKGDB_MAGIC) { + return RPMRC_FAIL; + } + version = le2h(header + PKGDB_OFFSET_VERSION); + if (version != PKGDB_VERSION) { + rpmlog(RPMLOG_ERR, _("rpmpkg: Version mismatch. Expected version: %u. " + "Found version: %u\n"), PKGDB_VERSION, version); + return RPMRC_FAIL; + } + generation = le2h(header + PKGDB_OFFSET_GENERATION); + slotnpages = le2h(header + PKGDB_OFFSET_SLOTNPAGES); + nextpkgidx = le2h(header + PKGDB_OFFSET_NEXTPKGIDX); + /* free slots if our internal data no longer matches */ + if (pkgdb->slots && (pkgdb->generation != generation || pkgdb->slotnpages != slotnpages)) { + free(pkgdb->slots); + pkgdb->slots = 0; + if (pkgdb->slothash) { + free(pkgdb->slothash); + pkgdb->slothash = 0; + } + } + pkgdb->generation = generation; + pkgdb->slotnpages = slotnpages; + pkgdb->nextpkgidx = nextpkgidx; + pkgdb->header_ok = 1; + return RPMRC_OK; +} + +static int rpmpkgWriteHeader(rpmpkgdb pkgdb) +{ + unsigned char header[PKGDB_HEADER_SIZE]; + memset(header, 0, sizeof(header)); + h2le(PKGDB_MAGIC, header + PKGDB_OFFSET_MAGIC); + h2le(PKGDB_VERSION, header + PKGDB_OFFSET_VERSION); + h2le(pkgdb->generation, header + PKGDB_OFFSET_GENERATION); + h2le(pkgdb->slotnpages, header + PKGDB_OFFSET_SLOTNPAGES); + h2le(pkgdb->nextpkgidx, header + PKGDB_OFFSET_NEXTPKGIDX); + if (pwrite(pkgdb->fd, header, sizeof(header), 0) != sizeof(header)) { + return RPMRC_FAIL; + } + if (pkgdb->dofsync && fsync(pkgdb->fd)) + return RPMRC_FAIL; /* write error */ + return RPMRC_OK; +} + +/*** Slot management ***/ + +#define SLOT_MAGIC ('S' | 'l' << 8 | 'o' << 16 | 't' << 24) + +#define SLOT_SIZE 16 +#define BLK_SIZE 16 +#define PAGE_SIZE 4096 + +/* the first slots (i.e. 32 bytes) are used for the header */ +#define SLOT_START (PKGDB_HEADER_SIZE / SLOT_SIZE) + +static inline unsigned int hashpkgidx(unsigned int h) +{ + h *= 0x5bd1e995; + h ^= h >> 16; + return h; +} + +static int rpmpkgHashSlots(rpmpkgdb pkgdb) +{ + unsigned int nslots, num; + unsigned int *hash; + unsigned int h, hh, hmask; + int i; + pkgslot *slot; + + pkgdb->nslothash = 0; + num = pkgdb->nslots; + while (num & (num - 1)) + num = num & (num - 1); + num *= 4; + hash = pkgdb->slothash; + if (!hash || pkgdb->nslothash != num) { + free(pkgdb->slothash); + hash = pkgdb->slothash = calloc(num, sizeof(unsigned int)); + if (!hash) + return RPMRC_FAIL; + pkgdb->nslothash = num; + } else { + memset(hash, 0, num * sizeof(unsigned int)); + } + hmask = num - 1; + nslots = pkgdb->nslots; + for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { + for (h = hashpkgidx(slot->pkgidx) & hmask, hh = 7; hash[h] != 0; h = (h + hh++) & hmask) + ; + hash[h] = i + 1; + } + pkgdb->slothash = hash; + pkgdb->nslothash = num; + return RPMRC_OK; +} + +static int rpmpkgReadSlots(rpmpkgdb pkgdb) +{ + unsigned int slotnpages = pkgdb->slotnpages; + struct stat stb; + unsigned char pagebuf[PAGE_SIZE]; + unsigned int page; + unsigned int i, minblkoff, fileblks, slotno, freeslot, o; + pkgslot *slot; + + /* free old slot data */ + if (pkgdb->slots) { + free(pkgdb->slots); + pkgdb->slots = 0; + } + if (pkgdb->slothash) { + free(pkgdb->slothash); + pkgdb->slothash = 0; + } + pkgdb->nslots = 0; + pkgdb->freeslot = 0; + + /* calculate current database size in blks */ + if (fstat(pkgdb->fd, &stb)) + return RPMRC_FAIL; + if (stb.st_size % BLK_SIZE) + return RPMRC_FAIL; /* hmm */ + fileblks = stb.st_size / BLK_SIZE; + + /* read (and somewhat verify) all slots */ + pkgdb->aslots = slotnpages * (PAGE_SIZE / SLOT_SIZE); + pkgdb->slots = calloc(pkgdb->aslots, sizeof(*pkgdb->slots)); + if (!pkgdb->slots) { + return RPMRC_FAIL; + } + i = 0; + slot = pkgdb->slots; + minblkoff = slotnpages * (PAGE_SIZE / BLK_SIZE); + slotno = SLOT_START; + freeslot = 0; + for (page = 0; page < slotnpages; page++) { + if (pread(pkgdb->fd, pagebuf, PAGE_SIZE, page * PAGE_SIZE) != PAGE_SIZE) + return RPMRC_FAIL; + for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < PAGE_SIZE; o += SLOT_SIZE, slotno++) { + unsigned char *pp = pagebuf + o; + unsigned int blkoff, blkcnt, pkgidx; + if (le2h(pp) != SLOT_MAGIC) { + return RPMRC_FAIL; + } + blkoff = le2h(pp + 8); + if (!blkoff) { + if (!freeslot) + freeslot = slotno; + continue; + } + pkgidx = le2h(pp + 4); + blkcnt = le2h(pp + 12); + slot->pkgidx = pkgidx; + slot->blkoff = blkoff; + slot->blkcnt = blkcnt; + slot->slotno = slotno; + if (slot->blkoff + slot->blkcnt > fileblks) + return RPMRC_FAIL; /* truncated database */ + if (!slot->pkgidx || !slot->blkcnt || slot->blkoff < minblkoff) + return RPMRC_FAIL; /* bad entry */ + i++; + slot++; + } + } + pkgdb->nslots = i; + pkgdb->slotorder = SLOTORDER_UNORDERED; /* XXX: always order? */ + pkgdb->fileblks = fileblks; + pkgdb->freeslot = freeslot; + if (rpmpkgHashSlots(pkgdb)) { + free(pkgdb->slots); + pkgdb->slots = 0; + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int orderslots_blkoff_cmp(const void *a, const void *b) +{ + unsigned int blkoffa = ((const pkgslot *)a)->blkoff; + unsigned int blkoffb = ((const pkgslot *)b)->blkoff; + return blkoffa > blkoffb ? 1 : blkoffa < blkoffb ? -1 : 0; +} + +static void rpmpkgOrderSlots(rpmpkgdb pkgdb, int slotorder) +{ + if (pkgdb->slotorder == slotorder) + return; + if (slotorder == SLOTORDER_BLKOFF) { + if (pkgdb->nslots > 1) + qsort(pkgdb->slots, pkgdb->nslots, sizeof(*pkgdb->slots), orderslots_blkoff_cmp); + } + pkgdb->slotorder = slotorder; + rpmpkgHashSlots(pkgdb); +} + +static inline pkgslot *rpmpkgFindSlot(rpmpkgdb pkgdb, unsigned int pkgidx) +{ + unsigned int i, h, hh, hmask = pkgdb->nslothash - 1; + unsigned int *hash = pkgdb->slothash; + + for (h = hashpkgidx(pkgidx) & hmask, hh = 7; (i = hash[h]) != 0; h = (h + hh++) & hmask) + if (pkgdb->slots[i - 1].pkgidx == pkgidx) + return pkgdb->slots + (i - 1); + return 0; +} + +static int rpmpkgFindEmptyOffset(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkcnt, unsigned *blkoffp, pkgslot **oldslotp, int dontprepend) +{ + unsigned int i, nslots = pkgdb->nslots; + unsigned int bestblkoff = 0; + unsigned int freecnt, bestfreecnt = 0; + unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); + pkgslot *slot, *oldslot = 0; + + if (pkgdb->slotorder != SLOTORDER_BLKOFF) + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + + if (dontprepend && nslots) { + lastblkend = pkgdb->slots[0].blkoff; + } + /* best fit strategy */ + for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { + if (slot->blkoff < lastblkend) { + return RPMRC_FAIL; /* eek, slots overlap! */ + } + if (slot->pkgidx == pkgidx) { + if (oldslot) { + return RPMRC_FAIL; /* eek, two slots with our pkgid ! */ + } + oldslot = slot; + } + freecnt = slot->blkoff - lastblkend; + if (freecnt >= blkcnt) { + if (!bestblkoff || bestfreecnt > freecnt) { + bestblkoff = lastblkend; + bestfreecnt = freecnt; + } + } + lastblkend = slot->blkoff + slot->blkcnt; + } + if (!bestblkoff) { + bestblkoff = lastblkend; /* append to end */ + } + *oldslotp = oldslot; + *blkoffp = bestblkoff; + return RPMRC_OK; +} + +static int rpmpkgNeighbourCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt, unsigned int *newblkcnt) +{ + unsigned int i, nslots = pkgdb->nslots; + unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); + pkgslot *slot, *left = 0, *right = 0; + + if (pkgdb->slotorder != SLOTORDER_BLKOFF) + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + if (blkoff < lastblkend) + return RPMRC_FAIL; + for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { + if (slot->blkoff < lastblkend) + return RPMRC_FAIL; /* eek, slots overlap! */ + if (slot->blkoff < blkoff) + left = slot; + if (!right && slot->blkoff >= blkoff) + right = slot; + lastblkend = slot->blkoff + slot->blkcnt; + } + if (left && left->blkoff + left->blkcnt != blkoff) + return RPMRC_FAIL; /* must always start right after the block */ + if (!left && blkoff != pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) + return RPMRC_FAIL; + if (right && right->blkoff < blkoff + blkcnt) + return RPMRC_FAIL; + /* check if neighbour blobs are in good shape */ + if (left && rpmpkgVerifyblob(pkgdb, left->pkgidx, left->blkoff, left->blkcnt) != RPMRC_OK) + return RPMRC_FAIL; + if (right && rpmpkgVerifyblob(pkgdb, right->pkgidx, right->blkoff, right->blkcnt) != RPMRC_OK) + return RPMRC_FAIL; + *newblkcnt = right ? right->blkoff - blkoff : blkcnt; + /* bounds are intect. free area. */ + return RPMRC_OK; +} + +static int rpmpkgWriteslot(rpmpkgdb pkgdb, unsigned int slotno, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) +{ + unsigned char buf[SLOT_SIZE]; + /* sanity */ + if (slotno < SLOT_START) + return RPMRC_FAIL; + if (blkoff && slotno == pkgdb->freeslot) + pkgdb->freeslot = 0; + h2le(SLOT_MAGIC, buf); + h2le(pkgidx, buf + 4); + h2le(blkoff, buf + 8); + h2le(blkcnt, buf + 12); + if (pwrite(pkgdb->fd, buf, sizeof(buf), slotno * SLOT_SIZE) != sizeof(buf)) { + return RPMRC_FAIL; + } + pkgdb->generation++; + if (rpmpkgWriteHeader(pkgdb)) { + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb, int pageno) +{ + unsigned char page[PAGE_SIZE]; + int i, off = pageno ? 0 : SLOT_START * SLOT_SIZE; + memset(page, 0, sizeof(page)); + for (i = 0; i < PAGE_SIZE / SLOT_SIZE; i++) + h2le(SLOT_MAGIC, page + i * SLOT_SIZE); + if (pwrite(pkgdb->fd, page, PAGE_SIZE - off, pageno * PAGE_SIZE + off) != PAGE_SIZE - off) { + return RPMRC_FAIL; + } + if (pkgdb->dofsync && fsync(pkgdb->fd)) { + return RPMRC_FAIL; /* write error */ + } + return RPMRC_OK; +} + +/*** Blk primitives ***/ + +static int rpmpkgZeroBlks(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) +{ + unsigned char buf[65536]; + unsigned int towrite; + off_t fileoff; + + memset(buf, 0, sizeof(buf)); + fileoff = (off_t)blkoff * BLK_SIZE; + for (towrite = blkcnt * BLK_SIZE; towrite; ) { + unsigned int chunk = towrite > 65536 ? 65536 : towrite; + if (pwrite(pkgdb->fd, buf, chunk, fileoff) != chunk) { + return RPMRC_FAIL; /* write error */ + } + fileoff += chunk; + towrite -= chunk; + } + if (blkoff + blkcnt > pkgdb->fileblks) + pkgdb->fileblks = blkoff + blkcnt; + return RPMRC_OK; +} + +static int rpmpkgValidateZeroCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) +{ + unsigned long long buf[(65536 / sizeof(unsigned long long)) + 1]; + off_t fileoff; + off_t tocheck; + int i; + + if (blkoff > pkgdb->fileblks) + return RPMRC_FAIL; /* huh? */ + fileoff = (off_t)blkoff * BLK_SIZE; + tocheck = blkoff + blkcnt > pkgdb->fileblks ? pkgdb->fileblks - blkoff : blkcnt; + tocheck *= BLK_SIZE; + while (tocheck >= 65536) { + if (pread(pkgdb->fd, (void *)buf, 65536, fileoff) != 65536) + return RPMRC_FAIL; /* read error */ + for (i = 0; i < 65536 / sizeof(unsigned long long); i++) + if (buf[i]) + return RPMRC_FAIL; /* not empty */ + fileoff += 65536; + tocheck -= 65536; + } + if (tocheck) { + int cnt = (int)tocheck / sizeof(unsigned long long); + buf[cnt++] = 0; + if (pread(pkgdb->fd, (void *)buf, tocheck, fileoff) != tocheck) + return RPMRC_FAIL; /* read error */ + for (i = 0; i < cnt; i++) + if (buf[i]) + return RPMRC_FAIL; /* not empty */ + } + return RPMRC_OK; +} + +static int rpmpkgValidateZero(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) +{ + if (rpmpkgValidateZeroCheck(pkgdb, blkoff, blkcnt) == RPMRC_OK) + return RPMRC_OK; + rpmlog(RPMLOG_WARNING, _("rpmpkg: detected non-zero blob, trying auto repair\n")); + /* auto-repair interrupted transactions */ + if (rpmpkgNeighbourCheck(pkgdb, blkoff, blkcnt, &blkcnt) != RPMRC_OK) + return RPMRC_FAIL; + if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt) != RPMRC_OK) + return RPMRC_FAIL; + return RPMRC_OK; +} + + +/*** Blob primitives ***/ + +/* head: magic + pkgidx + timestamp + bloblen */ +/* tail: adler32 + bloblen + magic */ + +#define BLOBHEAD_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'S' << 24) +#define BLOBTAIL_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'E' << 24) + +#define BLOBHEAD_SIZE (4 + 4 + 4 + 4) +#define BLOBTAIL_SIZE (4 + 4 + 4) + +static int rpmpkgReadBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int *bloblp, unsigned int *tstampp) +{ + unsigned char buf[BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE]; + unsigned int bloblen, toread, tstamp; + off_t fileoff; + unsigned int adl; + int verifyadler = bloblp ? 0 : 1; + + /* sanity */ + if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) + return RPMRC_FAIL; /* blkcnt too small */ + /* read header */ + fileoff = (off_t)blkoff * BLK_SIZE; + if (pread(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) + return RPMRC_FAIL; /* read error */ + if (le2h(buf) != BLOBHEAD_MAGIC) + return RPMRC_FAIL; /* bad blob */ + if (le2h(buf + 4) != pkgidx) + return RPMRC_FAIL; /* bad blob */ + tstamp = le2h(buf + 8); + bloblen = le2h(buf + 12); + if (blkcnt != (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) + return RPMRC_FAIL; /* bad blob */ + adl = ADLER32_INIT; + if (verifyadler) + adl = update_adler32(adl, buf, BLOBHEAD_SIZE); + /* read in 64K chunks */ + fileoff += BLOBHEAD_SIZE; + toread = blkcnt * BLK_SIZE - BLOBHEAD_SIZE; + if (!bloblp) + toread -= BLOBTAIL_SIZE; + while (toread) { + unsigned int chunk = toread > 65536 ? 65536 : toread; + if (pread(pkgdb->fd, blob, chunk, fileoff) != chunk) { + return RPMRC_FAIL; /* read error */ + } + if (verifyadler) { + if (!bloblp) + adl = update_adler32(adl, blob, chunk); + else if (toread > BLOBTAIL_SIZE) + adl = update_adler32(adl, blob, toread - BLOBTAIL_SIZE > chunk ? chunk : toread - BLOBTAIL_SIZE); + } + if (bloblp) + blob += chunk; + toread -= chunk; + fileoff += chunk; + } + /* read trailer */ + if (bloblp) { + memcpy(buf, blob - BLOBTAIL_SIZE, BLOBTAIL_SIZE); + } else if (pread(pkgdb->fd, buf, BLOBTAIL_SIZE, fileoff) != BLOBTAIL_SIZE) { + return RPMRC_FAIL; /* read error */ + } + if (verifyadler && le2h(buf) != adl) { + return RPMRC_FAIL; /* bad blob, adler32 mismatch */ + } + if (le2h(buf + 4) != bloblen) { + return RPMRC_FAIL; /* bad blob, bloblen mismatch */ + } + if (le2h(buf + 8) != BLOBTAIL_MAGIC) { + return RPMRC_FAIL; /* bad blob */ + } + if (bloblp) + *bloblp = bloblen; + if (tstampp) + *tstampp = tstamp; + return RPMRC_OK; +} + +static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) +{ + unsigned char buf[65536]; + return rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, buf, 0, 0); +} + +static int rpmpkgWriteBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int blobl, unsigned int now) +{ + unsigned char buf[(BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE) + BLK_SIZE]; + unsigned int towrite, pad; + unsigned int adl; + off_t fileoff; + + /* sanity */ + if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) + return RPMRC_FAIL; /* blkcnt too small */ + if (blkcnt != (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) + return RPMRC_FAIL; /* blkcnt mismatch */ + fileoff = (off_t)blkoff * BLK_SIZE; + h2le(BLOBHEAD_MAGIC, buf); + h2le(pkgidx, buf + 4); + h2le(now, buf + 8); + h2le(blobl, buf + 12); + if (pwrite(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) { + return RPMRC_FAIL; /* write error */ + } + adl = ADLER32_INIT; + adl = update_adler32(adl, buf, BLOBHEAD_SIZE); + /* write in 64K chunks */ + fileoff += BLOBHEAD_SIZE; + for (towrite = blobl; towrite;) { + unsigned int chunk = towrite > 65536 ? 65536 : towrite; + if (pwrite(pkgdb->fd, blob, chunk, fileoff) != chunk) { + return RPMRC_FAIL; /* write error */ + } + adl = update_adler32(adl, blob, chunk); + blob += chunk; + towrite -= chunk; + fileoff += chunk; + } + /* pad if needed */ + pad = blkcnt * BLK_SIZE - (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE); + if (pad) { + memset(buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, 0, pad); + adl = update_adler32(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad); + } + h2le(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE)); + h2le(blobl, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 4); + h2le(BLOBTAIL_MAGIC, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 8); + if (pwrite(pkgdb->fd, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad + BLOBTAIL_SIZE, fileoff) != pad + BLOBTAIL_SIZE) { + return RPMRC_FAIL; /* write error */ + } + /* update file length */ + if (blkoff + blkcnt > pkgdb->fileblks) + pkgdb->fileblks = blkoff + blkcnt; + if (pkgdb->dofsync && fsync(pkgdb->fd)) { + return RPMRC_FAIL; /* write error */ + } + return RPMRC_OK; +} + +static int rpmpkgDelBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) +{ + if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt)) + return RPMRC_FAIL; + if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt)) + return RPMRC_FAIL; + if (pkgdb->dofsync && fsync(pkgdb->fd)) + return RPMRC_FAIL; /* write error */ + return RPMRC_OK; +} + + +static int rpmpkgMoveBlob(rpmpkgdb pkgdb, pkgslot *slot, unsigned int newblkoff) +{ + unsigned int pkgidx = slot->pkgidx; + unsigned int blkoff = slot->blkoff; + unsigned int blkcnt = slot->blkcnt; + unsigned char *blob; + unsigned int tstamp, blobl; + + blob = malloc((size_t)blkcnt * BLK_SIZE); + if (rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, &blobl, &tstamp)) { + free(blob); + return RPMRC_FAIL; + } + if (rpmpkgWriteBlob(pkgdb, pkgidx, newblkoff, blkcnt, blob, blobl, tstamp)) { + free(blob); + return RPMRC_FAIL; + } + free(blob); + if (rpmpkgWriteslot(pkgdb, slot->slotno, pkgidx, newblkoff, blkcnt)) { + return RPMRC_FAIL; + } + if (rpmpkgDelBlob(pkgdb, pkgidx, blkoff, blkcnt)) { + return RPMRC_FAIL; + } + slot->blkoff = newblkoff; + pkgdb->slotorder = SLOTORDER_UNORDERED; + return RPMRC_OK; +} + +static int rpmpkgAddSlotPage(rpmpkgdb pkgdb) +{ + unsigned int cutoff; + if (pkgdb->slotorder != SLOTORDER_BLKOFF) + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + cutoff = (pkgdb->slotnpages + 1) * (PAGE_SIZE / BLK_SIZE); + + /* now move every blob before cutoff */ + while (pkgdb->nslots && pkgdb->slots[0].blkoff < cutoff) { + unsigned int newblkoff; + pkgslot *slot = pkgdb->slots, *oldslot; + + oldslot = 0; + if (rpmpkgFindEmptyOffset(pkgdb, slot->pkgidx, slot->blkcnt, &newblkoff, &oldslot, 1)) { + return RPMRC_FAIL; + } + if (!oldslot || oldslot != slot) { + return RPMRC_FAIL; + } + if (rpmpkgMoveBlob(pkgdb, slot, newblkoff)) { + return RPMRC_FAIL; + } + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + } + + /* make sure our new page is empty */ + if (rpmpkgValidateZero(pkgdb, pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE), PAGE_SIZE / BLK_SIZE)) { + return RPMRC_FAIL; + } + if (rpmpkgWriteEmptySlotpage(pkgdb, pkgdb->slotnpages)) { + return RPMRC_FAIL; + } + + /* announce free page */ + pkgdb->freeslot = pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE); + pkgdb->slotnpages++; + pkgdb->generation++; + if (rpmpkgWriteHeader(pkgdb)) { + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmpkgGetLock(rpmpkgdb pkgdb, int type) +{ + if (!pkgdb->fd) + return RPMRC_FAIL; + if (flock(pkgdb->fd, type)) + return RPMRC_FAIL; + return RPMRC_OK; +} + +int rpmpkgLock(rpmpkgdb pkgdb, int excl) +{ + unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared; + if (*lockcntp > 0 || (!excl && pkgdb->locked_excl)) { + (*lockcntp)++; + return RPMRC_OK; + } + pkgdb->header_ok = 0; + if (rpmpkgGetLock(pkgdb, excl ? LOCK_EX : LOCK_SH)) { + return RPMRC_FAIL; + } + (*lockcntp)++; + return RPMRC_OK; +} + +static int rpmpkgLockInternal(rpmpkgdb pkgdb, int excl) +{ + if (excl && pkgdb->rdonly) + return RPMRC_FAIL; + + return rpmpkgLock(pkgdb, excl); +} + +int rpmpkgUnlock(rpmpkgdb pkgdb, int excl) +{ + unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared; + if (*lockcntp == 0) { + return RPMRC_FAIL; + } + if (*lockcntp > 1 || (!excl && pkgdb->locked_excl)) { + (*lockcntp)--; + return RPMRC_OK; + } + if (excl && pkgdb->locked_shared) { + /* excl -> shared switch */ + if (rpmpkgGetLock(pkgdb, LOCK_SH)) { + return RPMRC_FAIL; + } + (*lockcntp)--; + return RPMRC_OK; + } + flock(pkgdb->fd, LOCK_UN); + (*lockcntp)--; + pkgdb->header_ok = 0; + return RPMRC_OK; +} + +static int rpmpkgLockReadHeader(rpmpkgdb pkgdb, int excl) +{ + if (rpmpkgLockInternal(pkgdb, excl)) + return RPMRC_FAIL; + if (rpmpkgReadHeader(pkgdb)) { + rpmpkgUnlock(pkgdb, excl); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmpkgInitInternal(rpmpkgdb pkgdb) +{ + struct stat stb; + if (fstat(pkgdb->fd, &stb)) { + return RPMRC_FAIL; + } + if (stb.st_size == 0) { + if (rpmpkgWriteEmptySlotpage(pkgdb, 0)) { + return RPMRC_FAIL; + } + pkgdb->slotnpages = 1; + if (!pkgdb->nextpkgidx) + pkgdb->nextpkgidx = 1; + pkgdb->generation++; + if (rpmpkgWriteHeader(pkgdb)) { + return RPMRC_FAIL; + } + } + return RPMRC_OK; +} + +static int rpmpkgInit(rpmpkgdb pkgdb) +{ + int rc; + + if (rpmpkgLockInternal(pkgdb, 1)) + return RPMRC_FAIL; + rc = rpmpkgInitInternal(pkgdb); + rpmpkgUnlock(pkgdb, 1); + return rc; +} + +int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode) +{ + struct stat stb; + rpmpkgdb pkgdb; + + *pkgdbp = 0; + pkgdb = calloc(1, sizeof(*pkgdb)); + pkgdb->filename = strdup(filename); + if (!pkgdb->filename) { + free(pkgdb); + return RPMRC_FAIL; + } + if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY) + pkgdb->rdonly = 1; + if ((pkgdb->fd = open(filename, flags, mode)) == -1) { + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + if (flags & O_CREAT) { + char *filenameCopy; + DIR *pdir; + + if ((filenameCopy = strdup(pkgdb->filename)) == NULL) { + close(pkgdb->fd); + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + + if ((pdir = opendir(dirname(filenameCopy))) == NULL) { + free(filenameCopy); + close(pkgdb->fd); + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + + if (fsync(dirfd(pdir)) == -1) { + closedir(pdir); + free(filenameCopy); + close(pkgdb->fd); + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + closedir(pdir); + free(filenameCopy); + + } + if (fstat(pkgdb->fd, &stb)) { + close(pkgdb->fd); + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + if (stb.st_size == 0) { + if (rpmpkgInit(pkgdb)) { + close(pkgdb->fd); + free(pkgdb->filename); + free(pkgdb); + return RPMRC_FAIL; + } + } + pkgdb->flags = flags; + pkgdb->mode = mode; + pkgdb->dofsync = 1; + *pkgdbp = pkgdb; + return RPMRC_OK; +} + +void rpmpkgClose(rpmpkgdb pkgdb) +{ + if (pkgdb->fd >= 0) { + close(pkgdb->fd); + pkgdb->fd = -1; + } + if (pkgdb->slots) + free(pkgdb->slots); + pkgdb->slots = 0; + if (pkgdb->slothash) + free(pkgdb->slothash); + pkgdb->slothash = 0; + free(pkgdb->filename); + free(pkgdb); +} + +void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync) +{ + pkgdb->dofsync = dofsync; +} + + +static int rpmpkgGetInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp) +{ + pkgslot *slot; + unsigned char *blob; + + if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) { + return RPMRC_FAIL; + } + slot = rpmpkgFindSlot(pkgdb, pkgidx); + if (!slot) { + return RPMRC_NOTFOUND; + } + blob = malloc((size_t)slot->blkcnt * BLK_SIZE); + if (rpmpkgReadBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt, blob, bloblp, (unsigned int *)0)) { + free(blob); + return RPMRC_FAIL; + } + *blobp = blob; + return RPMRC_OK; +} + +static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl) +{ + unsigned int blkcnt, blkoff, slotno; + pkgslot *oldslot; + + /* we always read all slots when writing, just in case */ + if (rpmpkgReadSlots(pkgdb)) { + return RPMRC_FAIL; + } + blkcnt = (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE; + /* find a nice place for the blob */ + if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) { + return RPMRC_FAIL; + } + /* create new slot page if we don't have a free slot and can't reuse an old one */ + if (!oldslot && !pkgdb->freeslot) { + if (rpmpkgAddSlotPage(pkgdb)) { + return RPMRC_FAIL; + } + /* redo rpmpkgFindEmptyOffset to get another free area */ + if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) { + return RPMRC_FAIL; + } + } + /* make sure that we don't overwrite data */ + if (rpmpkgValidateZero(pkgdb, blkoff, blkcnt)) { + return RPMRC_FAIL; + } + /* write new blob */ + if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, (unsigned int)time(0))) { + return RPMRC_FAIL; + } + /* write slot */ + slotno = oldslot ? oldslot->slotno : pkgdb->freeslot; + if (!slotno) { + return RPMRC_FAIL; + } + if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) { + free(pkgdb->slots); + pkgdb->slots = 0; + return RPMRC_FAIL; + } + /* erase old blob */ + if (oldslot && oldslot->blkoff) { + if (rpmpkgDelBlob(pkgdb, pkgidx, oldslot->blkoff, oldslot->blkcnt)) { + free(pkgdb->slots); + pkgdb->slots = 0; + return RPMRC_FAIL; + } + } + if (oldslot) { + /* just update the slot, no need to free the slot data */ + oldslot->blkoff = blkoff; + oldslot->blkcnt = blkcnt; + pkgdb->slotorder = SLOTORDER_UNORDERED; + } else { + free(pkgdb->slots); + pkgdb->slots = 0; + } + return RPMRC_OK; +} + +static int rpmpkgDelInternal(rpmpkgdb pkgdb, unsigned int pkgidx) +{ + pkgslot *slot; + unsigned int blkoff, blkcnt; + + /* we always read all slots when writing, just in case */ + if (rpmpkgReadSlots(pkgdb)) { + return RPMRC_FAIL; + } + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + slot = rpmpkgFindSlot(pkgdb, pkgidx); + if (!slot) { + return RPMRC_OK; + } + if (rpmpkgWriteslot(pkgdb, slot->slotno, 0, 0, 0)) { + return RPMRC_FAIL; + } + if (rpmpkgDelBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt)) { + return RPMRC_FAIL; + } + if (pkgdb->nslots > 1 && slot->blkoff < pkgdb->fileblks / 2) { + /* we freed a blob in the first half of our data. do some extra work */ + int i; + if (slot == pkgdb->slots) { + blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); + } else { + blkoff = slot[-1].blkoff + slot[-1].blkcnt; + } + if (slot < pkgdb->slots + pkgdb->nslots - 1) { + blkcnt = slot[1].blkoff - blkoff; + } else { + blkcnt = slot->blkoff + slot->blkcnt - blkoff; + } + slot->blkoff = 0; + slot->blkcnt = 0; + slot = pkgdb->slots + pkgdb->nslots - 2; + if (slot->blkcnt < slot[1].blkcnt) + slot++; /* bigger slot first */ + for (i = 0; i < 2; i++, slot++) { + if (slot == pkgdb->slots + pkgdb->nslots) + slot -= 2; + if (!slot->blkoff || slot->blkoff < blkoff) + continue; + if (slot->blkoff < pkgdb->fileblks / 2) + continue; + if (slot->blkcnt > blkcnt) + continue; + rpmpkgMoveBlob(pkgdb, slot, blkoff); + blkoff += slot->blkcnt; + blkcnt -= slot->blkcnt; + } + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + } else { + slot->blkoff = 0; + slot->blkcnt = 0; + } + /* check if we can truncate the file */ + slot = pkgdb->slots + pkgdb->nslots - 1; + if (!slot->blkoff && pkgdb->nslots > 1) { + slot--; + } + if (slot->blkoff) + blkoff = slot->blkoff + slot->blkcnt; + else + blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); + if (blkoff < pkgdb->fileblks / 4 * 3) { + /* truncate the file */ + if (!rpmpkgValidateZero(pkgdb, blkoff, pkgdb->fileblks - blkoff)) { + if (!ftruncate(pkgdb->fd, blkoff * BLK_SIZE)) { + pkgdb->fileblks = blkoff; + } + } + } + free(pkgdb->slots); + pkgdb->slots = 0; + return RPMRC_OK; +} + +static int rpmpkgListInternal(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp) +{ + unsigned int i, nslots, *pkgidxlist; + pkgslot *slot; + + if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) { + return RPMRC_FAIL; + } + if (!pkgidxlistp) { + *npkgidxlistp = pkgdb->nslots; + return RPMRC_OK; + } + rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); + nslots = pkgdb->nslots; + pkgidxlist = calloc(nslots + 1, sizeof(unsigned int)); + for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { + pkgidxlist[i] = slot->pkgidx; + } + *pkgidxlistp = pkgidxlist; + *npkgidxlistp = nslots; + return RPMRC_OK; +} + +int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp) +{ + int rc; + + *blobp = 0; + *bloblp = 0; + if (!pkgidx) + return RPMRC_FAIL; + if (rpmpkgLockReadHeader(pkgdb, 0)) + return RPMRC_FAIL; + rc = rpmpkgGetInternal(pkgdb, pkgidx, blobp, bloblp); + rpmpkgUnlock(pkgdb, 0); +#ifdef RPMPKG_LZO + if (!rc) + rc = rpmpkgLZODecompress(blobp, bloblp); +#endif + return rc; +} + +int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl) +{ + int rc; + + if (!pkgidx) { + return RPMRC_FAIL; + } + if (rpmpkgLockReadHeader(pkgdb, 1)) + return RPMRC_FAIL; +#ifdef RPMPKG_LZO + if (rpmpkgLZOCompress(&blob, &blobl)) { + rpmpkgUnlock(pkgdb, 1); + return RPMRC_FAIL; + } +#endif + rc = rpmpkgPutInternal(pkgdb, pkgidx, blob, blobl); +#ifdef RPMPKG_LZO + free(blob); +#endif + rpmpkgUnlock(pkgdb, 1); + return rc; +} + +int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx) +{ + int rc; + + if (!pkgidx) { + return RPMRC_FAIL; + } + if (rpmpkgLockReadHeader(pkgdb, 1)) + return RPMRC_FAIL; + rc = rpmpkgDelInternal(pkgdb, pkgidx); + rpmpkgUnlock(pkgdb, 1); + return rc; +} + +int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp) +{ + int rc; + if (pkgidxlistp) + *pkgidxlistp = 0; + *npkgidxlistp = 0; + if (rpmpkgLockReadHeader(pkgdb, 0)) + return RPMRC_FAIL; + rc = rpmpkgListInternal(pkgdb, pkgidxlistp, npkgidxlistp); + rpmpkgUnlock(pkgdb, 0); + return rc; +} + +int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp) +{ + if (rpmpkgLockReadHeader(pkgdb, 1)) + return RPMRC_FAIL; + *pkgidxp = pkgdb->nextpkgidx++; + if (rpmpkgWriteHeader(pkgdb)) { + rpmpkgUnlock(pkgdb, 1); + return RPMRC_FAIL; + } + /* no fsync needed. also no need to increase the generation count, + * as the header is always read in */ + rpmpkgUnlock(pkgdb, 1); + return RPMRC_OK; +} + +int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp) +{ + if (rpmpkgLockReadHeader(pkgdb, 0)) + return RPMRC_FAIL; + *generationp = pkgdb->generation; + rpmpkgUnlock(pkgdb, 0); + return RPMRC_OK; +} + +int rpmpkgStats(rpmpkgdb pkgdb) +{ + unsigned int usedblks = 0; + int i; + + if (rpmpkgLockReadHeader(pkgdb, 0)) + return RPMRC_FAIL; + if (rpmpkgReadSlots(pkgdb)) { + rpmpkgUnlock(pkgdb, 0); + return RPMRC_FAIL; + } + for (i = 0; i < pkgdb->nslots; i++) + usedblks += pkgdb->slots[i].blkcnt; + printf("--- Package DB Stats\n"); + printf("Filename: %s\n", pkgdb->filename); + printf("Generation: %d\n", pkgdb->generation); + printf("Slot pages: %d\n", pkgdb->slotnpages); + printf("Used slots: %d\n", pkgdb->nslots); + printf("Free slots: %d\n", pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE) - pkgdb->nslots); + printf("Blob area size: %d\n", (pkgdb->fileblks - pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) * BLK_SIZE); + printf("Blob area used: %d\n", usedblks * BLK_SIZE); + rpmpkgUnlock(pkgdb, 0); + return RPMRC_OK; +} + +#ifdef RPMPKG_LZO + +#include "lzo/lzoconf.h" +#include "lzo/lzo1x.h" + +#define BLOBLZO_MAGIC ('L' | 'Z' << 8 | 'O' << 16 | 'B' << 24) + +static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp) +{ + unsigned char *blob = *blobp; + unsigned int blobl = *bloblp; + unsigned char *lzoblob, *workmem; + unsigned int lzoblobl; + lzo_uint blobl2; + + if (lzo_init() != LZO_E_OK) { + return RPMRC_FAIL; + } + workmem = malloc(LZO1X_1_MEM_COMPRESS); + if (!workmem) { + return RPMRC_FAIL; + } + lzoblobl = 4 + 4 + blobl + blobl / 16 + 64 + 3; + lzoblob = malloc(lzoblobl); + if (!lzoblob) { + free(workmem); + return RPMRC_FAIL; + } + h2le(BLOBLZO_MAGIC, lzoblob); + h2le(blobl, lzoblob + 4); + if (lzo1x_1_compress(blob, blobl, lzoblob + 8, &blobl2, workmem) != LZO_E_OK) { + free(workmem); + free(lzoblob); + return RPMRC_FAIL; + } + free(workmem); + *blobp = lzoblob; + *bloblp = 8 + blobl2; + return RPMRC_OK; +} + +static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp) +{ + unsigned char *lzoblob = *blobp; + unsigned int lzoblobl = *bloblp; + unsigned char *blob; + unsigned int blobl; + lzo_uint blobl2; + + if (!lzoblob || lzoblobl < 8) + return RPMRC_FAIL; + if (le2h(lzoblob) != BLOBLZO_MAGIC) + return RPMRC_FAIL; + if (lzo_init() != LZO_E_OK) + return RPMRC_FAIL; + blobl = le2h(lzoblob + 4); + blob = malloc(blobl ? blobl : 1); + if (!blob) + return RPMRC_FAIL; + if (lzo1x_decompress(lzoblob + 8, lzoblobl - 8, blob, &blobl2, 0) != LZO_E_OK || blobl2 != blobl) { + free(blob); + return RPMRC_FAIL; + } + free(lzoblob); + *blobp = blob; + *bloblp = blobl; + return RPMRC_OK; +} + +#endif diff --git a/lib/backend/ndb/rpmpkg.h b/lib/backend/ndb/rpmpkg.h new file mode 100644 index 000000000..7e5d0c67e --- /dev/null +++ b/lib/backend/ndb/rpmpkg.h @@ -0,0 +1,20 @@ +struct rpmpkgdb_s; +typedef struct rpmpkgdb_s *rpmpkgdb; + +int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode); +void rpmpkgClose(rpmpkgdb pkgdbp); +void rpmpkgSetFsync(rpmpkgdb pkgdbp, int dofsync); + +int rpmpkgLock(rpmpkgdb pkgdb, int excl); +int rpmpkgUnlock(rpmpkgdb pkgdb, int excl); + +int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp); +int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl); +int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx); +int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp); + +int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp); +int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp); + +int rpmpkgStats(rpmpkgdb pkgdb); + diff --git a/lib/backend/ndb/rpmxdb.c b/lib/backend/ndb/rpmxdb.c new file mode 100644 index 000000000..55cc197b3 --- /dev/null +++ b/lib/backend/ndb/rpmxdb.c @@ -0,0 +1,1221 @@ +#define _GNU_SOURCE + +#include "system.h" + +#include <rpm/rpmlog.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <endian.h> +#include <libgen.h> + +#include "rpmxdb.h" + +#define RPMRC_OK 0 +#define RPMRC_NOTFOUND 1 +#define RPMRC_FAIL 2 + +typedef struct rpmxdb_s { + rpmpkgdb pkgdb; /* master database */ + char *filename; + int fd; + int flags; + int mode; + int rdonly; + unsigned int pagesize; + unsigned int generation; + unsigned int slotnpages; + unsigned int usergeneration; + + unsigned char *mapped; + unsigned int mappedlen; + + struct xdb_slot { + unsigned int slotno; + unsigned int blobtag; + unsigned int subtag; + unsigned char *mapped; + int mapflags; + unsigned int startpage; + unsigned int pagecnt; + void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize); + void *mapcallbackdata; + unsigned int next; + unsigned int prev; + } *slots; + unsigned int nslots; + unsigned int firstfree; + unsigned int usedblobpages; + unsigned int systempagesize; + int dofsync; +} *rpmxdb; + + +static inline void h2le(unsigned int x, unsigned char *p) +{ + p[0] = x; + p[1] = x >> 8; + p[2] = x >> 16; + p[3] = x >> 24; +} + +/* aligned versions */ +static inline unsigned int le2ha(unsigned char *p) +{ + unsigned int x = *(unsigned int *)p; + return le32toh(x); +} + +static inline void h2lea(unsigned int x, unsigned char *p) +{ + *(unsigned int *)p = htole32(x); +} + + +#define XDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'X' << 24) +#define XDB_VERSION 0 + +#define XDB_OFFSET_MAGIC 0 +#define XDB_OFFSET_VERSION 4 +#define XDB_OFFSET_GENERATION 8 +#define XDB_OFFSET_SLOTNPAGES 12 +#define XDB_OFFSET_PAGESIZE 16 +#define XDB_OFFSET_USERGENERATION 20 + +/* must be multiple of SLOT_SIZE */ +#define XDB_HEADER_SIZE 32 + +#define SLOT_MAGIC ('S' | 'l' << 8 | 'o' << 16) + +#define SLOT_SIZE 16 +#define SLOT_START (XDB_HEADER_SIZE / SLOT_SIZE) + +static void rpmxdbUnmap(rpmxdb xdb) +{ + munmap(xdb->mapped, xdb->mappedlen); + xdb->mapped = 0; + xdb->mappedlen = 0; +} + +/* slot mapping functions */ +static int mapslot(rpmxdb xdb, struct xdb_slot *slot) +{ + void *mapped; + size_t off, size, shift; + + if (slot->mapped) + return RPMRC_FAIL; + size = slot->pagecnt * xdb->pagesize; + off = slot->startpage * xdb->pagesize; + shift = 0; + if (xdb->pagesize != xdb->systempagesize) { + shift = off & (xdb->systempagesize - 1); + off -= shift; + size += shift; + size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + } + mapped = mmap(0, size, slot->mapflags, MAP_SHARED, xdb->fd, off); + if (mapped == MAP_FAILED) + return RPMRC_FAIL; + slot->mapped = (unsigned char *)mapped + shift; + return RPMRC_OK; +} + +static void unmapslot(rpmxdb xdb, struct xdb_slot *slot) +{ + size_t size; + unsigned char *mapped = slot->mapped; + if (!mapped) + return; + size = slot->pagecnt * xdb->pagesize; + if (xdb->pagesize != xdb->systempagesize) { + size_t off = slot->startpage * xdb->pagesize; + size_t shift = off & (xdb->systempagesize - 1); + mapped -= shift; + size += shift; + size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + } + munmap(mapped, size); + slot->mapped = 0; +} + +static int remapslot(rpmxdb xdb, struct xdb_slot *slot, unsigned int newpagecnt) +{ + void *mapped; + size_t off, oldsize, newsize, shift; + oldsize = slot->pagecnt * xdb->pagesize; + newsize = newpagecnt * xdb->pagesize; + off = slot->startpage * xdb->pagesize; + shift = 0; + if (xdb->pagesize != xdb->systempagesize) { + off = slot->startpage * xdb->pagesize; + shift = off & (xdb->systempagesize - 1); + off -= shift; + oldsize += shift; + oldsize = (oldsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + newsize += shift; + newsize = (newsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + } + if (slot->mapped) + mapped = mremap(slot->mapped - shift, oldsize, newsize, MREMAP_MAYMOVE); + else + mapped = mmap(0, newsize, slot->mapflags, MAP_SHARED, xdb->fd, off); + if (mapped == MAP_FAILED) + return RPMRC_FAIL; + slot->mapped = (unsigned char *)mapped + shift; + slot->pagecnt = newpagecnt; + return RPMRC_OK; +} + + +static int usedslots_cmp(const void *a, const void *b) +{ + struct xdb_slot *sa = *(struct xdb_slot **)a; + struct xdb_slot *sb = *(struct xdb_slot **)b; + if (sa->startpage == sb->startpage) { + return sa->pagecnt > sb->pagecnt ? 1 : sa->pagecnt < sb->pagecnt ? -1 : 0; + } + return sa->startpage > sb->startpage ? 1 : -1; +} + +static int rpmxdbReadHeader(rpmxdb xdb) +{ + struct xdb_slot *slot; + unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)]; + unsigned int slotnpages, pagesize, generation, usergeneration, version; + unsigned int page, *lastfreep; + unsigned char *pageptr; + struct xdb_slot *slots, **usedslots, *lastslot; + unsigned int nslots; + unsigned int usedblobpages; + int i, nused, slotno; + struct stat stb; + size_t mapsize; + + if (xdb->mapped) { + if (le2ha(xdb->mapped + XDB_OFFSET_GENERATION) == xdb->generation) { + return RPMRC_OK; + } + rpmxdbUnmap(xdb); + } + if (fstat(xdb->fd, &stb)) { + return RPMRC_FAIL; + } + if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header)) { + return RPMRC_FAIL; + } + if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC) + return RPMRC_FAIL; + version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION); + if (version != XDB_VERSION) { + rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. " + "Found version: %u\n"), XDB_VERSION, version); + return RPMRC_FAIL; + } + + generation = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION); + slotnpages = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES); + pagesize = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE); + usergeneration = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION); + if (!slotnpages || !pagesize || stb.st_size % pagesize != 0) + return RPMRC_FAIL; + xdb->pagesize = pagesize; + + /* round up */ + mapsize = slotnpages * pagesize; + mapsize = (mapsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + xdb->mapped = mmap(0, mapsize, xdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, xdb->fd, 0); + if ((void *)xdb->mapped == MAP_FAILED) { + xdb->mapped = 0; + return RPMRC_FAIL; + } + xdb->mappedlen = mapsize; + + /* read in all slots */ + xdb->firstfree = 0; + nslots = slotnpages * (pagesize / SLOT_SIZE) - SLOT_START + 1; + slots = calloc(nslots + 1, sizeof(struct xdb_slot)); + if (!slots) { + rpmxdbUnmap(xdb); + return RPMRC_FAIL; + } + usedslots = calloc(nslots + 1, sizeof(int)); + if (!usedslots) { + rpmxdbUnmap(xdb); + free(slots); + return RPMRC_FAIL; + } + nused = 0; + slotno = 1; + slot = slots + 1; + usedblobpages = 0; + lastfreep = &xdb->firstfree; + for (page = 0, pageptr = xdb->mapped; page < slotnpages; page++, pageptr += pagesize) { + unsigned int o; + for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < pagesize; o += SLOT_SIZE, slotno++, slot++) { + unsigned char *pp = pageptr + o; + slot->slotno = slotno; + slot->subtag = le2ha(pp); + if ((slot->subtag & 0x00ffffff) != SLOT_MAGIC) { + free(slots); + free(usedslots); + rpmxdbUnmap(xdb); + return RPMRC_FAIL; + } + slot->subtag = (slot->subtag >> 24) & 255; + slot->blobtag = le2ha(pp + 4); + slot->startpage = le2ha(pp + 8); + slot->pagecnt = le2ha(pp + 12); + if (slot->pagecnt == 0 && slot->startpage) /* empty but used slot? */ + slot->startpage = slotnpages; + if (!slot->startpage) { + *lastfreep = slotno; + lastfreep = &slot->next; + } else { + usedslots[nused++] = slot; + usedblobpages += slot->pagecnt; + } + } + } + if (nused > 1) { + qsort(usedslots, nused, sizeof(*usedslots), usedslots_cmp); + } + /* now chain em */ + slots[0].pagecnt = slotnpages; + lastslot = slots; + for (i = 0; i < nused; i++, lastslot = slot) { + slot = usedslots[i]; + if (lastslot->startpage + lastslot->pagecnt > slot->startpage) { + free(slots); + free(usedslots); + rpmxdbUnmap(xdb); + return RPMRC_FAIL; + } + lastslot->next = slot->slotno; + slot->prev = lastslot->slotno; + } + lastslot->next = nslots; + slots[nslots].slotno = nslots; + slots[nslots].prev = lastslot->slotno; + slots[nslots].startpage = stb.st_size / pagesize; + free(usedslots); + /* now sync with the old slot data */ + if (xdb->slots) { + for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) { + if (slot->startpage && (slot->mapped || slot->mapcallback)) { + struct xdb_slot *nslot; + if (i >= nslots || !slots[i].startpage || slots[i].blobtag != slot->blobtag || slots[i].subtag != slot->subtag) { + /* slot is gone */ + if (slot->mapped) { + unmapslot(xdb, slot); + slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0); + } + continue; + } + nslot = slots + i; + if (slot->mapcallback) { + nslot->mapflags = slot->mapflags; + nslot->mapcallback = slot->mapcallback; + nslot->mapcallbackdata = slot->mapcallbackdata; + } + if (slot->startpage != nslot->startpage || slot->pagecnt != nslot->pagecnt) { + /* slot moved or was resized */ + if (slot->mapped) + unmapslot(xdb, slot); + if (nslot->mapcallback) { + if (nslot->pagecnt) { + mapslot(xdb, nslot); + nslot->mapcallback(xdb, nslot->mapcallbackdata, nslot->mapped, nslot->mapped ? nslot->pagecnt * xdb->pagesize : 0); + } else { + nslot->mapcallback(xdb, nslot->mapcallbackdata, 0, 0); + } + } + } + } + } + free(xdb->slots); + } + xdb->slots = slots; + xdb->nslots = nslots; + xdb->generation = generation; + xdb->slotnpages = slotnpages; + xdb->usergeneration = usergeneration; + xdb->usedblobpages = usedblobpages; + return RPMRC_OK; +} + +static int rpmxdbWriteHeader(rpmxdb xdb) +{ + if (!xdb->mapped) + return RPMRC_FAIL; + h2lea(XDB_MAGIC, xdb->mapped + XDB_OFFSET_MAGIC); + h2lea(XDB_VERSION, xdb->mapped + XDB_OFFSET_VERSION); + h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION); + h2lea(xdb->slotnpages, xdb->mapped + XDB_OFFSET_SLOTNPAGES); + h2lea(xdb->pagesize, xdb->mapped + XDB_OFFSET_PAGESIZE); + h2lea(xdb->usergeneration, xdb->mapped + XDB_OFFSET_USERGENERATION); + return RPMRC_OK; +} + +static void rpmxdbUpdateSlot(rpmxdb xdb, struct xdb_slot *slot) +{ + unsigned char *pp = xdb->mapped + (SLOT_START - 1 + slot->slotno) * SLOT_SIZE; + h2lea(SLOT_MAGIC | (slot->subtag << 24), pp); + h2lea(slot->blobtag, pp + 4); + if (slot->pagecnt || !slot->startpage) + h2lea(slot->startpage, pp + 8); + else + h2lea(1, pp + 8); /* "empty but used" blobs always start at 1 */ + h2lea(slot->pagecnt, pp + 12); + xdb->generation++; + h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION); +} + +static int rpmxdbWriteEmptyPages(rpmxdb xdb, unsigned int pageno, unsigned int count) +{ + unsigned char *page; + if (!count) + return RPMRC_OK; + page = malloc(xdb->pagesize); + if (!page) + return RPMRC_FAIL; + memset(page, 0, xdb->pagesize); + for (; count; count--, pageno++) { + if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) { + free(page); + return RPMRC_FAIL; + } + } + free(page); + return RPMRC_OK; +} + +static int rpmxdbWriteEmptySlotpage(rpmxdb xdb, int pageno) +{ + unsigned char *page; + int i, spp; + page = malloc(xdb->pagesize); + if (!page) + return RPMRC_FAIL; + memset(page, 0, xdb->pagesize); + spp = xdb->pagesize / SLOT_SIZE; /* slots per page */ + for (i = pageno ? 0 : SLOT_START; i < spp; i++) + h2le(SLOT_MAGIC, page + i * SLOT_SIZE); + if (!pageno) { + /* only used when called from InitInternal */ + if (xdb->mapped) { + free(page); + return RPMRC_FAIL; + } + xdb->mapped = page; + rpmxdbWriteHeader(xdb); + xdb->mapped = 0; + } + if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) { + free(page); + return RPMRC_FAIL; + } + free(page); + return RPMRC_OK; +} + +static int rpmxdbInitInternal(rpmxdb xdb) +{ + struct stat stb; + if (fstat(xdb->fd, &stb)) { + return RPMRC_FAIL; + } + if (stb.st_size == 0) { + xdb->slotnpages = 1; + xdb->generation++; + xdb->pagesize = sysconf(_SC_PAGE_SIZE); + if (rpmxdbWriteEmptySlotpage(xdb, 0)) { + return RPMRC_FAIL; + } + } + return RPMRC_OK; +} + +/* we use the master pdb for locking */ +static int rpmxdbLockOnly(rpmxdb xdb, int excl) +{ + if (excl && xdb->rdonly) + return RPMRC_FAIL; + return rpmpkgLock(xdb->pkgdb, excl); +} + +/* this is the same as rpmxdbLockReadHeader. It does the + * ReadHeader to sync the mappings if xdb moved some blobs. + */ +int rpmxdbLock(rpmxdb xdb, int excl) +{ + if (rpmxdbLockOnly(xdb, excl)) + return RPMRC_FAIL; + if (rpmxdbReadHeader(xdb)) { + rpmxdbUnlock(xdb, excl); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +int rpmxdbUnlock(rpmxdb xdb, int excl) +{ + return rpmpkgUnlock(xdb->pkgdb, excl); +} + +static int rpmxdbLockReadHeader(rpmxdb xdb, int excl) +{ + if (rpmxdbLockOnly(xdb, excl)) + return RPMRC_FAIL; + if (rpmxdbReadHeader(xdb)) { + rpmxdbUnlock(xdb, excl); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int rpmxdbInit(rpmxdb xdb) +{ + int rc; + + if (rpmxdbLockOnly(xdb, 1)) + return RPMRC_FAIL; + rc = rpmxdbInitInternal(xdb); + rpmxdbUnlock(xdb, 1); + return rc; +} + +int rpmxdbOpen(rpmxdb *xdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode) +{ + struct stat stb; + rpmxdb xdb; + + *xdbp = 0; + xdb = calloc(1, sizeof(*xdb)); + xdb->pkgdb = pkgdb; + xdb->filename = strdup(filename); + xdb->systempagesize = sysconf(_SC_PAGE_SIZE); + if (!xdb->filename) { + free(xdb); + return RPMRC_FAIL; + } + if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY) + xdb->rdonly = 1; + if ((xdb->fd = open(filename, flags, mode)) == -1) { + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + if (flags & O_CREAT) { + char *filenameCopy; + DIR *pdir; + + if ((filenameCopy = strdup(xdb->filename)) == NULL) { + close(xdb->fd); + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + + if ((pdir = opendir(dirname(filenameCopy))) == NULL) { + free(filenameCopy); + close(xdb->fd); + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + + if (fsync(dirfd(pdir)) == -1) { + closedir(pdir); + free(filenameCopy); + close(xdb->fd); + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + closedir(pdir); + free(filenameCopy); + } + if (fstat(xdb->fd, &stb)) { + close(xdb->fd); + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + if (stb.st_size == 0) { + if (rpmxdbInit(xdb)) { + close(xdb->fd); + free(xdb->filename); + free(xdb); + return RPMRC_FAIL; + } + } + xdb->flags = flags; + xdb->mode = mode; + xdb->dofsync = 1; + *xdbp = xdb; + return RPMRC_OK; +} + +void rpmxdbClose(rpmxdb xdb) +{ + struct xdb_slot *slot; + int i; + + for (i = 1, slot = xdb->slots + 1; i < xdb->nslots; i++, slot++) { + if (slot->mapped) { + unmapslot(xdb, slot); + slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0); + } + } + if (xdb->slots) + free(xdb->slots); + if (xdb->fd >= 0) + close(xdb->fd); + if (xdb->filename) + free(xdb->filename); + free(xdb); +} + +/* moves the blob to a given new location (possibly resizeing) */ +static int moveblobto(rpmxdb xdb, struct xdb_slot *oldslot, struct xdb_slot *afterslot, unsigned int newpagecnt) +{ + struct xdb_slot *nextslot; + unsigned int newstartpage, oldpagecnt; + unsigned int tocopy; + int didmap; + + newstartpage = afterslot->startpage + afterslot->pagecnt; + nextslot = xdb->slots + afterslot->next; + + /* make sure there's enough room */ + if (newpagecnt > nextslot->startpage - newstartpage) + return RPMRC_FAIL; + +#if 0 + printf("moveblobto %d %d %d %d, afterslot %d\n", oldslot->startpage, oldslot->pagecnt, newstartpage, newpagecnt, afterslot->slotno); +#endif + /* map old content */ + didmap = 0; + oldpagecnt = oldslot->pagecnt; + if (!oldslot->mapped && oldpagecnt) { + if (mapslot(xdb, oldslot)) + return RPMRC_FAIL; + didmap = 1; + } + + /* copy content */ + tocopy = newpagecnt > oldpagecnt ? oldpagecnt : newpagecnt; + if (tocopy && pwrite(xdb->fd, oldslot->mapped, tocopy * xdb->pagesize, newstartpage * xdb->pagesize) != tocopy * xdb->pagesize) { + if (didmap) + unmapslot(xdb, oldslot); + return RPMRC_FAIL; + } + /* zero out new pages */ + if (newpagecnt > oldpagecnt) { + if (rpmxdbWriteEmptyPages(xdb, newstartpage + oldpagecnt, newpagecnt - oldpagecnt)) { + if (didmap) + unmapslot(xdb, oldslot); + return RPMRC_FAIL; + } + } + + if (oldslot->mapped) + unmapslot(xdb, oldslot); + + /* set new offset and position */ + oldslot->startpage = newstartpage; + oldslot->pagecnt = newpagecnt; + rpmxdbUpdateSlot(xdb, oldslot); + xdb->usedblobpages -= oldpagecnt; + xdb->usedblobpages += newpagecnt; + + if (afterslot != oldslot && nextslot != oldslot) { + /* remove from old chain */ + xdb->slots[oldslot->prev].next = oldslot->next; + xdb->slots[oldslot->next].prev = oldslot->prev; + + /* chain into new position, between lastslot and nextslot */ + oldslot->prev = afterslot->slotno; + afterslot->next = oldslot->slotno; + + oldslot->next = nextslot->slotno; + nextslot->prev = oldslot->slotno; + } + + /* map again (if needed) */ + if (oldslot->mapcallback) { + if (newpagecnt) { + if (mapslot(xdb, oldslot)) + oldslot->mapped = 0; /* XXX: HELP, what can we do here? */ + } + oldslot->mapcallback(xdb, oldslot->mapcallbackdata, oldslot->mapped, oldslot->mapped ? oldslot->pagecnt * xdb->pagesize : 0); + } + return RPMRC_OK; +} + +/* moves the blob to a new location (possibly resizeing) */ +static int moveblob(rpmxdb xdb, struct xdb_slot *oldslot, unsigned int newpagecnt) +{ + struct xdb_slot *slot, *lastslot; + unsigned int nslots; + unsigned int freecnt; + int i; + + nslots = xdb->nslots; + freecnt = 0; + lastslot = xdb->slots; + for (i = xdb->slots[0].next; ; lastslot = slot, i = slot->next) { + slot = xdb->slots + i; + freecnt = slot->startpage - (lastslot->startpage + lastslot->pagecnt); + if (freecnt >= newpagecnt) + break; + if (i == nslots) + break; + } + if (i == nslots && newpagecnt > freecnt) { + /* need to grow the file */ + if (rpmxdbWriteEmptyPages(xdb, slot->startpage, newpagecnt - freecnt)) { + return RPMRC_FAIL; + } + slot->startpage += newpagecnt - freecnt; + } + return moveblobto(xdb, oldslot, lastslot, newpagecnt); +} + +/* move the two blobs at the end of our file to the free area after the provided slot */ +static int moveblobstofront(rpmxdb xdb, struct xdb_slot *afterslot) +{ + struct xdb_slot *slot1, *slot2; + unsigned int freestart = afterslot->startpage + afterslot->pagecnt; + unsigned int freecount = xdb->slots[afterslot->next].startpage - freestart; + + slot1 = xdb->slots + xdb->slots[xdb->nslots].prev; + if (slot1 == xdb->slots) + slot1 = slot2 = 0; + else { + slot2 = xdb->slots + slot1->prev; + if (slot2 == xdb->slots) + slot2 = 0; + } + if (slot1->pagecnt < slot2->pagecnt) { + struct xdb_slot *tmp = slot1; + slot1 = slot2; + slot2 = tmp; + } + if (slot1 && slot1->pagecnt && slot1->pagecnt <= freecount && slot1->startpage > freestart) { + if (moveblobto(xdb, slot1, afterslot, slot1->pagecnt)) + return RPMRC_FAIL; + freestart += slot1->pagecnt; + freecount -= slot1->pagecnt; + afterslot = slot1; + } + if (slot2 && slot2->pagecnt && slot2->pagecnt <= freecount && slot2->startpage > freestart) { + if (moveblobto(xdb, slot2, afterslot, slot2->pagecnt)) + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +/* add a single page containing empty slots */ +static int addslotpage(rpmxdb xdb) +{ + unsigned char *newaddr; + struct xdb_slot *slot; + int i, spp, nslots; + size_t newmappedlen; + + if (xdb->firstfree) + return RPMRC_FAIL; + + /* move first blob if needed */ + nslots = xdb->nslots; + for (i = xdb->slots[0].next; i != nslots; i = slot->next) { + slot = xdb->slots + i; + if (slot->pagecnt) + break; + } + if (i != nslots && slot->pagecnt && slot->startpage == xdb->slotnpages) { + /* the blob at this slot is in the way. move it. */ + if (moveblob(xdb, slot, slot->pagecnt)) + return RPMRC_FAIL; + } + + spp = xdb->pagesize / SLOT_SIZE; /* slots per page */ + slot = realloc(xdb->slots, (nslots + 1 + spp) * sizeof(*slot)); + if (!slot) + return RPMRC_FAIL; + xdb->slots = slot; + + if (rpmxdbWriteEmptySlotpage(xdb, xdb->slotnpages)) { + return RPMRC_FAIL; + } + /* remap slots */ + newmappedlen = xdb->slotnpages * xdb->pagesize + xdb->pagesize; + newmappedlen = (newmappedlen + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1); + newaddr = mremap(xdb->mapped, xdb->mappedlen, newmappedlen, MREMAP_MAYMOVE); + if (newaddr == MAP_FAILED) + return RPMRC_FAIL; + xdb->mapped = newaddr; + xdb->mappedlen = newmappedlen; + + /* update the header */ + xdb->slotnpages++; + xdb->generation++; + rpmxdbWriteHeader(xdb); + + /* fixup empty but used slots */ + for (i = xdb->slots[0].next; i != nslots; i = slot->next) { + slot = xdb->slots + i; + if (slot->startpage >= xdb->slotnpages) + break; + slot->startpage = xdb->slotnpages; + if (slot->pagecnt) + abort(); + } + + /* move tail element to the new end */ + slot = xdb->slots + nslots + spp; + *slot = xdb->slots[nslots]; + slot->slotno = nslots + spp; + xdb->slots[slot->prev].next = slot->slotno; + xdb->nslots += spp; + + /* add new free slots to the firstfree chain */ + memset(xdb->slots + nslots, 0, sizeof(*slot) * spp); + for (i = 0; i < spp - 1; i++) { + xdb->slots[nslots + i].slotno = nslots + i; + xdb->slots[nslots + i].next = i + 1; + } + xdb->slots[nslots + i].slotno = nslots + i; + xdb->firstfree = nslots; + return RPMRC_OK; +} + +static int createblob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag) +{ + struct xdb_slot *slot; + unsigned int id; + + if (subtag > 255) + return RPMRC_FAIL; + if (!xdb->firstfree) { + if (addslotpage(xdb)) + return RPMRC_FAIL; + } + id = xdb->firstfree; + slot = xdb->slots + xdb->firstfree; + xdb->firstfree = slot->next; + + slot->mapped = 0; + slot->blobtag = blobtag; + slot->subtag = subtag; + slot->startpage = xdb->slotnpages; + slot->pagecnt = 0; + rpmxdbUpdateSlot(xdb, slot); + /* enqueue */ + slot->prev = 0; + slot->next = xdb->slots[0].next; + xdb->slots[slot->next].prev = id; + xdb->slots[0].next = id; +#if 0 + printf("createblob #%d %d/%d\n", id, blobtag, subtag); +#endif + if (slot->slotno != id) + abort(); + if (slot->mapped) + abort(); + *idp = id; + return RPMRC_OK; +} + +int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags) +{ + struct xdb_slot *slot; + unsigned int i, nslots; + if (rpmxdbLockReadHeader(xdb, flags ? 1 : 0)) + return RPMRC_FAIL; + nslots = xdb->nslots; + slot = 0; + for (i = xdb->slots[0].next; i != nslots; i = slot->next) { + slot = xdb->slots + i; + if (slot->blobtag == blobtag && slot->subtag == subtag) + break; + } + if (i == nslots) + i = 0; + if (i && (flags & O_TRUNC) != 0) { + if (rpmxdbResizeBlob(xdb, i, 0)) { + rpmxdbUnlock(xdb, flags ? 1 : 0); + return RPMRC_FAIL; + } + } + if (!i && (flags & O_CREAT) != 0) { + if (createblob(xdb, &i, blobtag, subtag)) { + rpmxdbUnlock(xdb, flags ? 1 : 0); + return RPMRC_FAIL; + } + } + *idp = i; + rpmxdbUnlock(xdb, flags ? 1 : 0); + return i ? RPMRC_OK : RPMRC_NOTFOUND; +} + +int rpmxdbDelBlob(rpmxdb xdb, unsigned int id) +{ + struct xdb_slot *slot; + if (!id) + return RPMRC_FAIL; + if (rpmxdbLockReadHeader(xdb, 1)) + return RPMRC_FAIL; + if (id >= xdb->nslots) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + slot = xdb->slots + id; + if (!slot->startpage) { + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; + } + if (slot->mapped) { + unmapslot(xdb, slot); + slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0); + } + /* remove from old chain */ + xdb->slots[slot->prev].next = slot->next; + xdb->slots[slot->next].prev = slot->prev; + xdb->usedblobpages -= slot->pagecnt; + + if (xdb->usedblobpages * 2 < xdb->slots[xdb->nslots].startpage && (slot->startpage + slot->pagecnt) * 2 < xdb->slots[xdb->nslots].startpage) { + /* freed in first half of pages, move last two blobs if we can */ + moveblobstofront(xdb, xdb->slots + slot->prev); + } + + /* zero slot */ + memset(slot, 0, sizeof(*slot)); + slot->slotno = id; + rpmxdbUpdateSlot(xdb, slot); + + /* enqueue into free chain */ + slot->next = xdb->firstfree; + xdb->firstfree = slot->slotno; + + /* check if we should truncate the file */ + slot = xdb->slots + xdb->slots[xdb->nslots].prev; + if (slot->startpage + slot->pagecnt < xdb->slots[xdb->nslots].startpage / 4 * 3) { + unsigned int newend = slot->startpage + slot->pagecnt; + if (!ftruncate(xdb->fd, newend * xdb->pagesize)) + xdb->slots[xdb->nslots].startpage = newend; + } + + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; +} + +int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize) +{ + struct xdb_slot *slot; + unsigned int oldpagecnt, newpagecnt; + if (!id) + return RPMRC_FAIL; + if (rpmxdbLockReadHeader(xdb, 1)) + return RPMRC_FAIL; + if (id >= xdb->nslots) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + slot = xdb->slots + id; + if (!slot->startpage) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + oldpagecnt = slot->pagecnt; + newpagecnt = (newsize + xdb->pagesize - 1) / xdb->pagesize; + if (oldpagecnt && newpagecnt && newpagecnt <= oldpagecnt) { + /* reducing size. zero to end of page */ + unsigned int pg = newsize & (xdb->pagesize - 1); + if (pg) { + if (slot->mapped) { + memset(slot->mapped + pg, 0, xdb->pagesize - pg); + } else { + char *empty = calloc(1, xdb->pagesize - pg); + if (!empty) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + if (pwrite(xdb->fd, empty, xdb->pagesize - pg, (slot->startpage + newpagecnt - 1) * xdb->pagesize + pg ) != xdb->pagesize - pg) { + free(empty); + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + free(empty); + } + } + } + if (newpagecnt == oldpagecnt) { + /* no size change */ + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; + } + if (!newpagecnt) { + /* special case: zero size blob, no longer mapped */ + if (slot->mapped) + unmapslot(xdb, slot); + slot->pagecnt = 0; + slot->startpage = xdb->slotnpages; + /* remove from old chain */ + xdb->slots[slot->prev].next = slot->next; + xdb->slots[slot->next].prev = slot->prev; + /* enqueue into head */ + slot->prev = 0; + slot->next = xdb->slots[0].next; + xdb->slots[slot->next].prev = slot->slotno; + xdb->slots[0].next = slot->slotno; + rpmxdbUpdateSlot(xdb, slot); + xdb->usedblobpages -= oldpagecnt; + if (slot->mapcallback) + slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0); + } else if (newpagecnt <= xdb->slots[slot->next].startpage - slot->startpage) { + /* can do it inplace */ + if (newpagecnt > oldpagecnt) { + /* zero new pages */ + if (rpmxdbWriteEmptyPages(xdb, slot->startpage + oldpagecnt, newpagecnt - oldpagecnt)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + } + if (slot->mapcallback) { + if (remapslot(xdb, slot, newpagecnt)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + } else { + if (slot->mapped) + unmapslot(xdb, slot); + slot->pagecnt = newpagecnt; + } + rpmxdbUpdateSlot(xdb, slot); + xdb->usedblobpages -= oldpagecnt; + xdb->usedblobpages += newpagecnt; + if (slot->mapcallback) + slot->mapcallback(xdb, slot->mapcallbackdata, slot->mapped, slot->pagecnt * xdb->pagesize); + } else { + /* need to relocate to a new page area */ + if (moveblob(xdb, slot, newpagecnt)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + } + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; +} + +int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata) +{ + struct xdb_slot *slot; + if (!id || !mapcallback) + return RPMRC_FAIL; + if ((flags & (O_RDONLY|O_RDWR)) == O_RDWR && xdb->rdonly) + return RPMRC_FAIL; + if (rpmxdbLockReadHeader(xdb, 0)) + return RPMRC_FAIL; + if (id >= xdb->nslots) { + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + slot = xdb->slots + id; + if (!slot->startpage || slot->mapped) { + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + slot->mapflags = (flags & (O_RDONLY|O_RDWR)) == O_RDWR ? PROT_READ | PROT_WRITE : PROT_READ; + if (slot->pagecnt) { + if (mapslot(xdb, slot)) { + slot->mapflags = 0; + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + } + slot->mapcallback = mapcallback; + slot->mapcallbackdata = mapcallbackdata; + mapcallback(xdb, mapcallbackdata, slot->mapped, slot->mapped ? slot->pagecnt * xdb->pagesize : 0); + rpmxdbUnlock(xdb, 0); + return RPMRC_OK; +} + +int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id) +{ + struct xdb_slot *slot; + if (!id) + return RPMRC_OK; + if (rpmxdbLockReadHeader(xdb, 0)) + return RPMRC_FAIL; + if (id >= xdb->nslots) { + rpmxdbUnlock(xdb, 0); + return RPMRC_FAIL; + } + slot = xdb->slots + id; + if (slot->mapped) { + unmapslot(xdb, slot); + slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0); + } + slot->mapcallback = 0; + slot->mapcallbackdata = 0; + slot->mapflags = 0; + rpmxdbUnlock(xdb, 0); + return RPMRC_OK; +} + +int rpmxdbRenameBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag) +{ + struct xdb_slot *slot; + unsigned int otherid; + unsigned int id = *idp; + int rc; + + if (!id || subtag > 255) + return RPMRC_FAIL; + if (rpmxdbLockReadHeader(xdb, 1)) + return RPMRC_FAIL; + if (id >= xdb->nslots) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + slot = xdb->slots + id; +#if 0 + printf("rpmxdbRenameBlob #%d %d/%d -> %d/%d\n", id, slot->blobtag, slot->subtag, blobtag, subtag); +#endif + if (!slot->startpage) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + if (slot->blobtag == blobtag && slot->subtag == subtag) { + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; + } + rc = rpmxdbLookupBlob(xdb, &otherid, blobtag, subtag, 0); + if (rc == RPMRC_NOTFOUND) + otherid = 0; + else if (rc) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + if (otherid) { +#if 0 + printf("(replacing #%d)\n", otherid); +#endif + if (rpmxdbDelBlob(xdb, otherid)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + /* get otherid back from free chain */ + if (xdb->firstfree != otherid) + return RPMRC_FAIL; + xdb->firstfree = xdb->slots[otherid].next; + + slot->blobtag = blobtag; + slot->subtag = subtag; + xdb->slots[otherid] = *slot; + /* fixup ids */ + xdb->slots[otherid].slotno = otherid; + xdb->slots[slot->prev].next = otherid; + xdb->slots[slot->next].prev = otherid; + /* write */ + rpmxdbUpdateSlot(xdb, xdb->slots + otherid); + memset(slot, 0, sizeof(*slot)); + slot->slotno = id; + rpmxdbUpdateSlot(xdb, slot); + slot->next = xdb->firstfree; + xdb->firstfree = slot->slotno; + *idp = otherid; + } else { + slot = xdb->slots + id; + slot->blobtag = blobtag; + slot->subtag = subtag; + rpmxdbUpdateSlot(xdb, slot); + } + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; +} + +void rpmxdbSetFsync(rpmxdb xdb, int dofsync) +{ + xdb->dofsync = dofsync; +} + +int rpmxdbIsRdonly(rpmxdb xdb) +{ + return xdb->rdonly; +} + +int rpmxdbSetUserGeneration(rpmxdb xdb, unsigned int usergeneration) +{ + if (rpmxdbLockReadHeader(xdb, 1)) + return RPMRC_FAIL; + /* sync before the update */ + if (xdb->dofsync && fsync(xdb->fd)) { + rpmxdbUnlock(xdb, 1); + return RPMRC_FAIL; + } + xdb->usergeneration = usergeneration; + xdb->generation++; + rpmxdbWriteHeader(xdb); + rpmxdbUnlock(xdb, 1); + return RPMRC_OK; +} + +int rpmxdbGetUserGeneration(rpmxdb xdb, unsigned int *usergenerationp) +{ + if (rpmxdbLockReadHeader(xdb, 0)) + return RPMRC_FAIL; + *usergenerationp = xdb->usergeneration; + rpmxdbUnlock(xdb, 0); + return RPMRC_OK; +} + +int rpmxdbStats(rpmxdb xdb) +{ + struct xdb_slot *slot; + unsigned int i, nslots; + + if (rpmxdbLockReadHeader(xdb, 0)) + return RPMRC_FAIL; + nslots = xdb->nslots; + printf("--- XDB Stats\n"); + printf("Filename: %s\n", xdb->filename); + printf("Generation: %d\n", xdb->generation); + printf("Slot pages: %d\n", xdb->slotnpages); + printf("Blob pages: %d\n", xdb->usedblobpages); + printf("Free pages: %d\n", xdb->slots[nslots].startpage - xdb->usedblobpages - xdb->slotnpages); + printf("Pagesize: %d / %d\n", xdb->pagesize, xdb->systempagesize); + for (i = 1, slot = xdb->slots + i; i < nslots; i++, slot++) { + if (!slot->startpage) + continue; + printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : ""); + } +#if 0 + printf("Again in offset order:\n"); + for (i = xdb->slots[0].next; i != nslots; i = slot->next) { + slot = xdb->slots + i; + printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : ""); + } +#endif +#if 0 + printf("Free chain:\n"); + for (i = xdb->firstfree; i; i = slot->next) { + slot = xdb->slots + i; + printf("%2d [%2d]: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->slotno, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : ""); + } +#endif + rpmxdbUnlock(xdb, 0); + return RPMRC_OK; +} + diff --git a/lib/backend/ndb/rpmxdb.h b/lib/backend/ndb/rpmxdb.h new file mode 100644 index 000000000..4358536ef --- /dev/null +++ b/lib/backend/ndb/rpmxdb.h @@ -0,0 +1,27 @@ +#include "rpmpkg.h" + +struct rpmxdb_s; +typedef struct rpmxdb_s *rpmxdb; + +int rpmxdbOpen(rpmxdb *xdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode); +void rpmxdbClose(rpmxdb xdb); +void rpmxdbSetFsync(rpmxdb xdb, int dofsync); +int rpmxdbIsRdonly(rpmxdb xdb); + +int rpmxdbLock(rpmxdb xdb, int excl); +int rpmxdbUnlock(rpmxdb xdb, int excl); + +int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags); +int rpmxdbDelBlob(rpmxdb xdb, unsigned int id) ; + +int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata); +int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id); + +int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize); +int rpmxdbRenameBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag); + +int rpmxdbSetUserGeneration(rpmxdb xdb, unsigned int usergeneration); +int rpmxdbGetUserGeneration(rpmxdb xdb, unsigned int *usergenerationp); + +int rpmxdbStats(rpmxdb xdb); + diff --git a/lib/cpio.c b/lib/cpio.c index 382eeb65a..57c959258 100644 --- a/lib/cpio.c +++ b/lib/cpio.c @@ -16,7 +16,6 @@ #else #include <sys/types.h> /* already included from system.h */ #endif -#include <errno.h> #include <string.h> #include <rpm/rpmio.h> @@ -24,6 +23,7 @@ #include <rpm/rpmstring.h> #include "lib/cpio.h" +#include "lib/rpmarchive.h" #include "debug.h" @@ -35,6 +35,47 @@ struct rpmcpio_s { off_t fileend; }; +/* + * Size limit for individual files in "new ascii format" cpio archives. + * The max size of the entire archive is unlimited from cpio POV, + * but subject to filesystem limitations. + */ +#define CPIO_FILESIZE_MAX UINT32_MAX + +#define CPIO_NEWC_MAGIC "070701" +#define CPIO_CRC_MAGIC "070702" +#define CPIO_STRIPPED_MAGIC "07070X" +#define CPIO_TRAILER "TRAILER!!!" + +/** \ingroup payload + * Cpio archive header information. + */ +struct cpioCrcPhysicalHeader { + /* char magic[6]; handled separately */ + char inode[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devMajor[8]; + char devMinor[8]; + char rdevMajor[8]; + char rdevMinor[8]; + char namesize[8]; + char checksum[8]; /* ignored !! */ +}; + +#define PHYS_HDR_SIZE 104 /* Don't depend on sizeof(struct) */ + +struct cpioStrippedPhysicalHeader { + /* char magic[6]; handled separately */ + char fx[8]; +}; + +#define STRIPPED_PHYS_HDR_SIZE 8 /* Don't depend on sizeof(struct) */ + rpmcpio_t rpmcpioOpen(FD_t fd, char mode) { if ((mode & O_ACCMODE) != O_RDONLY && @@ -83,16 +124,16 @@ static unsigned long strntoul(const char *str,char **endptr, int base, size_t nu static int rpmcpioWritePad(rpmcpio_t cpio, ssize_t modulo) { char buf[modulo]; - ssize_t left, writen; + ssize_t left, written; memset(buf, 0, modulo); left = (modulo - ((cpio->offset) % modulo)) % modulo; if (left <= 0) return 0; - writen = Fwrite(&buf, left, 1, cpio->fd); - if (writen != left) { - return CPIOERR_WRITE_FAILED; + written = Fwrite(&buf, left, 1, cpio->fd); + if (written != left) { + return RPMERR_WRITE_FAILED; } - cpio->offset += writen; + cpio->offset += written; return 0; } @@ -107,7 +148,7 @@ static int rpmcpioReadPad(rpmcpio_t cpio) read = Fread(&buf, left, 1, cpio->fd); cpio->offset += read; if (read != left) { - return CPIOERR_READ_FAILED; + return RPMERR_READ_FAILED; } return 0; } @@ -116,7 +157,7 @@ static int rpmcpioReadPad(rpmcpio_t cpio) \ log = strntoul(phys, &end, 16, sizeof(phys)); \ \ - if ( (end - phys) != sizeof(phys) ) return CPIOERR_BAD_HEADER; + if ( (end - phys) != sizeof(phys) ) return RPMERR_BAD_HEADER; #define SET_NUM_FIELD(phys, val, space) \ sprintf(space, "%8.8lx", (unsigned long) (val)); \ \ @@ -126,10 +167,10 @@ static int rpmcpioTrailerWrite(rpmcpio_t cpio) { struct cpioCrcPhysicalHeader hdr; int rc; - size_t writen; + size_t written; if (cpio->fileend != cpio->offset) { - return CPIOERR_WRITE_FAILED; + return RPMERR_WRITE_FAILED; } rc = rpmcpioWritePad(cpio, 4); @@ -137,18 +178,24 @@ static int rpmcpioTrailerWrite(rpmcpio_t cpio) return rc; memset(&hdr, '0', PHYS_HDR_SIZE); - memcpy(&hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic)); memcpy(&hdr.nlink, "00000001", 8); memcpy(&hdr.namesize, "0000000b", 8); - writen = Fwrite(&hdr, PHYS_HDR_SIZE, 1, cpio->fd); - cpio->offset += writen; - if (writen != PHYS_HDR_SIZE) { - return CPIOERR_WRITE_FAILED; + + written = Fwrite(CPIO_NEWC_MAGIC, 6, 1, cpio->fd); + cpio->offset += written; + if (written != 6) { + return RPMERR_WRITE_FAILED; } - writen = Fwrite(&CPIO_TRAILER, sizeof(CPIO_TRAILER), 1, cpio->fd); - cpio->offset += writen; - if (writen != sizeof(CPIO_TRAILER)) { - return CPIOERR_WRITE_FAILED; + + written = Fwrite(&hdr, PHYS_HDR_SIZE, 1, cpio->fd); + cpio->offset += written; + if (written != PHYS_HDR_SIZE) { + return RPMERR_WRITE_FAILED; + } + written = Fwrite(&CPIO_TRAILER, sizeof(CPIO_TRAILER), 1, cpio->fd); + cpio->offset += written; + if (written != sizeof(CPIO_TRAILER)) { + return RPMERR_WRITE_FAILED; } /* @@ -166,20 +213,20 @@ int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st) struct cpioCrcPhysicalHeader hdr_s; struct cpioCrcPhysicalHeader * hdr = &hdr_s; char field[64]; - size_t len, writen; + size_t len, written; dev_t dev; int rc = 0; if ((cpio->mode & O_ACCMODE) != O_WRONLY) { - return CPIOERR_WRITE_FAILED; + return RPMERR_WRITE_FAILED; } if (cpio->fileend != cpio->offset) { - return CPIOERR_WRITE_FAILED; + return RPMERR_WRITE_FAILED; } if (st->st_size >= CPIO_FILESIZE_MAX) { - return CPIOERR_FILE_SIZE; + return RPMERR_FILE_SIZE; } rc = rpmcpioWritePad(cpio, 4); @@ -187,7 +234,6 @@ int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st) return rc; } - memcpy(hdr->magic, CPIO_NEWC_MAGIC, sizeof(hdr->magic)); SET_NUM_FIELD(hdr->inode, st->st_ino, field); SET_NUM_FIELD(hdr->mode, st->st_mode, field); SET_NUM_FIELD(hdr->uid, st->st_uid, field); @@ -206,16 +252,22 @@ int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st) memcpy(hdr->checksum, "00000000", 8); - writen = Fwrite(hdr, PHYS_HDR_SIZE, 1, cpio->fd); - cpio->offset += writen; - if (writen != PHYS_HDR_SIZE) { - return CPIOERR_WRITE_FAILED; + written = Fwrite(CPIO_NEWC_MAGIC, 6, 1, cpio->fd); + cpio->offset += written; + if (written != 6) { + return RPMERR_WRITE_FAILED; + } + + written = Fwrite(hdr, PHYS_HDR_SIZE, 1, cpio->fd); + cpio->offset += written; + if (written != PHYS_HDR_SIZE) { + return RPMERR_WRITE_FAILED; } - writen = Fwrite(path, len, 1, cpio->fd); - cpio->offset += writen; - if (writen != len) { - return CPIOERR_WRITE_FAILED; + written = Fwrite(path, len, 1, cpio->fd); + cpio->offset += written; + if (written != len) { + return RPMERR_WRITE_FAILED; } rc = rpmcpioWritePad(cpio, 4); @@ -225,34 +277,77 @@ int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st) return rc; } -ssize_t rpmcpioWrite(rpmcpio_t cpio, void * buf, size_t size) +int rpmcpioStrippedHeaderWrite(rpmcpio_t cpio, int fx, off_t fsize) +{ + struct cpioStrippedPhysicalHeader hdr_s; + struct cpioStrippedPhysicalHeader * hdr = &hdr_s; + char field[64]; + size_t written; + int rc = 0; + + if ((cpio->mode & O_ACCMODE) != O_WRONLY) { + return RPMERR_WRITE_FAILED; + } + + if (cpio->fileend != cpio->offset) { + return RPMERR_WRITE_FAILED; + } + + rc = rpmcpioWritePad(cpio, 4); + if (rc) { + return rc; + } + + SET_NUM_FIELD(hdr->fx, fx, field); + + written = Fwrite(CPIO_STRIPPED_MAGIC, 6, 1, cpio->fd); + cpio->offset += written; + if (written != 6) { + return RPMERR_WRITE_FAILED; + } + + written = Fwrite(hdr, STRIPPED_PHYS_HDR_SIZE, 1, cpio->fd); + cpio->offset += written; + if (written != STRIPPED_PHYS_HDR_SIZE) { + return RPMERR_WRITE_FAILED; + } + + rc = rpmcpioWritePad(cpio, 4); + + cpio->fileend = cpio->offset + fsize; + + return rc; +} + +ssize_t rpmcpioWrite(rpmcpio_t cpio, const void * buf, size_t size) { - size_t writen, left; + size_t written, left; if ((cpio->mode & O_ACCMODE) != O_WRONLY) { - return CPIOERR_WRITE_FAILED; + return RPMERR_WRITE_FAILED; } // Do not write beyond file length left = cpio->fileend - cpio->offset; size = size > left ? left : size; - writen = Fwrite(buf, size, 1, cpio->fd); - cpio->offset += writen; - return writen; + written = Fwrite(buf, size, 1, cpio->fd); + cpio->offset += written; + return written; } -int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, struct stat * st) +int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, int * fx) { struct cpioCrcPhysicalHeader hdr; int nameSize; char * end; - unsigned int major, minor; int rc = 0; ssize_t read; + char magic[6]; + rpm_loff_t fsize; if ((cpio->mode & O_ACCMODE) != O_RDONLY) { - return CPIOERR_READ_FAILED; + return RPMERR_READ_FAILED; } /* Move to next file */ @@ -262,7 +357,7 @@ int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, struct stat * st) while (cpio->fileend != cpio->offset) { read = cpio->fileend - cpio->offset > 8*BUFSIZ ? 8*BUFSIZ : cpio->fileend - cpio->offset; if (rpmcpioRead(cpio, &buf, read) != read) { - return CPIOERR_READ_FAILED; + return RPMERR_READ_FAILED; } } } @@ -270,56 +365,75 @@ int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, struct stat * st) rc = rpmcpioReadPad(cpio); if (rc) return rc; - read = Fread(&hdr, PHYS_HDR_SIZE, 1, cpio->fd); + read = Fread(&magic, 6, 1, cpio->fd); cpio->offset += read; - if (read != PHYS_HDR_SIZE) - return CPIOERR_READ_FAILED; + if (read != 6) + return RPMERR_BAD_MAGIC; + + /* read stripped header */ + if (!strncmp(CPIO_STRIPPED_MAGIC, magic, + sizeof(CPIO_STRIPPED_MAGIC)-1)) { + struct cpioStrippedPhysicalHeader shdr; + read = Fread(&shdr, STRIPPED_PHYS_HDR_SIZE, 1, cpio->fd); + cpio->offset += read; + if (read != STRIPPED_PHYS_HDR_SIZE) + return RPMERR_BAD_HEADER; + + GET_NUM_FIELD(shdr.fx, *fx); + rc = rpmcpioReadPad(cpio); + + if (!rc && *fx == -1) + rc = RPMERR_ITER_END; + return rc; + } - if (strncmp(CPIO_CRC_MAGIC, hdr.magic, sizeof(CPIO_CRC_MAGIC)-1) && - strncmp(CPIO_NEWC_MAGIC, hdr.magic, sizeof(CPIO_NEWC_MAGIC)-1)) { - return CPIOERR_BAD_MAGIC; + if (strncmp(CPIO_CRC_MAGIC, magic, sizeof(CPIO_CRC_MAGIC)-1) && + strncmp(CPIO_NEWC_MAGIC, magic, sizeof(CPIO_NEWC_MAGIC)-1)) { + return RPMERR_BAD_MAGIC; } - GET_NUM_FIELD(hdr.inode, st->st_ino); - GET_NUM_FIELD(hdr.mode, st->st_mode); - GET_NUM_FIELD(hdr.uid, st->st_uid); - GET_NUM_FIELD(hdr.gid, st->st_gid); - GET_NUM_FIELD(hdr.nlink, st->st_nlink); - GET_NUM_FIELD(hdr.mtime, st->st_mtime); - GET_NUM_FIELD(hdr.filesize, st->st_size); - - GET_NUM_FIELD(hdr.devMajor, major); - GET_NUM_FIELD(hdr.devMinor, minor); - st->st_dev = makedev(major, minor); - - GET_NUM_FIELD(hdr.rdevMajor, major); - GET_NUM_FIELD(hdr.rdevMinor, minor); - st->st_rdev = makedev(major, minor); + read = Fread(&hdr, PHYS_HDR_SIZE, 1, cpio->fd); + cpio->offset += read; + if (read != PHYS_HDR_SIZE) + return RPMERR_BAD_HEADER; + + GET_NUM_FIELD(hdr.filesize, fsize); GET_NUM_FIELD(hdr.namesize, nameSize); + if (nameSize <= 0 || nameSize > 4096) { + return RPMERR_BAD_HEADER; + } - *path = xmalloc(nameSize + 1); - read = Fread(*path, nameSize, 1, cpio->fd); - (*path)[nameSize] = '\0'; + char name[nameSize + 1]; + read = Fread(name, nameSize, 1, cpio->fd); + name[nameSize] = '\0'; cpio->offset += read; if (read != nameSize ) { - return CPIOERR_BAD_HEADER; + return RPMERR_BAD_HEADER; } rc = rpmcpioReadPad(cpio); - cpio->fileend = cpio->offset + st->st_size; + cpio->fileend = cpio->offset + fsize; - if (!rc && rstreq(*path, CPIO_TRAILER)) - rc = CPIOERR_HDR_TRAILER; + if (!rc && rstreq(name, CPIO_TRAILER)) + rc = RPMERR_ITER_END; + + if (!rc && path) + *path = xstrdup(name); return rc; } +void rpmcpioSetExpectedFileSize(rpmcpio_t cpio, off_t fsize) +{ + cpio->fileend = cpio->offset + fsize; +} + ssize_t rpmcpioRead(rpmcpio_t cpio, void * buf, size_t size) { size_t read, left; if ((cpio->mode & O_ACCMODE) != O_RDONLY) { - return CPIOERR_READ_FAILED; + return RPMERR_READ_FAILED; } left = cpio->fileend - cpio->offset; @@ -349,67 +463,3 @@ rpmcpio_t rpmcpioFree(rpmcpio_t cpio) } return NULL; } - -const char * rpmcpioStrerror(int rc) -{ - static char msg[256]; - const char *s; - int myerrno = errno; - size_t l; - - strcpy(msg, "cpio: "); - switch (rc) { - default: { - char *t = msg + strlen(msg); - sprintf(t, _("(error 0x%x)"), (unsigned)rc); - s = NULL; - break; - } - case CPIOERR_BAD_MAGIC: s = _("Bad magic"); break; - case CPIOERR_BAD_HEADER: s = _("Bad/unreadable header");break; - - case CPIOERR_OPEN_FAILED: s = "open"; break; - case CPIOERR_CHMOD_FAILED: s = "chmod"; break; - case CPIOERR_CHOWN_FAILED: s = "chown"; break; - case CPIOERR_WRITE_FAILED: s = "write"; break; - case CPIOERR_UTIME_FAILED: s = "utime"; break; - case CPIOERR_UNLINK_FAILED: s = "unlink"; break; - case CPIOERR_RENAME_FAILED: s = "rename"; break; - case CPIOERR_SYMLINK_FAILED: s = "symlink"; break; - case CPIOERR_STAT_FAILED: s = "stat"; break; - case CPIOERR_LSTAT_FAILED: s = "lstat"; break; - case CPIOERR_MKDIR_FAILED: s = "mkdir"; break; - case CPIOERR_RMDIR_FAILED: s = "rmdir"; break; - case CPIOERR_MKNOD_FAILED: s = "mknod"; break; - case CPIOERR_MKFIFO_FAILED: s = "mkfifo"; break; - case CPIOERR_LINK_FAILED: s = "link"; break; - case CPIOERR_READLINK_FAILED: s = "readlink"; break; - case CPIOERR_READ_FAILED: s = "read"; break; - case CPIOERR_COPY_FAILED: s = "copy"; break; - case CPIOERR_LSETFCON_FAILED: s = "lsetfilecon"; break; - case CPIOERR_SETCAP_FAILED: s = "cap_set_file"; break; - - case CPIOERR_HDR_SIZE: s = _("Header size too big"); break; - case CPIOERR_FILE_SIZE: s = _("File too large for archive"); break; - case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type"); break; - case CPIOERR_MISSING_HARDLINK: s = _("Missing hard link(s)"); break; - case CPIOERR_DIGEST_MISMATCH: s = _("Digest mismatch"); break; - case CPIOERR_INTERNAL: s = _("Internal error"); break; - case CPIOERR_UNMAPPED_FILE: s = _("Archive file not in header"); break; - case CPIOERR_ENOENT: s = strerror(ENOENT); break; - case CPIOERR_ENOTEMPTY: s = strerror(ENOTEMPTY); break; - } - - l = sizeof(msg) - strlen(msg) - 1; - if (s != NULL) { - if (l > 0) strncat(msg, s, l); - l -= strlen(s); - } - if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) { - s = _(" failed - "); - if (l > 0) strncat(msg, s, l); - l -= strlen(s); - if (l > 0) strncat(msg, strerror(myerrno), l); - } - return msg; -} diff --git a/lib/cpio.h b/lib/cpio.h index 7364caf27..b33b5fc13 100644 --- a/lib/cpio.h +++ b/lib/cpio.h @@ -12,81 +12,6 @@ * */ -/** \ingroup payload - * @note CPIO_CHECK_ERRNO bit is set only if errno is valid. - */ -#define CPIOERR_CHECK_ERRNO 0x00008000 - -/** \ingroup payload - */ -enum cpioErrorReturns { - CPIOERR_BAD_MAGIC = 2, - CPIOERR_BAD_HEADER = 3, - CPIOERR_OPEN_FAILED = 4 | CPIOERR_CHECK_ERRNO, - CPIOERR_CHMOD_FAILED = 5 | CPIOERR_CHECK_ERRNO, - CPIOERR_CHOWN_FAILED = 6 | CPIOERR_CHECK_ERRNO, - CPIOERR_WRITE_FAILED = 7 | CPIOERR_CHECK_ERRNO, - CPIOERR_UTIME_FAILED = 8 | CPIOERR_CHECK_ERRNO, - CPIOERR_UNLINK_FAILED = 9 | CPIOERR_CHECK_ERRNO, - CPIOERR_RENAME_FAILED = 10 | CPIOERR_CHECK_ERRNO, - CPIOERR_SYMLINK_FAILED = 11 | CPIOERR_CHECK_ERRNO, - CPIOERR_STAT_FAILED = 12 | CPIOERR_CHECK_ERRNO, - CPIOERR_LSTAT_FAILED = 13 | CPIOERR_CHECK_ERRNO, - CPIOERR_MKDIR_FAILED = 14 | CPIOERR_CHECK_ERRNO, - CPIOERR_RMDIR_FAILED = 15 | CPIOERR_CHECK_ERRNO, - CPIOERR_MKNOD_FAILED = 16 | CPIOERR_CHECK_ERRNO, - CPIOERR_MKFIFO_FAILED = 17 | CPIOERR_CHECK_ERRNO, - CPIOERR_LINK_FAILED = 18 | CPIOERR_CHECK_ERRNO, - CPIOERR_READLINK_FAILED = 19 | CPIOERR_CHECK_ERRNO, - CPIOERR_READ_FAILED = 20 | CPIOERR_CHECK_ERRNO, - CPIOERR_COPY_FAILED = 21 | CPIOERR_CHECK_ERRNO, - CPIOERR_LSETFCON_FAILED = 22 | CPIOERR_CHECK_ERRNO, - CPIOERR_HDR_SIZE = 23, - CPIOERR_HDR_TRAILER = 24, - CPIOERR_UNKNOWN_FILETYPE= 25, - CPIOERR_MISSING_HARDLINK= 26, - CPIOERR_DIGEST_MISMATCH = 27, - CPIOERR_INTERNAL = 28, - CPIOERR_UNMAPPED_FILE = 29, - CPIOERR_ENOENT = 30, - CPIOERR_ENOTEMPTY = 31, - CPIOERR_SETCAP_FAILED = 32 | CPIOERR_CHECK_ERRNO, - CPIOERR_FILE_SIZE = 33, -}; - -/* - * Size limit for individual files in "new ascii format" cpio archives. - * The max size of the entire archive is unlimited from cpio POV, - * but subject to filesystem limitations. - */ -#define CPIO_FILESIZE_MAX UINT32_MAX - -#define CPIO_NEWC_MAGIC "070701" -#define CPIO_CRC_MAGIC "070702" -#define CPIO_TRAILER "TRAILER!!!" - -/** \ingroup payload - * Cpio archive header information. - */ -struct cpioCrcPhysicalHeader { - char magic[6]; - char inode[8]; - char mode[8]; - char uid[8]; - char gid[8]; - char nlink[8]; - char mtime[8]; - char filesize[8]; - char devMajor[8]; - char devMinor[8]; - char rdevMajor[8]; - char rdevMinor[8]; - char namesize[8]; - char checksum[8]; /* ignored !! */ -}; - -#define PHYS_HDR_SIZE 110 /* Don't depend on sizeof(struct) */ - typedef struct rpmcpio_s * rpmcpio_t; #ifdef __cplusplus @@ -109,33 +34,43 @@ rpmcpio_t rpmcpioFree(rpmcpio_t cpio); /** * Write cpio header. - * @retval fsm file path and stat info - * @param st + * @param cpio cpio archive + * @param path file name + * @param st stat struct with meta data * @return 0 on success */ RPM_GNUC_INTERNAL int rpmcpioHeaderWrite(rpmcpio_t cpio, char * path, struct stat * st); +RPM_GNUC_INTERNAL +int rpmcpioStrippedHeaderWrite(rpmcpio_t cpio, int fx, off_t fsize); -ssize_t rpmcpioWrite(rpmcpio_t cpio, void * buf, size_t size); +ssize_t rpmcpioWrite(rpmcpio_t cpio, const void * buf, size_t size); /** - * Read cpio header. + * Read cpio header. Iff fx is returned as -1 a cpio header was read + * and the file name is found in path. Otherwise a stripped header was read + * and the fx is the number of the file in the header/rpmfi. In this case + * rpmcpioSetExpectedFileSize() needs to be called with the file size of the + * payload content - with may be zero for hard links, directory or other + * special files. * @retval fsm file path and stat info - * @retval st + * @retval path path of the file + * @retval fx number in the header of the file read * @return 0 on success */ RPM_GNUC_INTERNAL -int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, struct stat * st); - -ssize_t rpmcpioRead(rpmcpio_t cpio, void * buf, size_t size); +int rpmcpioHeaderRead(rpmcpio_t cpio, char ** path, int * fx); -/** \ingroup payload - * Return formatted error message on payload handling failure. - * @param rc error code - * @return formatted error string +/** + * Tell the cpio object the expected file size in the payload. + * The size must be zero for all but the last of hard linked files, + * directories and special files. + * This is needed after reading a stripped cpio header! See above. */ -/* XXX should be RPM_GNUC_INTERNAL too but build/pack.c uses */ -const char * rpmcpioStrerror(int rc); +RPM_GNUC_INTERNAL +void rpmcpioSetExpectedFileSize(rpmcpio_t cpio, off_t fsize); + +ssize_t rpmcpioRead(rpmcpio_t cpio, void * buf, size_t size); #ifdef __cplusplus } diff --git a/lib/depends.c b/lib/depends.c index fa11725a8..4719b6738 100644 --- a/lib/depends.c +++ b/lib/depends.c @@ -14,8 +14,11 @@ #include "lib/rpmts_internal.h" #include "lib/rpmte_internal.h" #include "lib/rpmds_internal.h" +#include "lib/rpmfi_internal.h" /* rpmfiles stuff for now */ #include "lib/misc.h" +#include "lib/backend/dbiset.h" + #include "debug.h" const char * const RPMVERSION = VERSION; @@ -39,7 +42,7 @@ const int rpmFLAGS = RPMSENSE_EQUAL; #undef HTKEYTYPE #undef HTDATATYPE -#define HASHTYPE removedHash +#define HASHTYPE packageHash #define HTKEYTYPE unsigned int #define HTDATATYPE struct rpmte_s * #include "rpmhash.C" @@ -47,6 +50,28 @@ const int rpmFLAGS = RPMSENSE_EQUAL; #undef HTKEYTYPE #undef HTDATATYPE +#define HASHTYPE filedepHash +#define HTKEYTYPE const char * +#define HTDATATYPE const char * +#include "rpmhash.H" +#include "rpmhash.C" +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE + +#define HASHTYPE depexistsHash +#define HTKEYTYPE const char * +#include "lib/rpmhash.H" +#include "lib/rpmhash.C" +#undef HASHTYPE +#undef HTKEYTYPE + +enum addOp_e { + RPMTE_INSTALL = 0, + RPMTE_UPGRADE = 1, + RPMTE_REINSTALL = 2, +}; + /** * Check for supported payload format in header. * @param h header to check @@ -88,22 +113,23 @@ static rpmRC headerCheckPayloadFormat(Header h) { static int removePackage(rpmts ts, Header h, rpmte depends) { tsMembers tsmem = rpmtsMembers(ts); - rpmte p; + rpmte p, *pp; unsigned int dboffset = headerGetInstance(h); /* Can't remove what's not installed */ if (dboffset == 0) return 1; /* Filter out duplicate erasures. */ - if (removedHashHasEntry(tsmem->removedPackages, dboffset)) { - return 0; + if (packageHashGetEntry(tsmem->removedPackages, dboffset, &pp, NULL, NULL)) { + rpmteSetDependsOn(pp[0], depends); + return 0; } p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL); if (p == NULL) return 1; - removedHashAddEntry(tsmem->removedPackages, dboffset, p); + packageHashAddEntry(tsmem->removedPackages, dboffset, p); if (tsmem->orderCount >= tsmem->orderAlloced) { tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta; @@ -119,7 +145,7 @@ static int removePackage(rpmts ts, Header h, rpmte depends) } /* Return rpmdb iterator with removals optionally pruned out */ -static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, +rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, const char * key, int prune) { rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0); @@ -163,22 +189,29 @@ static int rpmNameVersionCompare(Header first, Header second) } /* Add erase elements for older packages of same color (if any). */ -static int addUpgradeErasures(rpmts ts, rpm_color_t tscolor, +static int addSelfErasures(rpmts ts, rpm_color_t tscolor, int op, rpmte p, rpm_color_t hcolor, Header h) { Header oh; rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0); int rc = 0; + int cmp; - while((oh = rpmdbNextIterator(mi)) != NULL) { + while ((oh = rpmdbNextIterator(mi)) != NULL) { /* Ignore colored packages not in our rainbow. */ if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR))) continue; - /* Skip packages that contain identical NEVRA. */ - if (rpmNameVersionCompare(h, oh) == 0) + cmp = rpmNameVersionCompare(h, oh); + + /* On upgrade, skip packages that contain identical NEVR. */ + if ((op == RPMTE_UPGRADE) && (cmp == 0)) continue; + /* On reinstall, skip packages with differing NEVR. */ + if ((op == RPMTE_REINSTALL) && (cmp != 0)) + continue; + if (removePackage(ts, oh, p)) { rc = 1; break; @@ -205,7 +238,7 @@ static int addObsoleteErasures(rpmts ts, rpm_color_t tscolor, rpmte p) mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name, 1); - while((oh = rpmdbNextIterator(mi)) != NULL) { + while ((oh = rpmdbNextIterator(mi)) != NULL) { const char *oarch = headerGetString(oh, RPMTAG_ARCH); int match; @@ -396,8 +429,8 @@ rpmal rpmtsCreateAl(rpmts ts, rpmElementTypes types) return al; } -int rpmtsAddInstallElement(rpmts ts, Header h, - fnpyKey key, int upgrade, rpmRelocation * relocs) +static int addPackage(rpmts ts, Header h, + fnpyKey key, int op, rpmRelocation * relocs) { tsMembers tsmem = rpmtsMembers(ts); rpm_color_t tscolor = rpmtsColor(ts); @@ -414,10 +447,10 @@ int rpmtsAddInstallElement(rpmts ts, Header h, /* Source packages are never "upgraded" */ if (isSource) - upgrade = 0; + op = RPMTE_INSTALL; /* Do lazy (readonly?) open of rpm database for upgrades. */ - if (upgrade && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) { + if (op != RPMTE_INSTALL && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) { if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0) goto exit; } @@ -430,7 +463,7 @@ int rpmtsAddInstallElement(rpmts ts, Header h, /* Check binary packages for redundancies in the set */ if (!isSource) { - oc = findPos(ts, tscolor, p, upgrade); + oc = findPos(ts, tscolor, p, (op == RPMTE_UPGRADE)); /* If we're replacing a previously added element, free the old one */ if (oc >= 0 && oc < tsmem->orderCount) { rpmalDel(tsmem->addedPackages, tsmem->order[oc]); @@ -462,22 +495,42 @@ int rpmtsAddInstallElement(rpmts ts, Header h, /* Add erasure elements for old versions and obsoletions on upgrades */ /* XXX TODO: If either of these fails, we'd need to undo all additions */ - if (upgrade) { - addUpgradeErasures(ts, tscolor, p, rpmteColor(p), h); + if (op != RPMTE_INSTALL) + addSelfErasures(ts, tscolor, op, p, rpmteColor(p), h); + if (op == RPMTE_UPGRADE) addObsoleteErasures(ts, tscolor, p); - } exit: return ec; } +int rpmtsAddInstallElement(rpmts ts, Header h, + fnpyKey key, int upgrade, rpmRelocation * relocs) +{ + int op = (upgrade == 0) ? RPMTE_INSTALL : RPMTE_UPGRADE; + if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) + return 1; + return addPackage(ts, h, key, op, relocs); +} + +int rpmtsAddReinstallElement(rpmts ts, Header h, fnpyKey key) +{ + if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) + return 1; + /* TODO: pull relocations from installed package */ + /* TODO: should reinstall of non-installed package fail? */ + return addPackage(ts, h, key, RPMTE_REINSTALL, NULL); +} + int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset) { + if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) + return 1; return removePackage(ts, h, NULL); } /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */ -static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) +static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep, dbiIndexSet *matches) { const char * Name = rpmdsN(dep); const char * DNEVR = rpmdsDNEVR(dep); @@ -491,7 +544,7 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) unsigned int keyhash = 0; /* See if we already looked this up */ - if (prune) { + if (prune && !matches) { keyhash = depCacheKeyHash(dcache, DNEVR); if (depCacheGetHEntry(dcache, DNEVR, keyhash, &cachedrc, NULL, NULL)) { rc = *cachedrc; @@ -500,6 +553,8 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) } } + if (matches) + *matches = dbiIndexSetNew(0); /* * See if a filename dependency is a real file in some package, * taking file state into account: replaced, wrong colored and @@ -508,6 +563,16 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) if (deptag != RPMTAG_OBSOLETENAME && Name[0] == '/') { mi = rpmtsPrunedIterator(ts, RPMDBI_INSTFILENAMES, Name, prune); while ((h = rpmdbNextIterator(mi)) != NULL) { + /* Ignore self-conflicts */ + if (deptag == RPMTAG_CONFLICTNAME) { + unsigned int instance = headerGetInstance(h); + if (instance && instance == rpmdsInstance(dep)) + continue; + } + if (matches) { + dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0); + continue; + } rpmdsNotify(dep, "(db files)", rc); break; } @@ -531,7 +596,17 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) int prix = (selfevr) ? -1 : rpmdbGetIteratorFileNum(mi); int match = rpmdsMatches(tspool, h, prix, dep, selfevr, _rpmds_nopromote); + /* Ignore self-obsoletes and self-conflicts */ + if (match && (deptag == RPMTAG_OBSOLETENAME || deptag == RPMTAG_CONFLICTNAME)) { + unsigned int instance = headerGetInstance(h); + if (instance && instance == rpmdsInstance(dep)) + match = 0; + } if (match) { + if (matches) { + dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0); + continue; + } rpmdsNotify(dep, "(db provides)", rc); break; } @@ -540,16 +615,85 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) } rc = (h != NULL) ? 0 : 1; + if (matches) { + dbiIndexSetUniq(*matches, 0); + rc = dbiIndexSetCount(*matches) ? 0 : 1; + } + /* Cache the relatively expensive rpmdb lookup results */ /* Caching the oddball non-pruned case would mess up other results */ - if (prune) + if (prune && !matches) depCacheAddHEntry(dcache, xstrdup(DNEVR), keyhash, rc); return rc; } +static dbiIndexSet unsatisfiedDependSet(rpmts ts, rpmds dep) +{ + dbiIndexSet set1 = NULL, set2 = NULL; + tsMembers tsmem = rpmtsMembers(ts); + rpmsenseFlags dsflags = rpmdsFlags(dep); + + if (dsflags & RPMSENSE_RPMLIB) + goto exit; + + if (rpmdsIsRich(dep)) { + rpmds ds1, ds2; + rpmrichOp op; + char *emsg = 0; + + if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) { + rpmdsNotify(dep, emsg ? emsg : "(parse error)", 1); + _free(emsg); + goto exit; + } + /* only a subset of ops is supported in set mode */ + if (op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT + && op != RPMRICHOP_OR && op != RPMRICHOP_SINGLE) { + rpmdsNotify(dep, "(unsupported op in set mode)", 1); + goto exit_rich; + } + + set1 = unsatisfiedDependSet(ts, ds1); + if (op == RPMRICHOP_SINGLE) + goto exit_rich; + if (op != RPMRICHOP_OR && dbiIndexSetCount(set1) == 0) + goto exit_rich; + set2 = unsatisfiedDependSet(ts, ds2); + if (op == RPMRICHOP_WITH) { + dbiIndexSetFilterSet(set1, set2, 0); + } else if (op == RPMRICHOP_WITHOUT) { + dbiIndexSetPruneSet(set1, set2, 0); + } else if (op == RPMRICHOP_OR) { + dbiIndexSetAppendSet(set1, set2, 0); + } +exit_rich: + ds1 = rpmdsFree(ds1); + ds2 = rpmdsFree(ds2); + goto exit; + } + + /* match database entries */ + rpmdbProvides(ts, NULL, dep, &set1); + + /* Pretrans dependencies can't be satisfied by added packages. */ + if (!(dsflags & RPMSENSE_PRETRANS)) { + rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep); + if (matches) { + for (rpmte *p = matches; *p; p++) + dbiIndexSetAppendOne(set1, rpmalLookupTE(tsmem->addedPackages, *p), 1, 0); + } + _free(matches); + } + +exit: + set2 = dbiIndexSetFree(set2); + return set1 ? set1 : dbiIndexSetNew(0); +} + /** * Check dep for an unsatisfied dependency. * @param ts transaction set + * @param dcache dependency cache * @param dep dependency * @return 0 if satisfied, 1 if not satisfied */ @@ -584,29 +728,78 @@ retry: if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags)) goto exit; + /* Handle rich dependencies */ + if (rpmdsIsRich(dep)) { + rpmds ds1, ds2; + rpmrichOp op; + char *emsg = 0; + if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) { + rc = rpmdsTagN(dep) == RPMTAG_CONFLICTNAME ? 0 : 1; + if (rpmdsInstance(dep) != 0) + rc = !rc; /* ignore errors for installed packages */ + rpmdsNotify(dep, emsg ? emsg : "(parse error)", rc); + _free(emsg); + goto exit; + } + if (op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) { + /* switch to set mode processing */ + dbiIndexSet set = unsatisfiedDependSet(ts, dep); + rc = dbiIndexSetCount(set) ? 0 : 1; + dbiIndexSetFree(set); + ds1 = rpmdsFree(ds1); + ds2 = rpmdsFree(ds2); + rpmdsNotify(dep, "(rich)", rc); + goto exit; + } + if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) { + /* A IF B -> A OR NOT(B) */ + /* A UNLESS B -> A AND NOT(B) */ + if (rpmdsIsRich(ds2)) { + /* check if this has an ELSE clause */ + rpmds ds21 = NULL, ds22 = NULL; + rpmrichOp op2; + if (rpmdsParseRichDep(ds2, &ds21, &ds22, &op2, NULL) == RPMRC_OK && op2 == RPMRICHOP_ELSE) { + /* A IF B ELSE C -> (A OR NOT(B)) AND (C OR B) */ + /* A UNLESS B ELSE C -> (A AND NOT(B)) OR (C AND B) */ + rc = !unsatisfiedDepend(ts, dcache, ds21); /* NOT(B) */ + if ((rc && op == RPMRICHOP_IF) || (!rc && op == RPMRICHOP_UNLESS)) { + rc = unsatisfiedDepend(ts, dcache, ds1); /* A */ + } else { + rc = unsatisfiedDepend(ts, dcache, ds22); /* C */ + } + rpmdsFree(ds21); + rpmdsFree(ds22); + goto exitrich; + } + rpmdsFree(ds21); + rpmdsFree(ds22); + } + rc = !unsatisfiedDepend(ts, dcache, ds2); /* NOT(B) */ + if ((rc && op == RPMRICHOP_IF) || (!rc && op == RPMRICHOP_UNLESS)) + rc = unsatisfiedDepend(ts, dcache, ds1); + } else { + rc = unsatisfiedDepend(ts, dcache, ds1); + if ((rc && op == RPMRICHOP_OR) || (!rc && op == RPMRICHOP_AND)) + rc = unsatisfiedDepend(ts, dcache, ds2); + } +exitrich: + ds1 = rpmdsFree(ds1); + ds2 = rpmdsFree(ds2); + rpmdsNotify(dep, "(rich)", rc); + goto exit; + } + /* Pretrans dependencies can't be satisfied by added packages. */ if (!(dsflags & RPMSENSE_PRETRANS)) { - rpmte match = rpmalSatisfiesDepend(tsmem->addedPackages, dep); - - /* - * Handle definitive matches within the added package set. - * Self-obsoletes and -conflicts fall through here as we need to - * check for possible other matches in the rpmdb. - */ - if (match) { - rpmTagVal dtag = rpmdsTagN(dep); - /* Requires match, look no further */ - if (dtag == RPMTAG_REQUIRENAME) - goto exit; - - /* Conflicts/obsoletes match on another package, look no further */ - if (rpmteDS(match, dtag) != dep) - goto exit; - } + rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep); + int match = matches && *matches; + _free(matches); + if (match) + goto exit; } /* See if the rpmdb provides it */ - if (rpmdbProvides(ts, dcache, dep) == 0) + if (rpmdbProvides(ts, dcache, dep, NULL) == 0) goto exit; /* Search for an unsatisfied dependency. */ @@ -637,7 +830,7 @@ exit: /* Check a dependency set for problems */ static void checkDS(rpmts ts, depCache dcache, rpmte te, const char * pkgNEVRA, rpmds ds, - const char * depName, rpm_color_t tscolor) + rpm_color_t tscolor) { rpm_color_t dscolor; /* require-problems are unsatisfied, others appear "satisfied" */ @@ -645,10 +838,6 @@ static void checkDS(rpmts ts, depCache dcache, rpmte te, ds = rpmdsInit(ds); while (rpmdsNext(ds) >= 0) { - /* Filter out dependencies that came along for the ride. */ - if (depName != NULL && !rstreq(depName, rpmdsN(ds))) - continue; - /* Ignore colored dependencies not in our rainbow. */ dscolor = rpmdsColor(ds); if (tscolor && dscolor && !(tscolor & dscolor)) @@ -659,19 +848,33 @@ static void checkDS(rpmts ts, depCache dcache, rpmte te, } } -/* Check a given dependency type against installed packages */ +/* Check a given dependency against installed packages */ static void checkInstDeps(rpmts ts, depCache dcache, rpmte te, rpmTag depTag, const char *dep) { Header h; rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep, 1); rpmstrPool pool = rpmtsPool(ts); + /* require-problems are unsatisfied, others appear "satisfied" */ + int is_problem = (depTag == RPMTAG_REQUIRENAME); while ((h = rpmdbNextIterator(mi)) != NULL) { - char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA); - rpmds ds = rpmdsNewPool(pool, h, depTag, 0); + char * pkgNEVRA; + rpmds ds; - checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0); + /* Ignore self-obsoletes and self-conflicts */ + if (depTag == RPMTAG_OBSOLETENAME || depTag == RPMTAG_CONFLICTNAME) { + unsigned int instance = headerGetInstance(h); + if (instance && instance == rpmteDBInstance(te)) + continue; + } + + pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA); + ds = rpmdsNewPool(pool, h, depTag, 0); + rpmdsSetIx(ds, rpmdbGetIteratorFileNum(mi)); + + if (unsatisfiedDepend(ts, dcache, ds) == is_problem) + rpmteAddDepProblem(te, pkgNEVRA, ds, NULL); rpmdsFree(ds); free(pkgNEVRA); @@ -679,6 +882,116 @@ static void checkInstDeps(rpmts ts, depCache dcache, rpmte te, rpmdbFreeIterator(mi); } +static void checkNotInstDeps(rpmts ts, depCache dcache, rpmte te, + rpmTag depTag, const char *dep) +{ + char *ndep = rmalloc(strlen(dep) + 2); + ndep[0] = '!'; + strcpy(ndep + 1, dep); + checkInstDeps(ts, dcache, te, depTag, ndep); + free(ndep); +} + +static void checkInstFileDeps(rpmts ts, depCache dcache, rpmte te, + rpmTag depTag, rpmfi fi, int is_not, + filedepHash cache, fingerPrintCache *fpcp) +{ + fingerPrintCache fpc = *fpcp; + fingerPrint * fp = NULL; + const char *basename = rpmfiBN(fi); + const char *dirname; + const char **dirnames = 0; + int ndirnames = 0; + int i; + + filedepHashGetEntry(cache, basename, &dirnames, &ndirnames, NULL); + if (!ndirnames) + return; + if (!fpc) + *fpcp = fpc = fpCacheCreate(1001, NULL); + dirname = rpmfiDN(fi); + fpLookup(fpc, dirname, basename, &fp); + for (i = 0; i < ndirnames; i++) { + char *fpdep = 0; + const char *dep; + if (!strcmp(dirnames[i], dirname)) { + dep = rpmfiFN(fi); + } else if (fpLookupEquals(fpc, fp, dirnames[i], basename)) { + fpdep = rmalloc(strlen(dirnames[i]) + strlen(basename) + 1); + strcpy(fpdep, dirnames[i]); + strcat(fpdep, basename); + dep = fpdep; + } else { + continue; + } + if (!is_not) + checkInstDeps(ts, dcache, te, depTag, dep); + else + checkNotInstDeps(ts, dcache, te, depTag, dep); + _free(fpdep); + } + _free(fp); +} + +static void addFileDepToHash(filedepHash hash, char *key, size_t keylen) +{ + int i; + char *basename, *dirname; + if (!keylen || key[0] != '/') + return; + for (i = keylen - 1; key[i] != '/'; i--) + ; + dirname = rmalloc(i + 2); + memcpy(dirname, key, i + 1); + dirname[i + 1] = 0; + basename = rmalloc(keylen - i); + memcpy(basename, key + i + 1, keylen - i - 1); + basename[keylen - i - 1] = 0; + filedepHashAddEntry(hash, basename, dirname); +} + +static void addDepToHash(depexistsHash hash, char *key, size_t keylen) +{ + char *keystr; + if (!keylen) + return; + keystr = rmalloc(keylen + 1); + strncpy(keystr, key, keylen); + keystr[keylen] = 0; + depexistsHashAddEntry(hash, keystr); +} + +static void addIndexToDepHashes(rpmts ts, rpmDbiTag tag, + depexistsHash dephash, filedepHash filehash, + depexistsHash depnothash, filedepHash filenothash) +{ + char *key; + size_t keylen; + rpmdbIndexIterator ii = rpmdbIndexIteratorInit(rpmtsGetRdb(ts), tag); + + if (!ii) + return; + while ((rpmdbIndexIteratorNext(ii, (const void**)&key, &keylen)) == 0) { + if (!key || !keylen) + continue; + if (*key == '!' && keylen > 1) { + key++; + keylen--; + if (*key == '/' && filenothash) + addFileDepToHash(filenothash, key, keylen); + if (depnothash) + addDepToHash(depnothash, key, keylen); + } else { + if (*key == '/' && filehash) + addFileDepToHash(filehash, key, keylen); + if (dephash) + addDepToHash(dephash, key, keylen); + } + } + rpmdbIndexIteratorFree(ii); +} + + int rpmtsCheck(rpmts ts) { rpm_color_t tscolor = rpmtsColor(ts); @@ -686,20 +999,67 @@ int rpmtsCheck(rpmts ts) int closeatexit = 0; int rc = 0; depCache dcache = NULL; + filedepHash confilehash = NULL; /* file conflicts of installed packages */ + filedepHash connotfilehash = NULL; /* file conflicts of installed packages */ + depexistsHash connothash = NULL; + filedepHash reqfilehash = NULL; /* file requires of installed packages */ + filedepHash reqnotfilehash = NULL; /* file requires of installed packages */ + depexistsHash reqnothash = NULL; + fingerPrintCache fpc = NULL; + rpmdb rdb = NULL; (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0); /* Do lazy, readonly, open of rpm database. */ - if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) { + rdb = rpmtsGetRdb(ts); + if (rdb == NULL && rpmtsGetDBMode(ts) != -1) { if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0) goto exit; + rdb = rpmtsGetRdb(ts); closeatexit = 1; } + if (rdb) + rpmdbCtrl(rdb, RPMDB_CTRL_LOCK_RO); + /* XXX FIXME: figure some kind of heuristic for the cache size */ dcache = depCacheCreate(5001, rstrhash, strcmp, (depCacheFreeKey)rfree, NULL); + /* build hashes of all confilict sdependencies */ + confilehash = filedepHashCreate(257, rstrhash, strcmp, + (filedepHashFreeKey)rfree, + (filedepHashFreeData)rfree); + connothash = depexistsHashCreate(257, rstrhash, strcmp, + (filedepHashFreeKey)rfree); + connotfilehash = filedepHashCreate(257, rstrhash, strcmp, + (filedepHashFreeKey)rfree, + (filedepHashFreeData)rfree); + addIndexToDepHashes(ts, RPMTAG_CONFLICTNAME, NULL, confilehash, connothash, connotfilehash); + if (!filedepHashNumKeys(confilehash)) + confilehash = filedepHashFree(confilehash); + if (!depexistsHashNumKeys(connothash)) + connothash= depexistsHashFree(connothash); + if (!filedepHashNumKeys(connotfilehash)) + connotfilehash = filedepHashFree(connotfilehash); + + /* build hashes of all requires dependencies */ + reqfilehash = filedepHashCreate(8191, rstrhash, strcmp, + (filedepHashFreeKey)rfree, + (filedepHashFreeData)rfree); + reqnothash = depexistsHashCreate(257, rstrhash, strcmp, + (filedepHashFreeKey)rfree); + reqnotfilehash = filedepHashCreate(257, rstrhash, strcmp, + (filedepHashFreeKey)rfree, + (filedepHashFreeData)rfree); + addIndexToDepHashes(ts, RPMTAG_REQUIRENAME, NULL, reqfilehash, reqnothash, reqnotfilehash); + if (!filedepHashNumKeys(reqfilehash)) + reqfilehash = filedepHashFree(reqfilehash); + if (!depexistsHashNumKeys(reqnothash)) + reqnothash= depexistsHashFree(reqnothash); + if (!filedepHashNumKeys(reqnotfilehash)) + reqnotfilehash = filedepHashFree(reqnotfilehash); + /* * Look at all of the added packages and make sure their dependencies * are satisfied. @@ -712,15 +1072,18 @@ int rpmtsCheck(rpmts ts) rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p)); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME), - NULL, tscolor); + tscolor); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME), - NULL, tscolor); + tscolor); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_OBSOLETENAME), - NULL, tscolor); + tscolor); /* Check provides against conflicts in installed packages. */ while (rpmdsNext(provides) >= 0) { - checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides)); + const char *dep = rpmdsN(provides); + checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, dep); + if (reqnothash && depexistsHashHasEntry(reqnothash, dep)) + checkNotInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, dep); } /* Skip obsoletion checks for source packages (ie build) */ @@ -729,6 +1092,20 @@ int rpmtsCheck(rpmts ts) /* Check package name (not provides!) against installed obsoletes */ checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p)); + + /* Check filenames against installed conflicts */ + if (confilehash || reqnotfilehash) { + rpmfiles files = rpmteFiles(p); + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD); + while (rpmfiNext(fi) >= 0) { + if (confilehash) + checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, 0, confilehash, &fpc); + if (reqnotfilehash) + checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 1, reqnotfilehash, &fpc); + } + rpmfiFree(fi); + rpmfilesFree(files); + } } rpmtsiFree(pi); @@ -738,25 +1115,47 @@ int rpmtsCheck(rpmts ts) pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) { rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME)); - rpmfi fi = rpmfiInit(rpmteFI(p), 0); rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n", rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p)); /* Check provides and filenames against installed dependencies. */ while (rpmdsNext(provides) >= 0) { - checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides)); + const char *dep = rpmdsN(provides); + checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, dep); + if (connothash && depexistsHashHasEntry(connothash, dep)) + checkNotInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, dep); } - while (rpmfiNext(fi) >= 0) { - if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) - checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi)); + if (reqfilehash || connotfilehash) { + rpmfiles files = rpmteFiles(p); + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);; + while (rpmfiNext(fi) >= 0) { + if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) { + if (reqfilehash) + checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 0, reqfilehash, &fpc); + if (connotfilehash) + checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, 1, connotfilehash, &fpc); + } + } + rpmfiFree(fi); + rpmfilesFree(files); } } rpmtsiFree(pi); + if (rdb) + rpmdbCtrl(rdb, RPMDB_CTRL_UNLOCK_RO); + exit: depCacheFree(dcache); + filedepHashFree(confilehash); + filedepHashFree(connotfilehash); + depexistsHashFree(connothash); + filedepHashFree(reqfilehash); + filedepHashFree(reqnotfilehash); + depexistsHashFree(reqnothash); + fpCacheFree(fpc); (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0); diff --git a/lib/formats.c b/lib/formats.c index 23f40277f..cd69073ea 100644 --- a/lib/formats.c +++ b/lib/formats.c @@ -21,22 +21,34 @@ #include "debug.h" +#define RPM_ANY_CLASS 255 + +typedef char * (*headerTagFormatFunction) (rpmtd td, char **emsg); + /** \ingroup header * Define header tag output formats. */ -struct headerFormatFunc_s { +struct headerFmt_s { rpmtdFormats fmt; /*!< Value of extension */ const char *name; /*!< Name of extension. */ + rpmTagClass class; /*!< Class of source data (RPM_ANY_CLASS for any) */ headerTagFormatFunction func; /*!< Pointer to formatter function. */ }; -/** - * barebones string representation with no extra formatting - * @param td tag data container - * @return formatted string - */ -static char * stringFormat(rpmtd td) +static const char *classEr(rpmTagClass class) +{ + switch (class) { + case RPM_BINARY_CLASS: return _("(not a blob)"); + case RPM_NUMERIC_CLASS: return _("(not a number)"); + case RPM_STRING_CLASS: return _("(not a string)"); + default: break; + } + return _("(invalid type)"); +} + +/* barebones string representation with no extra formatting */ +static char * stringFormat(rpmtd td, char **emsg) { char *val = NULL; @@ -44,109 +56,80 @@ static char * stringFormat(rpmtd td) case RPM_NUMERIC_CLASS: rasprintf(&val, "%" PRIu64, rpmtdGetNumber(td)); break; - case RPM_STRING_CLASS: - val = xstrdup(rpmtdGetString(td)); + case RPM_STRING_CLASS: { + const char *str = rpmtdGetString(td); + if (str) + val = xstrdup(str); break; + } case RPM_BINARY_CLASS: val = pgpHexStr(td->data, td->count); break; default: - val = xstrdup("(unknown type)"); + *emsg = xstrdup("(unknown type)"); break; } return val; } +/* arbitrary number format as per format arg */ static char * numFormat(rpmtd td, const char *format) { char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - rasprintf(&val, format, rpmtdGetNumber(td)); - } - + rasprintf(&val, format, rpmtdGetNumber(td)); return val; } -/** - * octalFormat. - * @param td tag data container - * @return formatted string - */ -static char * octalFormat(rpmtd td) + +/* octal number formatting */ +static char * octalFormat(rpmtd td, char **emsg) { return numFormat(td, "%o"); } -/** - * hexFormat. - * @param td tag data container - * @return formatted string - */ -static char * hexFormat(rpmtd td) +/* hexadecimal format */ +static char * hexFormat(rpmtd td, char **emsg) { return numFormat(td, "%x"); } -/** - * @param td tag data container - * @return formatted string - */ -static char * realDateFormat(rpmtd td, const char * strftimeFormat) +/* arbitrary date formatting as per strftimeFormat arg */ +static char * realDateFormat(rpmtd td, const char * strftimeFormat, char **emsg) { char * val = NULL; + struct tm * tstruct; + char buf[1024]; + time_t dateint = rpmtdGetNumber(td); + tstruct = localtime(&dateint); - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - struct tm * tstruct; - char buf[50]; - time_t dateint = rpmtdGetNumber(td); - tstruct = localtime(&dateint); - - /* XXX TODO: deal with non-fitting date string correctly */ - buf[0] = '\0'; - if (tstruct) - (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct); - val = xstrdup(buf); - } + buf[0] = '\0'; + if (tstruct) + if (strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct) == 0) + *emsg = xstrdup("date output too long"); + val = xstrdup(buf); return val; } -/** - * Format a date. - * @param td tag data container - * @return formatted string - */ -static char * dateFormat(rpmtd td) +/* date formatting */ +static char * dateFormat(rpmtd td, char **emsg) { - return realDateFormat(td, _("%c")); + return realDateFormat(td, _("%c"), emsg); } -/** - * Format a day. - * @param td tag data container - * @return formatted string - */ -static char * dayFormat(rpmtd td) +/* day formatting */ +static char * dayFormat(rpmtd td, char **emsg) { - return realDateFormat(td, _("%a %b %d %Y")); + return realDateFormat(td, _("%a %b %d %Y"), emsg); } -/** - * Return shell escape formatted data. - * @param td tag data container - * @return formatted string - */ -static char * shescapeFormat(rpmtd td) +/* shell escape formatting */ +static char * shescapeFormat(rpmtd td, char **emsg) { char * result = NULL, * dst, * src; if (rpmtdClass(td) == RPM_NUMERIC_CLASS) { rasprintf(&result, "%" PRIu64, rpmtdGetNumber(td)); - } else { + } else if (rpmtdClass(td) == RPM_STRING_CLASS) { char *buf = xstrdup(rpmtdGetString(td));; result = dst = xmalloc(strlen(buf) * 4 + 3); @@ -164,139 +147,95 @@ static char * shescapeFormat(rpmtd td) *dst++ = '\''; *dst = '\0'; free(buf); + } else { + *emsg = xstrdup(_("(invalid type)")); } return result; } -/** - * Identify type of trigger. - * @param td tag data container - * @return formatted string - */ -static char * triggertypeFormat(rpmtd td) +/* trigger type formatting (from rpmsense flags) */ +static char * triggertypeFormat(rpmtd td, char **emsg) { char * val; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - uint64_t item = rpmtdGetNumber(td); - if (item & RPMSENSE_TRIGGERPREIN) - val = xstrdup("prein"); - else if (item & RPMSENSE_TRIGGERIN) - val = xstrdup("in"); - else if (item & RPMSENSE_TRIGGERUN) - val = xstrdup("un"); - else if (item & RPMSENSE_TRIGGERPOSTUN) - val = xstrdup("postun"); - else - val = xstrdup(""); - } + uint64_t item = rpmtdGetNumber(td); + if (item & RPMSENSE_TRIGGERPREIN) + val = xstrdup("prein"); + else if (item & RPMSENSE_TRIGGERIN) + val = xstrdup("in"); + else if (item & RPMSENSE_TRIGGERUN) + val = xstrdup("un"); + else if (item & RPMSENSE_TRIGGERPOSTUN) + val = xstrdup("postun"); + else + val = xstrdup(""); return val; } -/** - * Identify type of dependency. - * @param td tag data container - * @return formatted string - */ -static char * deptypeFormat(rpmtd td) +/* dependency type formatting (from rpmsense flags) */ +static char * deptypeFormat(rpmtd td, char **emsg) { char *val = NULL; - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); + ARGV_t sdeps = NULL; + uint64_t item = rpmtdGetNumber(td); + + if (item & RPMSENSE_SCRIPT_PRE) + argvAdd(&sdeps, "pre"); + if (item & RPMSENSE_SCRIPT_POST) + argvAdd(&sdeps, "post"); + if (item & RPMSENSE_SCRIPT_PREUN) + argvAdd(&sdeps, "preun"); + if (item & RPMSENSE_SCRIPT_POSTUN) + argvAdd(&sdeps, "postun"); + if (item & RPMSENSE_SCRIPT_VERIFY) + argvAdd(&sdeps, "verify"); + if (item & RPMSENSE_INTERP) + argvAdd(&sdeps, "interp"); + if (item & RPMSENSE_RPMLIB) + argvAdd(&sdeps, "rpmlib"); + if ((item & RPMSENSE_FIND_REQUIRES) || (item & RPMSENSE_FIND_PROVIDES)) + argvAdd(&sdeps, "auto"); + if (item & RPMSENSE_PREREQ) + argvAdd(&sdeps, "prereq"); + if (item & RPMSENSE_PRETRANS) + argvAdd(&sdeps, "pretrans"); + if (item & RPMSENSE_POSTTRANS) + argvAdd(&sdeps, "posttrans"); + if (item & RPMSENSE_CONFIG) + argvAdd(&sdeps, "config"); + if (item & RPMSENSE_MISSINGOK) + argvAdd(&sdeps, "missingok"); + + if (sdeps) { + val = argvJoin(sdeps, ","); } else { - ARGV_t sdeps = NULL; - uint64_t item = rpmtdGetNumber(td); - - if (item & RPMSENSE_SCRIPT_PRE) - argvAdd(&sdeps, "pre"); - if (item & RPMSENSE_SCRIPT_POST) - argvAdd(&sdeps, "post"); - if (item & RPMSENSE_SCRIPT_PREUN) - argvAdd(&sdeps, "preun"); - if (item & RPMSENSE_SCRIPT_POSTUN) - argvAdd(&sdeps, "postun"); - if (item & RPMSENSE_SCRIPT_VERIFY) - argvAdd(&sdeps, "verify"); - if (item & RPMSENSE_INTERP) - argvAdd(&sdeps, "interp"); - if (item & RPMSENSE_RPMLIB) - argvAdd(&sdeps, "rpmlib"); - if ((item & RPMSENSE_FIND_REQUIRES) || (item & RPMSENSE_FIND_PROVIDES)) - argvAdd(&sdeps, "auto"); - if (item & RPMSENSE_PREREQ) - argvAdd(&sdeps, "prereq"); - if (item & RPMSENSE_PRETRANS) - argvAdd(&sdeps, "pretrans"); - if (item & RPMSENSE_POSTTRANS) - argvAdd(&sdeps, "posttrans"); - if (item & RPMSENSE_CONFIG) - argvAdd(&sdeps, "config"); - if (item & RPMSENSE_MISSINGOK) - argvAdd(&sdeps, "missingok"); - - if (sdeps) { - val = argvJoin(sdeps, ","); - } else { - val = xstrdup("manual"); - } - - argvFree(sdeps); + val = xstrdup("manual"); } + + argvFree(sdeps); return val; } -/** - * Format file permissions for display. - * @param td tag data container - * @return formatted string - */ -static char * permsFormat(rpmtd td) +/* file permissions formatting */ +static char * permsFormat(rpmtd td, char **emsg) { - char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - val = rpmPermsString(rpmtdGetNumber(td)); - } - - return val; + return rpmPermsString(rpmtdGetNumber(td)); } -/** - * Format file flags for display. - * @param td tag data container - * @return formatted string - */ -static char * fflagsFormat(rpmtd td) +/* file flags formatting */ +static char * fflagsFormat(rpmtd td, char **emsg) { - char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - val = rpmFFlagsString(rpmtdGetNumber(td), ""); - } - - return val; + return rpmFFlagsString(rpmtdGetNumber(td), ""); } -/** - * Wrap a pubkey in ascii armor for display. - * @todo Permit selectable display formats (i.e. binary). - * @param td tag data container - * @return formatted string - */ -static char * armorFormat(rpmtd td) +/* pubkey ascii armor formatting */ +static char * armorFormat(rpmtd td, char **emsg) { const char * enc; const unsigned char * s; unsigned char * bs = NULL; - char *val; + char *val = NULL; size_t ns; int atype; @@ -310,8 +249,10 @@ static char * armorFormat(rpmtd td) case RPM_STRING_TYPE: case RPM_STRING_ARRAY_TYPE: enc = rpmtdGetString(td); - if (rpmBase64Decode(enc, (void **)&bs, &ns)) - return xstrdup(_("(not base64)")); + if (rpmBase64Decode(enc, (void **)&bs, &ns)) { + *emsg = xstrdup(_("(not base64)")); + goto exit; + } s = bs; atype = PGPARMOR_PUBKEY; /* XXX check pkt for pubkey */ break; @@ -323,7 +264,8 @@ static char * armorFormat(rpmtd td) case RPM_INT64_TYPE: case RPM_I18NSTRING_TYPE: default: - return xstrdup(_("(invalid type)")); + *emsg = xstrdup(_("(invalid type)")); + goto exit; break; } @@ -332,48 +274,35 @@ static char * armorFormat(rpmtd td) if (atype == PGPARMOR_PUBKEY) { free(bs); } + +exit: return val; } -/** - * Encode binary data in base64 for display. - * @todo Permit selectable display formats (i.e. binary). - * @param td tag data container - * @return formatted string - */ -static char * base64Format(rpmtd td) +/* base64 encoding formatting */ +static char * base64Format(rpmtd td, char **emsg) { - char * val = NULL; - - if (rpmtdType(td) != RPM_BIN_TYPE) { - val = xstrdup(_("(not a blob)")); - } else { - val = rpmBase64Encode(td->data, td->count, -1); - if (val == NULL) - val = xstrdup(""); - } + char * val = rpmBase64Encode(td->data, td->count, -1); + if (val == NULL) + val = xstrdup(""); return val; } -/** - * Wrap tag data in simple header xml markup. - * @param td tag data container - * @return formatted string - */ -static char * xmlFormat(rpmtd td) +/* xml formatting */ +static char * xmlFormat(rpmtd td, char **emsg) { const char *xtag = NULL; char *val = NULL; char *s = NULL; - rpmtdFormats fmt = RPMTD_FORMAT_STRING; + headerTagFormatFunction fmt = stringFormat; switch (rpmtdClass(td)) { case RPM_STRING_CLASS: xtag = "string"; break; case RPM_BINARY_CLASS: - fmt = RPMTD_FORMAT_BASE64; + fmt = base64Format; xtag = "base64"; break; case RPM_NUMERIC_CLASS: @@ -381,12 +310,14 @@ static char * xmlFormat(rpmtd td) break; case RPM_NULL_TYPE: default: - return xstrdup(_("(invalid xml type)")); + *emsg = xstrdup(_("(invalid xml type)")); + goto exit; break; } - /* XXX TODO: handle errors */ - s = rpmtdFormat(td, fmt, NULL); + s = fmt(td, emsg); + if (s == NULL) + goto exit; if (s[0] == '\0') { val = rstrscat(NULL, "\t<", xtag, "/>", NULL); @@ -413,226 +344,204 @@ static char * xmlFormat(rpmtd td) } free(s); +exit: return val; } -/** - * Display signature fingerprint and time. - * @param td tag data container - * @return formatted string - */ -static char * pgpsigFormat(rpmtd td) +/* signature fingerprint and time formatting */ +static char * pgpsigFormat(rpmtd td, char **emsg) { char * val = NULL; + pgpDigParams sigp = NULL; - if (rpmtdType(td) != RPM_BIN_TYPE) { - val = xstrdup(_("(not a blob)")); + if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) { + *emsg = xstrdup(_("(not an OpenPGP signature)")); } else { - pgpDigParams sigp = NULL; - - if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) { - val = xstrdup(_("(not an OpenPGP signature)")); + char dbuf[BUFSIZ]; + char *keyid = pgpHexStr(sigp->signid, sizeof(sigp->signid)); + unsigned int dateint = sigp->time; + time_t date = dateint; + struct tm * tms = localtime(&date); + unsigned int key_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO); + unsigned int hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO); + + if (!(tms && strftime(dbuf, sizeof(dbuf), "%c", tms) > 0)) { + rasprintf(emsg, _("Invalid date %u"), dateint); } else { - char dbuf[BUFSIZ]; - char *keyid = pgpHexStr(sigp->signid, sizeof(sigp->signid)); - unsigned int dateint = pgpGrab(sigp->time, sizeof(sigp->time)); - time_t date = dateint; - struct tm * tms = localtime(&date); - unsigned int key_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO); - unsigned int hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO); - - if (!(tms && strftime(dbuf, sizeof(dbuf), "%c", tms) > 0)) { - snprintf(dbuf, sizeof(dbuf), - _("Invalid date %u"), dateint); - dbuf[sizeof(dbuf)-1] = '\0'; - } - rasprintf(&val, "%s/%s, %s, Key ID %s", - pgpValString(PGPVAL_PUBKEYALGO, key_algo), - pgpValString(PGPVAL_HASHALGO, hash_algo), - dbuf, keyid); - - free(keyid); - pgpDigParamsFree(sigp); + pgpValString(PGPVAL_PUBKEYALGO, key_algo), + pgpValString(PGPVAL_HASHALGO, hash_algo), + dbuf, keyid); } - } - - return val; -} -/** - * Format dependency flags for display. - * @param td tag data container - * @return formatted string - */ -static char * depflagsFormat(rpmtd td) -{ - char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - uint64_t anint = rpmtdGetNumber(td); - val = xcalloc(4, 1); - - if (anint & RPMSENSE_LESS) - strcat(val, "<"); - if (anint & RPMSENSE_GREATER) - strcat(val, ">"); - if (anint & RPMSENSE_EQUAL) - strcat(val, "="); + free(keyid); + pgpDigParamsFree(sigp); } return val; } -static char * depflag_strongFormat(rpmtd td) +/* dependency flags formatting */ +static char * depflagsFormat(rpmtd td, char **emsg) { char * val = NULL; + uint64_t anint = rpmtdGetNumber(td); + val = xcalloc(4, 1); + + if (anint & RPMSENSE_LESS) + strcat(val, "<"); + if (anint & RPMSENSE_GREATER) + strcat(val, ">"); + if (anint & RPMSENSE_EQUAL) + strcat(val, "="); - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - uint64_t anint = rpmtdGetNumber(td); - val = xstrdup(anint & RPMSENSE_STRONG ? "strong" : ""); - } return val; } - -/** - * Return tag container array size. - * @param td tag data container - * @return formatted string - */ -static char * arraysizeFormat(rpmtd td) +/* tag container array size */ +static char * arraysizeFormat(rpmtd td, char **emsg) { char *val = NULL; rasprintf(&val, "%u", rpmtdCount(td)); return val; } -static char * fstateFormat(rpmtd td) +/* file state formatting */ +static char * fstateFormat(rpmtd td, char **emsg) { char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - const char * str; - rpmfileState fstate = rpmtdGetNumber(td); - switch (fstate) { - case RPMFILE_STATE_NORMAL: - str = _("normal"); - break; - case RPMFILE_STATE_REPLACED: - str = _("replaced"); - break; - case RPMFILE_STATE_NOTINSTALLED: - str = _("not installed"); - break; - case RPMFILE_STATE_NETSHARED: - str = _("net shared"); - break; - case RPMFILE_STATE_WRONGCOLOR: - str = _("wrong color"); - break; - case RPMFILE_STATE_MISSING: - str = _("missing"); - break; - default: - str = _("(unknown)"); - break; - } - - val = xstrdup(str); + const char * str; + rpmfileState fstate = rpmtdGetNumber(td); + switch (fstate) { + case RPMFILE_STATE_NORMAL: + str = _("normal"); + break; + case RPMFILE_STATE_REPLACED: + str = _("replaced"); + break; + case RPMFILE_STATE_NOTINSTALLED: + str = _("not installed"); + break; + case RPMFILE_STATE_NETSHARED: + str = _("net shared"); + break; + case RPMFILE_STATE_WRONGCOLOR: + str = _("wrong color"); + break; + case RPMFILE_STATE_MISSING: + str = _("missing"); + break; + default: + str = _("(unknown)"); + break; } + + val = xstrdup(str); return val; } +/* file verification flags formatting with optional padding */ static char * verifyFlags(rpmtd td, const char *pad) { - char * val = NULL; - - if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { - val = xstrdup(_("(not a number)")); - } else { - val = rpmVerifyString(rpmtdGetNumber(td), pad); - } - return val; + return rpmVerifyString(rpmtdGetNumber(td), pad); } -static char * vflagsFormat(rpmtd td) +static char * vflagsFormat(rpmtd td, char **emsg) { return verifyFlags(td, ""); } -static char * fstatusFormat(rpmtd td) +static char * fstatusFormat(rpmtd td, char **emsg) { return verifyFlags(td, "."); } -static char * expandFormat(rpmtd td) +/* macro expansion formatting */ +static char * expandFormat(rpmtd td, char **emsg) { - char *val = NULL; - if (rpmtdClass(td) != RPM_STRING_CLASS) { - val = xstrdup(_("(not a string)")); - } else { - val = rpmExpand(td->data, NULL); - } - return val; + return rpmExpand(rpmtdGetString(td), NULL); } -static const struct headerFormatFunc_s rpmHeaderFormats[] = { - { RPMTD_FORMAT_STRING, "string", stringFormat }, - { RPMTD_FORMAT_ARMOR, "armor", armorFormat }, - { RPMTD_FORMAT_BASE64, "base64", base64Format }, - { RPMTD_FORMAT_PGPSIG, "pgpsig", pgpsigFormat }, - { RPMTD_FORMAT_DEPFLAGS, "depflags", depflagsFormat }, - { RPMTD_FORMAT_DEPTYPE, "deptype", deptypeFormat }, - { RPMTD_FORMAT_FFLAGS, "fflags", fflagsFormat }, - { RPMTD_FORMAT_PERMS, "perms", permsFormat }, - { RPMTD_FORMAT_PERMS, "permissions", permsFormat }, - { RPMTD_FORMAT_TRIGGERTYPE, "triggertype", triggertypeFormat }, - { RPMTD_FORMAT_XML, "xml", xmlFormat }, - { RPMTD_FORMAT_OCTAL, "octal", octalFormat }, - { RPMTD_FORMAT_HEX, "hex", hexFormat }, - { RPMTD_FORMAT_DATE, "date", dateFormat }, - { RPMTD_FORMAT_DAY, "day", dayFormat }, - { RPMTD_FORMAT_SHESCAPE, "shescape", shescapeFormat }, - { RPMTD_FORMAT_ARRAYSIZE, "arraysize", arraysizeFormat }, - { RPMTD_FORMAT_FSTATE, "fstate", fstateFormat }, - { RPMTD_FORMAT_VFLAGS, "vflags", vflagsFormat }, - { RPMTD_FORMAT_EXPAND, "expand", expandFormat }, - { RPMTD_FORMAT_FSTATUS, "fstatus", fstatusFormat }, - { RPMTD_FORMAT_DEPFLAG_STRONG, "depflag_strong", depflag_strongFormat }, - { -1, NULL, NULL } +static const struct headerFmt_s rpmHeaderFormats[] = { + { RPMTD_FORMAT_STRING, "string", + RPM_ANY_CLASS, stringFormat }, + { RPMTD_FORMAT_ARMOR, "armor", + RPM_ANY_CLASS, armorFormat }, + { RPMTD_FORMAT_BASE64, "base64", + RPM_BINARY_CLASS, base64Format }, + { RPMTD_FORMAT_PGPSIG, "pgpsig", + RPM_BINARY_CLASS, pgpsigFormat }, + { RPMTD_FORMAT_DEPFLAGS, "depflags", + RPM_NUMERIC_CLASS, depflagsFormat }, + { RPMTD_FORMAT_DEPTYPE, "deptype", + RPM_NUMERIC_CLASS, deptypeFormat }, + { RPMTD_FORMAT_FFLAGS, "fflags", + RPM_NUMERIC_CLASS, fflagsFormat }, + { RPMTD_FORMAT_PERMS, "perms", + RPM_NUMERIC_CLASS, permsFormat }, + { RPMTD_FORMAT_PERMS, "permissions", + RPM_NUMERIC_CLASS, permsFormat }, + { RPMTD_FORMAT_TRIGGERTYPE, "triggertype", + RPM_NUMERIC_CLASS, triggertypeFormat }, + { RPMTD_FORMAT_XML, "xml", + RPM_ANY_CLASS, xmlFormat }, + { RPMTD_FORMAT_OCTAL, "octal", + RPM_NUMERIC_CLASS, octalFormat }, + { RPMTD_FORMAT_HEX, "hex", + RPM_NUMERIC_CLASS, hexFormat }, + { RPMTD_FORMAT_DATE, "date", + RPM_NUMERIC_CLASS, dateFormat }, + { RPMTD_FORMAT_DAY, "day", + RPM_NUMERIC_CLASS, dayFormat }, + { RPMTD_FORMAT_SHESCAPE, "shescape", + RPM_ANY_CLASS, shescapeFormat }, + { RPMTD_FORMAT_ARRAYSIZE, "arraysize", + RPM_ANY_CLASS, arraysizeFormat }, + { RPMTD_FORMAT_FSTATE, "fstate", + RPM_NUMERIC_CLASS, fstateFormat }, + { RPMTD_FORMAT_VFLAGS, "vflags", + RPM_NUMERIC_CLASS, vflagsFormat }, + { RPMTD_FORMAT_EXPAND, "expand", + RPM_STRING_CLASS, expandFormat }, + { RPMTD_FORMAT_FSTATUS, "fstatus", + RPM_NUMERIC_CLASS, fstatusFormat }, + { -1, NULL, 0, NULL } }; -headerTagFormatFunction rpmHeaderFormatFuncByName(const char *fmt) +headerFmt rpmHeaderFormatByName(const char *fmt) { - const struct headerFormatFunc_s * ext; - headerTagFormatFunction func = NULL; + const struct headerFmt_s * ext; for (ext = rpmHeaderFormats; ext->name != NULL; ext++) { - if (rstreq(ext->name, fmt)) { - func = ext->func; - break; - } + if (rstreq(ext->name, fmt)) + return ext; } - return func; + return NULL; } -headerTagFormatFunction rpmHeaderFormatFuncByValue(rpmtdFormats fmt) +headerFmt rpmHeaderFormatByValue(rpmtdFormats fmt) { - const struct headerFormatFunc_s * ext; - headerTagFormatFunction func = NULL; + const struct headerFmt_s * ext; for (ext = rpmHeaderFormats; ext->name != NULL; ext++) { - if (fmt == ext->fmt) { - func = ext->func; - break; - } + if (fmt == ext->fmt) + return ext; } - return func; + return NULL; } +char *rpmHeaderFormatCall(headerFmt fmt, rpmtd td) +{ + char *ret = NULL; + char *err = NULL; + + if (fmt->class != RPM_ANY_CLASS && rpmtdClass(td) != fmt->class) + err = xstrdup(classEr(fmt->class)); + else + ret = fmt->func(td, &err); + + if (err) { + free(ret); + ret = err; + } + return ret; +} diff --git a/lib/fprint.c b/lib/fprint.c index 6f6172cf2..b810e4d2b 100644 --- a/lib/fprint.c +++ b/lib/fprint.c @@ -6,7 +6,7 @@ #include <rpm/rpmfileutil.h> /* for rpmCleanPath */ #include <rpm/rpmts.h> -#include <rpm/rpmdb.h> +#include <rpm/rpmsq.h> #include "lib/rpmdb_internal.h" #include "lib/rpmfi_internal.h" @@ -251,7 +251,7 @@ int fpLookup(fingerPrintCache cache, /** * Return hash value for a finger print. * Hash based on dev and inode only! - * @param key pointer to finger print entry + * @param fp pointer to finger print entry * @return hash value */ static unsigned int fpHashFunction(const fingerPrint * fp) @@ -335,7 +335,7 @@ fingerPrint * fpLookupList(fingerPrintCache cache, rpmstrPool pool, /* Check file for to be installed symlinks in their path and correct their fp */ static void fpLookupSubdir(rpmFpHash symlinks, fingerPrintCache fpc, rpmte p, int filenr) { - rpmfi fi = rpmteFI(p); + rpmfiles fi = rpmteFiles(p); struct fingerPrint_s current_fp; const char *currentsubdir; size_t lensubDir, bnStart, bnEnd; @@ -343,13 +343,13 @@ static void fpLookupSubdir(rpmFpHash symlinks, fingerPrintCache fpc, rpmte p, in struct rpmffi_s * recs; int numRecs; int i; - fingerPrint *fp = rpmfiFps(fi) + filenr; + fingerPrint *fp = rpmfilesFps(fi) + filenr; int symlinkcount = 0; struct rpmffi_s ffi = { p, filenr}; if (fp->subDirId == 0) { - rpmFpHashAddEntry(fpc->fp, fp, ffi); - return; + rpmFpHashAddEntry(fpc->fp, fp, ffi); + goto exit; } currentsubdir = rpmstrPoolStr(fpc->pool, fp->subDirId); @@ -364,88 +364,98 @@ static void fpLookupSubdir(rpmFpHash symlinks, fingerPrintCache fpc, rpmte p, in current_fp.subDirId = 0; while (bnEnd < lensubDir) { - char found = 0; + char found = 0; - current_fp.baseNameId = rpmstrPoolIdn(fpc->pool, + current_fp.baseNameId = rpmstrPoolIdn(fpc->pool, currentsubdir + bnStart, bnEnd - bnStart, 1); - rpmFpHashGetEntry(symlinks, ¤t_fp, &recs, &numRecs, NULL); - - for (i=0; i<numRecs; i++) { - rpmfi foundfi = rpmteFI(recs[i].p); - char const *linktarget = rpmfiFLinkIndex(foundfi, recs[i].fileno); - char *link; - - if (linktarget && *linktarget != '\0') { - const char *bn; - /* this "directory" is a symlink */ - link = NULL; - if (*linktarget != '/') { - const char *dn, *subDir = NULL; - dn = rpmstrPoolStr(fpc->pool, current_fp.entry->dirId); - if (current_fp.subDirId) { - subDir = rpmstrPoolStr(fpc->pool, - current_fp.subDirId); - } - rstrscat(&link, dn, - subDir ? subDir : "", - "/", NULL); - } - rstrscat(&link, linktarget, "/", NULL); - if (strlen(currentsubdir + bnEnd)) { - rstrscat(&link, currentsubdir + bnEnd, NULL); - } - - bn = rpmstrPoolStr(fpc->pool, fp->baseNameId); - doLookup(fpc, link, bn, fp); - - free(link); - symlinkcount++; - - /* setup current_fp for the new path */ - found = 1; - current_fp = *fp; - if (fp->subDirId == 0) { - /* directory exists - no need to look for symlinks */ - rpmFpHashAddEntry(fpc->fp, fp, ffi); - return; - } - currentsubdir = rpmstrPoolStr(fpc->pool, fp->subDirId); - lensubDir = rpmstrPoolStrlen(fpc->pool, fp->subDirId); - /* no subDir for now */ - current_fp.subDirId = 0; - - /* Set baseName to the upper most dir */ - bnStart = bnEnd = 1; - while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') - bnEnd++; - break; - - } - } - if (symlinkcount>50) { - // found too many symlinks in the path - // most likley a symlink cicle - // giving up - // TODO warning/error - break; - } - if (found) { - continue; // restart loop after symlink - } - - /* Set former baseName as subDir */ - bnEnd++; - current_fp.subDirId = rpmstrPoolIdn(fpc->pool, currentsubdir, bnEnd, 1); - - /* set baseName to the next lower dir */ - bnStart = bnEnd; - while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') + rpmFpHashGetEntry(symlinks, ¤t_fp, &recs, &numRecs, NULL); + + for (i = 0; i < numRecs; i++) { + rpmfiles foundfi = rpmteFiles(recs[i].p); + char const *linktarget = rpmfilesFLink(foundfi, recs[i].fileno); + char *link; + + /* Ignore already removed (by eg %pretrans) links */ + if (linktarget && rpmteType(recs[i].p) == TR_REMOVED) { + char *path = rpmfilesFN(foundfi, recs[i].fileno); + struct stat sb; + if (lstat(path, &sb) == -1) + linktarget = NULL; + free(path); + } + + foundfi = rpmfilesFree(foundfi); + + if (linktarget && *linktarget != '\0') { + const char *bn; + /* this "directory" is a symlink */ + link = NULL; + if (*linktarget != '/') { + const char *dn, *subDir = NULL; + dn = rpmstrPoolStr(fpc->pool, current_fp.entry->dirId); + if (current_fp.subDirId) { + subDir = rpmstrPoolStr(fpc->pool, current_fp.subDirId); + } + rstrscat(&link, dn, subDir ? subDir : "", "/", NULL); + } + rstrscat(&link, linktarget, "/", NULL); + if (strlen(currentsubdir + bnEnd)) { + rstrscat(&link, currentsubdir + bnEnd, NULL); + } + + bn = rpmstrPoolStr(fpc->pool, fp->baseNameId); + doLookup(fpc, link, bn, fp); + + free(link); + symlinkcount++; + + /* setup current_fp for the new path */ + found = 1; + current_fp = *fp; + if (fp->subDirId == 0) { + /* directory exists - no need to look for symlinks */ + rpmFpHashAddEntry(fpc->fp, fp, ffi); + goto exit; + } + currentsubdir = rpmstrPoolStr(fpc->pool, fp->subDirId); + lensubDir = rpmstrPoolStrlen(fpc->pool, fp->subDirId); + /* no subDir for now */ + current_fp.subDirId = 0; + + /* Set baseName to the upper most dir */ + bnStart = bnEnd = 1; + while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') + bnEnd++; + break; + + } + } + if (symlinkcount > 50) { + // found too many symlinks in the path + // most likley a symlink cicle + // giving up + // TODO warning/error + break; + } + if (found) { + continue; // restart loop after symlink + } + + /* Set former baseName as subDir */ + bnEnd++; + current_fp.subDirId = rpmstrPoolIdn(fpc->pool, currentsubdir, bnEnd, 1); + + /* set baseName to the next lower dir */ + bnStart = bnEnd; + while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') bnEnd++; } rpmFpHashAddEntry(fpc->fp, fp, ffi); +exit: + rpmfilesFree(fi); } fingerPrint * fpCacheGetByFp(fingerPrintCache cache, @@ -463,7 +473,7 @@ void fpCachePopulate(fingerPrintCache fpc, rpmts ts, int fileCount) rpmtsi pi; rpmte p; rpmfs fs; - rpmfi fi; + rpmfiles fi; int i, fc; if (fpc->fp == NULL) @@ -475,23 +485,23 @@ void fpCachePopulate(fingerPrintCache fpc, rpmts ts, int fileCount) pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { fingerPrint *fpList; - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); - if ((fi = rpmteFI(p)) == NULL) + if ((fi = rpmteFiles(p)) == NULL) continue; /* XXX can't happen */ (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0); - rpmfiFpLookup(fi, fpc); + rpmfilesFpLookup(fi, fpc); fs = rpmteGetFileStates(p); fc = rpmfsFC(fs); - fpList = rpmfiFps(fi); + fpList = rpmfilesFps(fi); /* collect symbolic links */ for (i = 0; i < fc; i++) { struct rpmffi_s ffi; char const *linktarget; if (XFA_SKIPPING(rpmfsGetAction(fs, i))) continue; - linktarget = rpmfiFLinkIndex(fi, i); + linktarget = rpmfilesFLink(fi, i); if (!(linktarget && *linktarget != '\0')) continue; ffi.p = p; @@ -499,7 +509,7 @@ void fpCachePopulate(fingerPrintCache fpc, rpmts ts, int fileCount) rpmFpHashAddEntry(symlinks, fpList + i, ffi); } (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), fc); - + rpmfilesFree(fi); } rpmtsiFree(pi); @@ -510,7 +520,7 @@ void fpCachePopulate(fingerPrintCache fpc, rpmts ts, int fileCount) pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); fs = rpmteGetFileStates(p); fc = rpmfsFC(fs); @@ -13,129 +13,23 @@ #include <rpm/rpmte.h> #include <rpm/rpmts.h> -#include <rpm/rpmsq.h> #include <rpm/rpmlog.h> +#include <rpm/rpmmacro.h> #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ -#include "lib/cpio.h" #include "lib/fsm.h" -#define fsmUNSAFE fsmStage -#include "lib/rpmfi_internal.h" /* XXX fi->apath, ... */ #include "lib/rpmte_internal.h" /* XXX rpmfs */ -#include "lib/rpmts_internal.h" /* rpmtsSELabelFoo() only */ -#include "lib/rpmplugins.h" /* rpm plugins hooks */ +#include "lib/rpmplugins.h" /* rpm plugins hooks */ #include "lib/rpmug.h" -#include "lib/cpio.h" #include "debug.h" #define _FSM_DEBUG 0 int _fsm_debug = _FSM_DEBUG; -extern int _fsm_debug; - -/** \ingroup payload - */ -enum cpioMapFlags_e { - CPIO_MAP_PATH = (1 << 0), - CPIO_MAP_MODE = (1 << 1), - CPIO_MAP_UID = (1 << 2), - CPIO_MAP_GID = (1 << 3), - CPIO_FOLLOW_SYMLINKS= (1 << 4), /*!< only for building. */ - CPIO_MAP_ABSOLUTE = (1 << 5), - CPIO_MAP_ADDDOT = (1 << 6), - CPIO_MAP_TYPE = (1 << 8), /*!< only for building. */ - CPIO_SBIT_CHECK = (1 << 9) -}; -typedef rpmFlags cpioMapFlags; - -typedef struct fsmIterator_s * FSMI_t; -typedef struct fsm_s * FSM_t; - -typedef struct hardLink_s * hardLink_t; - -typedef enum fileStage_e { - FSM_PKGINSTALL, - FSM_PKGERASE, - FSM_PKGBUILD, -} fileStage; - /* XXX Failure to remove is not (yet) cause for failure. */ static int strict_erasures = 0; -/** \ingroup payload - * Keeps track of the set of all hard links to a file in an archive. - */ -struct hardLink_s { - hardLink_t next; - const char ** nsuffix; - int * filex; - struct stat sb; - nlink_t nlink; - nlink_t linksLeft; - int linkIndex; - int createdPath; -}; - -/** \ingroup payload - * Iterator across package file info, forward on install, backward on erase. - */ -struct fsmIterator_s { - rpmfs fs; /*!< file state info. */ - rpmfi fi; /*!< transaction element file info. */ - int reverse; /*!< reversed traversal? */ - int isave; /*!< last returned iterator index. */ - int i; /*!< iterator index. */ -}; - -/** \ingroup payload - * File name and stat information. - */ -struct fsm_s { - char * path; /*!< Current file name. */ - char * buf; /*!< read: Buffer. */ - size_t bufsize; /*!< read: Buffer allocated size. */ - FSMI_t iter; /*!< File iterator. */ - int ix; /*!< Current file iterator index. */ - hardLink_t links; /*!< Pending hard linked file(s). */ - char ** failedFile; /*!< First file name that failed. */ - const char * osuffix; /*!< Old, preserved, file suffix. */ - const char * nsuffix; /*!< New, created, file suffix. */ - char * suffix; /*!< Current file suffix. */ - int postpone; /*!< Skip remaining stages? */ - int diskchecked; /*!< Has stat(2) been performed? */ - int exists; /*!< Does current file exist on disk? */ - cpioMapFlags mapFlags; /*!< Bit(s) to control mapping. */ - const char * dirName; /*!< File directory name. */ - const char * baseName; /*!< File base name. */ - struct selabel_handle *sehandle; /*!< SELinux label handle (if any). */ - rpmPlugins plugins; /*!< Rpm plugins handle */ - - unsigned fflags; /*!< File flags. */ - rpmFileAction action; /*!< File disposition. */ - fileStage goal; /*!< Package state machine goal. */ - struct stat sb; /*!< Current file stat(2) info. */ - struct stat osb; /*!< Original file stat(2) info. */ -}; - - -/** - * Retrieve transaction element file info from file state machine iterator. - * @param fsm file state machine - * @return transaction element file info - */ -static rpmfi fsmGetFi(const FSM_t fsm) -{ - const FSMI_t iter = fsm->iter; - return (iter ? iter->fi : NULL); -} - -static rpmfs fsmGetFs(const FSM_t fsm) -{ - const FSMI_t iter = fsm->iter; - return (iter ? iter->fs : NULL); -} - #define SUFFIX_RPMORIG ".rpmorig" #define SUFFIX_RPMSAVE ".rpmsave" #define SUFFIX_RPMNEW ".rpmnew" @@ -152,126 +46,20 @@ static const char * fileActionString(rpmFileAction a); /** \ingroup payload * Build path to file from file info, optionally ornamented with suffix. - * @param fsm file state machine data - * @param isDir directory or regular path? + * @param fi file info iterator * @param suffix suffix to use (NULL disables) * @retval path to file (malloced) */ -static char * fsmFsPath(const FSM_t fsm, int isDir, - const char * suffix) -{ - return rstrscat(NULL, - fsm->dirName, fsm->baseName, - (!isDir && suffix) ? suffix : "", - NULL); -} - -/** \ingroup payload - * Destroy file info iterator. - * @param p file info iterator - * @retval NULL always - */ -static FSMI_t mapFreeIterator(FSMI_t iter) +static char * fsmFsPath(rpmfi fi, const char * suffix) { - if (iter) { - iter->fs = NULL; /* rpmfs is not refcounted */ - iter->fi = rpmfiFree(iter->fi); - free(iter); - } - return NULL; -} - -/** \ingroup payload - * Create file info iterator. - * @param fi transaction element file info - * @return file info iterator - */ -static FSMI_t -mapInitIterator(rpmfs fs, rpmfi fi, int reverse) -{ - FSMI_t iter = NULL; - - iter = xcalloc(1, sizeof(*iter)); - iter->fs = fs; /* rpmfs is not refcounted */ - iter->fi = rpmfiLink(fi); - iter->reverse = reverse; - iter->i = (iter->reverse ? (rpmfiFC(fi) - 1) : 0); - iter->isave = iter->i; - return iter; -} - -/** \ingroup payload - * Return next index into file info. - * @param a file info iterator - * @return next index, -1 on termination - */ -static int mapNextIterator(FSMI_t iter) -{ - int i = -1; - - if (iter) { - const rpmfi fi = iter->fi; - if (iter->reverse) { - if (iter->i >= 0) i = iter->i--; - } else { - if (iter->i < rpmfiFC(fi)) i = iter->i++; - } - iter->isave = i; - } - return i; -} - -/** \ingroup payload - */ -static int cpioStrCmp(const void * a, const void * b) -{ - const char * afn = *(const char **)a; - const char * bfn = *(const char **)b; - - /* Match rpm-4.0 payloads with ./ prefixes. */ - if (afn[0] == '.' && afn[1] == '/') afn += 2; - if (bfn[0] == '.' && bfn[1] == '/') bfn += 2; - - /* If either path is absolute, make it relative. */ - if (afn[0] == '/') afn += 1; - if (bfn[0] == '/') bfn += 1; - - return strcmp(afn, bfn); -} - -/** \ingroup payload - * Locate archive path in file info. - * @param iter file info iterator - * @param fsmPath archive path - * @return index into file info, -1 if archive path was not found - */ -static int mapFind(FSMI_t iter, const char * fsmPath) -{ - int ix = -1; - - if (iter) { - const rpmfi fi = iter->fi; - int fc = rpmfiFC(fi); - if (fi && fc > 0 && fi->apath && fsmPath && *fsmPath) { - char ** p = NULL; - - if (fi->apath != NULL) - p = bsearch(&fsmPath, fi->apath, fc, sizeof(fsmPath), - cpioStrCmp); - if (p) { - iter->i = p - fi->apath; - ix = mapNextIterator(iter); - } - } - } - return ix; + return rstrscat(NULL, rpmfiDN(fi), rpmfiBN(fi), suffix ? suffix : "", NULL); } /** \ingroup payload * Directory name iterator. */ typedef struct dnli_s { - rpmfi fi; + rpmfiles fi; char * active; int reverse; int isave; @@ -280,7 +68,7 @@ typedef struct dnli_s { /** \ingroup payload * Destroy directory name iterator. - * @param a directory name iterator + * @param dnli directory name iterator * @retval NULL always */ static DNLI_t dnlFreeIterator(DNLI_t dnli) @@ -299,7 +87,7 @@ static DNLI_t dnlFreeIterator(DNLI_t dnli) * @param reverse traverse directory names in reverse order? * @return directory name iterator */ -static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse) +static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse) { DNLI_t dnli; int i, j; @@ -307,7 +95,7 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse) if (fi == NULL) return NULL; - dc = rpmfiDC(fi); + dc = rpmfilesDC(fi); dnli = xcalloc(1, sizeof(*dnli)); dnli->fi = fi; dnli->reverse = reverse; @@ -315,24 +103,24 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse) if (dc) { dnli->active = xcalloc(dc, sizeof(*dnli->active)); - int fc = rpmfiFC(fi); + int fc = rpmfilesFC(fi); /* Identify parent directories not skipped. */ for (i = 0; i < fc; i++) if (!XFA_SKIPPING(rpmfsGetAction(fs, i))) - dnli->active[rpmfiDIIndex(fi, i)] = 1; + dnli->active[rpmfilesDI(fi, i)] = 1; /* Exclude parent directories that are explicitly included. */ for (i = 0; i < fc; i++) { int dil; size_t dnlen, bnlen; - if (!S_ISDIR(rpmfiFModeIndex(fi, i))) + if (!S_ISDIR(rpmfilesFMode(fi, i))) continue; - dil = rpmfiDIIndex(fi, i); - dnlen = strlen(rpmfiDNIndex(fi, dil)); - bnlen = strlen(rpmfiBNIndex(fi, i)); + dil = rpmfilesDI(fi, i); + dnlen = strlen(rpmfilesDN(fi, dil)); + bnlen = strlen(rpmfilesBN(fi, i)); for (j = 0; j < dc; j++) { const char * dnl; @@ -340,13 +128,13 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse) if (!dnli->active[j] || j == dil) continue; - dnl = rpmfiDNIndex(fi, j); + dnl = rpmfilesDN(fi, j); jlen = strlen(dnl); if (jlen != (dnlen+bnlen+1)) continue; - if (!rstreqn(dnl, rpmfiDNIndex(fi, dil), dnlen)) + if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen)) continue; - if (!rstreqn(dnl+dnlen, rpmfiBNIndex(fi, i), bnlen)) + if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen)) continue; if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0') continue; @@ -366,7 +154,7 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse) rpmlog(RPMLOG_DEBUG, "========== Directories not explicitly included in package:\n"); } - rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfiDNIndex(fi, i)); + rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i)); } if (j) rpmlog(RPMLOG_DEBUG, "==========\n"); @@ -386,8 +174,8 @@ const char * dnlNextIterator(DNLI_t dnli) const char * dn = NULL; if (dnli) { - rpmfi fi = dnli->fi; - int dc = rpmfiDC(fi); + rpmfiles fi = dnli->fi; + int dc = rpmfilesDC(fi); int i = -1; if (dnli->active) @@ -396,7 +184,7 @@ const char * dnlNextIterator(DNLI_t dnli) } while (i >= 0 && i < dc && !dnli->active[i]); if (i >= 0 && i < dc) - dn = rpmfiDNIndex(fi, i); + dn = rpmfilesDN(fi, i); else i = -1; dnli->isave = i; @@ -404,251 +192,6 @@ const char * dnlNextIterator(DNLI_t dnli) return dn; } -/** - * Map next file path and action. - * @param fsm file state machine - * @param i file index - */ -static int fsmMapPath(FSM_t fsm, int i) -{ - rpmfi fi = fsmGetFi(fsm); /* XXX const except for fstates */ - int rc = 0; - - fsm->osuffix = NULL; - fsm->nsuffix = NULL; - fsm->action = FA_UNKNOWN; - - if (fi && i >= 0 && i < rpmfiFC(fi)) { - rpmfs fs = fsmGetFs(fsm); - /* XXX these should use rpmfiFFlags() etc */ - fsm->action = rpmfsGetAction(fs, i); - fsm->fflags = rpmfiFFlagsIndex(fi, i); - - /* src rpms have simple base name in payload. */ - fsm->dirName = rpmfiDNIndex(fi, rpmfiDIIndex(fi, i)); - fsm->baseName = rpmfiBNIndex(fi, i); - - /* Never create backup for %ghost files. */ - if (fsm->goal != FSM_PKGBUILD && !(fsm->fflags & RPMFILE_GHOST)) { - switch (fsm->action) { - case FA_ALTNAME: - fsm->nsuffix = SUFFIX_RPMNEW; - break; - case FA_SAVE: - fsm->osuffix = SUFFIX_RPMSAVE; - break; - case FA_BACKUP: - fsm->osuffix = (fsm->goal == FSM_PKGINSTALL) ? - SUFFIX_RPMORIG : SUFFIX_RPMSAVE; - break; - default: - break; - } - } - - if ((fsm->mapFlags & CPIO_MAP_PATH) || fsm->nsuffix) { - fsm->path = _free(fsm->path); - fsm->path = fsmFsPath(fsm, S_ISDIR(fsm->sb.st_mode), - (fsm->suffix ? fsm->suffix : fsm->nsuffix)); - } - } - return rc; -} - -/** \ingroup payload - * Save hard link in chain. - * @param fsm file state machine data - * @retval linkSet hard link set when complete - * @return Is chain only partially filled? - */ -static int saveHardLink(FSM_t fsm, hardLink_t * linkSet) -{ - struct stat * st = &fsm->sb; - int rc = 0; - int ix = -1; - int j; - hardLink_t *tailp, li; - - /* Find hard link set. */ - for (tailp = &fsm->links; (li = *tailp) != NULL; tailp = &li->next) { - if (li->sb.st_ino == st->st_ino && li->sb.st_dev == st->st_dev) - break; - } - - /* New hard link encountered, add new link to set. */ - if (li == NULL) { - li = xcalloc(1, sizeof(*li)); - li->next = NULL; - li->sb = *st; /* structure assignment */ - li->nlink = st->st_nlink; - li->linkIndex = fsm->ix; - li->createdPath = -1; - - li->filex = xcalloc(st->st_nlink, sizeof(li->filex[0])); - memset(li->filex, -1, (st->st_nlink * sizeof(li->filex[0]))); - li->nsuffix = xcalloc(st->st_nlink, sizeof(*li->nsuffix)); - - if (fsm->goal == FSM_PKGBUILD) - li->linksLeft = st->st_nlink; - if (fsm->goal == FSM_PKGINSTALL) - li->linksLeft = 0; - - *tailp = li; /* append to tail of linked list */ - } - - if (fsm->goal == FSM_PKGBUILD) --li->linksLeft; - li->filex[li->linksLeft] = fsm->ix; - li->nsuffix[li->linksLeft] = fsm->nsuffix; - if (fsm->goal == FSM_PKGINSTALL) li->linksLeft++; - - if (fsm->goal == FSM_PKGBUILD) - return (li->linksLeft > 0); - - if (fsm->goal != FSM_PKGINSTALL) - return 0; - - if (!(st->st_size || li->linksLeft == st->st_nlink)) - return 1; - - /* Here come the bits, time to choose a non-skipped file name. */ - { rpmfs fs = fsmGetFs(fsm); - - for (j = li->linksLeft - 1; j >= 0; j--) { - ix = li->filex[j]; - if (ix < 0 || XFA_SKIPPING(rpmfsGetAction(fs, ix))) - continue; - break; - } - } - - /* Are all links skipped or not encountered yet? */ - if (ix < 0 || j < 0) - return 1; /* XXX W2DO? */ - - /* Save the non-skipped file name and map index. */ - li->linkIndex = j; - if (linkSet) - *linkSet = li; - fsm->path = _free(fsm->path); - fsm->ix = ix; - rc = fsmMapPath(fsm, fsm->ix); - return rc; -} - -/** \ingroup payload - * Destroy set of hard links. - * @param li set of hard links - * @return NULL always - */ -static hardLink_t freeHardLink(hardLink_t li) -{ - if (li) { - li->nsuffix = _free(li->nsuffix); /* XXX elements are shared */ - li->filex = _free(li->filex); - _free(li); - } - return NULL; -} - -/* Check for hard links missing from payload */ -static int checkHardLinks(FSM_t fsm) -{ - int rc = 0; - rpmfs fs = fsmGetFs(fsm); - - for (hardLink_t li = fsm->links; li != NULL; li = li->next) { - if (li->linksLeft) { - for (nlink_t i = 0 ; i < li->linksLeft; i++) { - int ix = li->filex[i]; - if (ix < 0 || XFA_SKIPPING(rpmfsGetAction(fs, ix))) - continue; - rc = CPIOERR_MISSING_HARDLINK; - if (fsm->failedFile && *fsm->failedFile == NULL) { - if (!fsmMapPath(fsm, ix)) { - /* Out-of-sync hardlinks handled as sub-state */ - *fsm->failedFile = fsm->path; - fsm->path = NULL; - } - } - break; - } - } - } - return rc; -} - -static FSM_t fsmNew(fileStage goal, rpmfs fs, rpmfi fi, char ** failedFile) -{ - FSM_t fsm = xcalloc(1, sizeof(*fsm)); - - fsm->ix = -1; - fsm->goal = goal; - fsm->iter = mapInitIterator(fs, fi, (goal == FSM_PKGERASE)); - - /* common flags for all modes */ - fsm->mapFlags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID; - - if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) { - fsm->bufsize = 8 * BUFSIZ; - fsm->buf = xmalloc(fsm->bufsize); - } - - fsm->failedFile = failedFile; - if (fsm->failedFile) - *fsm->failedFile = NULL; - - return fsm; -} - -static FSM_t fsmFree(FSM_t fsm) -{ - hardLink_t li; - fsm->buf = _free(fsm->buf); - fsm->bufsize = 0; - - fsm->iter = mapFreeIterator(fsm->iter); - fsm->failedFile = NULL; - - fsm->path = _free(fsm->path); - fsm->suffix = _free(fsm->suffix); - - while ((li = fsm->links) != NULL) { - fsm->links = li->next; - li->next = NULL; - freeHardLink(li); - } - free(fsm); - return NULL; -} - -/* Find and set file security context */ -static int fsmSetSELabel(struct selabel_handle *sehandle, - const char *path, mode_t mode) -{ - int rc = 0; -#if WITH_SELINUX - if (sehandle) { - security_context_t scon = NULL; - - if (selabel_lookup_raw(sehandle, &scon, path, mode) == 0) { - rc = lsetfilecon(path, scon); - - if (_fsm_debug) { - rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", - __func__, path, scon, - (rc < 0 ? strerror(errno) : "")); - } - - if (rc < 0 && errno == EOPNOTSUPP) - rc = 0; - } - - freecon(scon); - } -#endif - return rc ? CPIOERR_LSETFCON_FAILED : 0; -} - static int fsmSetFCaps(const char *path, const char *captxt) { int rc = 0; @@ -656,7 +199,7 @@ static int fsmSetFCaps(const char *path, const char *captxt) if (captxt && *captxt != '\0') { cap_t fcaps = cap_from_text(captxt); if (fcaps == NULL || cap_set_file(path, fcaps) != 0) { - rc = CPIOERR_SETCAP_FAILED; + rc = RPMERR_SETCAP_FAILED; } cap_free(fcaps); } @@ -664,132 +207,102 @@ static int fsmSetFCaps(const char *path, const char *captxt) return rc; } -/** - * Map file stat(2) info. - * @param fsm file state machine - */ -static int fsmMapAttrs(FSM_t fsm) +static void wfd_close(FD_t *wfdp) { - struct stat * st = &fsm->sb; - rpmfi fi = fsmGetFi(fsm); - int i = fsm->ix; - - /* this check is pretty moot, rpmfi accessors check array bounds etc */ - if (fi && i >= 0 && i < rpmfiFC(fi)) { - ino_t finalInode = rpmfiFInodeIndex(fi, i); - mode_t finalMode = rpmfiFModeIndex(fi, i); - dev_t finalRdev = rpmfiFRdevIndex(fi, i); - time_t finalMtime = rpmfiFMtimeIndex(fi, i); - const char *user = rpmfiFUserIndex(fi, i); - const char *group = rpmfiFGroupIndex(fi, i); - uid_t uid = 0; - gid_t gid = 0; - - if (user && rpmugUid(user, &uid)) { - if (fsm->goal == FSM_PKGINSTALL) - rpmlog(RPMLOG_WARNING, - _("user %s does not exist - using root\n"), user); - finalMode &= ~S_ISUID; /* turn off suid bit */ + if (wfdp && *wfdp) { + int myerrno = errno; + static int oneshot = 0; + static int flush_io = 0; + if (!oneshot) { + flush_io = rpmExpandNumeric("%{?_flush_io}"); + oneshot = 1; } - - if (group && rpmugGid(group, &gid)) { - if (fsm->goal == FSM_PKGINSTALL) - rpmlog(RPMLOG_WARNING, - _("group %s does not exist - using root\n"), group); - finalMode &= ~S_ISGID; /* turn off sgid bit */ + if (flush_io) { + int fdno = Fileno(*wfdp); + fsync(fdno); } + Fclose(*wfdp); + *wfdp = NULL; + errno = myerrno; + } +} - if (fsm->mapFlags & CPIO_MAP_MODE) - st->st_mode = (st->st_mode & S_IFMT) | (finalMode & ~S_IFMT); - if (fsm->mapFlags & CPIO_MAP_TYPE) { - st->st_mode = (st->st_mode & ~S_IFMT) | (finalMode & S_IFMT); - if ((S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) - && st->st_nlink == 0) - st->st_nlink = 1; - st->st_ino = finalInode; - st->st_rdev = finalRdev; - st->st_mtime = finalMtime; - } - if (fsm->mapFlags & CPIO_MAP_UID) - st->st_uid = uid; - if (fsm->mapFlags & CPIO_MAP_GID) - st->st_gid = gid; +static int wfd_open(FD_t *wfdp, const char *dest) +{ + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ + { + mode_t old_umask = umask(0577); + *wfdp = Fopen(dest, "wx.ufdio"); + umask(old_umask); } + if (Ferror(*wfdp)) { + rc = RPMERR_OPEN_FAILED; + goto exit; + } + return 0; + +exit: + wfd_close(wfdp); + return rc; } /** \ingroup payload * Create file from payload stream. - * @param fsm file state machine data - * @param archive payload archive * @return 0 on success */ -static int expandRegular(FSM_t fsm, rpmpsm psm, rpmcpio_t archive, int nodigest) +static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest) { FD_t wfd = NULL; - const struct stat * st = &fsm->sb; - rpm_loff_t left = st->st_size; - const unsigned char * fidigest = NULL; - pgpHashAlgo digestalgo = 0; - int rc = 0; - - wfd = Fopen(fsm->path, "w.ufdio"); - if (Ferror(wfd)) { - rc = CPIOERR_OPEN_FAILED; - goto exit; - } - - if (!nodigest) { - rpmfi fi = fsmGetFi(fsm); - digestalgo = rpmfiDigestAlgo(fi); - fidigest = rpmfiFDigestIndex(fi, fsm->ix, NULL, NULL); - } - - if (st->st_size > 0 && fidigest) - fdInitDigest(wfd, digestalgo, 0); - - while (left) { - size_t len; - len = (left > fsm->bufsize ? fsm->bufsize : left); - if (rpmcpioRead(archive, fsm->buf, len) != len) { - rc = CPIOERR_READ_FAILED; - goto exit; - } - if ((Fwrite(fsm->buf, sizeof(*fsm->buf), len, wfd) != len) || Ferror(wfd)) { - rc = CPIOERR_WRITE_FAILED; - goto exit; - } + int rc; - left -= len; + rc = wfd_open(&wfd, dest); + if (rc != 0) + goto exit; - /* don't call this with fileSize == fileComplete */ - if (!rc && left) - rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmcpioTell(archive)); - } - - if (st->st_size > 0 && fidigest) { - void * digest = NULL; + rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm); + wfd_close(&wfd); +exit: + return rc; +} - (void) Fflush(wfd); - fdFiniDigest(wfd, digestalgo, &digest, NULL, 0); +static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files, + rpmpsm psm, int nodigest, int *setmeta, + int * firsthardlink, FD_t *firstlinkfile) +{ + int rc = 0; + int numHardlinks = rpmfiFNlink(fi); - if (digest != NULL && fidigest != NULL) { - size_t diglen = rpmDigestLength(digestalgo); - if (memcmp(digest, fidigest, diglen)) { - rc = CPIOERR_DIGEST_MISMATCH; - } + if (numHardlinks > 1) { + /* Create first hardlinked file empty */ + if (*firsthardlink < 0) { + *firsthardlink = rpmfiFX(fi); + rc = wfd_open(firstlinkfile, dest); } else { - rc = CPIOERR_DIGEST_MISMATCH; + /* Create hard links for others */ + char *fn = rpmfilesFN(files, *firsthardlink); + rc = link(fn, dest); + if (rc < 0) { + rc = RPMERR_LINK_FAILED; + } + free(fn); } - free(digest); } - -exit: - if (wfd) { - int myerrno = errno; - Fclose(wfd); - errno = myerrno; + /* Write normal files or fill the last hardlinked (already + existing) file with content */ + if (numHardlinks<=1) { + if (!rc) + rc = expandRegular(fi, dest, psm, nodigest); + } else if (rpmfiArchiveHasContent(fi)) { + if (!rc) + rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); + wfd_close(firstlinkfile); + *firsthardlink = -1; + } else { + *setmeta = 0; } + return rc; } @@ -797,7 +310,7 @@ static int fsmReadLink(const char *path, char *buf, size_t bufsize, size_t *linklen) { ssize_t llen = readlink(path, buf, bufsize - 1); - int rc = CPIOERR_READLINK_FAILED; + int rc = RPMERR_READLINK_FAILED; if (_fsm_debug) { rpmlog(RPMLOG_DEBUG, " %8s (%s, buf, %d) %s\n", @@ -813,168 +326,6 @@ static int fsmReadLink(const char *path, return rc; } -/** \ingroup payload - * Write next item to payload stream. - * @param fsm file state machine data - * @param writeData should data be written? - * @param archive payload archive - * @param ix file index - * @return 0 on success - */ -static int writeFile(FSM_t fsm, int writeData, rpmcpio_t archive, int ix) -{ - FD_t rfd = NULL; - char * path = fsm->path; - struct stat * st = &fsm->sb; - struct stat * ost = &fsm->osb; - char * symbuf = NULL; - rpm_loff_t left; - int rc = 0; - - st->st_size = (writeData ? ost->st_size : 0); - - if (S_ISDIR(st->st_mode)) { - st->st_size = 0; - } else if (S_ISLNK(st->st_mode)) { - /* - * While linux puts the size of a symlink in the st_size field, - * I don't think that's a specified standard. - */ - size_t linklen; - rc = fsmReadLink(fsm->path, fsm->buf, fsm->bufsize, &linklen); - if (rc) goto exit; - st->st_size = linklen; - rstrcat(&symbuf, fsm->buf); /* XXX save readlink return. */ - } - - if (fsm->mapFlags & CPIO_MAP_ABSOLUTE) { - fsm->path = rstrscat(NULL, (fsm->mapFlags & CPIO_MAP_ADDDOT) ? "." : "", - fsm->dirName, fsm->baseName, NULL); - } else if (fsm->mapFlags & CPIO_MAP_PATH) { - rpmfi fi = fsmGetFi(fsm); - fsm->path = xstrdup((fi->apath ? fi->apath[ix] : - rpmfiBNIndex(fi, ix))); - } - - rc = rpmcpioHeaderWrite(archive, fsm->path, st); - _free(fsm->path); - fsm->path = path; - - if (rc) goto exit; - - - if (writeData && S_ISREG(st->st_mode)) { - size_t len; - - rfd = Fopen(fsm->path, "r.ufdio"); - if (Ferror(rfd)) { - rc = CPIOERR_OPEN_FAILED; - goto exit; - } - - left = st->st_size; - - while (left) { - len = (left > fsm->bufsize ? fsm->bufsize : left); - if (Fread(fsm->buf, sizeof(*fsm->buf), len, rfd) != len || Ferror(rfd)) { - rc = CPIOERR_READ_FAILED; - goto exit; - } - - if (rpmcpioWrite(archive, fsm->buf, len) != len) { - rc = CPIOERR_WRITE_FAILED; - goto exit; - } - left -= len; - } - } else if (writeData && S_ISLNK(st->st_mode)) { - size_t len = strlen(symbuf); - if (rpmcpioWrite(archive, symbuf, len) != len) { - rc = CPIOERR_WRITE_FAILED; - goto exit; - } - } - -exit: - if (rfd) { - /* preserve any prior errno across close */ - int myerrno = errno; - Fclose(rfd); - errno = myerrno; - } - fsm->path = path; - free(symbuf); - return rc; -} - -/** \ingroup payload - * Write set of linked files to payload stream. - * @param fsm file state machine data - * @param archive payload archive - * @param li link to write - * @return 0 on success - */ -static int writeLinkedFile(FSM_t fsm, rpmcpio_t archive, hardLink_t li) -{ - char * path = fsm->path; - const char * nsuffix = fsm->nsuffix; - int ec = 0; - int rc; - int i; - - fsm->path = NULL; - fsm->nsuffix = NULL; - - for (i = li->nlink - 1; i >= 0; i--) { - - if (li->filex[i] < 0) continue; - - rc = fsmMapPath(fsm, li->filex[i]); - - /* Write data after last link. */ - rc = writeFile(fsm, (i == 0), archive, li->filex[i]); - if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) { - ec = rc; - *fsm->failedFile = xstrdup(fsm->path); - } - - fsm->path = _free(fsm->path); - li->filex[i] = -1; - } - - fsm->nsuffix = nsuffix; - fsm->path = path; - return ec; -} - -static int writeLinks(FSM_t fsm, rpmcpio_t archive) -{ - int j, rc = 0; - nlink_t i, nlink; - - for (hardLink_t li = fsm->links; li; li = li->next) { - /* Re-calculate link count for archive header. */ - for (j = -1, nlink = 0, i = 0; i < li->nlink; i++) { - if (li->filex[i] < 0) - continue; - nlink++; - if (j == -1) j = i; - } - /* XXX force the contents out as well. */ - if (j != 0) { - li->filex[0] = li->filex[j]; - li->filex[j] = -1; - } - li->sb.st_nlink = nlink; - - fsm->sb = li->sb; /* structure assignment */ - fsm->osb = fsm->sb; /* structure assignment */ - - if (!rc) rc = writeLinkedFile(fsm, archive, li); - } - return rc; -} - static int fsmStat(const char *path, int dolstat, struct stat *sb) { int rc; @@ -988,105 +339,10 @@ static int fsmStat(const char *path, int dolstat, struct stat *sb) __func__, path, (rc < 0 ? strerror(errno) : "")); if (rc < 0) { - rc = (errno == ENOENT ? CPIOERR_ENOENT : CPIOERR_LSTAT_FAILED); - /* WTH is this, and is it really needed, still? */ - memset(sb, 0, sizeof(*sb)); /* XXX s390x hackery */ - } - return rc; -} - -static int fsmVerify(FSM_t fsm); - -/** \ingroup payload - * Create pending hard links to existing file. - * @param fsm file state machine data - * @param li hard link - * @return 0 on success - */ -static int fsmMakeLinks(FSM_t fsm, hardLink_t li) -{ - char * path = fsm->path; - char * opath = NULL; - const char * nsuffix = fsm->nsuffix; - int ec = 0; - int rc; - int i; - - fsm->path = NULL; - fsm->nsuffix = NULL; - - rc = fsmMapPath(fsm, li->filex[li->createdPath]); - opath = fsm->path; - fsm->path = NULL; - for (i = 0; i < li->nlink; i++) { - if (li->filex[i] < 0) continue; - if (li->createdPath == i) continue; - - fsm->path = _free(fsm->path); - rc = fsmMapPath(fsm, li->filex[i]); - if (XFA_SKIPPING(fsm->action)) continue; - - rc = fsmVerify(fsm); - if (!rc) continue; - if (!(rc == CPIOERR_ENOENT)) break; - - /* XXX link(opath, fsm->path) */ - rc = link(opath, fsm->path); - if (_fsm_debug) - rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, - opath, fsm->path, (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_LINK_FAILED; - - if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) { - ec = rc; - *fsm->failedFile = xstrdup(fsm->path); - } - - li->linksLeft--; + rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_LSTAT_FAILED); + /* Ensure consistent struct content on failure */ + memset(sb, 0, sizeof(*sb)); } - fsm->path = _free(fsm->path); - free(opath); - - fsm->nsuffix = nsuffix; - fsm->path = path; - return ec; -} - -static int fsmCommit(FSM_t fsm, int ix); - -/** \ingroup payload - * Commit hard linked file set atomically. - * @param fsm file state machine data - * @return 0 on success - */ -static int fsmCommitLinks(FSM_t fsm) -{ - char * path = fsm->path; - const char * nsuffix = fsm->nsuffix; - struct stat * st = &fsm->sb; - int rc = 0; - nlink_t i; - hardLink_t li; - - fsm->path = NULL; - fsm->nsuffix = NULL; - - for (li = fsm->links; li != NULL; li = li->next) { - if (li->sb.st_ino == st->st_ino && li->sb.st_dev == st->st_dev) - break; - } - - for (i = 0; i < li->nlink; i++) { - if (li->filex[i] < 0) continue; - rc = fsmMapPath(fsm, li->filex[i]); - if (!XFA_SKIPPING(fsm->action)) - rc = fsmCommit(fsm, li->filex[i]); - fsm->path = _free(fsm->path); - li->filex[i] = -1; - } - - fsm->nsuffix = nsuffix; - fsm->path = path; return rc; } @@ -1098,9 +354,9 @@ static int fsmRmdir(const char *path) path, (rc < 0 ? strerror(errno) : "")); if (rc < 0) switch (errno) { - case ENOENT: rc = CPIOERR_ENOENT; break; - case ENOTEMPTY: rc = CPIOERR_ENOTEMPTY; break; - default: rc = CPIOERR_RMDIR_FAILED; break; + case ENOENT: rc = RPMERR_ENOENT; break; + case ENOTEMPTY: rc = RPMERR_ENOTEMPTY; break; + default: rc = RPMERR_RMDIR_FAILED; break; } return rc; } @@ -1112,7 +368,7 @@ static int fsmMkdir(const char *path, mode_t mode) rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__, path, (unsigned)(mode & 07777), (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_MKDIR_FAILED; + if (rc < 0) rc = RPMERR_MKDIR_FAILED; return rc; } @@ -1127,7 +383,7 @@ static int fsmMkfifo(const char *path, mode_t mode) } if (rc < 0) - rc = CPIOERR_MKFIFO_FAILED; + rc = RPMERR_MKFIFO_FAILED; return rc; } @@ -1144,24 +400,24 @@ static int fsmMknod(const char *path, mode_t mode, dev_t dev) } if (rc < 0) - rc = CPIOERR_MKNOD_FAILED; + rc = RPMERR_MKNOD_FAILED; return rc; } /** * Create (if necessary) directories not explicitly included in package. - * @param dnli file state machine data - * @param sehandle selinux label handle (bah) + * @param files file data + * @param fs file states * @param plugins rpm plugins handle * @return 0 on success */ -static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle, rpmPlugins plugins) +static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins) { - DNLI_t dnli = dnlInitIterator(fi, fs, 0); + DNLI_t dnli = dnlInitIterator(files, fs, 0); struct stat sb; const char *dpath; - int dc = rpmfiDC(fi); + int dc = rpmfilesDC(files); int rc = 0; int i; int ldnlen = 0; @@ -1213,21 +469,35 @@ static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle, rpmPlu if (rc == 0 && S_ISDIR(sb.st_mode)) { /* Move pre-existing path marker forward. */ dnlx[dc] = (te - dn); - } else if (rc == CPIOERR_ENOENT) { + } else if (rc == RPMERR_ENOENT) { *te = '\0'; mode_t mode = S_IFDIR | (_dirPerms & 07777); - rc = fsmMkdir(dn, mode); + rpmFsmOp op = (FA_CREATE|FAF_UNOWNED); + + /* Run fsm file pre hook for all plugins */ + rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op); + + if (!rc) + rc = fsmMkdir(dn, mode); + if (!rc) { - rc = fsmSetSELabel(sehandle, dn, mode); + rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn, + mode, op); + } + /* Run fsm file post hook for all plugins */ + rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc); + + if (!rc) { rpmlog(RPMLOG_DEBUG, "%s directory created with perms %04o\n", dn, (unsigned)(mode & 07777)); } + if (!rc) { - /* Run file closed hook for all plugins */ - rc = rpmpluginsCallFsmCommit(plugins, dn, mode, DIR_TYPE_UNOWNED); - } + /* Run file closed hook for all plugins */ + rc = rpmpluginsCallFsmCommit(plugins, dn, mode, DIR_TYPE_UNOWNED); + } *te = '/'; } if (rc) @@ -1267,72 +537,14 @@ static void removeSBITS(const char *path) } } -/********************************************************************/ - -static void fsmReset(FSM_t fsm) -{ - fsm->path = _free(fsm->path); - fsm->postpone = 0; - fsm->diskchecked = fsm->exists = 0; - fsm->action = FA_UNKNOWN; - fsm->osuffix = NULL; - fsm->nsuffix = NULL; - memset(&(fsm->sb), 0, sizeof(fsm->sb)); - memset(&(fsm->osb), 0, sizeof(fsm->sb)); -} - -static int fsmInit(FSM_t fsm) +static void fsmDebug(const char *fpath, rpmFileAction action, + const struct stat *st) { - int rc = 0; - - /* On non-install, mode must be known so that dirs don't get suffix. */ - if (fsm->goal != FSM_PKGINSTALL) { - rpmfi fi = fsmGetFi(fsm); - fsm->sb.st_mode = rpmfiFModeIndex(fi, fsm->ix); - } - - /* Generate file path. */ - rc = fsmMapPath(fsm, fsm->ix); - if (rc) return rc; - - /* Perform lstat/stat for disk file. */ - if (fsm->path != NULL && - !(fsm->goal == FSM_PKGINSTALL && S_ISREG(fsm->sb.st_mode))) - { - int dolstat = !(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS); - rc = fsmStat(fsm->path, dolstat, &fsm->osb); - if (rc == CPIOERR_ENOENT) { - // errno = saveerrno; XXX temporary commented out - rc = 0; - fsm->exists = 0; - } else if (rc == 0) { - fsm->exists = 1; - } - } else { - /* Skip %ghost files on build. */ - fsm->exists = 0; - } - fsm->diskchecked = 1; - if (rc) return rc; - - /* On non-install, the disk file stat is what's remapped. */ - if (fsm->goal != FSM_PKGINSTALL) - fsm->sb = fsm->osb; /* structure assignment */ - - /* Remap file perms, owner, and group. */ - rc = fsmMapAttrs(fsm); - if (rc) return rc; - - fsm->postpone = XFA_SKIPPING(fsm->action); - rpmlog(RPMLOG_DEBUG, "%-10s %06o%3d (%4d,%4d)%6d %s\n", - fileActionString(fsm->action), (int)fsm->sb.st_mode, - (int)fsm->sb.st_nlink, (int)fsm->sb.st_uid, - (int)fsm->sb.st_gid, (int)fsm->sb.st_size, - (fsm->path ? fsm->path : "")); - - return rc; - + fileActionString(action), (int)st->st_mode, + (int)st->st_nlink, (int)st->st_uid, + (int)st->st_gid, (int)st->st_size, + (fpath ? fpath : "")); } static int fsmSymlink(const char *opath, const char *path) @@ -1345,29 +557,26 @@ static int fsmSymlink(const char *opath, const char *path) } if (rc < 0) - rc = CPIOERR_SYMLINK_FAILED; + rc = RPMERR_SYMLINK_FAILED; return rc; } -static int fsmUnlink(const char *path, cpioMapFlags mapFlags) +static int fsmUnlink(const char *path) { int rc = 0; - if (mapFlags & CPIO_SBIT_CHECK) - removeSBITS(path); + removeSBITS(path); rc = unlink(path); if (_fsm_debug) rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__, path, (rc < 0 ? strerror(errno) : "")); if (rc < 0) - rc = (errno == ENOENT ? CPIOERR_ENOENT : CPIOERR_UNLINK_FAILED); + rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_UNLINK_FAILED); return rc; } -static int fsmRename(const char *opath, const char *path, - cpioMapFlags mapFlags) +static int fsmRename(const char *opath, const char *path) { - if (mapFlags & CPIO_SBIT_CHECK) - removeSBITS(path); + removeSBITS(path); int rc = rename(opath, path); #if defined(ETXTBSY) && defined(__HPUX__) /* XXX HP-UX (and other os'es) don't permit rename to busy files. */ @@ -1382,30 +591,19 @@ static int fsmRename(const char *opath, const char *path, if (_fsm_debug) rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, opath, path, (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_RENAME_FAILED; + if (rc < 0) + rc = (errno == EISDIR ? RPMERR_EXIST_AS_DIR : RPMERR_RENAME_FAILED); return rc; } - -static int fsmChown(const char *path, uid_t uid, gid_t gid) +static int fsmRemove(const char *path, mode_t mode) { - int rc = chown(path, uid, gid); - if (rc < 0) { - struct stat st; - if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid) - rc = 0; - } - if (_fsm_debug) - rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__, - path, (int)uid, (int)gid, - (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_CHOWN_FAILED; - return rc; + return S_ISDIR(mode) ? fsmRmdir(path) : fsmUnlink(path); } -static int fsmLChown(const char *path, uid_t uid, gid_t gid) +static int fsmChown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - int rc = lchown(path, uid, gid); + int rc = S_ISLNK(mode) ? lchown(path, uid, gid) : chown(path, uid, gid); if (rc < 0) { struct stat st; if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid) @@ -1415,7 +613,7 @@ static int fsmLChown(const char *path, uid_t uid, gid_t gid) rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__, path, (int)uid, (int)gid, (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_CHOWN_FAILED; + if (rc < 0) rc = RPMERR_CHOWN_FAILED; return rc; } @@ -1431,76 +629,90 @@ static int fsmChmod(const char *path, mode_t mode) rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__, path, (unsigned)(mode & 07777), (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_CHMOD_FAILED; + if (rc < 0) rc = RPMERR_CHMOD_FAILED; return rc; } -static int fsmUtime(const char *path, time_t mtime) +static int fsmUtime(const char *path, mode_t mode, time_t mtime) { int rc = 0; - struct utimbuf stamp; - stamp.actime = mtime; - stamp.modtime = mtime; - rc = utime(path, &stamp); + struct timeval stamps[2] = { + { .tv_sec = mtime, .tv_usec = 0 }, + { .tv_sec = mtime, .tv_usec = 0 }, + }; + +#if HAVE_LUTIMES + rc = lutimes(path, stamps); +#else + if (!S_ISLNK(mode)) + rc = utimes(path, stamps); +#endif + if (_fsm_debug) rpmlog(RPMLOG_DEBUG, " %8s (%s, 0x%x) %s\n", __func__, path, (unsigned)mtime, (rc < 0 ? strerror(errno) : "")); - if (rc < 0) rc = CPIOERR_UTIME_FAILED; + if (rc < 0) rc = RPMERR_UTIME_FAILED; + /* ...but utime error is not critical for directories */ + if (rc && S_ISDIR(mode)) + rc = 0; return rc; } -static int fsmVerify(FSM_t fsm) +static int fsmVerify(const char *path, rpmfi fi, const struct stat *fsb) { int rc; - struct stat * st = &fsm->sb; - struct stat * ost = &fsm->osb; int saveerrno = errno; + struct stat dsb; + mode_t mode = rpmfiFMode(fi); - if (fsm->diskchecked && !fsm->exists) { - return CPIOERR_ENOENT; - } - if (S_ISREG(st->st_mode)) { + rc = fsmStat(path, 1, &dsb); + if (rc) + return rc; + + if (S_ISREG(mode)) { /* HP-UX (and other os'es) don't permit unlink on busy files. */ - char *rmpath = rstrscat(NULL, fsm->path, "-RPMDELETE", NULL); - rc = fsmRename(fsm->path, rmpath, fsm->mapFlags); + char *rmpath = rstrscat(NULL, path, "-RPMDELETE", NULL); + rc = fsmRename(path, rmpath); /* XXX shouldn't we take unlink return code here? */ if (!rc) - (void) fsmUnlink(rmpath, fsm->mapFlags); + (void) fsmUnlink(rmpath); else - rc = CPIOERR_UNLINK_FAILED; + rc = RPMERR_UNLINK_FAILED; free(rmpath); - return (rc ? rc : CPIOERR_ENOENT); /* XXX HACK */ - } else if (S_ISDIR(st->st_mode)) { - if (S_ISDIR(ost->st_mode)) return 0; - if (S_ISLNK(ost->st_mode)) { - rc = fsmStat(fsm->path, 0, &fsm->osb); - if (rc == CPIOERR_ENOENT) rc = 0; + return (rc ? rc : RPMERR_ENOENT); /* XXX HACK */ + } else if (S_ISDIR(mode)) { + if (S_ISDIR(dsb.st_mode)) return 0; + if (S_ISLNK(dsb.st_mode)) { + uid_t luid = dsb.st_uid; + rc = fsmStat(path, 0, &dsb); + if (rc == RPMERR_ENOENT) rc = 0; if (rc) return rc; errno = saveerrno; - if (S_ISDIR(ost->st_mode)) return 0; + /* Only permit directory symlinks by target owner and root */ + if (S_ISDIR(dsb.st_mode) && (luid == 0 || luid == fsb->st_uid)) + return 0; } - } else if (S_ISLNK(st->st_mode)) { - if (S_ISLNK(ost->st_mode)) { + } else if (S_ISLNK(mode)) { + if (S_ISLNK(dsb.st_mode)) { char buf[8 * BUFSIZ]; size_t len; - rc = fsmReadLink(fsm->path, buf, 8 * BUFSIZ, &len); + rc = fsmReadLink(path, buf, 8 * BUFSIZ, &len); errno = saveerrno; if (rc) return rc; - /* FSM_PROCESS puts link target to fsm->buf. */ - if (rstreq(fsm->buf, buf)) return 0; + if (rstreq(rpmfiFLink(fi), buf)) return 0; } - } else if (S_ISFIFO(st->st_mode)) { - if (S_ISFIFO(ost->st_mode)) return 0; - } else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { - if ((S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) && - (ost->st_rdev == st->st_rdev)) return 0; - } else if (S_ISSOCK(st->st_mode)) { - if (S_ISSOCK(ost->st_mode)) return 0; + } else if (S_ISFIFO(mode)) { + if (S_ISFIFO(dsb.st_mode)) return 0; + } else if (S_ISCHR(mode) || S_ISBLK(mode)) { + if ((S_ISCHR(dsb.st_mode) || S_ISBLK(dsb.st_mode)) && + (dsb.st_rdev == rpmfiFRdev(fi))) return 0; + } else if (S_ISSOCK(mode)) { + if (S_ISSOCK(dsb.st_mode)) return 0; } /* XXX shouldn't do this with commit/undo. */ - rc = fsmUnlink(fsm->path, fsm->mapFlags); - if (rc == 0) rc = CPIOERR_ENOENT; - return (rc ? rc : CPIOERR_ENOENT); /* XXX HACK */ + rc = fsmUnlink(path); + if (rc == 0) rc = RPMERR_ENOENT; + return (rc ? rc : RPMERR_ENOENT); /* XXX HACK */ } #define IS_DEV_LOG(_x) \ @@ -1512,88 +724,101 @@ static int fsmVerify(FSM_t fsm) /* Rename pre-existing modified or unmanaged file. */ -static int fsmBackup(FSM_t fsm) +static int fsmBackup(rpmfi fi, rpmFileAction action) { int rc = 0; + const char *suffix = NULL; - /* FIXME: %ghost can have backup action but no suffix */ - if ((fsm->action == FA_SAVE || fsm->action == FA_BACKUP) && fsm->osuffix) { - char * opath = fsmFsPath(fsm, S_ISDIR(fsm->sb.st_mode), NULL); - char * path = fsmFsPath(fsm, 0, fsm->osuffix); - rc = fsmRename(opath, path, fsm->mapFlags); - if (!rc) { - rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path); - fsm->exists = 0; /* it doesn't exist anymore... */ - } - free(path); - free(opath); + if (!(rpmfiFFlags(fi) & RPMFILE_GHOST)) { + switch (action) { + case FA_SAVE: + suffix = SUFFIX_RPMSAVE; + break; + case FA_BACKUP: + suffix = SUFFIX_RPMORIG; + break; + default: + break; + } + } + + if (suffix) { + char * opath = fsmFsPath(fi, NULL); + char * path = fsmFsPath(fi, suffix); + rc = fsmRename(opath, path); + if (!rc) { + rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path); + } + free(path); + free(opath); } return rc; } -static int fsmCommit(FSM_t fsm, int ix) +static int fsmSetmeta(const char *path, rpmfi fi, rpmPlugins plugins, + rpmFileAction action, const struct stat * st, + int nofcaps) +{ + int rc = 0; + const char *dest = rpmfiFN(fi); + + if (!rc && !getuid()) { + rc = fsmChown(path, st->st_mode, st->st_uid, st->st_gid); + } + if (!rc && !S_ISLNK(st->st_mode)) { + rc = fsmChmod(path, st->st_mode); + } + /* Set file capabilities (if enabled) */ + if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) { + rc = fsmSetFCaps(path, rpmfiFCaps(fi)); + } + if (!rc) { + rc = fsmUtime(path, st->st_mode, rpmfiFMtime(fi)); + } + if (!rc) { + rc = rpmpluginsCallFsmFilePrepare(plugins, fi, + path, dest, st->st_mode, action); + } + + return rc; +} + +static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *suffix, rpmPlugins plugins) { int rc = 0; - struct stat * st = &fsm->sb; /* XXX Special case /dev/log, which shouldn't be packaged anyways */ - if (!S_ISSOCK(st->st_mode) && !IS_DEV_LOG(fsm->path)) { - /* Backup on-disk file if needed. Directories are handled earlier */ - if (!S_ISDIR(st->st_mode)) - rc = fsmBackup(fsm); - /* Rename temporary to final file name. */ - if (!S_ISDIR(st->st_mode) && (fsm->suffix || fsm->nsuffix)) { - char *npath = fsmFsPath(fsm, 0, fsm->nsuffix); - rc = fsmRename(fsm->path, npath, fsm->mapFlags); - if (!rc && fsm->nsuffix) { - char * opath = fsmFsPath(fsm, 0, NULL); - rpmlog(RPMLOG_WARNING, _("%s created as %s\n"), - opath, npath); - free(opath); - } - free(fsm->path); - fsm->path = npath; - } - /* Set file security context (if enabled) */ - if (!rc && !getuid()) { - rc = fsmSetSELabel(fsm->sehandle, fsm->path, fsm->sb.st_mode); - } + if (!(S_ISSOCK(rpmfiFMode(fi)) && IS_DEV_LOG(*path))) { + const char *nsuffix = (action == FA_ALTNAME) ? SUFFIX_RPMNEW : NULL; + char *dest = *path; + /* Construct final destination path (nsuffix is usually NULL) */ + if (suffix) + dest = fsmFsPath(fi, nsuffix); + + /* Rename temporary to final file name if needed. */ + if (dest != *path) { + rc = fsmRename(*path, dest); + if (!rc && nsuffix) { + char * opath = fsmFsPath(fi, NULL); + rpmlog(RPMLOG_WARNING, _("%s created as %s\n"), + opath, dest); + free(opath); + } + free(*path); + *path = dest; + } /* Call fsm commit hook for all plugins */ if (!rc) { - rc = rpmpluginsCallFsmCommit(fsm->plugins, fsm->path, fsm->sb.st_mode, DIR_TYPE_NORMAL); - } - if (S_ISLNK(st->st_mode)) { - if (!rc && !getuid()) - rc = fsmLChown(fsm->path, fsm->sb.st_uid, fsm->sb.st_gid); - } else { - rpmfi fi = fsmGetFi(fsm); - if (!rc && !getuid()) - rc = fsmChown(fsm->path, fsm->sb.st_uid, fsm->sb.st_gid); - if (!rc) - rc = fsmChmod(fsm->path, fsm->sb.st_mode); - if (!rc) { - rc = fsmUtime(fsm->path, rpmfiFMtimeIndex(fi, ix)); - /* utime error is not critical for directories */ - if (rc && S_ISDIR(st->st_mode)) - rc = 0; - } - /* Set file capabilities (if enabled) */ - if (!rc && !S_ISDIR(st->st_mode) && !getuid()) { - rc = fsmSetFCaps(fsm->path, rpmfiFCapsIndex(fi, ix)); - } + rc = rpmpluginsCallFsmCommit(plugins, *path, rpmfiFMode(fi), DIR_TYPE_NORMAL); } } - if (rc && fsm->failedFile && *fsm->failedFile == NULL) { - *fsm->failedFile = fsm->path; - fsm->path = NULL; - } return rc; } /** * Return formatted string representation of file disposition. - * @param a file dispostion + * @param a file disposition * @return formatted string */ static const char * fileActionString(rpmFileAction a) @@ -1601,8 +826,6 @@ static const char * fileActionString(rpmFileAction a) switch (a) { case FA_UNKNOWN: return "unknown"; case FA_CREATE: return "create"; - case FA_COPYOUT: return "copyout"; - case FA_COPYIN: return "copyin"; case FA_BACKUP: return "backup"; case FA_SAVE: return "save"; case FA_SKIP: return "skip"; @@ -1611,14 +834,15 @@ static const char * fileActionString(rpmFileAction a) case FA_SKIPNSTATE: return "skipnstate"; case FA_SKIPNETSHARED: return "skipnetshared"; case FA_SKIPCOLOR: return "skipcolor"; + case FA_TOUCH: return "touch"; default: return "???"; } } /* Remember any non-regular file state for recording in the rpmdb */ -static void setFileState(rpmfs fs, int i, rpmFileAction action) +static void setFileState(rpmfs fs, int i) { - switch (action) { + switch (rpmfsGetAction(fs, i)) { case FA_SKIPNSTATE: rpmfsSetState(fs, i, RPMFILE_STATE_NOTINSTALLED); break; @@ -1628,219 +852,223 @@ static void setFileState(rpmfs fs, int i, rpmFileAction action) case FA_SKIPCOLOR: rpmfsSetState(fs, i, RPMFILE_STATE_WRONGCOLOR); break; + case FA_TOUCH: + rpmfsSetState(fs, i, RPMFILE_STATE_NORMAL); + break; default: break; } } -int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfi fi, FD_t cfd, +int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, rpmpsm psm, char ** failedFile) { + FD_t payload = rpmtePayload(te); + rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); rpmfs fs = rpmteGetFileStates(te); - FSM_t fsm = fsmNew(FSM_PKGINSTALL, fs, fi, failedFile); - rpmcpio_t archive = rpmcpioOpen(cfd, O_RDONLY); - struct stat * st = &fsm->sb; + rpmPlugins plugins = rpmtsPlugins(ts); + struct stat sb; int saveerrno = errno; int rc = 0; - int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST); - - if (!rpmteIsSource(te)) - fsm->mapFlags |= CPIO_SBIT_CHECK; - - if (archive == NULL) - rc = CPIOERR_INTERNAL; + int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0; + int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0; + int firsthardlink = -1; + FD_t firstlinkfile = NULL; + int skip; + rpmFileAction action; + char *tid = NULL; + const char *suffix; + char *fpath = NULL; + + if (fi == NULL) { + rc = RPMERR_BAD_MAGIC; + goto exit; + } - fsm->sehandle = rpmtsSELabelHandle(ts); - fsm->plugins = rpmtsPlugins(ts); - /* transaction id used for temporary path suffix while installing */ - rasprintf(&fsm->suffix, ";%08x", (unsigned)rpmtsGetTid(ts)); + rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts)); /* Detect and create directories not explicitly in package. */ - if (!rc) { - rc = fsmMkdirs(fi, rpmteGetFileStates(te), fsm->sehandle, fsm->plugins); - } + rc = fsmMkdirs(files, fs, plugins); while (!rc) { - hardLink_t li = NULL; - - /* Clean fsm, free'ing memory. */ - fsmReset(fsm); - /* Read next payload header. */ - rc = rpmcpioHeaderRead(archive, &(fsm->path), &(fsm->sb)); + rc = rpmfiNext(fi); - /* Detect and exit on end-of-payload. */ - if (rc == CPIOERR_HDR_TRAILER) { - rc = 0; + if (rc < 0) { + if (rc == RPMERR_ITER_END) + rc = 0; break; } - if (rc) break; - - /* Identify mapping index. */ - fsm->ix = mapFind(fsm->iter, fsm->path); - - /* Mapping error */ - if (fsm->ix < 0) { - if (fsm->failedFile && *fsm->failedFile == NULL) - *fsm->failedFile = xstrdup(fsm->path); - rc = CPIOERR_UNMAPPED_FILE; - break; + action = rpmfsGetAction(fs, rpmfiFX(fi)); + skip = XFA_SKIPPING(action); + suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; + if (action != FA_TOUCH) { + fpath = fsmFsPath(fi, suffix); + } else { + fpath = fsmFsPath(fi, ""); } - rc = fsmInit(fsm); + /* Remap file perms, owner, and group. */ + rc = rpmfiStat(fi, 1, &sb); - /* Exit on error. */ - if (rc) { - fsm->postpone = 1; - break; - } + fsmDebug(fpath, action, &sb); - /* Run fsm init hook for all plugins */ - rc = rpmpluginsCallFsmInit(fsm->plugins, fsm->path, fsm->sb.st_mode); - /* Exit on error. */ - if (rc) { - fsm->postpone = 1; + if (rc) break; - } - - if (S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1) - fsm->postpone = saveHardLink(fsm, &li); - - setFileState(rpmteGetFileStates(te), fsm->ix, fsm->action); - - if (!fsm->postpone) { - if (S_ISREG(st->st_mode)) { - rc = fsmVerify(fsm); - if (!(rc == CPIOERR_ENOENT)) return rc; - rc = expandRegular(fsm, psm, archive, nodigest); - } else if (S_ISDIR(st->st_mode)) { - /* Directories replacing something need early backup */ - rc = fsmBackup(fsm); - rc = fsmVerify(fsm); - if (rc == CPIOERR_ENOENT) { - mode_t mode = st->st_mode; + + /* Run fsm init hook for all plugins */ + //FIXME. functions related with msm. + rc = rpmpluginsCallFsmInit(plugins, fpath, sb.st_mode); + + /* Run fsm file pre hook for all plugins */ + rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, + sb.st_mode, action); + if (rc) { + skip = 1; + } else { + setFileState(fs, rpmfiFX(fi)); + } + + if (!skip) { + int setmeta = 1; + + /* Directories replacing something need early backup */ + if (!suffix) { + rc = fsmBackup(fi, action); + } + /* Assume file does't exist when tmp suffix is in use */ + if (!suffix) { + rc = fsmVerify(fpath, fi, &sb); + } else { + rc = (action == FA_TOUCH) ? 0 : RPMERR_ENOENT; + } + + if (S_ISREG(sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmMkfile(fi, fpath, files, psm, nodigest, + &setmeta, &firsthardlink, &firstlinkfile); + } + } else if (S_ISDIR(sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + mode_t mode = sb.st_mode; mode &= ~07777; mode |= 00700; - rc = fsmMkdir(fsm->path, mode); + rc = fsmMkdir(fpath, mode); } - } else if (S_ISLNK(st->st_mode)) { - if ((st->st_size + 1) > fsm->bufsize) { - rc = CPIOERR_HDR_SIZE; - } else if (rpmcpioRead(archive, fsm->buf, st->st_size) != st->st_size) { - rc = CPIOERR_READ_FAILED; - } else { - - fsm->buf[st->st_size] = '\0'; - /* fsmVerify() assumes link target in fsm->buf */ - rc = fsmVerify(fsm); - if (rc == CPIOERR_ENOENT) { - rc = fsmSymlink(fsm->buf, fsm->path); - } - } - } else if (S_ISFIFO(st->st_mode)) { - /* This mimics cpio S_ISSOCK() behavior but probably isnt' right */ - rc = fsmVerify(fsm); - if (rc == CPIOERR_ENOENT) { - rc = fsmMkfifo(fsm->path, 0000); + } else if (S_ISLNK(sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmSymlink(rpmfiFLink(fi), fpath); + } + } else if (S_ISFIFO(sb.st_mode)) { + /* This mimics cpio S_ISSOCK() behavior but probably isn't right */ + if (rc == RPMERR_ENOENT) { + rc = fsmMkfifo(fpath, 0000); } - } else if (S_ISCHR(st->st_mode) || - S_ISBLK(st->st_mode) || - S_ISSOCK(st->st_mode)) + } else if (S_ISCHR(sb.st_mode) || + S_ISBLK(sb.st_mode) || + S_ISSOCK(sb.st_mode)) { - rc = fsmVerify(fsm); - if (rc == CPIOERR_ENOENT) { - rc = fsmMknod(fsm->path, fsm->sb.st_mode, fsm->sb.st_rdev); + if (rc == RPMERR_ENOENT) { + rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev); } } else { /* XXX Special case /dev/log, which shouldn't be packaged anyways */ - if (!IS_DEV_LOG(fsm->path)) - rc = CPIOERR_UNKNOWN_FILETYPE; + if (!IS_DEV_LOG(fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; } - if (li != NULL) { - li->createdPath = li->linkIndex; - rc = fsmMakeLinks(fsm, li); - } - } + /* Set permissions, timestamps etc for non-hardlink entries */ + if (!rc && setmeta) { + rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps); + } + } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) { + /* we skip the hard linked file containing the content */ + /* write the content to the first used instead */ + char *fn = rpmfilesFN(files, firsthardlink); + rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm); + wfd_close(&firstlinkfile); + firsthardlink = -1; + free(fn); + } if (rc) { - if (!fsm->postpone) { + if (!skip) { /* XXX only erase if temp fn w suffix is in use */ - if (fsm->suffix) { - if (S_ISDIR(st->st_mode)) { - (void) fsmRmdir(fsm->path); - } else { - (void) fsmUnlink(fsm->path, fsm->mapFlags); - } + if (suffix && (action != FA_TOUCH)) { + (void) fsmRemove(fpath, sb.st_mode); } errno = saveerrno; - if (fsm->failedFile && *fsm->failedFile == NULL) - *fsm->failedFile = xstrdup(fsm->path); } + } else { + /* Notify on success. */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi)); - break; - } + if (!skip) { + /* Backup file if needed. Directories are handled earlier */ + if (suffix) + rc = fsmBackup(fi, action); - /* Notify on success. */ - rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmcpioTell(archive)); + if (!rc) + rc = fsmCommit(&fpath, fi, action, suffix, plugins); + } + } - if (!fsm->postpone) { - rc = ((S_ISREG(st->st_mode) && st->st_nlink > 1) - ? fsmCommitLinks(fsm) : fsmCommit(fsm, fsm->ix)); - } - if (rc) { - break; - } + if (rc) + *failedFile = xstrdup(fpath); + + /* Run fsm file post hook for all plugins */ + rpmpluginsCallFsmFilePost(plugins, fi, fpath, + sb.st_mode, action, rc); + fpath = _free(fpath); } - if (!rc) - rc = checkHardLinks(fsm); + rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ)); + rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST)); + +exit: /* No need to bother with close errors on read */ - rpmcpioFree(archive); - fsmFree(fsm); + rpmfiArchiveClose(fi); + rpmfiFree(fi); + Fclose(payload); + free(tid); + free(fpath); return rc; } -int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi, +int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files, rpmpsm psm, char ** failedFile) { + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK); rpmfs fs = rpmteGetFileStates(te); - FSM_t fsm = fsmNew(FSM_PKGERASE, fs, fi, failedFile); + rpmPlugins plugins = rpmtsPlugins(ts); + struct stat sb; int rc = 0; + char *fpath = NULL; - if (!rpmteIsSource(te)) - fsm->mapFlags |= CPIO_SBIT_CHECK; + while (!rc && rpmfiNext(fi) >= 0) { + rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi)); + fpath = fsmFsPath(fi, NULL); + rc = fsmStat(fpath, 1, &sb); - while (!rc) { - /* Clean fsm, free'ing memory. */ - fsmReset(fsm); - - /* Identify mapping index. */ - fsm->ix = mapNextIterator(fsm->iter); + fsmDebug(fpath, action, &sb); - /* Exit on end-of-payload. */ - if (fsm->ix < 0) - break; + /* Run fsm file pre hook for all plugins */ + rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, + sb.st_mode, action); - rc = fsmInit(fsm); - - if (!fsm->postpone) - rc = fsmBackup(fsm); + if (!XFA_SKIPPING(action)) + rc = fsmBackup(fi, action); /* Remove erased files. */ - if (!fsm->postpone && fsm->action == FA_ERASE) { - int missingok = (fsm->fflags & (RPMFILE_MISSINGOK | RPMFILE_GHOST)); + if (action == FA_ERASE) { + int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST)); - if (S_ISDIR(fsm->sb.st_mode)) { - rc = fsmRmdir(fsm->path); - } else { - rc = fsmUnlink(fsm->path, fsm->mapFlags); - } + rc = fsmRemove(fpath, sb.st_mode); /* * Missing %ghost or %missingok entries are not errors. @@ -1849,7 +1077,7 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi, * and complaining about job already done seems like kinderkarten * level "But it was MY turn!" whining... */ - if (rc == CPIOERR_ENOENT && missingok) { + if (rc == RPMERR_ENOENT && missingok) { rc = 0; } @@ -1858,112 +1086,41 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi, * to track at least some of the expected failures though, * such as when we knowingly left config file backups etc behind. */ - if (rc == CPIOERR_ENOTEMPTY) { + if (rc == RPMERR_ENOTEMPTY) { rc = 0; } if (rc) { int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING; rpmlog(lvl, _("%s %s: remove failed: %s\n"), - S_ISDIR(fsm->sb.st_mode) ? _("directory") : _("file"), - fsm->path, strerror(errno)); + S_ISDIR(sb.st_mode) ? _("directory") : _("file"), + fpath, strerror(errno)); } } + + /* Run fsm file post hook for all plugins */ + rpmpluginsCallFsmFilePost(plugins, fi, fpath, + sb.st_mode, action, rc); + /* XXX Failure to remove is not (yet) cause for failure. */ if (!strict_erasures) rc = 0; - if (rc) break; + if (rc) + *failedFile = xstrdup(fpath); - /* Notify on success. */ - /* On erase we're iterating backwards, fixup for progress */ - rpm_loff_t amount = (fsm->ix >= 0) ? - rpmfiFC(fsmGetFi(fsm)) - fsm->ix : 0; - rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount); + if (rc == 0) { + /* Notify on success. */ + /* On erase we're iterating backwards, fixup for progress */ + rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi); + rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount); + } + fpath = _free(fpath); } - fsmFree(fsm); + free(fpath); + rpmfiFree(fi); return rc; } -int rpmPackageFilesArchive(rpmfi fi, int isSrc, FD_t cfd, - rpm_loff_t * archiveSize, char ** failedFile) -{ - rpmfs fs = rpmfsNew(rpmfiFC(fi), 0);; - FSM_t fsm = fsmNew(FSM_PKGBUILD, fs, fi, failedFile);; - rpmcpio_t archive = rpmcpioOpen(cfd, O_WRONLY); - int rc = 0; - - fsm->mapFlags |= CPIO_MAP_TYPE; - if (isSrc) - fsm->mapFlags |= CPIO_FOLLOW_SYMLINKS; - - if (archive == NULL) { - rc = CPIOERR_INTERNAL; - } else { - int ghost, i, fc = rpmfiFC(fi); - - /* XXX Is this actually still needed? */ - for (i = 0; i < fc; i++) { - ghost = (rpmfiFFlagsIndex(fi, i) & RPMFILE_GHOST); - rpmfsSetAction(fs, i, ghost ? FA_SKIP : FA_COPYOUT); - } - } - - while (!rc) { - fsmReset(fsm); - - /* Identify mapping index. */ - fsm->ix = mapNextIterator(fsm->iter); - - /* Exit on end-of-payload. */ - if (fsm->ix < 0) - break; - - rc = fsmInit(fsm); - - /* Exit on error. */ - if (rc) { - fsm->postpone = 1; - break; - } - - if (S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1) - fsm->postpone = saveHardLink(fsm, NULL); - - if (fsm->postpone || fsm->fflags & RPMFILE_GHOST) /* XXX Don't if %ghost file. */ - continue; - /* Hardlinks are handled later */ - if (!(S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1)) { - /* Copy file into archive. */ - rc = writeFile(fsm, 1, archive, fsm->ix); - } - - if (rc) { - if (!fsm->postpone) { - if (fsm->failedFile && *fsm->failedFile == NULL) - *fsm->failedFile = xstrdup(fsm->path); - } - - break; - } - } - - /* Flush partial sets of hard linked files. */ - if (!rc) - rc = writeLinks(fsm, archive); - - /* Finish the payload stream */ - if (!rc) - rc = rpmcpioClose(archive); - - if (archiveSize) - *archiveSize = (rc == 0) ? rpmcpioTell(archive) : 0; - - rpmcpioFree(archive); - rpmfsFree(fs); - fsmFree(fsm); - - return rc; -} @@ -17,22 +17,21 @@ typedef struct rpmpsm_s * rpmpsm; /** * Execute a file actions for package * @param ts transaction set - * @param fi transaction element file info - * @param cfd + * @param te transaction set element + * @param files transaction element file info * @param psm owner psm (or NULL) - * @retval archiveSize pointer to archive size * @retval failedFile pointer to first file name that failed (malloced) * @return 0 on success */ -int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfi fi, FD_t cfd, +int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, rpmpsm psm, char ** failedFile); -int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi, +int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files, rpmpsm psm, char ** failedFile); -int rpmPackageFilesArchive(rpmfi fi, int isSrc, FD_t cfd, - rpm_loff_t * archiveSize, char ** failedFile); +RPM_GNUC_INTERNAL +int rpmfiArchiveReadToFilePsm(rpmfi fi, FD_t fd, int nodigest, rpmpsm psm); RPM_GNUC_INTERNAL void rpmpsmNotify(rpmpsm psm, int what, rpm_loff_t amount); diff --git a/lib/header.c b/lib/header.c index 90079f307..0f6efe485 100644 --- a/lib/header.c +++ b/lib/header.c @@ -69,8 +69,13 @@ static const int typeSizes[16] = { 0 }; +enum headerSorted_e { + HEADERSORT_NONE = 0, /* Not sorted */ + HEADERSORT_OFFSET = 1, /* Sorted by offset (on-disk format) */ + HEADERSORT_INDEX = 2, /* Sorted by index */ +}; + enum headerFlags_e { - HEADERFLAG_SORTED = (1 << 0), /*!< Are header entries sorted? */ HEADERFLAG_ALLOCATED = (1 << 1), /*!< Is 1st header region allocated? */ HEADERFLAG_LEGACY = (1 << 2), /*!< Header came from legacy source? */ HEADERFLAG_DEBUG = (1 << 3), /*!< Debug this header? */ @@ -79,6 +84,17 @@ enum headerFlags_e { typedef rpmFlags headerFlags; /** \ingroup header + * A single tag from a Header. + */ +typedef struct indexEntry_s * indexEntry; +struct indexEntry_s { + struct entryInfo_s info; /*!< Description of tag data. */ + rpm_data_t data; /*!< Location of tag data. */ + int length; /*!< No. bytes of data. */ + int rdlen; /*!< No. bytes of data in region. */ +}; + +/** \ingroup header * The Header data structure. */ struct headerToken_s { @@ -88,13 +104,14 @@ struct headerToken_s { int indexAlloced; /*!< Allocated size of tag array. */ unsigned int instance; /*!< Rpmdb instance (offset) */ headerFlags flags; + int sorted; /*!< Current sort method */ int nrefs; /*!< Reference count. */ }; /** \ingroup header * Maximum no. of bytes permitted in a header. */ -static const size_t headerMaxbytes = (32*1024*1024); +static const size_t headerMaxbytes = (256*1024*1024); #define INDEX_MALLOC_SIZE 8 @@ -102,16 +119,62 @@ static const size_t headerMaxbytes = (32*1024*1024); (((_e)->info.tag >= RPMTAG_HEADERIMAGE) && ((_e)->info.tag < RPMTAG_HEADERREGIONS)) #define ENTRY_IN_REGION(_e) ((_e)->info.offset < 0) +#define REGION_TAG_TYPE RPM_BIN_TYPE +#define REGION_TAG_COUNT sizeof(struct entryInfo_s) + +/** + * Sanity check on no. of tags. + * This check imposes a limit of 65K tags, more than enough. + */ +#define HEADER_TAGS_MAX 0x0000ffff +#define hdrchkTags(_ntags) ((_ntags) & (~HEADER_TAGS_MAX)) + +/** + * Sanity check on tag values. + * Catches out nasties like negative values and multiple regions. + **/ +#define hdrchkTag(_tag) ((_tag) < HEADER_I18NTABLE) + +/** + * Sanity check on type values. + */ +#define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE) + +/** + * Sanity check on data size and/or offset and/or count. + * This check imposes a limit of 256 MB -- file signatures + * may require a lot of space in the header. + */ +#define HEADER_DATA_MAX 0x0fffffff +#define hdrchkData(_nbytes) ((_nbytes) & (~HEADER_DATA_MAX)) + +/** + * Sanity check on data alignment for data type. + */ +#define hdrchkAlign(_type, _off) ((_off) & (typeAlign[_type]-1)) + +/** + * Sanity check on range of data offset. + */ +#define hdrchkRange(_dl, _off) ((_off) < 0 || (_off) > (_dl)) + +static int dataLength(rpm_tagtype_t type, rpm_constdata_t p, rpm_count_t count, + int onDisk, rpm_constdata_t pend); + +#ifndef htonll /* Convert a 64bit value to network byte order. */ RPM_GNUC_CONST static uint64_t htonll(uint64_t n) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ uint32_t *i = (uint32_t*)&n; uint32_t b = i[0]; i[0] = htonl(i[1]); i[1] = htonl(b); +#endif return n; } +#endif Header headerLink(Header h) { @@ -156,11 +219,11 @@ Header headerFree(Header h) return NULL; } -static Header headerCreate(void *blob, unsigned int pvlen, int32_t indexLen) +static Header headerCreate(void *blob, int32_t indexLen) { Header h = xcalloc(1, sizeof(*h)); if (blob) { - h->blob = (pvlen > 0) ? memcpy(xmalloc(pvlen), blob, pvlen) : blob; + h->blob = blob; h->indexAlloced = indexLen + 1; h->indexUsed = indexLen; } else { @@ -168,7 +231,7 @@ static Header headerCreate(void *blob, unsigned int pvlen, int32_t indexLen) h->indexUsed = 0; } h->instance = 0; - h->flags |= HEADERFLAG_SORTED; + h->sorted = HEADERSORT_NONE; h->index = (h->indexAlloced ? xcalloc(h->indexAlloced, sizeof(*h->index)) @@ -180,34 +243,50 @@ static Header headerCreate(void *blob, unsigned int pvlen, int32_t indexLen) Header headerNew(void) { - return headerCreate(NULL, 0, 0); + return headerCreate(NULL, 0); } -int headerVerifyInfo(int il, int dl, const void * pev, void * iv, int negate) +static rpmRC hdrblobVerifyInfo(hdrblob blob, char **emsg) { - entryInfo pe = (entryInfo) pev; - entryInfo info = iv; - int i; + struct entryInfo_s info; + int i, len = 0; + int32_t end = 0; + const char *ds = (const char *) blob->dataStart; + int32_t il = (blob->regionTag) ? blob->il-1 : blob->il; + entryInfo pe = (blob->regionTag) ? blob->pe+1 : blob->pe; for (i = 0; i < il; i++) { - info->tag = ntohl(pe[i].tag); - info->type = ntohl(pe[i].type); - info->offset = ntohl(pe[i].offset); - if (negate) - info->offset = -info->offset; - info->count = ntohl(pe[i].count); - - if (hdrchkType(info->type)) - return i; - if (hdrchkAlign(info->type, info->offset)) - return i; - if (hdrchkRange(dl, info->offset)) - return i; - if (hdrchkData(info->count)) - return i; + ei2h(&pe[i], &info); + + /* Previous data must not overlap */ + if (end > info.offset) + goto err; + + if (hdrchkTag(info.tag)) + goto err; + if (hdrchkType(info.type)) + goto err; + if (hdrchkAlign(info.type, info.offset)) + goto err; + if (hdrchkRange(blob->dl, info.offset)) + goto err; + + /* Verify the data actually fits */ + len = dataLength(info.type, ds + info.offset, + info.count, 1, ds + blob->dl); + end = info.offset + len; + if (hdrchkRange(blob->dl, end) || len <= 0) + goto err; + } + return 0; /* Everything ok */ +err: + if (emsg) { + rasprintf(emsg, + _("tag[%d]: BAD, tag %d type %d offset %d count %d len %d"), + i, info.tag, info.type, info.offset, info.count, len); } - return -1; + return i + 1; } static int indexCmp(const void * avp, const void * bvp) @@ -216,11 +295,11 @@ static int indexCmp(const void * avp, const void * bvp) return (ap->info.tag - bp->info.tag); } -void headerSort(Header h) +static void headerSort(Header h) { - if (!(h->flags & HEADERFLAG_SORTED)) { + if (h->sorted != HEADERSORT_INDEX) { qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp); - h->flags |= HEADERFLAG_SORTED; + h->sorted = HEADERSORT_INDEX; } } @@ -239,11 +318,11 @@ static int offsetCmp(const void * avp, const void * bvp) return rc; } -void headerUnsort(Header h) +static void headerUnsort(Header h) { - if (h->flags & HEADERFLAG_SORTED) { + if (h->sorted != HEADERSORT_OFFSET) { qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp); - h->flags &= ~HEADERFLAG_SORTED; + h->sorted = HEADERSORT_OFFSET; } } @@ -270,13 +349,8 @@ unsigned headerSizeof(Header h, int magicp) headerSort(h); - switch (magicp) { - case HEADER_MAGIC_YES: + if (magicp == HEADER_MAGIC_YES) size += sizeof(rpm_header_magic); - break; - case HEADER_MAGIC_NO: - break; - } size += 2 * sizeof(int32_t); /* count of index entries */ @@ -284,7 +358,7 @@ unsigned headerSizeof(Header h, int magicp) /* Regions go in as is ... */ if (ENTRY_IS_REGION(entry)) { size += entry->length; - /* XXX Legacy regions do not include the region tag and data. */ + /* Reserve space for legacy region tag + data */ if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) size += sizeof(struct entryInfo_s) + entry->info.count; continue; @@ -313,6 +387,8 @@ static inline int strtaglen(const char *str, rpm_count_t c, const char *end) const char *s; if (end) { + if (str >= end) + return -1; while ((s = memchr(start, '\0', end-start))) { if (--c == 0 || s > end) break; @@ -407,10 +483,7 @@ static int regionSwab(indexEntry entry, int il, int dl, for (; il > 0; il--, pe++) { struct indexEntry_s ie; - ie.info.tag = ntohl(pe->tag); - ie.info.type = ntohl(pe->type); - ie.info.count = ntohl(pe->count); - ie.info.offset = ntohl(pe->offset); + ei2h(pe, &ie.info); if (hdrchkType(ie.info.type)) return -1; @@ -507,7 +580,7 @@ void * headerExport(Header h, unsigned int *bsize) il += ril; dl += entry->rdlen + entry->info.count; - /* XXX Legacy regions do not include the region tag and data. */ + /* Reserve space for legacy region tag */ if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) il += 1; @@ -580,7 +653,7 @@ void * headerExport(Header h, unsigned int *bsize) src = (char *)entry->data; rdlen = entry->rdlen; - /* XXX Legacy regions do not include the region tag and data. */ + /* Legacy headers don't have regions originally, create one */ if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) { int32_t stei[4]; @@ -720,7 +793,8 @@ indexEntry findEntry(Header h, rpmTagVal tag, rpm_tagtype_t type) struct indexEntry_s key; if (h == NULL) return NULL; - if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h); + if (h->sorted != HEADERSORT_INDEX) + headerSort(h); key.info.tag = tag; @@ -750,7 +824,7 @@ int headerDel(Header h, rpmTagVal tag) entry = findEntry(h, tag, RPM_NULL_TYPE); if (!entry) return 1; - /* Make sure entry points to the first occurence of this tag. */ + /* Make sure entry points to the first occurrence of this tag. */ while (entry > h->index && (entry - 1)->info.tag == tag) entry--; @@ -778,92 +852,46 @@ int headerDel(Header h, rpmTagVal tag) return 0; } -Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags) +rpmRC hdrblobImport(hdrblob blob, int fast, Header *hdrp, char **emsg) { - const int32_t * ei = (int32_t *) blob; - int32_t il = ntohl(ei[0]); /* index length */ - int32_t dl = ntohl(ei[1]); /* data length */ - unsigned int pvlen = sizeof(il) + sizeof(dl) + - (il * sizeof(struct entryInfo_s)) + dl;; Header h = NULL; - entryInfo pe; - unsigned char * dataStart; - unsigned char * dataEnd; indexEntry entry; int rdlen; - int fast = (flags & HEADERIMPORT_FAST); - - /* Sanity checks on header intro. */ - if (bsize && bsize != pvlen) - goto errxit; - if (hdrchkTags(il) || hdrchkData(dl) || pvlen >= headerMaxbytes) - goto errxit; - h = headerCreate(blob, (flags & HEADERIMPORT_COPY) ? pvlen : 0, il); - - ei = h->blob; /* In case we had to copy */ - pe = (entryInfo) &ei[2]; - dataStart = (unsigned char *) (pe + il); - dataEnd = dataStart + dl; + h = headerCreate(blob->ei, blob->il); entry = h->index; - if (!(htonl(pe->tag) < RPMTAG_HEADERI18NTABLE)) { + if (!(htonl(blob->pe->tag) < RPMTAG_HEADERI18NTABLE)) { + /* An original v3 header, create a legacy region entry for it */ h->flags |= HEADERFLAG_LEGACY; entry->info.type = REGION_TAG_TYPE; entry->info.tag = RPMTAG_HEADERIMAGE; entry->info.count = REGION_TAG_COUNT; - entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */ - - entry->data = pe; - entry->length = pvlen - sizeof(il) - sizeof(dl); - rdlen = regionSwab(entry+1, il, 0, pe, - dataStart, dataEnd, entry->info.offset, fast); - if (rdlen != dl) + entry->info.offset = ((unsigned char *)blob->pe - blob->dataStart); /* negative offset */ + + entry->data = blob->pe; + entry->length = blob->pvlen - sizeof(blob->il) - sizeof(blob->dl); + rdlen = regionSwab(entry+1, blob->il, 0, blob->pe, + blob->dataStart, blob->dataEnd, + entry->info.offset, fast); + if (rdlen != blob->dl) goto errxit; entry->rdlen = rdlen; h->indexUsed++; } else { - int32_t rdl; + /* Either a v4 header or an "upgraded" v3 header with a legacy region */ int32_t ril; h->flags &= ~HEADERFLAG_LEGACY; - - entry->info.type = htonl(pe->type); - entry->info.count = htonl(pe->count); - entry->info.tag = htonl(pe->tag); - - if (!ENTRY_IS_REGION(entry)) - goto errxit; - if (entry->info.type != REGION_TAG_TYPE) - goto errxit; - if (entry->info.count != REGION_TAG_COUNT) - goto errxit; - - { int off = ntohl(pe->offset); - - if (off) { - size_t nb = REGION_TAG_COUNT; - int32_t stei[nb]; - if (hdrchkRange(dl, (off + nb))) - goto errxit; - /* XXX Hmm, why the copy? */ - memcpy(&stei, dataStart + off, nb); - rdl = -ntohl(stei[2]); /* negative offset */ - ril = rdl/sizeof(*pe); - if (hdrchkTags(ril) || hdrchkData(rdl)) - goto errxit; - } else { - ril = il; - rdl = (ril * sizeof(struct entryInfo_s)); - entry->info.tag = RPMTAG_HEADERIMAGE; - } - } - entry->info.offset = -rdl; /* negative offset */ - - entry->data = pe; - entry->length = pvlen - sizeof(il) - sizeof(dl); - rdlen = regionSwab(entry+1, ril-1, 0, pe+1, - dataStart, dataEnd, entry->info.offset, fast); + ei2h(blob->pe, &entry->info); + ril = (entry->info.offset != 0) ? blob->ril : blob->il; + + entry->info.offset = -(ril * sizeof(*blob->pe)); /* negative offset */ + entry->data = blob->pe; + entry->length = blob->pvlen - sizeof(blob->il) - sizeof(blob->dl); + rdlen = regionSwab(entry+1, ril-1, 0, blob->pe+1, + blob->dataStart, blob->dataEnd, + entry->info.offset, fast); if (rdlen < 0) goto errxit; entry->rdlen = rdlen; @@ -874,8 +902,8 @@ Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags) int rid = entry->info.offset+1; /* Load dribble entries from region. */ - rdlen = regionSwab(newEntry, ne, rdlen, pe+ril, - dataStart, dataEnd, rid, fast); + rdlen = regionSwab(newEntry, ne, rdlen, blob->pe+ril, + blob->dataStart, blob->dataEnd, rid, fast); if (rdlen < 0) goto errxit; @@ -902,24 +930,29 @@ Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags) rdlen += REGION_TAG_COUNT; - if (rdlen != dl) + if (rdlen != blob->dl) goto errxit; } - h->flags &= ~HEADERFLAG_SORTED; + /* Force sorting, dribble lookups can cause early sort on partial header */ + h->sorted = HEADERSORT_NONE; headerSort(h); h->flags |= HEADERFLAG_ALLOCATED; + if (hdrp) + *hdrp = h; - return h; + /* We own the memory now, avoid double-frees */ + blob->ei = NULL; + + return RPMRC_OK; errxit: if (h) { - if (flags & HEADERIMPORT_COPY) - free(h->blob); free(h->index); free(h); + rasprintf(emsg, _("hdr load: BAD")); } - return NULL; + return RPMRC_FAIL; } Header headerReload(Header h, rpmTagVal tag) @@ -956,54 +989,14 @@ Header headerCopyLoad(const void * uh) Header headerRead(FD_t fd, int magicp) { - int32_t block[4]; - int32_t * ei = NULL; - int32_t il; - int32_t dl; Header h = NULL; - unsigned int len, blen; - - if (magicp == HEADER_MAGIC_YES) { - int32_t magic; - - if (Freadall(fd, block, 4*sizeof(*block)) != 4*sizeof(*block)) - goto exit; - - magic = block[0]; - - if (memcmp(&magic, rpm_header_magic, sizeof(magic))) - goto exit; - - il = ntohl(block[2]); - dl = ntohl(block[3]); - } else { - if (Freadall(fd, block, 2*sizeof(*block)) != 2*sizeof(*block)) - goto exit; - - il = ntohl(block[0]); - dl = ntohl(block[1]); - } - - blen = (il * sizeof(struct entryInfo_s)) + dl; - len = sizeof(il) + sizeof(dl) + blen; + struct hdrblob_s blob; + char *buf = NULL; - /* Sanity checks on header intro. */ - if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes) - goto exit; + if (hdrblobRead(fd, magicp, 0, 0, &blob, &buf) == RPMRC_OK) + hdrblobImport(&blob, 0, &h, &buf); - ei = xmalloc(len); - ei[0] = htonl(il); - ei[1] = htonl(dl); - - if (Freadall(fd, (char *)&ei[2], blen) != blen) - goto exit; - - h = headerImport(ei, len, 0); - -exit: - if (h == NULL && ei != NULL) { - free(ei); - } + free(buf); return h; } @@ -1011,19 +1004,15 @@ int headerWrite(FD_t fd, Header h, int magicp) { ssize_t nb; unsigned int length; - void * uh; + void * uh = headerExport(h, &length); - uh = headerExport(h, &length); if (uh == NULL) return 1; - switch (magicp) { - case HEADER_MAGIC_YES: - nb = Fwrite(rpm_header_magic, sizeof(uint8_t), sizeof(rpm_header_magic), fd); + + if (magicp == HEADER_MAGIC_YES) { + nb = Fwrite(rpm_header_magic, sizeof(rpm_header_magic), 1, fd); if (nb != sizeof(rpm_header_magic)) goto exit; - break; - case HEADER_MAGIC_NO: - break; } nb = Fwrite(uh, sizeof(char), length, fd); @@ -1040,6 +1029,16 @@ int headerIsEntry(Header h, rpmTagVal tag) } +/* simple heuristic to find out if the header is from * a source rpm + * or not: source rpms contain at least the spec file and have all + * files in one directory with an empty name. + */ +int headerIsSourceHeuristic(Header h) +{ + indexEntry entry = findEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE); + return entry && entry->info.count == 1 && entry->data && !*(const char *)entry->data; +} + /** \ingroup header * Retrieve data from header entry. * Relevant flags (others are ignored), if neither is set allocation @@ -1050,7 +1049,6 @@ int headerIsEntry(Header h, rpmTagVal tag) * @todo Permit retrieval of regions other than HEADER_IMUTABLE. * @param entry header entry * @param td tag data container - * @param minMem string pointers refer to header memory? * @param flags flags to control memory allocation * @return 1 on success, otherwise error. */ @@ -1167,6 +1165,7 @@ static int copyTdEntry(const indexEntry entry, rpmtd td, headerGetFlags flags) } td->type = entry->info.type; td->count = count; + td->size = entry->length; if (td->data && entry->data != td->data) { td->flags |= RPMTD_ALLOCED; @@ -1311,18 +1310,10 @@ static int intGetTdEntry(Header h, rpmtd td, headerGetFlags flags) return 0; } - if (flags & HEADERGET_RAW) { + if (entry->info.type == RPM_I18NSTRING_TYPE && !(flags & HEADERGET_RAW)) + rc = copyI18NEntry(h, entry, td, flags); + else rc = copyTdEntry(entry, td, flags); - } else { - switch (entry->info.type) { - case RPM_I18NSTRING_TYPE: - rc = copyI18NEntry(h, entry, td, flags); - break; - default: - rc = copyTdEntry(entry, td, flags); - break; - } - } if (rc == 0) td->flags |= RPMTD_INVALID; @@ -1438,7 +1429,7 @@ static int intAddEntry(Header h, rpmtd td) entry->length = length; if (h->indexUsed > 0 && td->tag < h->index[h->indexUsed-1].info.tag) - h->flags &= ~HEADERFLAG_SORTED; + h->sorted = HEADERSORT_NONE; h->indexUsed++; return 1; @@ -1650,7 +1641,7 @@ int headerMod(Header h, rpmtd td) if (data == NULL) return 0; - /* make sure entry points to the first occurence of this tag */ + /* make sure entry points to the first occurrence of this tag */ while (entry > h->index && (entry - 1)->info.tag == td->tag) entry--; @@ -1773,3 +1764,264 @@ ssize_t Freadall(FD_t fd, void * buf, ssize_t size) return total; } +static rpmRC hdrblobVerifyRegion(rpmTagVal regionTag, int exact_size, + hdrblob blob, char **buf) +{ + rpmRC rc = RPMRC_FAIL; + struct entryInfo_s trailer, einfo; + unsigned char * regionEnd = NULL; + + /* Check that we have at least on tag */ + if (blob->il < 1) { + rasprintf(buf, _("region: no tags")); + goto exit; + } + + /* Convert the 1st tag element. */ + ei2h(blob->pe, &einfo); + + if (!regionTag && (einfo.tag == RPMTAG_HEADERSIGNATURES || + einfo.tag == RPMTAG_HEADERIMMUTABLE || + einfo.tag == RPMTAG_HEADERIMAGE)) { + regionTag = einfo.tag; + } + + /* Is there an immutable header region tag? */ + if (!(einfo.tag == regionTag)) { + rc = RPMRC_NOTFOUND; + goto exit; + } + + /* Is the region tag sane? */ + if (!(einfo.type == REGION_TAG_TYPE && einfo.count == REGION_TAG_COUNT)) { + rasprintf(buf, + _("region tag: BAD, tag %d type %d offset %d count %d"), + einfo.tag, einfo.type, einfo.offset, einfo.count); + goto exit; + } + + /* Is the trailer within the data area? */ + if (hdrchkRange(blob->dl, einfo.offset + REGION_TAG_COUNT)) { + rasprintf(buf, + _("region offset: BAD, tag %d type %d offset %d count %d"), + einfo.tag, einfo.type, einfo.offset, einfo.count); + goto exit; + } + + /* Is there an immutable header region tag trailer? */ + memset(&trailer, 0, sizeof(trailer)); + regionEnd = blob->dataStart + einfo.offset; + (void) memcpy(&trailer, regionEnd, REGION_TAG_COUNT); + regionEnd += REGION_TAG_COUNT; + blob->rdl = regionEnd - blob->dataStart; + + ei2h(&trailer, &einfo); + /* Trailer offset is negative and has a special meaning */ + einfo.offset = -einfo.offset; + if (!(einfo.tag == regionTag && + einfo.type == REGION_TAG_TYPE && einfo.count == REGION_TAG_COUNT)) + { + rasprintf(buf, + _("region trailer: BAD, tag %d type %d offset %d count %d"), + einfo.tag, einfo.type, einfo.offset, einfo.count); + goto exit; + } + + /* Does the region actually fit within the header? */ + blob->ril = einfo.offset/sizeof(*blob->pe); + if ((einfo.offset % sizeof(*blob->pe)) || hdrchkRange(blob->il, blob->ril) || + hdrchkRange(blob->dl, blob->rdl)) { + rasprintf(buf, _("region %d size: BAD, ril %d il %d rdl %d dl %d"), + regionTag, blob->ril, blob->il, blob->rdl, blob->dl); + goto exit; + } + + /* In package files region size is expected to match header size. */ + if (exact_size && !(blob->il == blob->ril && blob->dl == blob->rdl)) { + rasprintf(buf, + _("region %d: tag number mismatch il %d ril %d dl %d rdl %d\n"), + regionTag, blob->il, blob->ril, blob->dl, blob->rdl); + goto exit; + } + + blob->regionTag = regionTag; + rc = RPMRC_OK; + +exit: + return rc; +} + +rpmRC hdrblobRead(FD_t fd, int magic, int exact_size, rpmTagVal regionTag, hdrblob blob, char **emsg) +{ + int32_t block[4]; + int32_t *bs = (magic != 0) ? &block[0] : &block[2]; + int blen = (magic != 0) ? sizeof(block) : sizeof(block) / 2; + int32_t il; + int32_t dl; + int32_t * ei = NULL; + size_t uc; + size_t nb; + rpmRC rc = RPMRC_FAIL; /* assume failure */ + int xx; + int32_t il_max = HEADER_TAGS_MAX; + int32_t dl_max = HEADER_DATA_MAX; + + if (regionTag == RPMTAG_HEADERSIGNATURES) { + il_max = 32; + dl_max = 8192; + } + + memset(block, 0, sizeof(block)); + if ((xx = Freadall(fd, bs, blen)) != blen) { + rasprintf(emsg, + _("hdr size(%d): BAD, read returned %d"), blen, xx); + goto exit; + } + if (magic && memcmp(block, rpm_header_magic, sizeof(rpm_header_magic))) { + rasprintf(emsg, _("hdr magic: BAD")); + goto exit; + } + il = ntohl(block[2]); + if (hdrchkRange(il_max, il)) { + rasprintf(emsg, _("hdr tags: BAD, no. of tags(%d) out of range"), il); + goto exit; + } + dl = ntohl(block[3]); + if (hdrchkRange(dl_max, dl)) { + rasprintf(emsg, _("hdr data: BAD, no. of bytes(%d) out of range"), dl); + goto exit; + } + + nb = (il * sizeof(struct entryInfo_s)) + dl; + uc = sizeof(il) + sizeof(dl) + nb; + ei = xmalloc(uc); + ei[0] = block[2]; + ei[1] = block[3]; + if ((xx = Freadall(fd, (char *)&ei[2], nb)) != nb) { + rasprintf(emsg, _("hdr blob(%zd): BAD, read returned %d"), nb, xx); + goto exit; + } + + if (regionTag == RPMTAG_HEADERSIGNATURES) { + size_t sigSize = uc + sizeof(rpm_header_magic); + size_t pad = (8 - (sigSize % 8)) % 8; + size_t trc; + if (pad && (trc = Freadall(fd, block, pad)) != pad) { + rasprintf(emsg, _("sigh pad(%zd): BAD, read %zd bytes"), pad, trc); + goto exit; + } + } + + rc = hdrblobInit(ei, uc, regionTag, exact_size, blob, emsg); + +exit: + if (rc != RPMRC_OK) { + free(ei); + blob->ei = NULL; + if (emsg && *emsg && regionTag == RPMTAG_HEADERSIGNATURES) { + /* rstrscat() cannot handle overlap even if it claims so */ + char *tmp = rstrscat(NULL, _("signature "), *emsg, NULL); + free(*emsg); + *emsg = tmp; + } + } + + return rc; +} + +rpmRC hdrblobInit(const void *uh, size_t uc, + rpmTagVal regionTag, int exact_size, + struct hdrblob_s *blob, char **emsg) +{ + rpmRC rc = RPMRC_FAIL; + + memset(blob, 0, sizeof(*blob)); + blob->ei = (int32_t *) uh; /* discards const */ + blob->il = ntohl(blob->ei[0]); + blob->dl = ntohl(blob->ei[1]); + blob->pe = (entryInfo) &(blob->ei[2]); + blob->pvlen = sizeof(blob->il) + sizeof(blob->dl) + + (blob->il * sizeof(*blob->pe)) + blob->dl; + blob->dataStart = (uint8_t *) (blob->pe + blob->il); + blob->dataEnd = blob->dataStart + blob->dl; + + /* Is the blob the right size? */ + if (blob->pvlen >= headerMaxbytes || (uc && blob->pvlen != uc)) { + rasprintf(emsg, _("blob size(%d): BAD, 8 + 16 * il(%d) + dl(%d)"), + blob->pvlen, blob->il, blob->dl); + goto exit; + } + + if (hdrblobVerifyRegion(regionTag, exact_size, blob, emsg) == RPMRC_FAIL) + goto exit; + + /* Sanity check the rest of the header structure. */ + if (hdrblobVerifyInfo(blob, emsg)) + goto exit; + + rc = RPMRC_OK; + +exit: + return rc; +} + +rpmRC hdrblobGet(hdrblob blob, uint32_t tag, rpmtd td) +{ + rpmRC rc = RPMRC_NOTFOUND; + struct indexEntry_s entry; + struct entryInfo_s einfo; + const struct entryInfo_s *pe = blob->pe; + uint32_t ntag = htonl(tag); + int tsize; + + memset(&einfo, 0, sizeof(einfo)); + rpmtdReset(td); + + for (int i = 1; i < blob->il; i++, pe++) { + if (pe->tag != ntag) + continue; + ei2h(pe, &einfo); + + /* We can only handle non-byteswappable data */ + tsize = typeSizes[einfo.type]; + if (tsize != 1 && tsize != -1) + return RPMRC_FAIL; + + entry.info = einfo; /* struct assignment */ + entry.data = blob->dataStart + einfo.offset; + entry.length = dataLength(einfo.type, blob->dataStart + einfo.offset, + einfo.count, 1, blob->dataEnd); + entry.rdlen = 0; + td->tag = einfo.tag; + rc = copyTdEntry(&entry, td, HEADERGET_MINMEM) ? RPMRC_OK : RPMRC_FAIL; + break; + } + return rc; +} + +Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags) +{ + Header h = NULL; + struct hdrblob_s hblob; + char *buf = NULL; + void * b = blob; + + if (flags & HEADERIMPORT_COPY) { + if (bsize == 0 && hdrblobInit(b, 0, 0, 0, &hblob, &buf) == RPMRC_OK) + bsize = hblob.pvlen; + if (bsize == 0) + goto exit; + b = memcpy(xmalloc(bsize), b, bsize); + } + + /* Sanity checks on header intro. */ + if (hdrblobInit(b, bsize, 0, 0, &hblob, &buf) == RPMRC_OK) + hdrblobImport(&hblob, (flags & HEADERIMPORT_FAST), &h, &buf); + +exit: + if (h == NULL && b != blob) + free(b); + free(buf); + + return h; +} diff --git a/lib/header.h b/lib/header.h index ddaba4f68..a616deefc 100644 --- a/lib/header.h +++ b/lib/header.h @@ -55,18 +55,6 @@ Header headerFree( Header h); Header headerLink(Header h); /** \ingroup header - * Sort tags in header. - * @param h header - */ -void headerSort(Header h); - -/** \ingroup header - * Restore tags in header to original ordering. - * @param h header - */ -void headerUnsort(Header h); - -/** \ingroup header * Return size of on-disk header representation in bytes. * @param h header * @param magicp include size of 8 bytes for (magic, 0)? @@ -75,17 +63,6 @@ void headerUnsort(Header h); unsigned int headerSizeof(Header h, int magicp); /** \ingroup header - * Perform simple sanity and range checks on header tag(s). - * @param il no. of tags in header - * @param dl no. of bytes in header data. - * @param pev 1st element in tag array, big-endian - * @param iv failing (or last) tag element, host-endian - * @param negate negative offset expected? - * @return -1 on success, otherwise failing tag element index - */ -int headerVerifyInfo(int il, int dl, const void * pev, void * iv, int negate); - -/** \ingroup header * Convert header to on-disk representation. * @deprecated Use headerExport() instead * @param h header (with pointers) @@ -245,9 +222,9 @@ int headerPut(Header h, rpmtd td, headerPutFlags flags); * @return 1 on success, 0 on failure * */ +int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size); int headerPutString(Header h, rpmTagVal tag, const char *val); int headerPutStringArray(Header h, rpmTagVal tag, const char **val, rpm_count_t size); -int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size); int headerPutChar(Header h, rpmTagVal tag, const char *val, rpm_count_t size); int headerPutUint8(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size); int headerPutUint16(Header h, rpmTagVal tag, const uint16_t *val, rpm_count_t size); @@ -348,65 +325,6 @@ int headerNext(HeaderIterator hi, rpmtd td); rpmTagVal headerNextTag(HeaderIterator hi); /** \ingroup header - * Return name, version, release strings from header. - * @param h header - * @retval *np name pointer (or NULL) - * @retval *vp version pointer (or NULL) - * @retval *rp release pointer (or NULL) - * @return 0 always - */ -RPM_GNUC_DEPRECATED -int headerNVR(Header h, - const char ** np, - const char ** vp, - const char ** rp); - -/** \ingroup header - * Return name, epoch, version, release, arch strings from header. - * @param h header - * @retval *np name pointer (or NULL) - * @retval *ep epoch pointer (or NULL) - * @retval *vp version pointer (or NULL) - * @retval *rp release pointer (or NULL) - * @retval *ap arch pointer (or NULL) - * @return 0 always - */ -RPM_GNUC_DEPRECATED -int headerNEVRA(Header h, - const char ** np, - uint32_t ** ep, - const char ** vp, - const char ** rp, - const char ** ap); - -/** \ingroup header - * Return (malloc'd) header name-version-release string. - * @param h header - * @retval np name tag value - * @return name-version-release string - */ -RPM_GNUC_DEPRECATED -char * headerGetNEVR(Header h, const char ** np ); - -/** \ingroup header - * Return (malloc'd) header name-version-release.arch string. - * @param h header - * @retval np name tag value - * @return name-version-release string - */ -RPM_GNUC_DEPRECATED -char * headerGetNEVRA(Header h, const char ** np ); - -/* \ingroup header - * Return (malloc'd) header (epoch:)version-release string. - * @param h header - * @retval np name tag value (or NULL) - * @return (epoch:)version-release string - */ -RPM_GNUC_DEPRECATED -char * headerGetEVR(Header h, const char **np); - -/** \ingroup header * Return any non-array tag from header, converted to string * @param h header * @param tag tag to retrieve @@ -431,14 +349,6 @@ const char * headerGetString(Header h, rpmTagVal tag); uint64_t headerGetNumber(Header h, rpmTagVal tag); /** \ingroup header - * Return header color. - * @param h header - * @return header color - */ -RPM_GNUC_DEPRECATED -rpm_color_t headerGetColor(Header h); - -/** \ingroup header * Check if header is a source or binary package header * @param h header * @return 0 == binary, 1 == source diff --git a/lib/header_internal.h b/lib/header_internal.h index f51435fee..81c8c1e9d 100644 --- a/lib/header_internal.h +++ b/lib/header_internal.h @@ -6,6 +6,7 @@ */ #include <rpm/header.h> +#include <netinet/in.h> /** \ingroup header * Description of tag data. @@ -18,51 +19,60 @@ struct entryInfo_s { rpm_count_t count; /*!< Number of tag elements. */ }; -#define REGION_TAG_TYPE RPM_BIN_TYPE -#define REGION_TAG_COUNT sizeof(struct entryInfo_s) +typedef struct hdrblob_s * hdrblob; +struct hdrblob_s { + int32_t *ei; + int32_t il; + int32_t dl; + entryInfo pe; + int32_t pvlen; + uint8_t *dataStart; + uint8_t *dataEnd; -/** \ingroup header - * A single tag from a Header. - */ -typedef struct indexEntry_s * indexEntry; -struct indexEntry_s { - struct entryInfo_s info; /*!< Description of tag data. */ - rpm_data_t data; /*!< Location of tag data. */ - int length; /*!< No. bytes of data. */ - int rdlen; /*!< No. bytes of data in region. */ + rpmTagVal regionTag; + int32_t ril; + int32_t rdl; }; -/** - * Sanity check on no. of tags. - * This check imposes a limit of 65K tags, more than enough. - */ -#define hdrchkTags(_ntags) ((_ntags) & 0xffff0000) +#ifdef __cplusplus +extern "C" { +#endif -/** - * Sanity check on type values. - */ -#define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE) +/* convert entry info to host endianess */ +static inline void ei2h(const struct entryInfo_s *pe, struct entryInfo_s *info) +{ + info->tag = ntohl(pe->tag); + info->type = ntohl(pe->type); + info->offset = ntohl(pe->offset); + info->count = ntohl(pe->count); +} -/** - * Sanity check on data size and/or offset and/or count. - * This check imposes a limit of 16 MB, more than enough. - */ -#define HEADER_DATA_MAX 0x00ffffff -#define hdrchkData(_nbytes) ((_nbytes) & (~HEADER_DATA_MAX)) +static inline void ei2td(const struct entryInfo_s *info, + unsigned char * dataStart, size_t len, + struct rpmtd_s *td) +{ + td->tag = info->tag; + td->type = info->type; + td->count = info->count; + td->size = len; + td->data = dataStart + info->offset; + td->ix = -1; + td->flags = RPMTD_IMMUTABLE; +} -/** - * Sanity check on data alignment for data type. - */ -#define hdrchkAlign(_type, _off) ((_off) & (typeAlign[_type]-1)) +RPM_GNUC_INTERNAL +rpmRC hdrblobInit(const void *uh, size_t uc, + rpmTagVal regionTag, int exact_size, + struct hdrblob_s *blob, char **emsg); -/** - * Sanity check on range of data offset. - */ -#define hdrchkRange(_dl, _off) ((_off) < 0 || (_off) > (_dl)) +RPM_GNUC_INTERNAL +rpmRC hdrblobRead(FD_t fd, int magic, int exact_size, rpmTagVal regionTag, hdrblob blob, char **emsg); -#ifdef __cplusplus -extern "C" { -#endif +RPM_GNUC_INTERNAL +rpmRC hdrblobImport(hdrblob blob, int fast, Header *hdrp, char **emsg); + +RPM_GNUC_INTERNAL +rpmRC hdrblobGet(hdrblob blob, uint32_t tag, rpmtd td); /** \ingroup header * Set header instance (rpmdb record number) @@ -75,6 +85,14 @@ void headerSetInstance(Header h, unsigned int instance); /* Package IO helper to consolidate partial read and error handling */ RPM_GNUC_INTERNAL ssize_t Freadall(FD_t fd, void * buf, ssize_t size); + +/* XXX here only temporarily */ +RPM_GNUC_INTERNAL +void headerMergeLegacySigs(Header h, Header sigh); +RPM_GNUC_INTERNAL +void applyRetrofits(Header h, int leadtype); +RPM_GNUC_INTERNAL +int headerIsSourceHeuristic(Header h); #ifdef __cplusplus } #endif diff --git a/lib/headerfmt.c b/lib/headerfmt.c index a865ee7a8..fdb3aadb7 100644 --- a/lib/headerfmt.c +++ b/lib/headerfmt.c @@ -4,6 +4,7 @@ #include "system.h" +#include <stdarg.h> #include <rpm/header.h> #include <rpm/rpmtag.h> #include <rpm/rpmstring.h> @@ -20,7 +21,7 @@ */ typedef struct sprintfTag_s * sprintfTag; struct sprintfTag_s { - headerTagFormatFunction fmt; + headerFmt fmt; rpmTagVal tag; int justOne; char * format; @@ -217,6 +218,25 @@ static char * hsaReserve(headerSprintfArgs hsa, size_t need) return hsa->val + hsa->vallen; } +RPM_GNUC_PRINTF(2, 3) +static void hsaError(headerSprintfArgs hsa, const char *fmt, ...) +{ + /* Use thread local static buffer as headerFormat() errmsg arg is const */ + static __thread char errbuf[BUFSIZ]; + + if (fmt == NULL) { + hsa->errmsg = NULL; + } else { + va_list ap; + + va_start(ap, fmt); + vsnprintf(errbuf, sizeof(errbuf), fmt, ap); + va_end(ap); + + hsa->errmsg = errbuf; + } +} + /** * Search tags for a name. * @param hsa headerSprintf args @@ -246,7 +266,7 @@ static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name) /* Search extensions for specific format. */ if (stag->type != NULL) - stag->fmt = rpmHeaderFormatFuncByName(stag->type); + stag->fmt = rpmHeaderFormatByName(stag->type); return stag->fmt ? 0 : 1; } @@ -333,13 +353,13 @@ static int parseFormat(headerSprintfArgs hsa, char * str, chptr = start; while (*chptr && *chptr != '{' && *chptr != '%') { if (!risdigit(*chptr) && *chptr != '-') { - hsa->errmsg = _("invalid field width"); + hsaError(hsa, _("invalid field width")); goto errxit; } chptr++; } if (!*chptr || *chptr == '%') { - hsa->errmsg = _("missing { after %"); + hsaError(hsa, _("missing { after %%")); goto errxit; } @@ -361,7 +381,7 @@ static int parseFormat(headerSprintfArgs hsa, char * str, dst = next = start; while (*next && *next != '}') next++; if (!*next) { - hsa->errmsg = _("missing } after %{"); + hsaError(hsa, _("missing } after %%{")); goto errxit; } *next++ = '\0'; @@ -372,7 +392,7 @@ static int parseFormat(headerSprintfArgs hsa, char * str, if (*chptr != '\0') { *chptr++ = '\0'; if (!*chptr) { - hsa->errmsg = _("empty tag format"); + hsaError(hsa, _("empty tag format")); goto errxit; } token->u.tag.type = chptr; @@ -383,14 +403,14 @@ static int parseFormat(headerSprintfArgs hsa, char * str, } if (!*start) { - hsa->errmsg = _("empty tag name"); + hsaError(hsa, _("empty tag name")); goto errxit; } token->type = PTOK_TAG; if (findTag(hsa, token, start)) { - hsa->errmsg = _("unknown tag"); + hsaError(hsa, _("unknown tag: \"%s\""), start); goto errxit; } @@ -410,7 +430,7 @@ static int parseFormat(headerSprintfArgs hsa, char * str, } if (!start) { - hsa->errmsg = _("] expected at end of array"); + hsaError(hsa, _("] expected at end of array")); goto errxit; } @@ -422,7 +442,7 @@ static int parseFormat(headerSprintfArgs hsa, char * str, case ']': if (state != PARSER_IN_ARRAY) { - hsa->errmsg = _("unexpected ]"); + hsaError(hsa, _("unexpected ]")); goto errxit; } *start++ = '\0'; @@ -432,7 +452,7 @@ static int parseFormat(headerSprintfArgs hsa, char * str, case '}': if (state != PARSER_IN_EXPR) { - hsa->errmsg = _("unexpected }"); + hsaError(hsa, _("unexpected }")); goto errxit; } *start++ = '\0'; @@ -483,19 +503,19 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, char * chptr; char * end; - hsa->errmsg = NULL; + hsaError(hsa, NULL); chptr = str; while (*chptr && *chptr != '?') chptr++; if (*chptr != '?') { - hsa->errmsg = _("? expected in expression"); + hsaError(hsa, _("? expected in expression")); return 1; } *chptr++ = '\0';; if (*chptr != '{') { - hsa->errmsg = _("{ expected after ? in expression"); + hsaError(hsa, _("{ expected after ? in expression")); return 1; } @@ -507,7 +527,7 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/ if (!(end && *end)) { - hsa->errmsg = _("} expected in expression"); + hsaError(hsa, _("} expected in expression")); token->u.cond.ifFormat = freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens); return 1; @@ -515,7 +535,7 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, chptr = end; if (*chptr != ':' && *chptr != '|') { - hsa->errmsg = _(": expected following ? subexpression"); + hsaError(hsa, _(": expected following ? subexpression")); token->u.cond.ifFormat = freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens); return 1; @@ -533,7 +553,7 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, chptr++; if (*chptr != '{') { - hsa->errmsg = _("{ expected after : in expression"); + hsaError(hsa, _("{ expected after : in expression")); token->u.cond.ifFormat = freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens); return 1; @@ -547,7 +567,7 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */ if (!(end && *end)) { - hsa->errmsg = _("} expected in expression"); + hsaError(hsa, _("} expected in expression")); token->u.cond.ifFormat = freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens); return 1; @@ -555,7 +575,7 @@ static int parseExpression(headerSprintfArgs hsa, sprintfToken token, chptr = end; if (*chptr != '|') { - hsa->errmsg = _("| expected at end of expression"); + hsaError(hsa, _("| expected at end of expression")); token->u.cond.ifFormat = freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens); token->u.cond.elseFormat = @@ -624,9 +644,9 @@ static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element) char * t, * te; rpmtd td; - if ((td = getData(hsa, tag->tag))) { + if ((td = getData(hsa, tag->tag)) && td->count > element) { td->ix = element; /* Ick, use iterators instead */ - val = tag->fmt(td); + val = rpmHeaderFormatCall(tag->fmt, td); } else { val = xstrdup("(none)"); } @@ -748,16 +768,10 @@ static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token, found = 1; count = rpmtdCount(td); - if (numElements > 1 && count != numElements) - switch (td->type) { - default: - hsa->errmsg = - _("array iterator used with different sized arrays"); + if (numElements > 0 && count != numElements) { + hsaError(hsa, + _("array iterator used with different sized arrays")); return NULL; - break; - case RPM_BIN_TYPE: - case RPM_STRING_TYPE: - break; } if (count > numElements) numElements = count; @@ -821,13 +835,6 @@ static unsigned int tagId(rpmTagVal tag) return tag; } -static rpmtd tagFree(rpmtd td) -{ - rpmtdFreeData(td); - rpmtdFree(td); - return NULL; -} - char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg) { struct headerSprintfArgs_s hsa; @@ -845,7 +852,7 @@ char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg) if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN)) goto exit; - hsa.cache = tagCacheCreate(128, tagId, tagCmp, NULL, tagFree); + hsa.cache = tagCacheCreate(128, tagId, tagCmp, NULL, rpmtdFree); hsa.val = xstrdup(""); tag = diff --git a/lib/headerutil.c b/lib/headerutil.c index a9f1221eb..2003219b6 100644 --- a/lib/headerutil.c +++ b/lib/headerutil.c @@ -1,5 +1,5 @@ /** \ingroup rpmdb - * \file lib/hdrNVR.c + * \file lib/headerutil.c */ #include "system.h" @@ -7,63 +7,10 @@ #include <rpm/rpmtypes.h> #include <rpm/header.h> #include <rpm/rpmstring.h> +#include <rpm/rpmds.h> #include "debug.h" -static int NEVRA(Header h, const char **np, - uint32_t **ep, const char **vp, const char **rp, - const char **ap) -{ - if (np) *np = headerGetString(h, RPMTAG_NAME); - if (vp) *vp = headerGetString(h, RPMTAG_VERSION); - if (rp) *rp = headerGetString(h, RPMTAG_RELEASE); - if (ap) *ap = headerGetString(h, RPMTAG_ARCH); - if (ep) { - struct rpmtd_s td; - headerGet(h, RPMTAG_EPOCH, &td, HEADERGET_DEFAULT); - *ep = rpmtdGetUint32(&td); - } - return 0; -} - -int headerNVR(Header h, const char **np, const char **vp, const char **rp) -{ - return NEVRA(h, np, NULL, vp, rp, NULL); -} - -int headerNEVRA(Header h, const char **np, - uint32_t **ep, const char **vp, const char **rp, - const char **ap) -{ - return NEVRA(h, np, ep, vp, rp, ap); -} - -static char *getNEVRA(Header h, rpmTag tag, const char **np) -{ - if (np) *np = headerGetString(h, RPMTAG_NAME); - return headerGetAsString(h, tag); -} - -char * headerGetNEVR(Header h, const char ** np) -{ - return getNEVRA(h, RPMTAG_NEVR, np); -} - -char * headerGetNEVRA(Header h, const char ** np) -{ - return getNEVRA(h, RPMTAG_NEVRA, np); -} - -char * headerGetEVR(Header h, const char ** np) -{ - return getNEVRA(h, RPMTAG_EVR, np); -} - -rpm_color_t headerGetColor(Header h) -{ - return headerGetNumber(h, RPMTAG_HEADERCOLOR); -} - int headerIsSource(Header h) { return (!headerIsEntry(h, RPMTAG_SOURCERPM)); @@ -84,7 +31,7 @@ Header headerCopy(Header h) } headerFreeIterator(hi); - return headerReload(nh, RPMTAG_HEADERIMAGE); + return nh; } void headerCopyTags(Header headerFrom, Header headerTo, @@ -243,3 +190,229 @@ int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size) return headerPutType(h, tag, RPM_BIN_TYPE, val, size); } +static int dncmp(const void * a, const void * b) +{ + const char *const * first = a; + const char *const * second = b; + return strcmp(*first, *second); +} + +static void compressFilelist(Header h) +{ + struct rpmtd_s fileNames; + char ** dirNames; + const char ** baseNames; + uint32_t * dirIndexes; + rpm_count_t count, realCount = 0; + int i; + int dirIndex = -1; + + /* + * This assumes the file list is already sorted, and begins with a + * single '/'. That assumption isn't critical, but it makes things go + * a bit faster. + */ + + if (headerIsEntry(h, RPMTAG_DIRNAMES)) { + headerDel(h, RPMTAG_OLDFILENAMES); + return; /* Already converted. */ + } + + if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) + return; + count = rpmtdCount(&fileNames); + if (count < 1) + return; + + dirNames = xmalloc(sizeof(*dirNames) * count); /* worst case */ + baseNames = xmalloc(sizeof(*dirNames) * count); + dirIndexes = xmalloc(sizeof(*dirIndexes) * count); + + /* HACK. Source RPM, so just do things differently */ + { const char *fn = rpmtdGetString(&fileNames); + if (fn && *fn != '/') { + dirIndex = 0; + dirNames[dirIndex] = xstrdup(""); + while ((i = rpmtdNext(&fileNames)) >= 0) { + dirIndexes[i] = dirIndex; + baseNames[i] = rpmtdGetString(&fileNames); + realCount++; + } + goto exit; + } + } + + /* + * XXX EVIL HACK, FIXME: + * This modifies (and then restores) a const string from rpmtd + * through basename retrieved from strrchr() which silently + * casts away const on return. + */ + while ((i = rpmtdNext(&fileNames)) >= 0) { + char ** needle; + char savechar; + char * baseName; + size_t len; + char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */ + + if (filename == NULL) /* XXX can't happen */ + continue; + baseName = strrchr(filename, '/'); + if (baseName == NULL) { + baseName = filename; + } else { + baseName += 1; + } + len = baseName - filename; + needle = dirNames; + savechar = *baseName; + *baseName = '\0'; + if (dirIndex < 0 || + (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) { + char *s = xmalloc(len + 1); + rstrlcpy(s, filename, len + 1); + dirIndexes[realCount] = ++dirIndex; + dirNames[dirIndex] = s; + } else + dirIndexes[realCount] = needle - dirNames; + + *baseName = savechar; + baseNames[realCount] = baseName; + realCount++; + } + +exit: + if (count > 0) { + headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, realCount); + headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, realCount); + headerPutStringArray(h, RPMTAG_DIRNAMES, + (const char **) dirNames, dirIndex + 1); + } + + rpmtdFreeData(&fileNames); + for (i = 0; i <= dirIndex; i++) { + free(dirNames[i]); + } + free(dirNames); + free(baseNames); + free(dirIndexes); + + headerDel(h, RPMTAG_OLDFILENAMES); +} + +static void expandFilelist(Header h) +{ + struct rpmtd_s filenames; + + if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) { + (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT); + if (rpmtdCount(&filenames) < 1) + return; + rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES); + headerPut(h, &filenames, HEADERPUT_DEFAULT); + rpmtdFreeData(&filenames); + } + + (void) headerDel(h, RPMTAG_DIRNAMES); + (void) headerDel(h, RPMTAG_BASENAMES); + (void) headerDel(h, RPMTAG_DIRINDEXES); +} + +/* + * Up to rpm 3.0.4, packages implicitly provided their own name-version-release. + * Retrofit an explicit "Provides: name = epoch:version-release. + */ +static void providePackageNVR(Header h) +{ + const char *name = headerGetString(h, RPMTAG_NAME); + char *pEVR = headerGetAsString(h, RPMTAG_EVR); + rpmsenseFlags pFlags = RPMSENSE_EQUAL; + int bingo = 1; + struct rpmtd_s pnames; + rpmds hds, nvrds; + + /* Generate provides for this package name-version-release. */ + if (!(name && pEVR)) + return; + + /* + * Rpm prior to 3.0.3 does not have versioned provides. + * If no provides at all are available, we can just add. + */ + if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) { + goto exit; + } + + /* + * Otherwise, fill in entries on legacy packages. + */ + if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) { + while (rpmtdNext(&pnames) >= 0) { + rpmsenseFlags fdummy = RPMSENSE_ANY; + + headerPutString(h, RPMTAG_PROVIDEVERSION, ""); + headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1); + } + goto exit; + } + + /* see if we already have this provide */ + hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0); + nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags); + if (rpmdsFind(hds, nvrds) >= 0) { + bingo = 0; + } + rpmdsFree(hds); + rpmdsFree(nvrds); + + +exit: + if (bingo) { + headerPutString(h, RPMTAG_PROVIDENAME, name); + headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR); + headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1); + } + rpmtdFreeData(&pnames); + free(pEVR); +} + +static void legacyRetrofit(Header h) +{ + /* + * The file list was moved to a more compressed format which not + * only saves memory (nice), but gives fingerprinting a nice, fat + * speed boost (very nice). Go ahead and convert old headers to + * the new style (this is a noop for new headers). + */ + compressFilelist(h); + + /* Retrofit "Provide: name = EVR" for binary packages. */ + if (!headerIsSource(h)) { + providePackageNVR(h); + } +} + +int headerConvert(Header h, int op) +{ + int rc = 1; + + if (h == NULL) + return 0; + + switch (op) { + case HEADERCONV_EXPANDFILELIST: + expandFilelist(h); + break; + case HEADERCONV_COMPRESSFILELIST: + compressFilelist(h); + break; + case HEADERCONV_RETROFIT_V3: + legacyRetrofit(h); + break; + default: + rc = 0; + break; + } + return rc; +}; + diff --git a/lib/legacy.c b/lib/legacy.c deleted file mode 100644 index 422c2b0e8..000000000 --- a/lib/legacy.c +++ /dev/null @@ -1,379 +0,0 @@ -/** - * \file lib/legacy.c - */ - -#include "system.h" - -#include <rpm/header.h> -#include <rpm/rpmmacro.h> -#include <rpm/rpmstring.h> -#include <rpm/rpmfi.h> -#include <rpm/rpmds.h> - -#include "debug.h" - -static int dncmp(const void * a, const void * b) -{ - const char *const * first = a; - const char *const * second = b; - return strcmp(*first, *second); -} - -static void compressFilelist(Header h) -{ - struct rpmtd_s fileNames; - char ** dirNames; - const char ** baseNames; - uint32_t * dirIndexes; - rpm_count_t count; - int i; - int dirIndex = -1; - - /* - * This assumes the file list is already sorted, and begins with a - * single '/'. That assumption isn't critical, but it makes things go - * a bit faster. - */ - - if (headerIsEntry(h, RPMTAG_DIRNAMES)) { - headerDel(h, RPMTAG_OLDFILENAMES); - return; /* Already converted. */ - } - - if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) - return; - count = rpmtdCount(&fileNames); - if (count < 1) - return; - - dirNames = xmalloc(sizeof(*dirNames) * count); /* worst case */ - baseNames = xmalloc(sizeof(*dirNames) * count); - dirIndexes = xmalloc(sizeof(*dirIndexes) * count); - - /* HACK. Source RPM, so just do things differently */ - { const char *fn = rpmtdGetString(&fileNames); - if (fn && *fn != '/') { - dirIndex = 0; - dirNames[dirIndex] = xstrdup(""); - while ((i = rpmtdNext(&fileNames)) >= 0) { - dirIndexes[i] = dirIndex; - baseNames[i] = rpmtdGetString(&fileNames); - } - goto exit; - } - } - - /* - * XXX EVIL HACK, FIXME: - * This modifies (and then restores) a const string from rpmtd - * through basename retrieved from strrchr() which silently - * casts away const on return. - */ - while ((i = rpmtdNext(&fileNames)) >= 0) { - char ** needle; - char savechar; - char * baseName; - size_t len; - char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */ - - if (filename == NULL) /* XXX can't happen */ - continue; - baseName = strrchr(filename, '/') + 1; - len = baseName - filename; - needle = dirNames; - savechar = *baseName; - *baseName = '\0'; - if (dirIndex < 0 || - (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) { - char *s = xmalloc(len + 1); - rstrlcpy(s, filename, len + 1); - dirIndexes[i] = ++dirIndex; - dirNames[dirIndex] = s; - } else - dirIndexes[i] = needle - dirNames; - - *baseName = savechar; - baseNames[i] = baseName; - } - -exit: - if (count > 0) { - headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count); - headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count); - headerPutStringArray(h, RPMTAG_DIRNAMES, - (const char **) dirNames, dirIndex + 1); - } - - rpmtdFreeData(&fileNames); - for (i = 0; i <= dirIndex; i++) { - free(dirNames[i]); - } - free(dirNames); - free(baseNames); - free(dirIndexes); - - headerDel(h, RPMTAG_OLDFILENAMES); -} - -static void expandFilelist(Header h) -{ - struct rpmtd_s filenames; - - if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) { - (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT); - if (rpmtdCount(&filenames) < 1) - return; - rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES); - headerPut(h, &filenames, HEADERPUT_DEFAULT); - rpmtdFreeData(&filenames); - } - - (void) headerDel(h, RPMTAG_DIRNAMES); - (void) headerDel(h, RPMTAG_BASENAMES); - (void) headerDel(h, RPMTAG_DIRINDEXES); -} - -/* - * Up to rpm 3.0.4, packages implicitly provided their own name-version-release. - * Retrofit an explicit "Provides: name = epoch:version-release. - */ -static void providePackageNVR(Header h) -{ - const char *name = headerGetString(h, RPMTAG_NAME); - char *pEVR = headerGetAsString(h, RPMTAG_EVR); - rpmsenseFlags pFlags = RPMSENSE_EQUAL; - int bingo = 1; - struct rpmtd_s pnames; - rpmds hds, nvrds; - - /* Generate provides for this package name-version-release. */ - if (!(name && pEVR)) - return; - - /* - * Rpm prior to 3.0.3 does not have versioned provides. - * If no provides at all are available, we can just add. - */ - if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) { - goto exit; - } - - /* - * Otherwise, fill in entries on legacy packages. - */ - if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) { - while (rpmtdNext(&pnames) >= 0) { - rpmsenseFlags fdummy = RPMSENSE_ANY; - - headerPutString(h, RPMTAG_PROVIDEVERSION, ""); - headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1); - } - goto exit; - } - - /* see if we already have this provide */ - hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0); - nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags); - if (rpmdsFind(hds, nvrds) >= 0) { - bingo = 0; - } - rpmdsFree(hds); - rpmdsFree(nvrds); - - -exit: - if (bingo) { - headerPutString(h, RPMTAG_PROVIDENAME, name); - headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR); - headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1); - } - rpmtdFreeData(&pnames); - free(pEVR); -} - -static void legacyRetrofit(Header h) -{ - /* - * The file list was moved to a more compressed format which not - * only saves memory (nice), but gives fingerprinting a nice, fat - * speed boost (very nice). Go ahead and convert old headers to - * the new style (this is a noop for new headers). - */ - compressFilelist(h); - - /* Retrofit "Provide: name = EVR" for binary packages. */ - if (!headerIsSource(h)) { - providePackageNVR(h); - } -} - -int headerConvert(Header h, int op) -{ - int rc = 1; - - if (h == NULL) - return 0; - - switch (op) { - case HEADERCONV_EXPANDFILELIST: - expandFilelist(h); - break; - case HEADERCONV_COMPRESSFILELIST: - compressFilelist(h); - break; - case HEADERCONV_RETROFIT_V3: - legacyRetrofit(h); - break; - default: - rc = 0; - break; - } - return rc; -}; - -/* - * Backwards compatibility wrappers for legacy interfaces. - * Remove these some day... - */ -#define _RPM_4_4_COMPAT -#include <rpm/rpmlegacy.h> - -/* dumb macro to avoid 50 copies of this code while converting... */ -#define TDWRAP() \ - if (type) \ - *type = td.type; \ - if (p) \ - *p = td.data; \ - else \ - rpmtdFreeData(&td); \ - if (c) \ - *c = td.count - -int headerRemoveEntry(Header h, rpm_tag_t tag) -{ - return headerDel(h, tag); -} - -static void *_headerFreeData(rpm_data_t data, rpm_tagtype_t type) -{ - if (data) { - if (type == RPM_FORCEFREE_TYPE || - type == RPM_STRING_ARRAY_TYPE || - type == RPM_I18NSTRING_TYPE || - type == RPM_BIN_TYPE) - free(data); - } - return NULL; -} - -void * headerFreeData(rpm_data_t data, rpm_tagtype_t type) -{ - return _headerFreeData(data, type); -} - -void * headerFreeTag(Header h, rpm_data_t data, rpm_tagtype_t type) -{ - return _headerFreeData(data, type); -} - -static int headerGetWrap(Header h, rpm_tag_t tag, - rpm_tagtype_t * type, - rpm_data_t * p, - rpm_count_t * c, - headerGetFlags flags) -{ - struct rpmtd_s td; - int rc; - - rc = headerGet(h, tag, &td, flags); - TDWRAP(); - return rc; -} - -int headerGetEntry(Header h, rpm_tag_t tag, - rpm_tagtype_t * type, - rpm_data_t * p, - rpm_count_t * c) -{ - return headerGetWrap(h, tag, type, p, c, HEADERGET_DEFAULT); -} - -int headerGetEntryMinMemory(Header h, rpm_tag_t tag, - rpm_tagtype_t * type, - rpm_data_t * p, - rpm_count_t * c) -{ - return headerGetWrap(h, tag, type, (rpm_data_t) p, c, HEADERGET_MINMEM); -} - -/* XXX shut up compiler warning from missing prototype */ -int headerGetRawEntry(Header h, rpm_tag_t tag, rpm_tagtype_t * type, rpm_data_t * p, - rpm_count_t * c); - -int headerGetRawEntry(Header h, rpm_tag_t tag, rpm_tagtype_t * type, rpm_data_t * p, - rpm_count_t * c) -{ - if (p == NULL) - return headerIsEntry(h, tag); - - return headerGetWrap(h, tag, type, p, c, HEADERGET_RAW); -} - -int headerNextIterator(HeaderIterator hi, - rpm_tag_t * tag, - rpm_tagtype_t * type, - rpm_data_t * p, - rpm_count_t * c) -{ - struct rpmtd_s td; - int rc; - - rc = headerNext(hi, &td); - if (tag) - *tag = td.tag; - TDWRAP(); - return rc; -} - -int headerModifyEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type, - rpm_constdata_t p, rpm_count_t c) -{ - struct rpmtd_s td = { - .tag = tag, - .type = type, - .data = (void *) p, - .count = c, - }; - return headerMod(h, &td); -} - -static int headerPutWrap(Header h, rpm_tag_t tag, rpm_tagtype_t type, - rpm_constdata_t p, rpm_count_t c, headerPutFlags flags) -{ - struct rpmtd_s td = { - .tag = tag, - .type = type, - .data = (void *) p, - .count = c, - }; - return headerPut(h, &td, flags); -} - -int headerAddOrAppendEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type, - rpm_constdata_t p, rpm_count_t c) -{ - return headerPutWrap(h, tag, type, p, c, HEADERPUT_APPEND); -} - -int headerAppendEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type, - rpm_constdata_t p, rpm_count_t c) -{ - return headerPutWrap(h, tag, type, p, c, HEADERPUT_APPEND); -} - -int headerAddEntry(Header h, rpm_tag_t tag, rpm_tagtype_t type, - rpm_constdata_t p, rpm_count_t c) -{ - return headerPutWrap(h, tag, type, p, c, HEADERPUT_DEFAULT); -} -#undef _RPM_4_4_COMPAT diff --git a/lib/manifest.c b/lib/manifest.c index 5d71f3f25..71c4c912c 100644 --- a/lib/manifest.c +++ b/lib/manifest.c @@ -94,8 +94,8 @@ rpmRC rpmReadPackageManifest(FD_t fd, int * argcPtr, char *** argvPtr) s++; if (*s == '\0') continue; - /* Sanity checks: skip obviously binary lines and dash (for stdin) */ - if (*s < 32 || rstreq(s, "-")) { + /* Sanity checks: skip obviously binary lines */ + if (*s < 32) { s = NULL; rpmrc = RPMRC_NOTFOUND; goto exit; @@ -117,6 +117,14 @@ rpmRC rpmReadPackageManifest(FD_t fd, int * argcPtr, char *** argvPtr) rpmrc = (rpmGlob(s, &ac, &av) == 0 ? RPMRC_OK : RPMRC_FAIL); if (rpmrc != RPMRC_OK) goto exit; + /* Sanity check: skip dash (for stdin) */ + for (i = 0; i < ac; i++) { + if (rstreq(av[i], "-")) { + rpmrc = RPMRC_NOTFOUND; + goto exit; + } + } + rpmlog(RPMLOG_DEBUG, "adding %d args from manifest.\n", ac); /* Count non-NULL args, keeping track of 1st arg after last NULL. */ diff --git a/lib/merge.c b/lib/merge.c deleted file mode 100644 index 738ad7a68..000000000 --- a/lib/merge.c +++ /dev/null @@ -1,347 +0,0 @@ -#ifndef __APPLE__ -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Peter McIlroy. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)merge.c 8.2 (Berkeley) 2/14/94"; -#endif /* LIBC_SCCS and not lint */ - -/* - * Hybrid exponential search/linear search merge sort with hybrid - * natural/pairwise first pass. Requires about .3% more comparisons - * for random data than LSMS with pairwise first pass alone. - * It works for objects as small as two bytes. - */ - -#define NATURAL -#define THRESHOLD 16 /* Best choice for natural merge cut-off. */ - -/* #define NATURAL to get hybrid natural merge. - * (The default is pairwise merging.) - */ - -#include "system.h" -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include "lib/rpmdb_internal.h" /* XXX for mergesort */ - -#define ISIZE sizeof(int) -#define PSIZE sizeof(unsigned char *) -#define ICOPY_LIST(src, dst, last) \ - do \ - *(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \ - while(src < last) -#define ICOPY_ELT(src, dst, i) \ - do \ - *(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \ - while (i -= ISIZE) - -#define CCOPY_LIST(src, dst, last) \ - do \ - *dst++ = *src++; \ - while (src < last) -#define CCOPY_ELT(src, dst, i) \ - do \ - *dst++ = *src++; \ - while (i -= 1) - -/* - * Find the next possible pointer head. (Trickery for forcing an array - * to do double duty as a linked list when objects do not align with word - * boundaries. - */ -/* Assumption: PSIZE is a power of 2. */ -#define EVAL(p) (unsigned char **) \ - ((unsigned char *)0 + \ - (((unsigned char *)p + PSIZE - 1 - (unsigned char *) 0) & ~(PSIZE - 1))) - -#define swap(a, b) { \ - s = b; \ - i = size; \ - do { \ - tmp = *a; *a++ = *s; *s++ = tmp; \ - } while (--i); \ - a -= size; \ - } -#define reverse(bot, top) { \ - s = top; \ - do { \ - i = size; \ - do { \ - tmp = *bot; *bot++ = *s; *s++ = tmp; \ - } while (--i); \ - s -= size2; \ - } while(bot < s); \ -} - -/* - * This is to avoid out-of-bounds addresses in sorting the - * last 4 elements. - */ -static void -insertionsort(unsigned char *a, size_t n, size_t size, - int (*cmp) (const void *, const void *)) -{ - unsigned char *ai, *s, *t, *u, tmp; - int i; - - for (ai = a+size; --n >= 1; ai += size) - for (t = ai; t > a; t -= size) { - u = t - size; - if (cmp(u, t) <= 0) - break; - swap(u, t); - } -} - -/* - * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of - * increasing order, list2 in a corresponding linked list. Checks for runs - * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL - * is defined. Otherwise simple pairwise merging is used.) - */ -static void -setup(unsigned char *list1, unsigned char *list2, - size_t n, size_t size, int (*cmp) (const void *, const void *)) -{ - int i, length, size2, tmp, sense; - unsigned char *f1, *f2, *s, *l2, *last, *p2; - - size2 = size*2; - if (n <= 5) { - insertionsort(list1, n, size, cmp); - *EVAL(list2) = (unsigned char*) list2 + n*size; - return; - } - /* - * Avoid running pointers out of bounds; limit n to evens - * for simplicity. - */ - i = 4 + (n & 1); - insertionsort(list1 + (n - i) * size, i, size, cmp); - last = list1 + size * (n - i); - *EVAL(list2 + (last - list1)) = list2 + n * size; - -#ifdef NATURAL - p2 = list2; - f1 = list1; - sense = (cmp(f1, f1 + size) > 0); - for (; f1 < last; sense = !sense) { - length = 2; - /* Find pairs with same sense. */ - for (f2 = f1 + size2; f2 < last; f2 += size2) { - if ((cmp(f2, f2+ size) > 0) != sense) - break; - length += 2; - } - if (length < THRESHOLD) { /* Pairwise merge */ - do { - p2 = *EVAL(p2) = f1 + size2 - list1 + list2; - if (sense > 0) - swap (f1, f1 + size); - } while ((f1 += size2) < f2); - } else { /* Natural merge */ - l2 = f2; - for (f2 = f1 + size2; f2 < l2; f2 += size2) { - if ((cmp(f2-size, f2) > 0) != sense) { - p2 = *EVAL(p2) = f2 - list1 + list2; - if (sense > 0) - reverse(f1, f2-size); - f1 = f2; - } - } - if (sense > 0) - reverse (f1, f2-size); - f1 = f2; - if (f2 < last || cmp(f2 - size, f2) > 0) - p2 = *EVAL(p2) = f2 - list1 + list2; - else - p2 = *EVAL(p2) = list2 + n*size; - } - } -#else /* pairwise merge only. */ - for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { - p2 = *EVAL(p2) = p2 + size2; - if (cmp (f1, f1 + size) > 0) - swap(f1, f1 + size); - } -#endif /* NATURAL */ -} - -/* - * Arguments are as for qsort. - */ -int -mergesort(void *base, size_t nmemb, size_t size, - int (*cmp) (const void *, const void *)) -{ - register int i, sense; - int big, iflag; - register unsigned char *f1, *f2, *t, *b, *q, *l1, *l2; - register unsigned char *tp2; - unsigned char *list2; - unsigned char *list1; - unsigned char *p2, *p, *last, **p1; - - if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ - errno = EINVAL; - return (-1); - } - - if (nmemb == 0) - return (0); - - /* - * XXX - * Stupid subtraction for the Cray. - */ - iflag = 0; - if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) - iflag = 1; - - if ((list2 = malloc(nmemb * size + PSIZE)) == NULL) - return (-1); - - list1 = base; - setup(list1, list2, nmemb, size, cmp); - last = list2 + nmemb * size; - i = big = 0; - while (*EVAL(list2) != last) { - l2 = list1; - p1 = EVAL(list1); - for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { - p2 = *EVAL(p2); - f1 = l2; - f2 = l1 = list1 + (p2 - list2); - if (p2 != last) - p2 = *EVAL(p2); - l2 = list1 + (p2 - list2); - while (f1 < l1 && f2 < l2) { - if ((*cmp)(f1, f2) <= 0) { - q = f2; - b = f1, t = l1; - sense = -1; - } else { - q = f1; - b = f2, t = l2; - sense = 0; - } - if (!big) { /* here i = 0 */ - while ((b += size) < t && cmp(q, b) >sense) - if (++i == 6) { - big = 1; - goto EXPONENTIAL; - } - } else { -EXPONENTIAL: for (i = size; ; i <<= 1) - if ((p = (b + i)) >= t) { - if ((p = t - size) > b && - (*cmp)(q, p) <= sense) - t = p; - else - b = p; - break; - } else if ((*cmp)(q, p) <= sense) { - t = p; - if (i == size) - big = 0; - goto FASTCASE; - } else - b = p; - while (t > b+size) { - i = (((t - b) / size) >> 1) * size; - if ((*cmp)(q, p = b + i) <= sense) - t = p; - else - b = p; - } - goto COPY; -FASTCASE: while (i > size) - if ((*cmp)(q, - p = b + (i >>= 1)) <= sense) - t = p; - else - b = p; -COPY: b = t; - } - i = size; - if (q == f1) { - if (iflag) { - ICOPY_LIST(f2, tp2, b); - ICOPY_ELT(f1, tp2, i); - } else { - CCOPY_LIST(f2, tp2, b); - CCOPY_ELT(f1, tp2, i); - } - } else { - if (iflag) { - ICOPY_LIST(f1, tp2, b); - ICOPY_ELT(f2, tp2, i); - } else { - CCOPY_LIST(f1, tp2, b); - CCOPY_ELT(f2, tp2, i); - } - } - } - if (f2 < l2) { - if (iflag) - ICOPY_LIST(f2, tp2, l2); - else - CCOPY_LIST(f2, tp2, l2); - } else if (f1 < l1) { - if (iflag) - ICOPY_LIST(f1, tp2, l1); - else - CCOPY_LIST(f1, tp2, l1); - } - *p1 = l2; - } - tp2 = list1; /* swap list1, list2 */ - list1 = list2; - list2 = tp2; - last = list2 + nmemb*size; - } - if (base == list2) { - memmove(list2, list1, nmemb*size); - list2 = list1; - } - free(list2); - return (0); -} -#else -/* mergesort is implemented in System on Mac OS X */ -#endif /* __APPLE__ */ diff --git a/lib/misc.h b/lib/misc.h index 15c9e3111..74e94a2e7 100644 --- a/lib/misc.h +++ b/lib/misc.h @@ -9,6 +9,9 @@ #include <string.h> #include <rpm/rpmtypes.h> #include <rpm/header.h> /* for headerGetFlags typedef, duh.. */ +#include "lib/rpmfs.h" + +typedef const struct headerFmt_s * headerFmt; #ifdef __cplusplus extern "C" { @@ -24,18 +27,43 @@ char * rpmVerifyString(uint32_t verifyResult, const char *pad); RPM_GNUC_INTERNAL char * rpmFFlagsString(uint32_t fflags, const char *pad); -typedef char * (*headerTagFormatFunction) (rpmtd td); typedef int (*headerTagTagFunction) (Header h, rpmtd td, headerGetFlags hgflags); RPM_GNUC_INTERNAL headerTagTagFunction rpmHeaderTagFunc(rpmTagVal tag); RPM_GNUC_INTERNAL -headerTagFormatFunction rpmHeaderFormatFuncByName(const char *fmt); +headerFmt rpmHeaderFormatByName(const char *fmt); + +RPM_GNUC_INTERNAL +headerFmt rpmHeaderFormatByValue(rpmtdFormats fmt); + +RPM_GNUC_INTERNAL +char * rpmHeaderFormatCall(headerFmt fmt, rpmtd td); + +RPM_GNUC_INTERNAL +int headerFindSpec(Header h); + +/** + * Relocate files in header. + * @todo multilib file dispositions need to be checked. + * @param relocs relocations + * @param numRelocations number of relocations + * @param fs file state set + * @param h package header to relocate + */ +RPM_GNUC_INTERNAL +void rpmRelocateFileList(rpmRelocation *relocs, int numRelocations, rpmfs fs, Header h); RPM_GNUC_INTERNAL -headerTagFormatFunction rpmHeaderFormatFuncByValue(rpmtdFormats fmt); +int rpmRelocateSrpmFileList(Header h, const char *rootDir); +RPM_GNUC_INTERNAL +void rpmRelocationBuild(Header h, rpmRelocation *rawrelocs, + int *rnrelocs, rpmRelocation **rrelocs, uint8_t **rbadrelocs); + +RPM_GNUC_INTERNAL +void rpmAtExit(void); #ifdef __cplusplus } #endif diff --git a/lib/order.c b/lib/order.c index c0ef5470a..7a1dd10fd 100644 --- a/lib/order.c +++ b/lib/order.c @@ -67,7 +67,8 @@ static void rpmTSIFree(tsortInfo tsi) static inline int addSingleRelation(rpmte p, rpmte q, - rpmsenseFlags dsflags) + rpmsenseFlags dsflags, + int reversed) { struct tsortInfo_s *tsi_p, *tsi_q; relation rel; @@ -80,9 +81,7 @@ static inline int addSingleRelation(rpmte p, /* Erasures are reversed installs. */ if (teType == TR_REMOVED) { - rpmte r = p; - p = q; - q = r; + reversed = ! reversed; flags = isErasePreReq(dsflags); } else { flags = isInstallPreReq(dsflags); @@ -94,32 +93,62 @@ static inline int addSingleRelation(rpmte p, RPMSENSE_SCRIPT_PRE : RPMSENSE_SCRIPT_PREUN; } + if (reversed) { + rpmte r = p; + p = q; + q = r; + } + tsi_p = rpmteTSI(p); tsi_q = rpmteTSI(q); /* if relation got already added just update the flags */ - if (tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == tsi_p) { + if (!reversed && + tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == tsi_p) { + /* must be latest one added to q as we add all rels to p at once */ tsi_q->tsi_relations->rel_flags |= flags; - tsi_p->tsi_forward_relations->rel_flags |= flags; - return 0; + /* search entry in p */ + for (struct relation_s * tsi = tsi_p->tsi_forward_relations; + tsi; tsi = tsi->rel_next) { + if (tsi->rel_suc == tsi_q) { + tsi->rel_flags |= flags; + return 0; + } + } + assert(0); } - /* Record next "q <- p" relation (i.e. "p" requires "q"). */ - if (p != q) { - /* bump p predecessor count */ - tsi_p->tsi_count++; + /* if relation got already added just update the flags */ + if (reversed && tsi_q->tsi_forward_relations && + tsi_q->tsi_forward_relations->rel_suc == tsi_p) { + /* must be latest one added to q as we add all rels to p at once */ + tsi_q->tsi_forward_relations->rel_flags |= flags; + /* search entry in p */ + for (struct relation_s * tsi = tsi_p->tsi_relations; + tsi; tsi = tsi->rel_next) { + if (tsi->rel_suc == tsi_q) { + tsi->rel_flags |= flags; + return 0; + } + } + assert(0); } + /* Record next "q <- p" relation (i.e. "p" requires "q"). */ + + /* bump p predecessor count */ + tsi_p->tsi_count++; + rel = xcalloc(1, sizeof(*rel)); rel->rel_suc = tsi_p; rel->rel_flags = flags; rel->rel_next = tsi_q->tsi_relations; tsi_q->tsi_relations = rel; - if (p != q) { - /* bump q successor count */ - tsi_q->tsi_qcnt++; - } + + + /* bump q successor count */ + tsi_q->tsi_qcnt++; rel = xcalloc(1, sizeof(*rel)); rel->rel_suc = tsi_q; @@ -134,6 +163,7 @@ static inline int addSingleRelation(rpmte p, /** * Record next "q <- p" relation (i.e. "p" requires "q"). * @param ts transaction set + * @param al packages list * @param p predecessor (i.e. package that "Requires: q") * @param requires relation * @return 0 always @@ -141,7 +171,8 @@ static inline int addSingleRelation(rpmte p, static inline int addRelation(rpmts ts, rpmal al, rpmte p, - rpmds requires) + rpmds requires, + int reversed) { rpmte q; rpmsenseFlags dsflags; @@ -152,47 +183,35 @@ static inline int addRelation(rpmts ts, if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG|RPMSENSE_PRETRANS|RPMSENSE_POSTTRANS)) return 0; - q = rpmalSatisfiesDepend(al, requires); + if (rpmdsIsRich(requires)) { + rpmds ds1, ds2; + rpmrichOp op; + if (rpmdsParseRichDep(requires, &ds1, &ds2, &op, NULL) == RPMRC_OK) { + if (op != RPMRICHOP_ELSE) + addRelation(ts, al, p, ds1, reversed); + if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) { + rpmds ds21, ds22; + rpmrichOp op2; + if (rpmdsParseRichDep(requires, &ds21, &ds22, &op2, NULL) == RPMRC_OK && op2 == RPMRICHOP_ELSE) { + addRelation(ts, al, p, ds22, reversed); + } + ds21 = rpmdsFree(ds21); + ds22 = rpmdsFree(ds22); + } + if (op == RPMRICHOP_AND || op == RPMRICHOP_OR) + addRelation(ts, al, p, ds2, reversed); + ds1 = rpmdsFree(ds1); + ds2 = rpmdsFree(ds2); + } + return 0; + } + q = rpmalSatisfiesDepend(al, p, requires); /* Avoid deps outside this transaction and self dependencies */ if (q == NULL || q == p) return 0; - addSingleRelation(p, q, dsflags); - - return 0; -} - -/* - * Collections might have special ordering requirements. Notably - * sepolicy collection requires having all the bits in the collection - * close to each other. We try to ensure this by creating a strongly - * connected component of such "grouped" collections, by introducing - * an artificial relation loop across the all its members. - */ -static int addCollRelations(rpmal al, rpmte p, ARGV_t *seenColls) -{ - ARGV_const_t qcolls; - - for (qcolls = rpmteCollections(p); qcolls && *qcolls; qcolls++) { - char * flags; - if (argvSearch(*seenColls, *qcolls, NULL)) - continue; - - flags = rstrscat(NULL, "%{__collection_", *qcolls, "_flags}", NULL); - if (rpmExpandNumeric(flags) & 0x1) { - rpmte *tes = rpmalAllInCollection(al, *qcolls); - for (rpmte *te = tes; te && *te; te++) { - rpmte next = (*(te + 1) != NULL) ? *(te + 1) : *tes; - addSingleRelation(*te, next, RPMSENSE_ANY); - } - _free(tes); - } - free(flags); - - argvAdd(seenColls, *qcolls); - argvSort(*seenColls, NULL); - } + addSingleRelation(p, q, dsflags, reversed); return 0; } @@ -552,7 +571,6 @@ int rpmtsOrder(rpmts ts) scc SCCs; int nelem = rpmtsNElements(ts); tsortInfo sortInfo = xcalloc(nelem, sizeof(struct tsortInfo_s)); - ARGV_t seenColls = NULL; (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ORDER), 0); @@ -571,22 +589,43 @@ int rpmtsOrder(rpmts ts) rpmal al = (rpmteType(p) == TR_REMOVED) ? erasedPackages : tsmem->addedPackages; rpmds requires = rpmdsInit(rpmteDS(p, RPMTAG_REQUIRENAME)); + rpmds recommends = rpmdsInit(rpmteDS(p, RPMTAG_RECOMMENDNAME)); + rpmds suggests = rpmdsInit(rpmteDS(p, RPMTAG_SUGGESTNAME)); + rpmds supplements = rpmdsInit(rpmteDS(p, RPMTAG_SUPPLEMENTNAME)); + rpmds enhances = rpmdsInit(rpmteDS(p, RPMTAG_ENHANCENAME)); rpmds order = rpmdsInit(rpmteDS(p, RPMTAG_ORDERNAME)); while (rpmdsNext(requires) >= 0) { /* Record next "q <- p" relation (i.e. "p" requires "q"). */ - (void) addRelation(ts, al, p, requires); + (void) addRelation(ts, al, p, requires, 0); + } + + while (rpmdsNext(recommends) >= 0) { + /* Record next "q <- p" relation (i.e. "p" recommends "q"). */ + (void) addRelation(ts, al, p, recommends, 0); + } + + while (rpmdsNext(suggests) >= 0) { + /* Record next "q <- p" relation (i.e. "p" suggests "q"). */ + (void) addRelation(ts, al, p, suggests, 0); } while (rpmdsNext(order) >= 0) { /* Record next "q <- p" ordering request */ - (void) addRelation(ts, al, p, order); + (void) addRelation(ts, al, p, order, 0); } - addCollRelations(al, p, &seenColls); + while (rpmdsNext(supplements) >= 0) { + /* Record next "p -> q" relation (i.e. "q" supplemented by "p"). */ + (void) addRelation(ts, al, p, supplements, 1); + } + + while (rpmdsNext(enhances) >= 0) { + /* Record next "p <- q" relation (i.e. "q" is enhanced by "p"). */ + (void) addRelation(ts, al, p, enhances, 1); + } } - seenColls = argvFree(seenColls); rpmtsiFree(pi); newOrder = xcalloc(tsmem->orderCount, sizeof(*newOrder)); diff --git a/lib/package.c b/lib/package.c index 157c96900..1d9e9e138 100644 --- a/lib/package.c +++ b/lib/package.c @@ -5,6 +5,7 @@ #include "system.h" #include <netinet/in.h> +#include <pthread.h> #include <rpm/rpmlib.h> /* XXX RPMSIGTAG, other sig stuff */ #include <rpm/rpmts.h> @@ -13,26 +14,20 @@ #include <rpm/rpmkeyring.h> #include "lib/rpmlead.h" -#include "lib/signature.h" -#include "rpmio/digest.h" #include "rpmio/rpmio_internal.h" /* fd digest bits */ #include "lib/header_internal.h" /* XXX headerCheck */ +#include "lib/rpmvs.h" #include "lib/rpmplugins.h" /* rpm plugins hooks */ #include "debug.h" -static const unsigned int nkeyids_max = 256; -static unsigned int nkeyids = 0; -static unsigned int nextkeyid = 0; -static unsigned int * keyids; - /** \ingroup header * Translate and merge legacy signature tags into header. * @param h header (dest) * @param sigh signature header (src) */ -static void headerMergeLegacySigs(Header h, Header sigh) +void headerMergeLegacySigs(Header h, Header sigh) { HeaderIterator hi; struct rpmtd_s td; @@ -61,6 +56,7 @@ static void headerMergeLegacySigs(Header h, Header sigh) td.tag = RPMTAG_ARCHIVESIZE; break; case RPMSIGTAG_SHA1: + case RPMSIGTAG_SHA256: case RPMSIGTAG_DSA: case RPMSIGTAG_RSA: default: @@ -68,13 +64,8 @@ static void headerMergeLegacySigs(Header h, Header sigh) continue; break; } - if (td.data == NULL) continue; /* XXX can't happen */ if (!headerIsEntry(h, td.tag)) { - if (hdrchkType(td.type)) - continue; - if (td.count < 0 || hdrchkData(td.count)) - continue; - switch(td.type) { + switch (td.type) { case RPM_NULL_TYPE: continue; break; @@ -104,25 +95,34 @@ static void headerMergeLegacySigs(Header h, Header sigh) /** * Remember current key id. - * @param dig OpenPGP packet containter + * XXX: This s*** needs to die. Hook it into keyring or sumthin... + * @param keyid signature keyid * @return 0 if new keyid, otherwise 1 */ -static int stashKeyid(pgpDigParams sigp) +static int stashKeyid(unsigned int keyid) { - unsigned int keyid; + static pthread_mutex_t keyid_lock = PTHREAD_MUTEX_INITIALIZER; + static const unsigned int nkeyids_max = 256; + static unsigned int nkeyids = 0; + static unsigned int nextkeyid = 0; + static unsigned int * keyids; + int i; + int seen = 0; - if (sigp == NULL) + if (keyid == 0) return 0; - keyid = pgpGrab(sigp->signid+4, 4); - if (keyid == 0) + /* Just pretend we didn't see the keyid if we fail to lock */ + if (pthread_mutex_lock(&keyid_lock)) return 0; if (keyids != NULL) for (i = 0; i < nkeyids; i++) { - if (keyid == keyids[i]) - return 1; + if (keyid == keyids[i]) { + seen = 1; + goto exit; + } } if (nkeyids < nkeyids_max) { @@ -134,346 +134,86 @@ static int stashKeyid(pgpDigParams sigp) nextkeyid++; nextkeyid %= nkeyids_max; - return 0; +exit: + pthread_mutex_unlock(&keyid_lock); + return seen; } -int parsePGPSig(rpmtd sigtd, const char *type, const char *fn, - pgpDigParams *sig) +static rpmRC handleHdrVS(struct rpmsinfo_s *sinfo, rpmRC rc, const char *msg, void *cbdata) { - int rc = pgpPrtParams(sigtd->data, sigtd->count, PGPTAG_SIGNATURE, sig); - - if (rc != 0) { - if (type && fn) { - rpmlog(RPMLOG_ERR, - _("skipping %s %s with unverifiable signature\n"), type, fn); - } else if (type) { - rpmlog(RPMLOG_ERR, - _("skipping %s with unverifiable signature\n"), type); - } + char **buf = cbdata; + if (buf) { + char *vsmsg = rpmsinfoMsg(sinfo, rc, msg); + *buf = rstrscat(buf, "\n", vsmsg, NULL); + free(vsmsg); } return rc; } -/* - * Argument monster to verify header-only signature/digest if there is - * one, otherwisereturn RPMRC_NOTFOUND to signal for plain sanity check. - */ -static rpmRC headerSigVerify(rpmKeyring keyring, rpmVSFlags vsflags, - int il, int dl, int ril, int rdl, - entryInfo pe, unsigned char * dataStart, - char **buf) +static void updateHdrDigests(rpmDigestBundle bundle, struct hdrblob_s *blob) { - size_t siglen = 0; - rpmRC rc = RPMRC_FAIL; - pgpDigParams sig = NULL; - struct rpmtd_s sigtd; - struct entryInfo_s info, einfo; - unsigned int hashalgo = 0; - - rpmtdReset(&sigtd); - memset(&info, 0, sizeof(info)); - memset(&einfo, 0, sizeof(einfo)); - - /* Find a header-only digest/signature tag. */ - for (int i = ril; i < il; i++) { - if (headerVerifyInfo(1, dl, pe+i, &einfo, 0) != -1) { - rasprintf(buf, - _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"), - i, einfo.tag, einfo.type, - einfo.offset, einfo.count); - goto exit; - } + int32_t ildl[2] = { htonl(blob->ril), htonl(blob->rdl) }; - switch (einfo.tag) { - case RPMTAG_SHA1HEADER: { - size_t blen = 0; - unsigned const char * b; - if (vsflags & RPMVSF_NOSHA1HEADER) - break; - for (b = dataStart + einfo.offset; *b != '\0'; b++) { - if (strchr("0123456789abcdefABCDEF", *b) == NULL) - break; - blen++; - } - if (einfo.type != RPM_STRING_TYPE || *b != '\0' || blen != 40) - { - rasprintf(buf, _("hdr SHA1: BAD, not hex\n")); - goto exit; - } - if (info.tag == 0) { - info = einfo; /* structure assignment */ - siglen = blen + 1; - } - } break; - case RPMTAG_RSAHEADER: - if (vsflags & RPMVSF_NORSAHEADER) - break; - if (einfo.type != RPM_BIN_TYPE) { - rasprintf(buf, _("hdr RSA: BAD, not binary\n")); - goto exit; - } - info = einfo; /* structure assignment */ - siglen = info.count; - break; - case RPMTAG_DSAHEADER: - if (vsflags & RPMVSF_NODSAHEADER) - break; - if (einfo.type != RPM_BIN_TYPE) { - rasprintf(buf, _("hdr DSA: BAD, not binary\n")); - goto exit; - } - info = einfo; /* structure assignment */ - siglen = info.count; - break; - default: - break; - } - } - - /* No header-only digest/signature found, get outta here */ - if (info.tag == 0) { - rc = RPMRC_NOTFOUND; - goto exit; - } - - sigtd.tag = info.tag; - sigtd.type = info.type; - sigtd.count = info.count; - sigtd.data = memcpy(xmalloc(siglen), dataStart + info.offset, siglen); - sigtd.flags = RPMTD_ALLOCED; - - switch (info.tag) { - case RPMTAG_RSAHEADER: - case RPMTAG_DSAHEADER: - if (parsePGPSig(&sigtd, "header", NULL, &sig)) - goto exit; - hashalgo = pgpDigParamsAlgo(sig, PGPVAL_HASHALGO); - break; - case RPMTAG_SHA1HEADER: - hashalgo = PGPHASHALGO_SHA1; - break; - default: - break; - } - - if (hashalgo) { - DIGEST_CTX ctx = rpmDigestInit(hashalgo, RPMDIGEST_NONE); - int32_t ildl[2] = { htonl(ril), htonl(rdl) }; - - rpmDigestUpdate(ctx, rpm_header_magic, sizeof(rpm_header_magic)); - rpmDigestUpdate(ctx, ildl, sizeof(ildl)); - rpmDigestUpdate(ctx, pe, (ril * sizeof(*pe))); - rpmDigestUpdate(ctx, dataStart, rdl); - - rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, buf); - - rpmDigestFinal(ctx, NULL, NULL, 0); - } - -exit: - rpmtdFreeData(&sigtd); - pgpDigParamsFree(sig); - - return rc; + rpmDigestBundleUpdate(bundle, rpm_header_magic, sizeof(rpm_header_magic)); + rpmDigestBundleUpdate(bundle, ildl, sizeof(ildl)); + rpmDigestBundleUpdate(bundle, blob->pe, (blob->ril * sizeof(*blob->pe))); + rpmDigestBundleUpdate(bundle, blob->dataStart, blob->rdl); } -static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags, - const void * uh, size_t uc, char ** msg) +rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg) { - char *buf = NULL; - int32_t * ei = (int32_t *) uh; - int32_t il = ntohl(ei[0]); - int32_t dl = ntohl(ei[1]); - entryInfo pe = (entryInfo) &ei[2]; - int32_t pvlen = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl; - unsigned char * dataStart = (unsigned char *) (pe + il); - struct indexEntry_s entry; - struct entryInfo_s info; - int32_t ril = 0; - unsigned char * regionEnd = NULL; - rpmRC rc = RPMRC_FAIL; /* assume failure */ - - /* Is the blob the right size? */ - if (uc > 0 && pvlen != uc) { - rasprintf(&buf, _("blob size(%d): BAD, 8 + 16 * il(%d) + dl(%d)\n"), - (int)uc, (int)il, (int)dl); - goto exit; - } - - memset(&entry, 0, sizeof(entry)); - memset(&info, 0, sizeof(info)); - - /* Check (and convert) the 1st tag element. */ - if (headerVerifyInfo(1, dl, pe, &entry.info, 0) != -1) { - rasprintf(&buf, _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"), - 0, entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - - /* Is there an immutable header region tag? */ - if (!(entry.info.tag == RPMTAG_HEADERIMMUTABLE)) { - rc = RPMRC_NOTFOUND; - goto exit; - } + rpmRC rc = RPMRC_FAIL; + rpmVSFlags vsflags = rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD; + rpmKeyring keyring = rpmtsGetKeyring(ts, 1); + struct hdrblob_s blob; - /* Is the region tag sane? */ - if (!(entry.info.type == REGION_TAG_TYPE && - entry.info.count == REGION_TAG_COUNT)) { - rasprintf(&buf, - _("region tag: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } + if (hdrblobInit(uh, uc, 0, 0, &blob, msg) == RPMRC_OK) { + struct rpmvs_s *vs = rpmvsCreate(&blob, vsflags); + rpmDigestBundle bundle = rpmDigestBundleNew(); - /* Is the trailer within the data area? */ - if (entry.info.offset + REGION_TAG_COUNT > dl) { - rasprintf(&buf, - _("region offset: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } + rpmswEnter(rpmtsOp(ts, RPMTS_OP_DIGEST), 0); - /* Is there an immutable header region tag trailer? */ - regionEnd = dataStart + entry.info.offset; - (void) memcpy(&info, regionEnd, REGION_TAG_COUNT); - regionEnd += REGION_TAG_COUNT; + rpmvsInitDigests(vs, RPMSIG_HEADER, bundle); + updateHdrDigests(bundle, &blob); - if (headerVerifyInfo(1, il * sizeof(*pe), &info, &entry.info, 1) != -1 || - !(entry.info.tag == RPMTAG_HEADERIMMUTABLE - && entry.info.type == REGION_TAG_TYPE - && entry.info.count == REGION_TAG_COUNT)) - { - rasprintf(&buf, - _("region trailer: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - memset(&info, 0, sizeof(info)); + rc = rpmvsVerifyItems(rpmtsPlugins(ts), vs, RPMSIG_HEADER, bundle, keyring, + handleHdrVS, msg); - /* Is the no. of tags in the region less than the total no. of tags? */ - ril = entry.info.offset/sizeof(*pe); - if ((entry.info.offset % sizeof(*pe)) || ril > il) { - rasprintf(&buf, _("region size: BAD, ril(%d) > il(%d)\n"), ril, il); - goto exit; - } + rpmswExit(rpmtsOp(ts, RPMTS_OP_DIGEST), uc); - /* Verify header-only digest/signature if there is one we can use. */ - rc = headerSigVerify(keyring, vsflags, - il, dl, ril, (regionEnd - dataStart), - pe, dataStart, &buf); + if (rc == RPMRC_OK && msg != NULL && *msg == NULL) + rasprintf(msg, "Header sanity check: OK"); -exit: - /* If no header-only digest/signature, then do simple sanity check. */ - if (rc == RPMRC_NOTFOUND) { - int xx = headerVerifyInfo(ril-1, dl, pe+1, &entry.info, 0); - if (xx != -1) { - rasprintf(&buf, - _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"), - xx+1, entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - rc = RPMRC_FAIL; - } else { - rasprintf(&buf, "Header sanity check: OK\n"); - rc = RPMRC_OK; - } + rpmDigestBundleFree(bundle); + rpmvsFree(vs); } - if (msg) - *msg = buf; - else - free(buf); - - return rc; -} - -rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg) -{ - rpmRC rc; - rpmVSFlags vsflags = rpmtsVSFlags(ts); - rpmKeyring keyring = rpmtsGetKeyring(ts, 1); - - rpmswEnter(rpmtsOp(ts, RPMTS_OP_DIGEST), 0); - rc = headerVerify(keyring, vsflags, uh, uc, msg); - rpmswExit(rpmtsOp(ts, RPMTS_OP_DIGEST), uc); rpmKeyringFree(keyring); return rc; } -static rpmRC rpmpkgReadHeader(rpmKeyring keyring, rpmVSFlags vsflags, - FD_t fd, Header *hdrp, char ** msg) +rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg) { char *buf = NULL; - int32_t block[4]; - int32_t il; - int32_t dl; - int32_t * ei = NULL; - size_t uc; - size_t nb; + struct hdrblob_s blob; Header h = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ - int xx; if (hdrp) *hdrp = NULL; if (msg) *msg = NULL; - memset(block, 0, sizeof(block)); - if ((xx = Freadall(fd, block, sizeof(block))) != sizeof(block)) { - rasprintf(&buf, - _("hdr size(%d): BAD, read returned %d\n"), (int)sizeof(block), xx); - goto exit; - } - if (memcmp(block, rpm_header_magic, sizeof(rpm_header_magic))) { - rasprintf(&buf, _("hdr magic: BAD\n")); - goto exit; - } - il = ntohl(block[2]); - if (hdrchkTags(il)) { - rasprintf(&buf, _("hdr tags: BAD, no. of tags(%d) out of range\n"), il); - goto exit; - } - dl = ntohl(block[3]); - if (hdrchkData(dl)) { - rasprintf(&buf, - _("hdr data: BAD, no. of bytes(%d) out of range\n"), dl); - goto exit; - } - - nb = (il * sizeof(struct entryInfo_s)) + dl; - uc = sizeof(il) + sizeof(dl) + nb; - ei = xmalloc(uc); - ei[0] = block[2]; - ei[1] = block[3]; - if ((xx = Freadall(fd, (char *)&ei[2], nb)) != nb) { - rasprintf(&buf, _("hdr blob(%zd): BAD, read returned %d\n"), nb, xx); - goto exit; - } - - /* Sanity check header tags */ - rc = headerVerify(keyring, vsflags, ei, uc, &buf); - if (rc != RPMRC_OK) + if (hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, &blob, &buf) != RPMRC_OK) goto exit; /* OK, blob looks sane, load the header. */ - h = headerImport(ei, uc, 0); - if (h == NULL) { - free(buf); - rasprintf(&buf, _("hdr load: BAD\n")); - rc = RPMRC_FAIL; - goto exit; - } - ei = NULL; /* XXX will be freed with header */ + rc = hdrblobImport(&blob, 0, &h, &buf); exit: if (hdrp && h && rc == RPMRC_OK) *hdrp = headerLink(h); - free(ei); headerFree(h); if (msg != NULL && *msg == NULL && buf != NULL) { @@ -485,241 +225,100 @@ exit: return rc; } -rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg) -{ - rpmRC rc; - rpmKeyring keyring = rpmtsGetKeyring(ts, 1); - rpmVSFlags vsflags = rpmtsVSFlags(ts); - - rc = rpmpkgReadHeader(keyring, vsflags, fd, hdrp, msg); - - rpmKeyringFree(keyring); - return rc; -} - -static rpmRC rpmpkgRead(rpmPlugins plugins, rpmKeyring keyring, rpmVSFlags vsflags, - FD_t fd, const char * fn, Header * hdrp) +void applyRetrofits(Header h, int leadtype) { - pgpDigParams sig = NULL; - char buf[8*BUFSIZ]; - ssize_t count; - Header sigh = NULL; - rpmTagVal sigtag; - struct rpmtd_s sigtd; - Header h = NULL; - char * msg = NULL; - rpmRC rc = RPMRC_FAIL; /* assume failure */ - int leadtype = -1; - headerGetFlags hgeflags = HEADERGET_DEFAULT; - DIGEST_CTX ctx = NULL; - - if (hdrp) *hdrp = NULL; - if (fn == NULL) - fn = Fdescr(fd); - - rpmtdReset(&sigtd); - - if ((rc = rpmLeadRead(fd, NULL, &leadtype, &msg)) != RPMRC_OK) { - /* Avoid message spew on manifests */ - if (rc != RPMRC_NOTFOUND) - rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg); - free(msg); - goto exit; - } - - /* Read the signature header. */ - rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg); - switch (rc) { - default: - rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn, - (msg && *msg ? msg : "\n")); - msg = _free(msg); - goto exit; - break; - case RPMRC_OK: - if (sigh == NULL) { - rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn); - rc = RPMRC_FAIL; - goto exit; + /* + * Make sure that either RPMTAG_SOURCERPM or RPMTAG_SOURCEPACKAGE + * is set. Use a simple heuristic to find the type if both are unset. + */ + if (!headerIsEntry(h, RPMTAG_SOURCERPM) && !headerIsEntry(h, RPMTAG_SOURCEPACKAGE)) { + /* the heuristic needs the compressed file list */ + if (headerIsEntry(h, RPMTAG_OLDFILENAMES)) + headerConvert(h, HEADERCONV_COMPRESSFILELIST); + if (headerIsSourceHeuristic(h)) { + /* Retrofit RPMTAG_SOURCEPACKAGE to srpms for compatibility */ + uint32_t one = 1; + headerPutUint32(h, RPMTAG_SOURCEPACKAGE, &one, 1); + } else { + /* + * Make sure binary rpms have RPMTAG_SOURCERPM set as that's + * what we use for differentiating binary vs source elsewhere. + */ + headerPutString(h, RPMTAG_SOURCERPM, "(none)"); } - break; } - msg = _free(msg); - -#define _chk(_mask, _tag) \ - (sigtag == 0 && !(vsflags & (_mask)) && headerIsEntry(sigh, (_tag))) /* - * Figger the most effective available signature. - * Prefer signatures over digests, then header-only over header+payload. - * DSA will be preferred over RSA if both exist because tested first. - * Note that NEEDPAYLOAD prevents header+payload signatures and digests. + * Convert legacy headers on the fly. Not having immutable region + * equals a truly ancient package, do full retrofit. OTOH newer + * packages might have been built with --nodirtokens, test and handle + * the non-compressed filelist case separately. */ - sigtag = 0; - if (_chk(RPMVSF_NODSAHEADER, RPMSIGTAG_DSA)) { - sigtag = RPMSIGTAG_DSA; - } else if (_chk(RPMVSF_NORSAHEADER, RPMSIGTAG_RSA)) { - sigtag = RPMSIGTAG_RSA; - } else if (_chk(RPMVSF_NODSA|RPMVSF_NEEDPAYLOAD, RPMSIGTAG_GPG)) { - sigtag = RPMSIGTAG_GPG; - fdInitDigest(fd, PGPHASHALGO_SHA1, 0); - } else if (_chk(RPMVSF_NORSA|RPMVSF_NEEDPAYLOAD, RPMSIGTAG_PGP)) { - sigtag = RPMSIGTAG_PGP; - fdInitDigest(fd, PGPHASHALGO_MD5, 0); - } else if (_chk(RPMVSF_NOSHA1HEADER, RPMSIGTAG_SHA1)) { - sigtag = RPMSIGTAG_SHA1; - } else if (_chk(RPMVSF_NOMD5|RPMVSF_NEEDPAYLOAD, RPMSIGTAG_MD5)) { - sigtag = RPMSIGTAG_MD5; - fdInitDigest(fd, PGPHASHALGO_MD5, 0); - } - - /* Read the metadata, computing digest(s) on the fly. */ - h = NULL; - msg = NULL; - - rc = rpmpkgReadHeader(keyring, vsflags, fd, &h, &msg); - - if (rc != RPMRC_OK || h == NULL) { - rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s"), fn, - (msg && *msg ? msg : "\n")); - msg = _free(msg); - goto exit; - } - msg = _free(msg); - - /* Any digests or signatures to check? */ - if (sigtag == 0) { - rc = RPMRC_OK; - goto exit; - } - - /* Retrieve the tag parameters from the signature header. */ - if (!headerGet(sigh, sigtag, &sigtd, hgeflags)) { - rc = RPMRC_FAIL; - goto exit; - } - - switch (sigtag) { - case RPMSIGTAG_RSA: - case RPMSIGTAG_DSA: - if (parsePGPSig(&sigtd, "package", fn, &sig)) { - rc = RPMRC_FAIL; - goto exit; - } - /* fallthrough */ - case RPMSIGTAG_SHA1: - { struct rpmtd_s utd; - unsigned int hashalgo = (sigtag == RPMSIGTAG_SHA1) ? - PGPHASHALGO_SHA1 : - pgpDigParamsAlgo(sig, PGPVAL_HASHALGO); - - if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, hgeflags)) - break; - ctx = rpmDigestInit(hashalgo, RPMDIGEST_NONE); - (void) rpmDigestUpdate(ctx, rpm_header_magic, sizeof(rpm_header_magic)); - (void) rpmDigestUpdate(ctx, utd.data, utd.count); - rpmtdFreeData(&utd); - } break; - case RPMSIGTAG_GPG: - case RPMSIGTAG_PGP5: /* XXX legacy */ - case RPMSIGTAG_PGP: - if (parsePGPSig(&sigtd, "package", fn, &sig)) { - rc = RPMRC_FAIL; - goto exit; - } - /* fallthrough */ - case RPMSIGTAG_MD5: - /* Legacy signatures need the compressed payload in the digest too. */ - while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {} - if (count < 0) { - rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), - fn, Fstrerror(fd)); - rc = RPMRC_FAIL; - goto exit; - } + if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) + headerConvert(h, HEADERCONV_RETROFIT_V3); + else if (headerIsEntry(h, RPMTAG_OLDFILENAMES)) + headerConvert(h, HEADERCONV_COMPRESSFILELIST); +} - ctx = rpmDigestBundleDupCtx(fdGetBundle(fd),(sigtag == RPMSIGTAG_MD5) ? - PGPHASHALGO_MD5 : - pgpDigParamsAlgo(sig, PGPVAL_HASHALGO)); - break; - default: - break; - } +struct pkgdata_s { + const char *fn; + rpmRC rc; +}; - /** @todo Implement disable/enable/warn/error/anal policy. */ - rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, &msg); - - /* Run verify hook for all plugins */ - rc = rpmpluginsCallVerify(plugins, keyring, &sigtd, sig, ctx, rc); - +static rpmRC handlePkgVS(struct rpmsinfo_s *sinfo, rpmRC rc, const char *msg, void *cbdata) +{ + struct pkgdata_s *pkgdata = cbdata; + int lvl = RPMLOG_DEBUG; + char *vsmsg = rpmsinfoMsg(sinfo, rc, msg); switch (rc) { case RPMRC_OK: /* Signature is OK. */ - rpmlog(RPMLOG_DEBUG, "%s: %s", fn, msg); break; case RPMRC_NOTTRUSTED: /* Signature is OK, but key is not trusted. */ case RPMRC_NOKEY: /* Public key is unavailable. */ /* XXX Print NOKEY/NOTTRUSTED warning only once. */ - { int lvl = (stashKeyid(sig) ? RPMLOG_DEBUG : RPMLOG_WARNING); - rpmlog(lvl, "%s: %s", fn, msg); - } break; - case RPMRC_NOTFOUND: /* Signature is unknown type. */ - rpmlog(RPMLOG_WARNING, "%s: %s", fn, msg); + if (stashKeyid(sinfo->keyid) == 0) + lvl = RPMLOG_WARNING; + break; + case RPMRC_NOTFOUND: /* Signature/digest not present. */ + lvl = RPMLOG_WARNING; break; default: case RPMRC_FAIL: /* Signature does not verify. */ - rpmlog(RPMLOG_ERR, "%s: %s", fn, msg); + lvl = RPMLOG_ERR; break; } - free(msg); -exit: - if (rc != RPMRC_FAIL && h != NULL && hdrp != NULL) { - /* Retrofit RPMTAG_SOURCEPACKAGE to srpms for compatibility */ - if (leadtype == RPMLEAD_SOURCE && headerIsSource(h)) { - if (!headerIsEntry(h, RPMTAG_SOURCEPACKAGE)) { - uint32_t one = 1; - headerPutUint32(h, RPMTAG_SOURCEPACKAGE, &one, 1); - } - } - /* - * Try to make sure binary rpms have RPMTAG_SOURCERPM set as that's - * what we use for differentiating binary vs source elsewhere. - */ - if (!headerIsEntry(h, RPMTAG_SOURCEPACKAGE) && headerIsSource(h)) { - headerPutString(h, RPMTAG_SOURCERPM, "(none)"); - } - /* - * Convert legacy headers on the fly. Not having immutable region - * equals a truly ancient package, do full retrofit. OTOH newer - * packages might have been built with --nodirtokens, test and handle - * the non-compressed filelist case separately. - */ - if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) - headerConvert(h, HEADERCONV_RETROFIT_V3); - else if (headerIsEntry(h, RPMTAG_OLDFILENAMES)) - headerConvert(h, HEADERCONV_COMPRESSFILELIST); - - /* Append (and remap) signature tags to the metadata. */ - headerMergeLegacySigs(h, sigh); + rpmlog(lvl, "%s: %s\n", pkgdata->fn, vsmsg); - /* Bump reference count for return. */ - *hdrp = headerLink(h); - } - rpmtdFreeData(&sigtd); - rpmDigestFinal(ctx, NULL, NULL, 0); - h = headerFree(h); - pgpDigParamsFree(sig); - sigh = rpmFreeSignature(sigh); + /* Remember actual return code, but don't override a previous failure */ + if (rc && pkgdata->rc != RPMRC_FAIL) + pkgdata->rc = rc; + + /* Preserve traditional behavior for now: only failure prevents read */ + if (rc != RPMRC_FAIL) + rc = RPMRC_OK; + + free(vsmsg); return rc; } rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp) { - rpmRC rc; - rpmVSFlags vsflags = rpmtsVSFlags(ts); + rpmVSFlags vsflags = rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD; rpmKeyring keyring = rpmtsGetKeyring(ts, 1); + struct pkgdata_s pkgdata = { + .fn = fn ? fn : Fdescr(fd), + .rc = RPMRC_OK, + }; + + /* XXX: lots of 3rd party software relies on the behavior */ + if (hdrp) + *hdrp = NULL; - rc = rpmpkgRead(rpmtsPlugins(ts), keyring, vsflags, fd, fn, hdrp); + rpmRC rc = rpmpkgRead(rpmtsPlugins(ts), keyring, vsflags, fd, handlePkgVS, &pkgdata, hdrp); + /* If there was a "substatus" (NOKEY in practise), return that instead */ + if (rc == RPMRC_OK && pkgdata.rc) + rc = pkgdata.rc; rpmKeyringFree(keyring); diff --git a/lib/poptALL.c b/lib/poptALL.c index 541e8c4ab..b7f48da36 100644 --- a/lib/poptALL.c +++ b/lib/poptALL.c @@ -4,7 +4,6 @@ */ #include "system.h" -const char *__progname; #include <rpm/rpmcli.h> #include <rpm/rpmlib.h> /* rpmEVR, rpmReadConfigFiles etc */ @@ -20,6 +19,7 @@ const char *__progname; #define POPT_PREDEFINE -996 #define POPT_DBPATH -995 #define POPT_UNDEFINE -994 +#define POPT_PIPE -993 static int _debug = 0; @@ -69,6 +69,27 @@ void rpmcliConfigured(void) exit(EXIT_FAILURE); } +static int cliDefine(const char *arg, int predefine) +{ + int rc; + char *s, *t; + /* XXX Convert '-' in macro name to underscore, skip leading %. */ + s = t = xstrdup(arg); + while (*t && !risspace(*t)) { + if (*t == '-') *t = '_'; + t++; + } + t = s; + if (*t == '%') t++; + + rc = rpmDefineMacro(NULL, t, RMIL_CMDLINE); + if (!predefine && rc == 0) + (void) rpmDefineMacro(rpmCLIMacroContext, t, RMIL_CMDLINE); + + free(s); + return rc; +} + /** */ static void rpmcliAllArgCallback( poptContext con, @@ -87,43 +108,32 @@ static void rpmcliAllArgCallback( poptContext con, rpmIncreaseVerbosity(); break; case POPT_PREDEFINE: - (void) rpmDefineMacro(NULL, arg, RMIL_CMDLINE); + if (cliDefine(arg, 1)) + exit(EXIT_FAILURE); break; case 'D': - { char *s, *t; - /* XXX Convert '-' in macro name to underscore, skip leading %. */ - s = t = xstrdup(arg); - while (*t && !risspace(*t)) { - if (*t == '-') *t = '_'; - t++; - } - t = s; - if (*t == '%') t++; - /* XXX Predefine macro if not initialized yet. */ - if (rpmcliInitialized < 0) - (void) rpmDefineMacro(NULL, t, RMIL_CMDLINE); rpmcliConfigured(); - (void) rpmDefineMacro(NULL, t, RMIL_CMDLINE); - (void) rpmDefineMacro(rpmCLIMacroContext, t, RMIL_CMDLINE); - free(s); + if (cliDefine(arg, 0)) + exit(EXIT_FAILURE); break; - } case POPT_UNDEFINE: rpmcliConfigured(); if (*arg == '%') arg++; - delMacro(NULL, arg); + rpmPopMacro(NULL, arg); break; case 'E': rpmcliConfigured(); - { char *val = rpmExpand(arg, NULL); + { char *val = NULL; + if (rpmExpandMacros(NULL, arg, &val, 0) < 0) + exit(EXIT_FAILURE); fprintf(stdout, "%s\n", val); free(val); } break; case POPT_DBPATH: rpmcliConfigured(); - addMacro(NULL, "_dbpath", NULL, arg, RMIL_CMDLINE); + rpmPushMacro(NULL, "_dbpath", NULL, arg, RMIL_CMDLINE); break; case POPT_SHOWVERSION: printVersion(stdout); @@ -138,6 +148,16 @@ static void rpmcliAllArgCallback( poptContext con, rpmDisplayQueryTags(stdout); exit(EXIT_SUCCESS); break; + case POPT_PIPE: + if (rpmcliPipeOutput) { + fprintf(stderr, + _("%s: error: more than one --pipe specified " + "(incompatible popt aliases?)\n"), xgetprogname()); + exit(EXIT_FAILURE); + } + rpmcliPipeOutput = xstrdup(arg); + break; + case RPMCLI_POPT_NODIGEST: rpmcliQueryFlags |= VERIFY_DIGEST; break; @@ -149,6 +169,10 @@ static void rpmcliAllArgCallback( poptContext con, case RPMCLI_POPT_NOHDRCHK: rpmcliQueryFlags |= VERIFY_HDRCHK; break; + + case RPMCLI_POPT_TARGETPLATFORM: + rpmcliInitialized = rpmReadConfigFiles(rpmcliRcfile, arg); + break; } } @@ -172,10 +196,17 @@ struct poptOption rpmcliAllPoptTable[] = { { "eval", 'E', POPT_ARG_STRING, 0, 'E', N_("print macro expansion of EXPR"), N_("'EXPR'") }, + { "target", '\0', POPT_ARG_STRING, NULL, RPMCLI_POPT_TARGETPLATFORM, + N_("Specify target platform"), N_("CPU-VENDOR-OS") }, { "macros", '\0', POPT_ARG_STRING, ¯ofiles, 0, N_("read <FILE:...> instead of default file(s)"), N_("<FILE:...>") }, + /* XXX this is a bit out of place here but kinda unavoidable... */ + { "noplugins", '\0', POPT_BIT_SET, + &rpmIArgs.transFlags, RPMTRANS_FLAG_NOPLUGINS, + N_("don't enable any plugins"), NULL }, + { "nodigest", '\0', 0, 0, RPMCLI_POPT_NODIGEST, N_("don't verify package digest(s)"), NULL }, { "nohdrchk", '\0', POPT_ARGFLAG_DOC_HIDDEN, 0, RPMCLI_POPT_NOHDRCHK, @@ -183,7 +214,7 @@ struct poptOption rpmcliAllPoptTable[] = { { "nosignature", '\0', 0, 0, RPMCLI_POPT_NOSIGNATURE, N_("don't verify package signature(s)"), NULL }, - { "pipe", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &rpmcliPipeOutput, 0, + { "pipe", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, 0, POPT_PIPE, N_("send stdout to CMD"), N_("CMD") }, { "rcfile", '\0', POPT_ARG_STRING, &rpmcliRcfile, 0, @@ -244,14 +275,6 @@ rpmcliInit(int argc, char *const argv[], struct poptOption * optionsTable) int rc; const char *ctx, *execPath; - setprogname(argv[0]); /* Retrofit glibc __progname */ - - /* XXX glibc churn sanity */ - if (__progname == NULL) { - if ((__progname = strrchr(argv[0], '/')) != NULL) __progname++; - else __progname = argv[0]; - } - #if defined(ENABLE_NLS) (void) setlocale(LC_ALL, "" ); @@ -268,7 +291,7 @@ rpmcliInit(int argc, char *const argv[], struct poptOption * optionsTable) } /* XXX hack to get popt working from build tree wrt lt-foo names */ - ctx = rstreqn(__progname, "lt-", 3) ? __progname + 3 : __progname; + ctx = rstreqn(xgetprogname(), "lt-", 3) ? xgetprogname() + 3 : xgetprogname(); optCon = poptGetContext(ctx, argc, (const char **)argv, optionsTable, 0); { @@ -285,12 +308,12 @@ rpmcliInit(int argc, char *const argv[], struct poptOption * optionsTable) /* Process all options, whine if unknown. */ while ((rc = poptGetNextOpt(optCon)) > 0) { fprintf(stderr, _("%s: option table misconfigured (%d)\n"), - __progname, rc); + xgetprogname(), rc); exit(EXIT_FAILURE); } if (rc < -1) { - fprintf(stderr, "%s: %s: %s\n", __progname, + fprintf(stderr, "%s: %s: %s\n", xgetprogname(), poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); exit(EXIT_FAILURE); diff --git a/lib/poptI.c b/lib/poptI.c index 051f0f56f..8272fa80f 100644 --- a/lib/poptI.c +++ b/lib/poptI.c @@ -26,7 +26,7 @@ struct rpmInstallArguments_s rpmIArgs = { RPM_GNUC_NORETURN static void argerror(const char * desc) { - fprintf(stderr, _("%s: %s\n"), __progname, desc); + fprintf(stderr, _("%s: %s\n"), xgetprogname(), desc); exit(EXIT_FAILURE); } @@ -87,6 +87,10 @@ static void installArgCallback( poptContext con, ia->transFlags |= RPMTRANS_FLAG_NOCONTEXTS; break; + case RPMCLI_POPT_NOCAPS: + ia->transFlags |= RPMTRANS_FLAG_NOCAPS; + break; + case RPMCLI_POPT_FORCE: ia->probFilter |= ( RPMPROB_FILTER_REPLACEPKG @@ -181,6 +185,8 @@ struct poptOption rpmInstallPoptTable[] = { N_("don't verify digest of files (obsolete)"), NULL }, { "nocontexts", '\0',0, NULL, RPMCLI_POPT_NOCONTEXTS, N_("don't install file security contexts"), NULL}, + { "nocaps", '\0',0, NULL, RPMCLI_POPT_NOCAPS, + N_("don't install file capabilities"), NULL}, { "noorder", '\0', POPT_BIT_SET, &rpmIArgs.installInterfaceFlags, INSTALL_NOORDER, @@ -224,10 +230,6 @@ struct poptOption rpmInstallPoptTable[] = { &rpmIArgs.transFlags, RPMTRANS_FLAG_NOTRIGGERPOSTUN, N_("do not execute any %%triggerpostun scriptlet(s)"), NULL}, - { "nocollections", '\0', POPT_BIT_SET, - &rpmIArgs.transFlags, RPMTRANS_FLAG_NOCOLLECTIONS, - N_("do not perform any collection actions"), NULL}, - { "oldpackage", '\0', POPT_BIT_SET, &rpmIArgs.probFilter, RPMPROB_FILTER_OLDPACKAGE, N_("upgrade to an old version of the package (--force on upgrades does this automatically)"), @@ -253,6 +255,10 @@ struct poptOption rpmInstallPoptTable[] = { &rpmIArgs.installInterfaceFlags, (INSTALL_UPGRADE|INSTALL_INSTALL), N_("upgrade package(s)"), N_("<packagefile>+") }, + { "reinstall", '\0', POPT_BIT_SET, + &rpmIArgs.installInterfaceFlags, (INSTALL_REINSTALL|INSTALL_INSTALL), + N_("reinstall package(s)"), + N_("<packagefile>+") }, POPT_TABLEEND }; diff --git a/lib/poptQV.c b/lib/poptQV.c index 3db17b0ab..e3ea2d7bc 100644 --- a/lib/poptQV.c +++ b/lib/poptQV.c @@ -21,6 +21,10 @@ struct rpmQVKArguments_s rpmQVKArgs; #define POPT_QUERYBYPKGID -1007 #define POPT_QUERYBYHDRID -1008 #define POPT_QUERYBYTID -1010 +#define POPT_WHATRECOMMENDS -1011 +#define POPT_WHATSUGGESTS -1012 +#define POPT_WHATSUPPLEMENTS -1013 +#define POPT_WHATENHANCES -1014 /* ========== Query/Verify/Signature source args */ static void rpmQVSourceArgCallback( poptContext con, @@ -45,6 +49,10 @@ static void rpmQVSourceArgCallback( poptContext con, case 'p': qva->qva_source |= RPMQV_RPM; break; case POPT_WHATPROVIDES: qva->qva_source |= RPMQV_WHATPROVIDES; break; case POPT_WHATREQUIRES: qva->qva_source |= RPMQV_WHATREQUIRES; break; + case POPT_WHATRECOMMENDS: qva->qva_source |= RPMQV_WHATRECOMMENDS; break; + case POPT_WHATSUGGESTS: qva->qva_source |= RPMQV_WHATSUGGESTS; break; + case POPT_WHATSUPPLEMENTS: qva->qva_source |= RPMQV_WHATSUPPLEMENTS; break; + case POPT_WHATENHANCES: qva->qva_source |= RPMQV_WHATENHANCES; break; case POPT_TRIGGEREDBY: qva->qva_source |= RPMQV_TRIGGEREDBY; break; case POPT_QUERYBYPKGID: qva->qva_source |= RPMQV_PKGID; break; case POPT_QUERYBYHDRID: qva->qva_source |= RPMQV_HDRID; break; @@ -93,6 +101,14 @@ struct poptOption rpmQVSourcePoptTable[] = { N_("query/verify the package(s) which require a dependency"), "CAPABILITY" }, { "whatprovides", '\0', 0, 0, POPT_WHATPROVIDES, N_("query/verify the package(s) which provide a dependency"), "CAPABILITY" }, + { "whatrecommends", '\0', 0, 0, POPT_WHATRECOMMENDS, + N_("query/verify the package(s) which recommends a dependency"), "CAPABILITY" }, + { "whatsuggests", '\0', 0, 0, POPT_WHATSUGGESTS, + N_("query/verify the package(s) which suggests a dependency"), "CAPABILITY" }, + { "whatsupplements", '\0', 0, 0, POPT_WHATSUPPLEMENTS, + N_("query/verify the package(s) which supplements a dependency"), "CAPABILITY" }, + { "whatenhances", '\0', 0, 0, POPT_WHATENHANCES, + N_("query/verify the package(s) which enhances a dependency"), "CAPABILITY" }, { "noglob", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &giFlags, RPMGI_NOGLOB, N_("do not glob arguments"), NULL}, @@ -115,6 +131,7 @@ static void queryArgCallback(poptContext con, case 'c': qva->qva_flags |= QUERY_FOR_CONFIG | QUERY_FOR_LIST; break; case 'd': qva->qva_flags |= QUERY_FOR_DOCS | QUERY_FOR_LIST; break; case 'L': qva->qva_flags |= QUERY_FOR_LICENSE | QUERY_FOR_LIST; break; + case 'A': qva->qva_flags |= QUERY_FOR_ARTIFACT | QUERY_FOR_LIST; break; case 'l': qva->qva_flags |= QUERY_FOR_LIST; break; case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break; @@ -144,6 +161,10 @@ static void queryArgCallback(poptContext con, qva->qva_flags |= VERIFY_CONTEXTS; break; + case RPMCLI_POPT_NOCAPS: + qva->qva_flags |= VERIFY_CAPS; + break; + #ifdef NOTYET case RPMCLI_POPT_FORCE: ia->probFilter |= @@ -174,6 +195,8 @@ struct poptOption rpmQueryPoptTable[] = { N_("list all documentation files"), NULL }, { "licensefiles", 'L', 0, 0, 'L', N_("list all license files"), NULL }, + { "artifactfiles", 'A', 0, 0, 'A', + N_("list all artifact files"), NULL }, { "dump", '\0', 0, 0, POPT_DUMP, N_("dump basic file information"), NULL }, { NULL, 'i', POPT_ARGFLAG_DOC_HIDDEN, 0, 'i', @@ -182,9 +205,15 @@ struct poptOption rpmQueryPoptTable[] = { N_("list files in package"), NULL }, /* Duplicate file attr flags from packages into command line options. */ - { "noghost", '\0', POPT_BIT_CLR|POPT_ARGFLAG_DOC_HIDDEN, + { "noghost", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &rpmQVKArgs.qva_fflags, RPMFILE_GHOST, N_("skip %%ghost files"), NULL }, + { "noconfig", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, + &rpmQVKArgs.qva_fflags, RPMFILE_CONFIG, + N_("skip %%config files"), NULL }, + { "noartifact", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, + &rpmQVKArgs.qva_fflags, RPMFILE_ARTIFACT, + N_("skip %%artifact files"), NULL }, { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, POPT_QUERYFORMAT, NULL, NULL }, @@ -228,12 +257,11 @@ struct poptOption rpmVerifyPoptTable[] = { { "nordev", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &rpmQVKArgs.qva_flags, VERIFY_RDEV, N_("don't verify mode of files"), NULL }, - { "nocaps", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, - &rpmQVKArgs.qva_flags, VERIFY_CAPS, - N_("don't verify capabilities of files"), NULL }, { "nocontexts", '\0', POPT_ARGFLAG_DOC_HIDDEN, NULL, RPMCLI_POPT_NOCONTEXTS, N_("don't verify file security contexts"), NULL }, + { "nocaps", '\0', POPT_ARGFLAG_DOC_HIDDEN, NULL, RPMCLI_POPT_NOCAPS, + N_("don't verify capabilities of files"), NULL }, { "nofiles", '\0', POPT_BIT_SET, &rpmQVKArgs.qva_flags, VERIFY_FILES, N_("don't verify files in package"), NULL}, { "nodeps", '\0', 0, NULL, RPMCLI_POPT_NODEPS, @@ -17,107 +17,38 @@ #include <rpm/rpmstring.h> #include <rpm/argv.h> -#include "lib/cpio.h" #include "lib/fsm.h" /* XXX CPIO_FOO/FSM_FOO constants */ #include "lib/rpmchroot.h" #include "lib/rpmfi_internal.h" /* XXX replaced/states... */ #include "lib/rpmte_internal.h" /* XXX internal apis */ #include "lib/rpmdb_internal.h" /* rpmdbAdd/Remove */ -#include "lib/rpmts_internal.h" /* ts->plugins */ +#include "lib/rpmts_internal.h" /* rpmtsPlugins() etc */ +#include "lib/rpmds_internal.h" /* rpmdsFilterTi() */ #include "lib/rpmscript.h" +#include "lib/misc.h" +#include "lib/rpmtriggers.h" #include "lib/rpmplugins.h" #include "debug.h" -typedef enum pkgStage_e { - PSM_UNKNOWN = 0, - PSM_INIT = 1, - PSM_PRE = 2, - PSM_PROCESS = 3, - PSM_POST = 4, - PSM_UNDO = 5, - PSM_FINI = 6, - - PSM_CREATE = 17, - PSM_DESTROY = 23, - - PSM_SCRIPT = 53, - PSM_TRIGGERS = 54, - PSM_IMMED_TRIGGERS = 55, - - PSM_RPMDB_ADD = 98, - PSM_RPMDB_REMOVE = 99 - -} pkgStage; - struct rpmpsm_s { rpmts ts; /*!< transaction set */ rpmte te; /*!< current transaction element */ - rpmfi fi; /*!< transaction element file info */ - const char * goalName; - char * failedFile; - rpmTagVal scriptTag; /*!< Scriptlet data tag. */ - int npkgs_installed; /*!< No. of installed instances. */ + rpmfiles files; /*!< transaction element file info */ int scriptArg; /*!< Scriptlet package arg. */ - rpmsenseFlags sense; /*!< One of RPMSENSE_TRIGGER{PREIN,IN,UN,POSTUN}. */ int countCorrection; /*!< 0 if installing, -1 if removing. */ rpmCallbackType what; /*!< Callback type. */ rpm_loff_t amount; /*!< Callback amount. */ rpm_loff_t total; /*!< Callback total. */ - pkgGoal goal; - pkgStage stage; /*!< Current psm stage. */ - pkgStage nstage; /*!< Next psm stage. */ int nrefs; /*!< Reference count. */ }; -static rpmpsm rpmpsmNew(rpmts ts, rpmte te); +static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal); +static rpmRC rpmpsmUnpack(rpmpsm psm); static rpmpsm rpmpsmFree(rpmpsm psm); -static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage); - -/** - * Macros to be defined from per-header tag values. - * @todo Should other macros be added from header when installing a package? - */ -static struct tagMacro { - const char *macroname; /*!< Macro name to define. */ - rpmTag tag; /*!< Header tag to use for value. */ -} const tagMacros[] = { - { "name", RPMTAG_NAME }, - { "version", RPMTAG_VERSION }, - { "release", RPMTAG_RELEASE }, - { "epoch", RPMTAG_EPOCH }, - { NULL, 0 } -}; - -/** - * Define per-header macros. - * @param h header - * @return 0 always - */ -static void rpmInstallLoadMacros(Header h) -{ - const struct tagMacro * tagm; - - for (tagm = tagMacros; tagm->macroname != NULL; tagm++) { - struct rpmtd_s td; - char *body; - if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT)) - continue; - - switch (rpmtdType(&td)) { - default: - body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL); - addMacro(NULL, tagm->macroname, NULL, body, -1); - free(body); - break; - case RPM_NULL_TYPE: - break; - } - rpmtdFreeData(&td); - } -} +static const char * pkgGoalString(pkgGoal goal); /** * Adjust file states in database for files shared with this package: @@ -133,9 +64,9 @@ static rpmRC markReplacedFiles(const rpmpsm psm) sharedFileInfo sfi; rpmdbMatchIterator mi; Header h; - int * offsets; + unsigned int * offsets; unsigned int prev; - int num; + unsigned int num; if (!replaced) return RPMRC_OK; @@ -227,17 +158,12 @@ static int rpmlibDeps(Header h) rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd, char ** specFilePtr, char ** cookie) { - rpmfi fi = NULL; - char * specFile = NULL; - const char *rootdir = rpmtsRootDir(ts); Header h = NULL; rpmpsm psm = NULL; rpmte te = NULL; rpmRC rpmrc; int specix = -1; - struct rpmtd_s filenames; - rpmtdReset(&filenames); rpmrc = rpmReadPackageFile(ts, fd, NULL, &h); switch (rpmrc) { case RPMRC_NOTTRUSTED: @@ -262,51 +188,9 @@ rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd, if (!rpmlibDeps(h)) goto exit; - if (headerGet(h, RPMTAG_BASENAMES, &filenames, HEADERGET_ALLOC)) { - struct rpmtd_s td; - const char *str; - const char *_cookie = headerGetString(h, RPMTAG_COOKIE); - if (cookie && _cookie) *cookie = xstrdup(_cookie); - - /* Try to find spec by file flags */ - if (_cookie && headerGet(h, RPMTAG_FILEFLAGS, &td, HEADERGET_MINMEM)) { - rpmfileAttrs *flags; - while (specix < 0 && (flags = rpmtdNextUint32(&td))) { - if (*flags & RPMFILE_SPECFILE) - specix = rpmtdGetIndex(&td); - } - } - /* Still no spec? Look by filename. */ - while (specix < 0 && (str = rpmtdNextString(&filenames))) { - if (rpmFileHasSuffix(str, ".spec")) - specix = rpmtdGetIndex(&filenames); - } - } - - if (rootdir && rstreq(rootdir, "/")) - rootdir = NULL; - - /* Macros need to be added before trying to create directories */ - rpmInstallLoadMacros(h); + specix = headerFindSpec(h); - if (specix >= 0) { - const char *bn; - - headerDel(h, RPMTAG_BASENAMES); - headerDel(h, RPMTAG_DIRNAMES); - headerDel(h, RPMTAG_DIRINDEXES); - - rpmtdInit(&filenames); - for (int i = 0; (bn = rpmtdNextString(&filenames)); i++) { - int spec = (i == specix); - char *fn = rpmGenPath(rpmtsRootDir(ts), - spec ? "%{_specdir}" : "%{_sourcedir}", bn); - headerPutString(h, RPMTAG_OLDFILENAMES, fn); - if (spec) specFile = xstrdup(fn); - free(fn); - } - headerConvert(h, HEADERCONV_COMPRESSFILELIST); - } else { + if (specix < 0) { rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n")); goto exit; }; @@ -322,142 +206,50 @@ rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd, rpmteSetFd(te, fd); rpmteSetHeader(te, h); - fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER); - h = headerFree(h); - - if (fi == NULL) { - goto exit; - } - fi->apath = filenames.data; /* Ick */ - rpmteSetFI(te, fi); - fi = rpmfiFree(fi); - - if (rpmMkdirs(rpmtsRootDir(ts), "%{_topdir}:%{_sourcedir}:%{_specdir}")) { - goto exit; - } { /* set all files to be installed */ rpmfs fs = rpmteGetFileStates(te); - int i; - unsigned int fc = rpmfiFC(fi); - for (i=0; i<fc; i++) rpmfsSetAction(fs, i, FA_CREATE); + int fc = rpmfsFC(fs); + for (int i = 0; i < fc; i++) + rpmfsSetAction(fs, i, FA_CREATE); } - psm = rpmpsmNew(ts, te); - psm->goal = PKG_INSTALL; + psm = rpmpsmNew(ts, te, PKG_INSTALL); - /* FIX: psm->fi->dnl should be owned. */ - if (rpmpsmStage(psm, PSM_PROCESS) == RPMRC_OK) + if (rpmpsmUnpack(psm) == RPMRC_OK) rpmrc = RPMRC_OK; - (void) rpmpsmStage(psm, PSM_FINI); rpmpsmFree(psm); exit: - if (specFilePtr && specFile && rpmrc == RPMRC_OK) - *specFilePtr = specFile; - else - free(specFile); - - headerFree(h); - rpmfiFree(fi); + if (rpmrc == RPMRC_OK && specix >= 0) { + if (cookie) + *cookie = headerGetAsString(h, RPMTAG_COOKIE); + if (specFilePtr) { + rpmfiles files = rpmteFiles(te); + *specFilePtr = rpmfilesFN(files, specix); + rpmfilesFree(files); + } + } /* XXX nuke the added package(s). */ - rpmtsClean(ts); + headerFree(h); + rpmtsEmpty(ts); return rpmrc; } -static rpmTagVal triggertag(rpmsenseFlags sense) -{ - rpmTagVal tag = RPMTAG_NOT_FOUND; - switch (sense) { - case RPMSENSE_TRIGGERIN: - tag = RPMTAG_TRIGGERIN; - break; - case RPMSENSE_TRIGGERUN: - tag = RPMTAG_TRIGGERUN; - break; - case RPMSENSE_TRIGGERPOSTUN: - tag = RPMTAG_TRIGGERPOSTUN; - break; - case RPMSENSE_TRIGGERPREIN: - tag = RPMTAG_TRIGGERPREIN; - break; - default: - break; - } - return tag; -} - -/** - * Run a scriptlet with args. - * - * Run a script with an interpreter. If the interpreter is not specified, - * /bin/sh will be used. If the interpreter is /bin/sh, then the args from - * the header will be ignored, passing instead arg1 and arg2. - * - * @param psm package state machine data - * @param prefixes install prefixes - * @param script scriptlet from header - * @param arg1 no. instances of package installed after scriptlet exec - * (-1 is no arg) - * @param arg2 ditto, but for the target package - * @return 0 on success - */ -static rpmRC runScript(rpmpsm psm, ARGV_const_t prefixes, - rpmScript script, int arg1, int arg2) -{ - rpmRC stoprc, rc = RPMRC_OK; - rpmTagVal stag = rpmScriptTag(script); - FD_t sfd = NULL; - int warn_only = (stag != RPMTAG_PREIN && - stag != RPMTAG_PREUN && - stag != RPMTAG_PRETRANS && - stag != RPMTAG_VERIFYSCRIPT); - int selinux = !(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_NOCONTEXTS); - - sfd = rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_START, stag, 0); - if (sfd == NULL) - sfd = rpmtsScriptFd(psm->ts); - - rpmtsSuspendResumeDBLock(psm->ts, 0); - rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0); - rc = rpmScriptRun(script, arg1, arg2, sfd, - prefixes, warn_only, selinux, psm->ts->plugins); - rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_SCRIPTLETS), 0); - rpmtsSuspendResumeDBLock(psm->ts, 1); - - /* Map warn-only errors to "notfound" for script stop callback */ - stoprc = (rc != RPMRC_OK && warn_only) ? RPMRC_NOTFOUND : rc; - rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_STOP, stag, stoprc); - - /* - * Notify callback for all errors. "total" abused for warning/error, - * rc only reflects whether the condition prevented install/erase - * (which is only happens with %prein and %preun scriptlets) or not. - */ - if (rc != RPMRC_OK) { - if (warn_only) { - rc = RPMRC_OK; - } - rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_SCRIPT_ERROR, stag, rc); - } - - return rc; -} - -static rpmRC runInstScript(rpmpsm psm) +static rpmRC runInstScript(rpmpsm psm, rpmTagVal scriptTag) { rpmRC rc = RPMRC_OK; struct rpmtd_s pfx; Header h = rpmteHeader(psm->te); - rpmScript script = rpmScriptFromTag(h, psm->scriptTag); + rpmScript script = rpmScriptFromTag(h, scriptTag); if (script) { headerGet(h, RPMTAG_INSTPREFIXES, &pfx, HEADERGET_ALLOC|HEADERGET_ARGV); - rc = runScript(psm, pfx.data, script, psm->scriptArg, -1); + rc = runScript(psm->ts, psm->te, h, pfx.data, script, psm->scriptArg, -1); rpmtdFreeData(&pfx); } @@ -470,18 +262,19 @@ static rpmRC runInstScript(rpmpsm psm) /** * Execute triggers. * @todo Trigger on any provides, not just package NVR. - * @param psm package state machine data + * @param ts transaction set + * @param te transaction element + * @param sense trigger type * @param sourceH header of trigger source * @param trigH header of triggered package * @param arg2 * @param triggersAlreadyRun * @return */ -static rpmRC handleOneTrigger(const rpmpsm psm, - Header sourceH, Header trigH, +static rpmRC handleOneTrigger(rpmts ts, rpmte te, rpmsenseFlags sense, + Header sourceH, Header trigH, int countCorrection, int arg2, unsigned char * triggersAlreadyRun) { - const rpmts ts = psm->ts; rpmds trigger = rpmdsInit(rpmdsNew(trigH, RPMTAG_TRIGGERNAME, 0)); struct rpmtd_s pfx; const char * sourceName = headerGetString(sourceH, RPMTAG_NAME); @@ -496,10 +289,9 @@ static rpmRC handleOneTrigger(const rpmpsm psm, (void) rpmdsSetNoPromote(trigger, 1); while ((i = rpmdsNext(trigger)) >= 0) { - struct rpmtd_s tindexes; uint32_t tix; - if (!(rpmdsFlags(trigger) & psm->sense)) + if (!(rpmdsFlags(trigger) & sense)) continue; if (!rstreq(rpmdsN(trigger), sourceName)) @@ -509,15 +301,7 @@ static rpmRC handleOneTrigger(const rpmpsm psm, if (!rpmdsAnyMatchesDep(sourceH, trigger, 1)) continue; - if (!headerGet(trigH, RPMTAG_TRIGGERINDEX, &tindexes, HEADERGET_MINMEM)) - continue; - - if (rpmtdSetIndex(&tindexes, i) < 0) { - rpmtdFreeData(&tindexes); - continue; - } - - tix = rpmtdGetNumber(&tindexes); + tix = rpmdsTi(trigger); if (triggersAlreadyRun == NULL || triggersAlreadyRun[tix] == 0) { int arg1 = rpmdbCountPackages(rpmtsGetRdb(ts), triggerName); @@ -526,10 +310,9 @@ static rpmRC handleOneTrigger(const rpmpsm psm, rc = RPMRC_FAIL; } else { rpmScript script = rpmScriptFromTriggerTag(trigH, - triggertag(psm->sense), tix); - arg1 += psm->countCorrection; - rc = runScript(psm, pfx.data, script, arg1, arg2); - + triggertag(sense), RPMSCRIPT_NORMALTRIGGER, tix); + arg1 += countCorrection; + rc = runScript(ts, te, trigH, pfx.data, script, arg1, arg2); if (triggersAlreadyRun != NULL) triggersAlreadyRun[tix] = 1; @@ -537,8 +320,6 @@ static rpmRC handleOneTrigger(const rpmpsm psm, } } - rpmtdFreeData(&tindexes); - /* * Each target/source header pair can only result in a single * script being run. @@ -555,9 +336,10 @@ static rpmRC handleOneTrigger(const rpmpsm psm, /** * Run trigger scripts in the database that are fired by this header. * @param psm package state machine data + * @param sense trigger type * @return 0 on success */ -static rpmRC runTriggers(rpmpsm psm) +static rpmRC runTriggers(rpmpsm psm, rpmsenseFlags sense) { const rpmts ts = psm->ts; int numPackage = -1; @@ -575,14 +357,13 @@ static rpmRC runTriggers(rpmpsm psm) { Header triggeredH; Header h = rpmteHeader(psm->te); rpmdbMatchIterator mi; - int countCorrection = psm->countCorrection; - psm->countCorrection = 0; mi = rpmtsInitIterator(ts, RPMDBI_TRIGGERNAME, N, 0); - while((triggeredH = rpmdbNextIterator(mi)) != NULL) - nerrors += handleOneTrigger(psm, h, triggeredH, numPackage, NULL); + while ((triggeredH = rpmdbNextIterator(mi)) != NULL) { + nerrors += handleOneTrigger(ts, NULL, sense, h, triggeredH, + 0, numPackage, NULL); + } rpmdbFreeIterator(mi); - psm->countCorrection = countCorrection; headerFree(h); } @@ -592,9 +373,10 @@ static rpmRC runTriggers(rpmpsm psm) /** * Run triggers from this header that are fired by headers in the database. * @param psm package state machine data + * @param sense trigger type * @return 0 on success */ -static rpmRC runImmedTriggers(rpmpsm psm) +static rpmRC runImmedTriggers(rpmpsm psm, rpmsenseFlags sense) { const rpmts ts = psm->ts; unsigned char * triggersRun; @@ -620,8 +402,10 @@ static rpmRC runImmedTriggers(rpmpsm psm) mi = rpmtsInitIterator(ts, RPMDBI_NAME, trigName, 0); - while((sourceH = rpmdbNextIterator(mi)) != NULL) { - nerrors += handleOneTrigger(psm, sourceH, h, + while ((sourceH = rpmdbNextIterator(mi)) != NULL) { + nerrors += handleOneTrigger(psm->ts, psm->te, + sense, sourceH, h, + psm->countCorrection, rpmdbGetIteratorCount(mi), triggersRun); } @@ -641,7 +425,7 @@ exit: static rpmpsm rpmpsmFree(rpmpsm psm) { if (psm) { - rpmfiFree(psm->fi); + rpmfilesFree(psm->files); rpmtsFree(psm->ts), /* XXX rpmte not refcounted yet */ memset(psm, 0, sizeof(*psm)); /* XXX trash and burn */ @@ -650,12 +434,55 @@ static rpmpsm rpmpsmFree(rpmpsm psm) return NULL; } -static rpmpsm rpmpsmNew(rpmts ts, rpmte te) +static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal) { rpmpsm psm = xcalloc(1, sizeof(*psm)); psm->ts = rpmtsLink(ts); - psm->fi = rpmfiLink(rpmteFI(te)); + psm->files = rpmteFiles(te); psm->te = te; /* XXX rpmte not refcounted yet */ + if (!rpmteIsSource(te)) { + /* + * When we run scripts, we pass an argument which is the number of + * versions of this package that will be installed when we are + * finished. + */ + int npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(te)); + switch (goal) { + case PKG_INSTALL: + case PKG_PRETRANS: + psm->scriptArg = npkgs_installed + 1; + psm->countCorrection = 0; + break; + case PKG_ERASE: + psm->scriptArg = npkgs_installed - 1; + psm->countCorrection = -1; + break; + case PKG_VERIFY: + case PKG_POSTTRANS: + psm->scriptArg = npkgs_installed; + psm->countCorrection = 0; + break; + default: + break; + } + } + + if (goal == PKG_INSTALL) { + Header h = rpmteHeader(te); + psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE); + headerFree(h); + } else if (goal == PKG_ERASE) { + psm->total = rpmfilesFC(psm->files); + } + /* Fake up something for packages with no files */ + if (psm->total == 0) + psm->total = 100; + + if (goal == PKG_INSTALL || goal == PKG_ERASE) { + rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", pkgGoalString(goal), + rpmteNEVRA(psm->te), rpmfilesFC(psm->files)); + } + return psm; } @@ -685,14 +512,13 @@ void rpmpsmNotify(rpmpsm psm, int what, rpm_loff_t amount) */ static void markReplacedInstance(rpmts ts, rpmte te) { - /* this must match rpmNameVersionCompare in depends.c */ rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(te), 0); rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(te)); rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(te)); rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(te)); - rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(te)); /* XXX shouldn't we also do this on colorless transactions? */ if (rpmtsColor(ts)) { + rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(te)); rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(te)); } @@ -703,323 +529,293 @@ static void markReplacedInstance(rpmts ts, rpmte te) rpmdbFreeIterator(mi); } -static rpmRC rpmpsmNext(rpmpsm psm, pkgStage nstage) +static rpmRC dbAdd(rpmts ts, rpmte te) { - psm->nstage = nstage; - return rpmpsmStage(psm, psm->nstage); + Header h = rpmteHeader(te); + rpm_time_t installTime = (rpm_time_t) time(NULL); + rpmfs fs = rpmteGetFileStates(te); + rpm_count_t fc = rpmfsFC(fs); + rpm_fstate_t * fileStates = rpmfsGetStates(fs); + rpm_color_t tscolor = rpmtsColor(ts); + rpm_tid_t tid = rpmtsGetTid(ts); + rpmRC rc; + + if (fileStates != NULL && fc > 0) { + headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc); + } + + headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1); + headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1); + headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1); + + (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0); + rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL; + (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0); + + if (rc == RPMRC_OK) { + rpmteSetDBInstance(te, headerGetInstance(h)); + packageHashAddEntry(ts->members->installedPackages, + headerGetInstance(h), te); + } + headerFree(h); + return rc; } -static rpmRC rpmpsmStage(rpmpsm psm, pkgStage stage) +static rpmRC dbRemove(rpmts ts, rpmte te) { - const rpmts ts = psm->ts; - rpmfi fi = psm->fi; + rpmRC rc; + + (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); + rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(te)) == 0) ? + RPMRC_OK : RPMRC_FAIL; + (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); + + if (rc == RPMRC_OK) + rpmteSetDBInstance(te, 0); + return rc; +} + +static rpmRC rpmpsmUnpack(rpmpsm psm) +{ + char *failedFile = NULL; + int fsmrc = 0; + int saved_errno = 0; rpmRC rc = RPMRC_OK; - switch (stage) { - case PSM_UNKNOWN: - break; - case PSM_INIT: - rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", - psm->goalName, rpmteNEVR(psm->te), rpmfiFC(fi)); + rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0); + /* make sure first progress call gets made */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0); - /* - * When we run scripts, we pass an argument which is the number of - * versions of this package that will be installed when we are - * finished. - */ - psm->npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(psm->te)); - if (psm->npkgs_installed < 0) { - rc = RPMRC_FAIL; - break; + if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) { + if (rpmfilesFC(psm->files) > 0) { + fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->files, + psm, &failedFile); + saved_errno = errno; } + } - if (psm->goal == PKG_INSTALL) { - Header h = rpmteHeader(psm->te); - psm->scriptArg = psm->npkgs_installed + 1; + /* XXX make sure progress reaches 100% */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, psm->total); + rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total); + + if (fsmrc) { + char *emsg; + errno = saved_errno; + emsg = rpmfileStrerror(fsmrc); + rpmlog(RPMLOG_ERR, + _("unpacking of archive failed%s%s: %s\n"), + (failedFile != NULL ? _(" on file ") : ""), + (failedFile != NULL ? failedFile : ""), + emsg); + free(emsg); + rc = RPMRC_FAIL; + + /* XXX notify callback on error. */ + rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0); + } + free(failedFile); + return rc; +} - psm->amount = 0; - psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE); - /* fake up something for packages with no files */ - if (psm->total == 0) - psm->total = 100; +static rpmRC rpmpsmRemove(rpmpsm psm) +{ + char *failedFile = NULL; + int fsmrc = 0; + + rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0); + /* make sure first progress call gets made */ + rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0); + + /* XXX should't we log errors from here? */ + if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) { + if (rpmfilesFC(psm->files) > 0) { + fsmrc = rpmPackageFilesRemove(psm->ts, psm->te, psm->files, + psm, &failedFile); + } + } + /* XXX make sure progress reaches 100% */ + rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, psm->total); + rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total); - /* HACK: reinstall abuses te instance to remove old header */ - if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG) - markReplacedInstance(ts, psm->te); + free(failedFile); + return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL; +} - if (rpmfiFC(fi) > 0) { - struct rpmtd_s filenames; - rpmTag ftag = RPMTAG_FILENAMES; - - if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { - ftag = RPMTAG_ORIGFILENAMES; - } - headerGet(h, ftag, &filenames, HEADERGET_EXT); - fi->apath = filenames.data; /* Ick.. */ - } - headerFree(h); - } - if (psm->goal == PKG_ERASE) { - psm->scriptArg = psm->npkgs_installed - 1; +static rpmRC rpmPackageInstall(rpmts ts, rpmpsm psm) +{ + rpmRC rc = RPMRC_OK; + int once = 1; - psm->amount = 0; - psm->total = rpmfiFC(fi) ? rpmfiFC(fi) : 100; - } - break; - case PSM_PRE: - if (psm->goal == PKG_INSTALL) { - psm->scriptTag = RPMTAG_PREIN; - psm->sense = RPMSENSE_TRIGGERPREIN; - psm->countCorrection = 0; /* XXX is this correct?!? */ - - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) { - /* Run triggers in other package(s) this package sets off. */ - rc = rpmpsmNext(psm, PSM_TRIGGERS); - if (rc) break; - - /* Run triggers in this package other package(s) set off. */ - rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); - if (rc) break; - } + rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0); + while (once--) { + /* HACK: replacepkgs abuses te instance to remove old header */ + if (rpmtsFilterFlags(psm->ts) & RPMPROB_FILTER_REPLACEPKG) + markReplacedInstance(ts, psm->te); - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) { - rc = rpmpsmNext(psm, PSM_SCRIPT); - if (rc) break; - } - } - if (psm->goal == PKG_ERASE) { - psm->scriptTag = RPMTAG_PREUN; - psm->sense = RPMSENSE_TRIGGERUN; - psm->countCorrection = -1; + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = runTriggers(psm, RPMSENSE_TRIGGERPREIN); + if (rc) break; - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) { - /* Run triggers in this package other package(s) set off. */ - rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); - if (rc) break; + /* Run triggers in this package other package(s) set off. */ + rc = runImmedTriggers(psm, RPMSENSE_TRIGGERPREIN); + if (rc) break; + } - /* Run triggers in other package(s) this package sets off. */ - rc = rpmpsmNext(psm, PSM_TRIGGERS); - if (rc) break; - } + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPRE)) { + rc = runInstScript(psm, RPMTAG_PREIN); + if (rc) break; + } + + rc = rpmpsmUnpack(psm); + if (rc) break; - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN)) - rc = rpmpsmNext(psm, PSM_SCRIPT); + /* + * If this package has already been installed, remove it from + * the database before adding the new one. + */ + if (rpmteDBInstance(psm->te)) { + rc = dbRemove(ts, psm->te); + if (rc) break; } - break; - case PSM_PROCESS: - if (psm->goal == PKG_INSTALL) { - int fsmrc = 0; - - rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0); - /* make sure first progress call gets made */ - rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0); - - if (rpmfiFC(fi) > 0 && !(rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)) { - rpmtransFlags oldtsflags; - FD_t payload = rpmtePayload(psm->te); - if (payload == NULL) { - rc = RPMRC_FAIL; - break; - } - oldtsflags = rpmtsFlags(ts); - if (headerIsEntry(fi->h, RPMTAG_REMOVETID)) - (void) rpmtsSetFlags(ts, oldtsflags | RPMTRANS_FLAG_NOMD5); + rc = dbAdd(ts, psm->te); + if (rc) break; - fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->fi, - payload, psm, &psm->failedFile); + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) { + /* Run upper file triggers i. e. with higher priorities */ + /* Run file triggers in other package(s) this package sets off. */ + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN, + RPMSCRIPT_FILETRIGGER, 1); + if (rc) break; - rpmswAdd(rpmtsOp(psm->ts, RPMTS_OP_UNCOMPRESS), - fdOp(payload, FDSTAT_READ)); - rpmswAdd(rpmtsOp(psm->ts, RPMTS_OP_DIGEST), - fdOp(payload, FDSTAT_DIGEST)); + /* Run file triggers in this package other package(s) set off. */ + rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN, + RPMSCRIPT_FILETRIGGER, 1); + if (rc) break; + } - if (headerIsEntry(fi->h, RPMTAG_REMOVETID)) - (void) rpmtsSetFlags(ts, oldtsflags); + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) { + rc = runInstScript(psm, RPMTAG_POSTIN); + if (rc) break; + } - Fclose(payload); - } + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = runTriggers(psm, RPMSENSE_TRIGGERIN); + if (rc) break; - /* XXX make sure progress reaches 100% */ - rpmpsmNotify(psm, 0, psm->total); - rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total); + /* Run triggers in this package other package(s) set off. */ + rc = runImmedTriggers(psm, RPMSENSE_TRIGGERIN); + if (rc) break; - if (fsmrc) { - rpmlog(RPMLOG_ERR, - _("unpacking of archive failed%s%s: %s\n"), - (psm->failedFile != NULL ? _(" on file ") : ""), - (psm->failedFile != NULL ? psm->failedFile : ""), - rpmcpioStrerror(fsmrc)); - rc = RPMRC_FAIL; + /* Run lower file triggers i. e. with lower priorities */ + /* Run file triggers in other package(s) this package sets off. */ + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN, + RPMSCRIPT_FILETRIGGER, 2); + if (rc) break; - /* XXX notify callback on error. */ - rpmtsNotify(ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0); - break; - } + /* Run file triggers in this package other package(s) set off. */ + rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERIN, + RPMSCRIPT_FILETRIGGER, 2); + if (rc) break; } - if (psm->goal == PKG_ERASE) { - if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB) break; - rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0); - /* make sure first progress call gets made */ - rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0); + rc = markReplacedFiles(psm); + } - /* XXX should't we log errors from here? */ - if (rpmfiFC(fi) > 0 && !(rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)) { - rc = rpmPackageFilesRemove(psm->ts, psm->te, psm->fi, - psm, &psm->failedFile); - } + rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0); - /* XXX make sure progress reaches 100% */ - rpmpsmNotify(psm, 0, psm->total); - rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total); - } - break; - case PSM_POST: - if (psm->goal == PKG_INSTALL) { - rpm_time_t installTime = (rpm_time_t) time(NULL); - rpmfs fs = rpmteGetFileStates(psm->te); - rpm_count_t fc = rpmfsFC(fs); - rpm_fstate_t * fileStates = rpmfsGetStates(fs); - Header h = rpmteHeader(psm->te); - rpm_color_t tscolor = rpmtsColor(ts); - - if (fileStates != NULL && fc > 0) { - headerPutChar(h, RPMTAG_FILESTATES, fileStates, fc); - } + return rc; +} - headerPutUint32(h, RPMTAG_INSTALLTIME, &installTime, 1); - headerPutUint32(h, RPMTAG_INSTALLCOLOR, &tscolor, 1); - headerFree(h); - - /* - * If this package has already been installed, remove it from - * the database before adding the new one. - */ - if (rpmteDBInstance(psm->te)) { - rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE); - if (rc) break; - } +static rpmRC rpmPackageErase(rpmts ts, rpmpsm psm) +{ + rpmRC rc = RPMRC_OK; + int once = 1; + + rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0); + while (once--) { - rc = rpmpsmNext(psm, PSM_RPMDB_ADD); + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) { + /* Run file triggers in this package other package(s) set off. */ + rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN, + RPMSCRIPT_FILETRIGGER, 1); if (rc) break; - psm->scriptTag = RPMTAG_POSTIN; - psm->sense = RPMSENSE_TRIGGERIN; - psm->countCorrection = 0; + /* Run file triggers in other package(s) this package sets off. */ + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN, + RPMSCRIPT_FILETRIGGER, 1); + if (rc) break; - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOST)) { - rc = rpmpsmNext(psm, PSM_SCRIPT); - if (rc) break; - } - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERIN)) { - /* Run triggers in other package(s) this package sets off. */ - rc = rpmpsmNext(psm, PSM_TRIGGERS); - if (rc) break; - - /* Run triggers in this package other package(s) set off. */ - rc = rpmpsmNext(psm, PSM_IMMED_TRIGGERS); - if (rc) break; - } + /* Run triggers in this package other package(s) set off. */ + rc = runImmedTriggers(psm, RPMSENSE_TRIGGERUN); + if (rc) break; - rc = markReplacedFiles(psm); + /* Run triggers in other package(s) this package sets off. */ + rc = runTriggers(psm, RPMSENSE_TRIGGERUN); + if (rc) break; + } + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPREUN)) { + rc = runInstScript(psm, RPMTAG_PREUN); + if (rc) break; } - if (psm->goal == PKG_ERASE) { - psm->scriptTag = RPMTAG_POSTUN; - psm->sense = RPMSENSE_TRIGGERPOSTUN; - psm->countCorrection = -1; + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERUN)) { + /* Run file triggers in this package other package(s) set off. */ + rc = runImmedFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN, + RPMSCRIPT_FILETRIGGER, 2); + if (rc) break; - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) { - rc = rpmpsmNext(psm, PSM_SCRIPT); - if (rc) break; - } + /* Run file triggers in other package(s) this package sets off. */ + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERUN, + RPMSCRIPT_FILETRIGGER, 2); + if (rc) break; + } - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) { - /* Run triggers in other package(s) this package sets off. */ - rc = rpmpsmNext(psm, PSM_TRIGGERS); - if (rc) break; - } + rc = rpmpsmRemove(psm); + if (rc) break; - rc = rpmpsmNext(psm, PSM_RPMDB_REMOVE); - } - break; - case PSM_UNDO: - break; - case PSM_FINI: - if (rc) { - if (psm->failedFile) - rpmlog(RPMLOG_ERR, - _("%s failed on file %s: %s\n"), - psm->goalName, psm->failedFile, rpmcpioStrerror(rc)); - else - rpmlog(RPMLOG_ERR, _("%s failed: %s\n"), - psm->goalName, rpmcpioStrerror(rc)); - - /* XXX notify callback on error. */ - rpmtsNotify(ts, psm->te, RPMCALLBACK_CPIO_ERROR, 0, 0); + /* Run file triggers in other package(s) this package sets off. */ + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) { + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN, + RPMSCRIPT_FILETRIGGER, 1); } - psm->failedFile = _free(psm->failedFile); - - fi->apath = _free(fi->apath); - break; + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOSTUN)) { + rc = runInstScript(psm, RPMTAG_POSTUN); + if (rc) break; + } - case PSM_CREATE: - break; - case PSM_DESTROY: - break; - case PSM_SCRIPT: /* Run current package scriptlets. */ - rc = runInstScript(psm); - break; - case PSM_TRIGGERS: - /* Run triggers in other package(s) this package sets off. */ - rc = runTriggers(psm); - break; - case PSM_IMMED_TRIGGERS: - /* Run triggers in this package other package(s) set off. */ - rc = runImmedTriggers(psm); - break; + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) { + /* Run triggers in other package(s) this package sets off. */ + rc = runTriggers(psm, RPMSENSE_TRIGGERPOSTUN); + if (rc) break; - case PSM_RPMDB_ADD: { - Header h = rpmteHeader(psm->te); + /* Run file triggers in other package(s) this package sets off. */ + rc = runFileTriggers(psm->ts, psm->te, RPMSENSE_TRIGGERPOSTUN, + RPMSCRIPT_FILETRIGGER, 2); + } + if (rc) break; - if (!headerIsEntry(h, RPMTAG_INSTALLTID)) { - rpm_tid_t tid = rpmtsGetTid(ts); - if (tid != 0 && tid != (rpm_tid_t)-1) - headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1); + if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) { + /* Prepare post transaction uninstall triggers */ + rpmtriggersPrepPostUnTransFileTrigs(psm->ts, psm->te); } - - (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBADD), 0); - rc = (rpmdbAdd(rpmtsGetRdb(ts), h) == 0) ? RPMRC_OK : RPMRC_FAIL; - (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBADD), 0); - if (rc == RPMRC_OK) - rpmteSetDBInstance(psm->te, headerGetInstance(h)); - headerFree(h); - } break; - - case PSM_RPMDB_REMOVE: - (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); - rc = (rpmdbRemove(rpmtsGetRdb(ts), rpmteDBInstance(psm->te)) == 0) ? - RPMRC_OK : RPMRC_FAIL; - (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_DBREMOVE), 0); - if (rc == RPMRC_OK) - rpmteSetDBInstance(psm->te, 0); - break; + rc = dbRemove(ts, psm->te); + } - default: - break; - } + rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_ERASE), 0); return rc; } static const char * pkgGoalString(pkgGoal goal) { - switch(goal) { + switch (goal) { case PKG_INSTALL: return " install"; case PKG_ERASE: return " erase"; case PKG_VERIFY: return " verify"; @@ -1038,42 +834,39 @@ rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal) if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) return RPMRC_OK; - psm = rpmpsmNew(ts, te); + psm = rpmpsmNew(ts, te, goal); if (rpmChrootIn() == 0) { - rpmtsOpX op; - psm->goal = goal; - psm->goalName = pkgGoalString(goal); + /* Run pre transaction element hook for all plugins */ + rc = rpmpluginsCallPsmPre(rpmtsPlugins(ts), te); - switch (goal) { - case PKG_INSTALL: - case PKG_ERASE: - /* Run pre transaction element hook for all plugins */ - if (rpmpluginsCallPsmPre(ts->plugins, te) != RPMRC_FAIL) { - - op = (goal == PKG_INSTALL) ? RPMTS_OP_INSTALL : RPMTS_OP_ERASE; - rpmswEnter(rpmtsOp(psm->ts, op), 0); - - rc = rpmpsmNext(psm, PSM_INIT); - if (!rc) rc = rpmpsmNext(psm, PSM_PRE); - if (!rc) rc = rpmpsmNext(psm, PSM_PROCESS); - if (!rc) rc = rpmpsmNext(psm, PSM_POST); - (void) rpmpsmNext(psm, PSM_FINI); - - rpmswExit(rpmtsOp(psm->ts, op), 0); + if (!rc) { + switch (goal) { + case PKG_INSTALL: + rc = rpmPackageInstall(ts, psm); + break; + case PKG_ERASE: + rc = rpmPackageErase(ts, psm); + break; + case PKG_PRETRANS: + case PKG_POSTTRANS: + case PKG_VERIFY: + rc = runInstScript(psm, goal); + break; + case PKG_TRANSFILETRIGGERIN: + rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERIN, + RPMSCRIPT_TRANSFILETRIGGER, 0); + break; + case PKG_TRANSFILETRIGGERUN: + rc = runImmedFileTriggers(ts, te, RPMSENSE_TRIGGERUN, + RPMSCRIPT_TRANSFILETRIGGER, 0); + break; + default: + break; } - - /* Run post transaction element hook for all plugins */ - rpmpluginsCallPsmPost(ts->plugins, te, rc); - break; - case PKG_PRETRANS: - case PKG_POSTTRANS: - case PKG_VERIFY: - psm->scriptTag = goal; - rc = rpmpsmStage(psm, PSM_SCRIPT); - break; - default: - break; } + /* Run post transaction element hook for all plugins */ + rpmpluginsCallPsmPost(rpmtsPlugins(ts), te, rc); + /* XXX an error here would require a full abort */ (void) rpmChrootOut(); } diff --git a/lib/query.c b/lib/query.c index 6f2d593a7..d857ae5bc 100644 --- a/lib/query.c +++ b/lib/query.c @@ -14,6 +14,7 @@ #include <rpm/rpmdb.h> #include <rpm/rpmfi.h> #include <rpm/rpmts.h> +#include <rpm/rpmsq.h> #include <rpm/rpmlog.h> #include <rpm/rpmfileutil.h> /* rpmCleanPath */ @@ -30,21 +31,16 @@ static void printFileInfo(const char * name, unsigned int mtime, unsigned short rdev, unsigned int nlink, const char * owner, const char * group, - const char * linkto) + const char * linkto, time_t now) { char sizefield[21]; char ownerfield[8+1], groupfield[8+1]; char timefield[100]; time_t when = mtime; /* important if sizeof(int32_t) ! sizeof(time_t) */ struct tm * tm; - static time_t now; char * perms = rpmPermsString(mode); char *link = NULL; - /* On first call, grab snapshot of now */ - if (now == 0) - now = time(NULL); - rstrlcpy(ownerfield, owner, sizeof(ownerfield)); rstrlcpy(groupfield, group, sizeof(groupfield)); @@ -99,6 +95,7 @@ int showQueryPackage(QVA_t qva, rpmts ts, Header h) rpmfi fi = NULL; rpmfiFlags fiflags = (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY); int rc = 0; /* XXX FIXME: need real return code */ + time_t now = 0; if (qva->qva_queryFormat != NULL) { const char *errstr; @@ -150,8 +147,12 @@ int showQueryPackage(QVA_t qva, rpmts ts, Header h) if ((qva->qva_flags & QUERY_FOR_LICENSE) && !(fflags & RPMFILE_LICENSE)) continue; - /* If not querying %ghost, skip ghost files. */ - if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST)) + /* If querying only ... yes we know the drill, and this is dumb. */ + if ((qva->qva_flags & QUERY_FOR_ARTIFACT) && !(fflags & RPMFILE_ARTIFACT)) + continue; + + /* Skip on attributes (eg from --noghost) */ + if (fflags & qva->qva_fflags) continue; if (qva->qva_flags & QUERY_FOR_STATE) { @@ -219,11 +220,14 @@ int showQueryPackage(QVA_t qva, rpmts ts, Header h) } if (fuser && fgroup) { + /* On first call, grab snapshot of now */ + if (now == 0) + now = time(NULL); if (buf) { rpmlog(RPMLOG_NOTICE, "%s", buf); } printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink, - fuser, fgroup, flink); + fuser, fgroup, flink, now); } else { rpmlog(RPMLOG_ERR, _("package has neither file owner or id lists\n")); @@ -262,7 +266,6 @@ void rpmDisplayQueryTags(FILE * fp) } fprintf(fp, "\n"); } - rpmtdFreeData(names); rpmtdFree(names); } @@ -274,7 +277,7 @@ static int rpmgiShowMatches(QVA_t qva, rpmts ts, rpmgi gi) while ((h = rpmgiNext(gi)) != NULL) { int rc; - rpmdbCheckSignals(); + rpmsqPoll(); if ((rc = qva->qva_showPackage(qva, ts, h)) != 0) ec = rc; headerFree(h); @@ -292,7 +295,7 @@ static int rpmcliShowMatches(QVA_t qva, rpmts ts, rpmdbMatchIterator mi) while ((h = rpmdbNextIterator(mi)) != NULL) { int rc; - rpmdbCheckSignals(); + rpmsqPoll(); if ((rc = qva->qva_showPackage(qva, ts, h)) != 0) ec = rc; } @@ -305,7 +308,7 @@ static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * ar int i; rpmdbMatchIterator mi = NULL; - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); if (qva->qva_showPackage == NULL) goto exit; @@ -385,6 +388,34 @@ static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * ar } break; + case RPMQV_WHATRECOMMENDS: + mi = rpmtsInitIterator(ts, RPMDBI_RECOMMENDNAME, arg, 0); + if (mi == NULL) { + rpmlog(RPMLOG_NOTICE, _("no package recommends %s\n"), arg); + } + break; + + case RPMQV_WHATSUGGESTS: + mi = rpmtsInitIterator(ts, RPMDBI_SUGGESTNAME, arg, 0); + if (mi == NULL) { + rpmlog(RPMLOG_NOTICE, _("no package suggests %s\n"), arg); + } + break; + + case RPMQV_WHATSUPPLEMENTS: + mi = rpmtsInitIterator(ts, RPMDBI_SUPPLEMENTNAME, arg, 0); + if (mi == NULL) { + rpmlog(RPMLOG_NOTICE, _("no package supplements %s\n"), arg); + } + break; + + case RPMQV_WHATENHANCES: + mi = rpmtsInitIterator(ts, RPMDBI_ENHANCENAME, arg, 0); + if (mi == NULL) { + rpmlog(RPMLOG_NOTICE, _("no package enhances %s\n"), arg); + } + break; + case RPMQV_WHATPROVIDES: if (arg[0] != '/' && arg[0] != '.') { mi = rpmtsInitIterator(ts, RPMDBI_PROVIDENAME, arg, 0); @@ -455,7 +486,9 @@ static rpmdbMatchIterator initQueryIterator(QVA_t qva, rpmts ts, const char * ar } mi = rpmdbFreeIterator(mi); if (! matches) { - rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg); + size_t l = strlen(arg); + if (!(l > 4 && !strcmp(arg + l - 4, ".rpm"))) + rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg); } else { mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0); } @@ -520,6 +553,7 @@ int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv) break; } case RPMQV_SPECRPMS: + case RPMQV_SPECBUILTRPMS: case RPMQV_SPECSRPM: for (ARGV_const_t arg = argv; arg && *arg; arg++) { ec += ((qva->qva_specQuery != NULL) @@ -530,6 +564,14 @@ int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv) for (ARGV_const_t arg = argv; arg && *arg; arg++) { rpmdbMatchIterator mi = initQueryIterator(qva, ts, *arg); ec += rpmcliShowMatches(qva, ts, mi); + if (mi == NULL && qva->qva_source == RPMQV_PACKAGE) { + size_t l = strlen(*arg); + if (l > 4 && !strcmp(*arg + l - 4, ".rpm")) { + rpmgi gi = rpmgiNew(ts, giFlags, argv); + ec += rpmgiShowMatches(qva, ts, gi); + rpmgiFree(gi); + } + } rpmdbFreeIterator(mi); } break; diff --git a/lib/relocation.c b/lib/relocation.c new file mode 100644 index 000000000..3ba4cfeab --- /dev/null +++ b/lib/relocation.c @@ -0,0 +1,560 @@ +#include "system.h" + +#include <rpm/rpmtypes.h> +#include <rpm/header.h> +#include <rpm/rpmfi.h> +#include <rpm/rpmfileutil.h> +#include <rpm/rpmmacro.h> +#include <rpm/rpmlog.h> + +#include "lib/rpmfs.h" +#include "lib/misc.h" + +#include "debug.h" + +/** + * Identify a file type. + * @param ft file type + * @return string to identify a file type + */ +static +const char * ftstring (rpmFileTypes ft) +{ + switch (ft) { + case XDIR: return "directory"; + case CDEV: return "char dev"; + case BDEV: return "block dev"; + case LINK: return "link"; + case SOCK: return "sock"; + case PIPE: return "fifo/pipe"; + case REG: return "file"; + default: return "unknown file type"; + } +} + +static char **duparray(char ** src, int size) +{ + char **dest = xmalloc((size+1) * sizeof(*dest)); + for (int i = 0; i < size; i++) { + dest[i] = xstrdup(src[i]); + } + free(src); + return dest; +} + +static int addPrefixes(Header h, rpmRelocation *relocations, int numRelocations) +{ + struct rpmtd_s validRelocs; + const char *validprefix; + const char ** actualRelocations; + int numActual = 0; + + headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM); + /* + * If no relocations are specified (usually the case), then return the + * original header. If there are prefixes, however, then INSTPREFIXES + * should be added for RPM_INSTALL_PREFIX environ variables in scriptlets, + * but, since relocateFileList() can be called more than once for + * the same header, don't bother if already present. + */ + if (relocations == NULL || numRelocations == 0) { + if (rpmtdCount(&validRelocs) > 0) { + if (!headerIsEntry(h, RPMTAG_INSTPREFIXES)) { + rpmtdSetTag(&validRelocs, RPMTAG_INSTPREFIXES); + headerPut(h, &validRelocs, HEADERPUT_DEFAULT); + } + rpmtdFreeData(&validRelocs); + } + return 0; + } + + actualRelocations = xmalloc(rpmtdCount(&validRelocs) * sizeof(*actualRelocations)); + rpmtdInit(&validRelocs); + while ((validprefix = rpmtdNextString(&validRelocs))) { + int j; + for (j = 0; j < numRelocations; j++) { + if (relocations[j].oldPath == NULL || /* XXX can't happen */ + !rstreq(validprefix, relocations[j].oldPath)) + continue; + /* On install, a relocate to NULL means skip the path. */ + if (relocations[j].newPath) { + actualRelocations[numActual] = relocations[j].newPath; + numActual++; + } + break; + } + if (j == numRelocations) { + actualRelocations[numActual] = validprefix; + numActual++; + } + } + rpmtdFreeData(&validRelocs); + + if (numActual) { + headerPutStringArray(h, RPMTAG_INSTPREFIXES, actualRelocations, numActual); + } + free(actualRelocations); + /* When any relocations are present there'll be more work to do */ + return 1; +} + +static void saveOrig(Header h) +{ + struct rpmtd_s td; + headerGet(h, RPMTAG_BASENAMES, &td, HEADERGET_MINMEM); + rpmtdSetTag(&td, RPMTAG_ORIGBASENAMES); + headerPut(h, &td, HEADERPUT_DEFAULT); + rpmtdFreeData(&td); + + headerGet(h, RPMTAG_DIRNAMES, &td, HEADERGET_MINMEM); + rpmtdSetTag(&td, RPMTAG_ORIGDIRNAMES); + headerPut(h, &td, HEADERPUT_DEFAULT); + rpmtdFreeData(&td); + + headerGet(h, RPMTAG_DIRINDEXES, &td, HEADERGET_MINMEM); + rpmtdSetTag(&td, RPMTAG_ORIGDIRINDEXES); + headerPut(h, &td, HEADERPUT_DEFAULT); + rpmtdFreeData(&td); +} + +void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations, + rpmfs fs, Header h) +{ + char ** baseNames; + char ** dirNames; + uint32_t * dirIndexes; + rpm_count_t fileCount, dirCount; + int nrelocated = 0; + int fileAlloced = 0; + char * fn = NULL; + int haveRelocatedBase = 0; + size_t maxlen = 0; + int i, j; + struct rpmtd_s bnames, dnames, dindexes, fmodes; + + if (!addPrefixes(h, relocations, numRelocations)) + return; + + if (rpmIsDebug()) { + rpmlog(RPMLOG_DEBUG, "========== relocations\n"); + for (i = 0; i < numRelocations; i++) { + if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */ + if (relocations[i].newPath == NULL) + rpmlog(RPMLOG_DEBUG, "%5d exclude %s\n", + i, relocations[i].oldPath); + else + rpmlog(RPMLOG_DEBUG, "%5d relocate %s -> %s\n", + i, relocations[i].oldPath, relocations[i].newPath); + } + } + + for (i = 0; i < numRelocations; i++) { + if (relocations[i].newPath == NULL) continue; + size_t len = strlen(relocations[i].newPath); + if (len > maxlen) maxlen = len; + } + + headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM); + headerGet(h, RPMTAG_DIRINDEXES, &dindexes, HEADERGET_ALLOC); + headerGet(h, RPMTAG_DIRNAMES, &dnames, HEADERGET_MINMEM); + headerGet(h, RPMTAG_FILEMODES, &fmodes, HEADERGET_MINMEM); + /* TODO XXX ugh.. use rpmtd iterators & friends instead */ + baseNames = bnames.data; + dirIndexes = dindexes.data; + fileCount = rpmtdCount(&bnames); + dirCount = rpmtdCount(&dnames); + /* XXX TODO: use rpmtdDup() instead */ + dirNames = dnames.data = duparray(dnames.data, dirCount); + dnames.flags |= RPMTD_PTR_ALLOCED; + + /* + * For all relocations, we go through sorted file/relocation lists + * backwards so that /usr/local relocations take precedence over /usr + * ones. + */ + + /* Relocate individual paths. */ + + for (i = fileCount - 1; i >= 0; i--) { + rpmFileTypes ft; + int fnlen; + + size_t len = maxlen + + strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1; + if (len >= fileAlloced) { + fileAlloced = len * 2; + fn = xrealloc(fn, fileAlloced); + } + +assert(fn != NULL); /* XXX can't happen */ + *fn = '\0'; + fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn; + + /* + * See if this file path needs relocating. + */ + /* + * XXX FIXME: Would a bsearch of the (already sorted) + * relocation list be a good idea? + */ + for (j = numRelocations - 1; j >= 0; j--) { + if (relocations[j].oldPath == NULL) /* XXX can't happen */ + continue; + len = !rstreq(relocations[j].oldPath, "/") + ? strlen(relocations[j].oldPath) + : 0; + + if (fnlen < len) + continue; + /* + * Only subdirectories or complete file paths may be relocated. We + * don't check for '\0' as our directory names all end in '/'. + */ + if (!(fn[len] == '/' || fnlen == len)) + continue; + + if (!rstreqn(relocations[j].oldPath, fn, len)) + continue; + break; + } + if (j < 0) continue; + + rpmtdSetIndex(&fmodes, i); + ft = rpmfiWhatis(rpmtdGetNumber(&fmodes)); + + /* On install, a relocate to NULL means skip the path. */ + if (relocations[j].newPath == NULL) { + if (ft == XDIR) { + /* Start with the parent, looking for directory to exclude. */ + for (j = dirIndexes[i]; j < dirCount; j++) { + len = strlen(dirNames[j]) - 1; + while (len > 0 && dirNames[j][len-1] == '/') len--; + if (fnlen != len) + continue; + if (!rstreqn(fn, dirNames[j], fnlen)) + continue; + break; + } + } + rpmfsSetAction(fs, i, FA_SKIPNSTATE); + rpmlog(RPMLOG_DEBUG, "excluding %s %s\n", + ftstring(ft), fn); + continue; + } + + /* Relocation on full paths only, please. */ + if (fnlen != len) continue; + + rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n", + fn, relocations[j].newPath); + nrelocated++; + + strcpy(fn, relocations[j].newPath); + { char * te = strrchr(fn, '/'); + if (te) { + if (te > fn) te++; /* root is special */ + fnlen = te - fn; + } else + te = fn + strlen(fn); + if (!rstreq(baseNames[i], te)) { /* basename changed too? */ + if (!haveRelocatedBase) { + /* XXX TODO: use rpmtdDup() instead */ + bnames.data = baseNames = duparray(baseNames, fileCount); + bnames.flags |= RPMTD_PTR_ALLOCED; + haveRelocatedBase = 1; + } + free(baseNames[i]); + baseNames[i] = xstrdup(te); + } + *te = '\0'; /* terminate new directory name */ + } + + /* Does this directory already exist in the directory list? */ + for (j = 0; j < dirCount; j++) { + if (fnlen != strlen(dirNames[j])) + continue; + if (!rstreqn(fn, dirNames[j], fnlen)) + continue; + break; + } + + if (j < dirCount) { + dirIndexes[i] = j; + continue; + } + + /* Creating new paths is a pita */ + dirNames = dnames.data = xrealloc(dnames.data, + sizeof(*dirNames) * (dirCount + 1)); + + dirNames[dirCount] = xstrdup(fn); + dirIndexes[i] = dirCount; + dirCount++; + dnames.count++; + } + + /* Finish off by relocating directories. */ + for (i = dirCount - 1; i >= 0; i--) { + for (j = numRelocations - 1; j >= 0; j--) { + + if (relocations[j].oldPath == NULL) /* XXX can't happen */ + continue; + size_t len = !rstreq(relocations[j].oldPath, "/") + ? strlen(relocations[j].oldPath) + : 0; + + if (len && !rstreqn(relocations[j].oldPath, dirNames[i], len)) + continue; + + /* + * Only subdirectories or complete file paths may be relocated. We + * don't check for '\0' as our directory names all end in '/'. + */ + if (dirNames[i][len] != '/') + continue; + + if (relocations[j].newPath) { /* Relocate the path */ + char *t = NULL; + rstrscat(&t, relocations[j].newPath, (dirNames[i] + len), NULL); + /* Unfortunately rpmCleanPath strips the trailing slash.. */ + (void) rpmCleanPath(t); + rstrcat(&t, "/"); + + rpmlog(RPMLOG_DEBUG, + "relocating directory %s to %s\n", dirNames[i], t); + free(dirNames[i]); + dirNames[i] = t; + nrelocated++; + } + } + } + + /* Save original filenames in header and replace (relocated) filenames. */ + if (nrelocated) { + saveOrig(h); + headerMod(h, &bnames); + headerMod(h, &dnames); + headerMod(h, &dindexes); + } + + rpmtdFreeData(&bnames); + rpmtdFreeData(&dnames); + rpmtdFreeData(&dindexes); + rpmtdFreeData(&fmodes); + free(fn); +} + +/** + * Macros to be defined from per-header tag values. + * @todo Should other macros be added from header when installing a package? + */ +static struct tagMacro { + const char *macroname; /*!< Macro name to define. */ + rpmTag tag; /*!< Header tag to use for value. */ +} const tagMacros[] = { + { "name", RPMTAG_NAME }, + { "version", RPMTAG_VERSION }, + { "release", RPMTAG_RELEASE }, + { "epoch", RPMTAG_EPOCH }, + { NULL, 0 } +}; + +/** + * Define or undefine per-header macros. + * @param h header + * @param define define/undefine? + * @return 0 always + */ +static void rpmInstallLoadMacros(Header h, int define) +{ + const struct tagMacro * tagm; + + for (tagm = tagMacros; tagm->macroname != NULL; tagm++) { + struct rpmtd_s td; + char *body; + if (!headerGet(h, tagm->tag, &td, HEADERGET_DEFAULT)) + continue; + + /* + * Undefine doesn't need the actual data for anything, but + * this way ensures we only undefine what was defined earlier. + */ + if (define) { + body = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL); + rpmPushMacro(NULL, tagm->macroname, NULL, body, -1); + free(body); + } else { + rpmPopMacro(NULL, tagm->macroname); + } + rpmtdFreeData(&td); + } +} + +int headerFindSpec(Header h) +{ + struct rpmtd_s filenames; + int specix = -1; + + if (headerGet(h, RPMTAG_BASENAMES, &filenames, HEADERGET_MINMEM)) { + struct rpmtd_s td; + const char *str; + + /* Try to find spec by file flags */ + if (headerGet(h, RPMTAG_FILEFLAGS, &td, HEADERGET_MINMEM)) { + rpmfileAttrs *flags; + while (specix < 0 && (flags = rpmtdNextUint32(&td))) { + if (*flags & RPMFILE_SPECFILE) + specix = rpmtdGetIndex(&td); + } + rpmtdFreeData(&td); + } + /* Still no spec? Look by filename. */ + while (specix < 0 && (str = rpmtdNextString(&filenames))) { + if (rpmFileHasSuffix(str, ".spec")) + specix = rpmtdGetIndex(&filenames); + } + rpmtdFreeData(&filenames); + } + return specix; +} + +/* + * Source rpms only contain basenames, on install the full paths are + * constructed with %{_specdir} and %{_sourcedir} macros. Because + * of that regular relocation wont work, we need to do it the hard + * way. Return spec file index on success, -1 on errors. + */ +int rpmRelocateSrpmFileList(Header h, const char *rootDir) +{ + int specix = headerFindSpec(h); + + if (specix >= 0) { + const char *bn; + struct rpmtd_s filenames; + /* save original file names */ + saveOrig(h); + + headerDel(h, RPMTAG_BASENAMES); + headerDel(h, RPMTAG_DIRNAMES); + headerDel(h, RPMTAG_DIRINDEXES); + + /* Macros need to be added before trying to create directories */ + rpmInstallLoadMacros(h, 1); + + /* ALLOC is needed as we modify the header */ + headerGet(h, RPMTAG_ORIGBASENAMES, &filenames, HEADERGET_ALLOC); + for (int i = 0; (bn = rpmtdNextString(&filenames)); i++) { + int spec = (i == specix); + char *fn = rpmGenPath(rootDir, + spec ? "%{_specdir}" : "%{_sourcedir}", bn); + headerPutString(h, RPMTAG_OLDFILENAMES, fn); + free(fn); + } + rpmtdFreeData(&filenames); + headerConvert(h, HEADERCONV_COMPRESSFILELIST); + rpmInstallLoadMacros(h, 0); + } + + return specix; +} + +/* stupid bubble sort, but it's probably faster here */ +static void sortRelocs(rpmRelocation *relocations, int numRelocations) +{ + for (int i = 0; i < numRelocations; i++) { + int madeSwap = 0; + for (int j = 1; j < numRelocations; j++) { + rpmRelocation tmpReloc; + if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */ + relocations[j ].oldPath == NULL || /* XXX can't happen */ + strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0) + continue; + tmpReloc = relocations[j - 1]; + relocations[j - 1] = relocations[j]; + relocations[j] = tmpReloc; + madeSwap = 1; + } + if (!madeSwap) break; + } +} + +static char * stripTrailingChar(char * s, char c) +{ + char * t; + for (t = s + strlen(s) - 1; *t == c && t >= s; t--) + *t = '\0'; + return s; +} + +void rpmRelocationBuild(Header h, rpmRelocation *rawrelocs, + int *rnrelocs, rpmRelocation **rrelocs, uint8_t **rbadrelocs) +{ + int i; + struct rpmtd_s validRelocs; + rpmRelocation * relocs = NULL; + uint8_t *badrelocs = NULL; + int nrelocs = 0; + + for (rpmRelocation *r = rawrelocs; r->oldPath || r->newPath; r++) + nrelocs++; + + headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM); + relocs = xmalloc(sizeof(*relocs) * (nrelocs+1)); + + /* Build sorted relocation list from raw relocations. */ + for (i = 0; i < nrelocs; i++) { + char * t; + + /* + * Default relocations (oldPath == NULL) are handled in the UI, + * not rpmlib. + */ + if (rawrelocs[i].oldPath == NULL) continue; /* XXX can't happen */ + + /* FIXME: Trailing /'s will confuse us greatly. Internal ones will + too, but those are more trouble to fix up. :-( */ + t = xstrdup(rawrelocs[i].oldPath); + relocs[i].oldPath = (t[0] == '/' && t[1] == '\0') + ? t + : stripTrailingChar(t, '/'); + + /* An old path w/o a new path is valid, and indicates exclusion */ + if (rawrelocs[i].newPath) { + int valid = 0; + const char *validprefix; + + t = xstrdup(rawrelocs[i].newPath); + relocs[i].newPath = (t[0] == '/' && t[1] == '\0') + ? t + : stripTrailingChar(t, '/'); + + /* FIX: relocations[i].oldPath == NULL */ + /* Verify that the relocation's old path is in the header. */ + rpmtdInit(&validRelocs); + while ((validprefix = rpmtdNextString(&validRelocs))) { + if (rstreq(validprefix, relocs[i].oldPath)) { + valid = 1; + break; + } + } + + if (!valid) { + if (badrelocs == NULL) + badrelocs = xcalloc(nrelocs, sizeof(*badrelocs)); + badrelocs[i] = 1; + } + } else { + relocs[i].newPath = NULL; + } + } + relocs[i].oldPath = NULL; + relocs[i].newPath = NULL; + sortRelocs(relocs, nrelocs); + + rpmtdFreeData(&validRelocs); + + *rrelocs = relocs; + *rnrelocs = nrelocs; + *rbadrelocs = badrelocs; +} + diff --git a/lib/rpmal.c b/lib/rpmal.c index 71f25916a..ca7ab053e 100644 --- a/lib/rpmal.c +++ b/lib/rpmal.c @@ -27,7 +27,7 @@ struct availablePackage_s { rpmte p; /*!< transaction member */ rpmds provides; /*!< Provides: dependencies. */ rpmds obsoletes; /*!< Obsoletes: dependencies. */ - rpmfi fi; /*!< File info set. */ + rpmfiles fi; /*!< File info set. */ }; /** \ingroup rpmdep @@ -38,11 +38,6 @@ typedef struct availableIndexEntry_s { unsigned int entryIx; /*!< Dependency index. */ } * availableIndexEntry; -struct fileNameEntry_s { - rpmsid dirName; - rpmsid baseName; -}; - #undef HASHTYPE #undef HTKEYTYPE #undef HTDATATYPE @@ -52,12 +47,18 @@ struct fileNameEntry_s { #include "lib/rpmhash.H" #include "lib/rpmhash.C" +typedef struct availableIndexFileEntry_s { + rpmsid dirName; + rpmalNum pkgNum; /*!< Containing package index. */ + unsigned int entryIx; /*!< Dependency index. */ +} * availableIndexFileEntry; + #undef HASHTYPE #undef HTKEYTYPE #undef HTDATATYPE #define HASHTYPE rpmalFileHash -#define HTKEYTYPE struct fileNameEntry_s -#define HTDATATYPE struct availableIndexEntry_s +#define HTKEYTYPE rpmsid +#define HTDATATYPE struct availableIndexFileEntry_s #include "lib/rpmhash.H" #include "lib/rpmhash.C" @@ -76,6 +77,7 @@ struct rpmal_s { rpmtransFlags tsflags; /*!< Transaction control flags. */ rpm_color_t tscolor; /*!< Transaction color. */ rpm_color_t prefcolor; /*!< Transaction preferred color. */ + fingerPrintCache fpc; }; /** @@ -87,6 +89,7 @@ static void rpmalFreeIndex(rpmal al) al->providesHash = rpmalDepHashFree(al->providesHash); al->obsoletesHash = rpmalDepHashFree(al->obsoletesHash); al->fileHash = rpmalFileHashFree(al->fileHash); + al->fpc = fpCacheFree(al->fpc); } rpmal rpmalCreate(rpmstrPool pool, int delta, rpmtransFlags tsflags, @@ -125,7 +128,7 @@ rpmal rpmalFree(rpmal al) for (i = 0; i < al->size; i++, alp++) { alp->obsoletes = rpmdsFree(alp->obsoletes); alp->provides = rpmdsFree(alp->provides); - alp->fi = rpmfiFree(alp->fi); + alp->fi = rpmfilesFree(alp->fi); } al->pool = rpmstrPoolFree(al->pool); al->list = _free(al->list); @@ -146,19 +149,6 @@ static int sidCmp(rpmsid a, rpmsid b) return (a != b); } -static unsigned int fileHash(struct fileNameEntry_s file) -{ - return file.dirName ^ file.baseName; -} - -static int fileCompare(struct fileNameEntry_s one, struct fileNameEntry_s two) -{ - int rc = (one.dirName != two.dirName);; - if (!rc) - rc = (one.baseName != two.baseName); - return rc; -} - void rpmalDel(rpmal al, rpmte p) { availablePackage alp; @@ -181,11 +171,10 @@ void rpmalDel(rpmal al, rpmte p) alp->p = NULL; } -static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfi fi) +static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfiles fi) { - struct fileNameEntry_s fileName; - struct availableIndexEntry_s fileEntry; - int fc = rpmfiFC(fi); + struct availableIndexFileEntry_s fileEntry; + int fc = rpmfilesFC(fi); rpm_color_t ficolor; int skipdoc = (al->tsflags & RPMTRANS_FLAG_NODOCS); int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS); @@ -194,22 +183,20 @@ static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfi fi) for (int i = 0; i < fc; i++) { /* Ignore colored provides not in our rainbow. */ - ficolor = rpmfiFColorIndex(fi, i); + ficolor = rpmfilesFColor(fi, i); if (al->tscolor && ficolor && !(al->tscolor & ficolor)) continue; /* Ignore files that wont be installed */ - if (skipdoc && (rpmfiFFlagsIndex(fi, i) & RPMFILE_DOC)) + if (skipdoc && (rpmfilesFFlags(fi, i) & RPMFILE_DOC)) continue; - if (skipconf && (rpmfiFFlagsIndex(fi, i) & RPMFILE_CONFIG)) + if (skipconf && (rpmfilesFFlags(fi, i) & RPMFILE_CONFIG)) continue; - fileName.dirName = rpmfiDNIdIndex(fi, rpmfiDIIndex(fi, i)); - fileName.baseName = rpmfiBNIdIndex(fi, i); - + fileEntry.dirName = rpmfilesDNId(fi, rpmfilesDI(fi, i)); fileEntry.entryIx = i; - rpmalFileHashAddEntry(al->fileHash, fileName, fileEntry); + rpmalFileHashAddEntry(al->fileHash, rpmfilesBNId(fi, i), fileEntry); } } @@ -275,7 +262,7 @@ void rpmalAdd(rpmal al, rpmte p) alp->provides = rpmdsLink(rpmteDS(p, RPMTAG_PROVIDENAME)); alp->obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME)); - alp->fi = rpmfiLink(rpmteFI(p)); + alp->fi = rpmteFiles(p); /* * Transition-time safe-guard to catch private-pool uses. @@ -286,7 +273,7 @@ void rpmalAdd(rpmal al, rpmte p) * NULL pool from NULL alp->provides in numerous cases? */ { - rpmstrPool fipool = rpmfiPool(alp->fi); + rpmstrPool fipool = rpmfilesPool(alp->fi); rpmstrPool dspool = rpmdsPool(alp->provides); assert(fipool == NULL || fipool == al->pool); @@ -312,10 +299,10 @@ static void rpmalMakeFileIndex(rpmal al) for (i = 0; i < al->size; i++) { alp = al->list + i; if (alp->fi != NULL) - fileCnt += rpmfiFC(alp->fi); + fileCnt += rpmfilesFC(alp->fi); } al->fileHash = rpmalFileHashCreate(fileCnt/4+128, - fileHash, fileCompare, NULL, NULL); + sidHash, sidCmp, NULL, NULL); for (i = 0; i < al->size; i++) { alp = al->list + i; rpmalAddFiles(al, i, alp->fi); @@ -403,7 +390,7 @@ rpmte * rpmalAllObsoletes(rpmal al, rpmds ds) return ret; } -static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName) +static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName, const rpmds filterds) { const char *slash; rpmte * ret = NULL; @@ -413,31 +400,45 @@ static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName) /* Split path into dirname and basename components for lookup */ if ((slash = strrchr(fileName, '/')) != NULL) { - availableIndexEntry result; + availableIndexFileEntry result; int resultCnt = 0; size_t bnStart = (slash - fileName) + 1; - struct fileNameEntry_s fne; - - fne.baseName = rpmstrPoolId(al->pool, fileName + bnStart, 0); - fne.dirName = rpmstrPoolIdn(al->pool, fileName, bnStart, 0); + rpmsid baseName; if (al->fileHash == NULL) rpmalMakeFileIndex(al); - rpmalFileHashGetEntry(al->fileHash, fne, &result, &resultCnt, NULL); + baseName = rpmstrPoolId(al->pool, fileName + bnStart, 0); + if (!baseName) + return NULL; /* no match possible */ + + rpmalFileHashGetEntry(al->fileHash, baseName, &result, &resultCnt, NULL); if (resultCnt > 0) { int i, found; ret = xmalloc((resultCnt+1) * sizeof(*ret)); + fingerPrint * fp = NULL; + rpmsid dirName = rpmstrPoolIdn(al->pool, fileName, bnStart, 1); + + if (!al->fpc) + al->fpc = fpCacheCreate(1001, NULL); + fpLookup(al->fpc, rpmstrPoolStr(al->pool, dirName), fileName + bnStart, &fp); for (found = i = 0; i < resultCnt; i++) { availablePackage alp = al->list + result[i].pkgNum; - if (alp->p == NULL) // deleted + if (alp->p == NULL) /* deleted */ + continue; + /* ignore self-conflicts/obsoletes */ + if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds) + continue; + if (result[i].dirName != dirName && + !fpLookupEquals(al->fpc, fp, rpmstrPoolStr(al->pool, result[i].dirName), fileName + bnStart)) continue; ret[found] = alp->p; found++; } + _free(fp); ret[found] = NULL; } } @@ -454,6 +455,8 @@ rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds) availableIndexEntry result; int resultCnt; int obsolete; + rpmTagVal dtag; + rpmds filterds = NULL; availablePackage alp; int rc; @@ -461,11 +464,14 @@ rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds) if (al == NULL || ds == NULL || (nameId = rpmdsNId(ds)) == 0) return ret; - obsolete = (rpmdsTagN(ds) == RPMTAG_OBSOLETENAME); + dtag = rpmdsTagN(ds); + obsolete = (dtag == RPMTAG_OBSOLETENAME); + if (dtag == RPMTAG_OBSOLETENAME || dtag == RPMTAG_CONFLICTNAME) + filterds = ds; name = rpmstrPoolStr(al->pool, nameId); if (!obsolete && *name == '/') { /* First, look for files "contained" in package ... */ - ret = rpmalAllFileSatisfiesDepend(al, name); + ret = rpmalAllFileSatisfiesDepend(al, name, filterds); if (ret != NULL && *ret != NULL) { rpmdsNotify(ds, "(added files)", 0); return ret; @@ -486,52 +492,70 @@ rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds) for (found=i=0; i<resultCnt; i++) { alp = al->list + result[i].pkgNum; - if (alp->p == NULL) // deleted + if (alp->p == NULL) /* deleted */ continue; - ix = result[i].entryIx; - - /* Obsoletes are on package name, filter out other provide matches */ - if (obsolete && !rstreq(rpmdsNIndex(alp->provides, ix), rpmteN(alp->p))) + /* ignore self-conflicts/obsoletes */ + if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds) continue; + ix = result[i].entryIx; - rc = rpmdsCompareIndex(alp->provides, ix, ds, rpmdsIx(ds)); - - if (rc) { - rpmdsNotify(ds, "(added provide)", 0); - ret[found] = alp->p; - found++; + if (obsolete) { + /* Obsoletes are on package NEVR only */ + rpmds thisds; + if (!rstreq(rpmdsNIndex(alp->provides, ix), rpmteN(alp->p))) + continue; + thisds = rpmteDS(alp->p, RPMTAG_NAME); + rc = rpmdsCompareIndex(thisds, rpmdsIx(thisds), ds, rpmdsIx(ds)); + } else { + rc = rpmdsCompareIndex(alp->provides, ix, ds, rpmdsIx(ds)); } + + if (rc) + ret[found++] = alp->p; } - if (found) + if (found) { + rpmdsNotify(ds, "(added provide)", 0); ret[found] = NULL; - else + } else { ret = _free(ret); + } return ret; } rpmte -rpmalSatisfiesDepend(const rpmal al, const rpmds ds) +rpmalSatisfiesDepend(const rpmal al, const rpmte te, const rpmds ds) { rpmte *providers = rpmalAllSatisfiesDepend(al, ds); rpmte best = NULL; + int bestscore = 0; if (providers) { - if (al->tscolor) { + rpm_color_t dscolor = rpmdsColor(ds); + for (rpmte *p = providers; *p; p++) { + int score = 0; + /* - * For colored dependencies, try to find a matching provider. + * For colored dependencies, prefer a matching colored provider. * Otherwise prefer provider of ts preferred color. */ - rpm_color_t dscolor = rpmdsColor(ds); - for (rpmte *p = providers; *p; p++) { + if (al->tscolor) { rpm_color_t tecolor = rpmteColor(*p); if (dscolor) { - if (dscolor == tecolor) best = *p; + if (dscolor == tecolor) score += 2; } else if (al->prefcolor) { - if (al->prefcolor == tecolor) best = *p; + if (al->prefcolor == tecolor) score += 2; } - if (best) break; + } + + /* Being self-provided is a bonus */ + if (*p == te) + score += 1; + + if (score > bestscore) { + bestscore = score; + best = *p; } } /* if not decided by now, just pick first match */ @@ -541,27 +565,13 @@ rpmalSatisfiesDepend(const rpmal al, const rpmds ds) return best; } -rpmte * -rpmalAllInCollection(const rpmal al, const char *collname) +unsigned int +rpmalLookupTE(const rpmal al, const rpmte te) { - rpmte *ret = NULL; - int found = 0; rpmalNum pkgNum; - - if (!al || !al->list || !collname) - return NULL; - - for (pkgNum = 0; pkgNum < al->size; pkgNum++) { - rpmte p = al->list[pkgNum].p; - if (rpmteHasCollection(p, collname)) { - ret = xrealloc(ret, sizeof(*ret) * (found + 1 + 1)); - ret[found] = p; - found++; - } - } - if (ret) { - ret[found] = NULL; - } - - return ret; + for (pkgNum=0; pkgNum < al->size; pkgNum++) + if (al->list[pkgNum].p == te) + break; + return pkgNum < al->size ? pkgNum : (unsigned int)-1; } + diff --git a/lib/rpmal.h b/lib/rpmal.h index 6f1327bdb..10fe756d4 100644 --- a/lib/rpmal.h +++ b/lib/rpmal.h @@ -73,22 +73,21 @@ rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds); /** * Lookup best provider for a dependency in the available list * @param al available list + * @param te transaction element * @param ds dependency set * @return best provider for the dependency, NULL if none */ RPM_GNUC_INTERNAL -rpmte rpmalSatisfiesDepend(const rpmal al, const rpmds ds); +rpmte rpmalSatisfiesDepend(const rpmal al, const rpmte te, const rpmds ds); /** - * Get a list of transaction elements that are memebers of a collection in the - * available list - * @param al available list - * @param collname collection name to search for - * @return NULL-terminated list of transaction elements that are - * members of the specified collection + * Return index of a transaction element in the available list + * @param al available list + * @param te transaction element + * @return index, (unsigned int)-1 if not found */ RPM_GNUC_INTERNAL -rpmte * rpmalAllInCollection(const rpmal al, const char * collname); +unsigned int rpmalLookupTE(const rpmal al, const rpmte te); #ifdef __cplusplus } diff --git a/lib/rpmarchive.h b/lib/rpmarchive.h new file mode 100644 index 000000000..c864e5b56 --- /dev/null +++ b/lib/rpmarchive.h @@ -0,0 +1,149 @@ +#ifndef H_ARCHIVE +#define H_ARCHIVE + +/** \ingroup payload + * \file lib/rpmarchive.h + * File archive (aka payload) API. + */ + +#define RPMERR_CHECK_ERRNO -32768 + +/** \ingroup payload + * Error codes for archive and file handling + */ +enum rpmfilesErrorCodes { + RPMERR_ITER_END = -1, + RPMERR_BAD_MAGIC = -2, + RPMERR_BAD_HEADER = -3, + RPMERR_HDR_SIZE = -4, + RPMERR_UNKNOWN_FILETYPE= -5, + RPMERR_MISSING_FILE = -6, + RPMERR_DIGEST_MISMATCH = -7, + RPMERR_INTERNAL = -8, + RPMERR_UNMAPPED_FILE = -9, + RPMERR_ENOENT = -10, + RPMERR_ENOTEMPTY = -11, + RPMERR_FILE_SIZE = -12, + RPMERR_ITER_SKIP = -13, + RPMERR_EXIST_AS_DIR = -14, + + RPMERR_OPEN_FAILED = -32768, + RPMERR_CHMOD_FAILED = -32769, + RPMERR_CHOWN_FAILED = -32770, + RPMERR_WRITE_FAILED = -32771, + RPMERR_UTIME_FAILED = -32772, + RPMERR_UNLINK_FAILED = -32773, + RPMERR_RENAME_FAILED = -32774, + RPMERR_SYMLINK_FAILED = -32775, + RPMERR_STAT_FAILED = -32776, + RPMERR_LSTAT_FAILED = -32777, + RPMERR_MKDIR_FAILED = -32778, + RPMERR_RMDIR_FAILED = -32779, + RPMERR_MKNOD_FAILED = -32780, + RPMERR_MKFIFO_FAILED = -32781, + RPMERR_LINK_FAILED = -32782, + RPMERR_READLINK_FAILED = -32783, + RPMERR_READ_FAILED = -32784, + RPMERR_COPY_FAILED = -32785, + RPMERR_LSETFCON_FAILED = -32786, + RPMERR_SETCAP_FAILED = -32787, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup payload + * Return formatted error message on payload handling failure. + * @param rc error code + * @return formatted error string (malloced) + */ +char * rpmfileStrerror(int rc); + +/** \ingroup payload + * Get new file iterator for writing the archive content. + * The returned rpmfi will only visit the files needing some content. + * You need to provide the content using rpmfiArchiveWrite() or + * rpmfiArchiveWriteFile(). Make sure to close the rpmfi with + * rpmfiArchiveClose() to get the trailer written. + * rpmfiSetFX() is not supported for this type of iterator. + * @param fd file + * @param files file info + * @return new rpmfi + */ +rpmfi rpmfiNewArchiveWriter(FD_t fd, rpmfiles files); + +/** \ingroup payload + * Get new file iterator for looping over the archive content. + * Returned rpmfi visites files in the order they are read from the payload. + * Content of the regular files can be retrieved with rpmfiArchiveRead() or + * rpmfiArchiveReadToFile() when they are visited with rpmfiNext(). + * rpmfiSetFX() is not supported for this type of iterator. + * @param fd file + * @param files file info + * @param itype how to handle hard links. See rpmFileIter. + * @return new rpmfi + */ + rpmfi rpmfiNewArchiveReader(FD_t fd, rpmfiles files, int itype); + +/** \ingroup payload + * Close payload archive + * @param fi file info + * @return > 0 on error + */ +int rpmfiArchiveClose(rpmfi fi); + +/** \ingroup payload + * Return current position in payload archive + * @param fi file info + * @return position + */ +rpm_loff_t rpmfiArchiveTell(rpmfi fi); + +/** \ingroup payload + * Write content into current file in archive + * @param fi file info + * @param buf pointer to content + * @param size number of bytes to write + * @return bytes actually written + */ +size_t rpmfiArchiveWrite(rpmfi fi, const void * buf, size_t size); + +/** \ingroup payload + * Write content from given file into current file in archive + * @param fi file info + * @param fd file descriptor of file to read + * @return > 0 on error + */ +int rpmfiArchiveWriteFile(rpmfi fi, FD_t fd); + +/** \ingroup payload + * Read content from current file in archive + * @param fi file info + * @param buf pointer to buffer + * @param size number of bytes to read + * @return bytes actually read + */ +size_t rpmfiArchiveRead(rpmfi fi, void * buf, size_t size); + +/** \ingroup payload + * Has current file content stored in the archive + * @param fi file info + * @ return 1 for regular files but 0 for hardlinks without content + */ +int rpmfiArchiveHasContent(rpmfi fi); + +/** \ingroup payload + * Write content from current file in archive to a file + * @param fi file info + * @param fd file descriptor of file to write to + * @param nodigest omit checksum check if 1 + * @return > 0 on error + */ +int rpmfiArchiveReadToFile(rpmfi fi, FD_t fd, int nodigest); + +#ifdef __cplusplus +} +#endif + +#endif /* H_ARCHIVE */ diff --git a/lib/rpmcallback.h b/lib/rpmcallback.h index b3b05c6c1..f07892d0a 100644 --- a/lib/rpmcallback.h +++ b/lib/rpmcallback.h @@ -1,6 +1,12 @@ #ifndef _RPMCALLBACK_H #define _RPMCALLBACK_H +/** \ingroup rpmcallback + * \file lib/rpmcallback.h + * + * (un)install callbacks + */ + #include <rpm/rpmtypes.h> #ifdef __cplusplus @@ -31,9 +37,22 @@ typedef enum rpmCallbackType_e { RPMCALLBACK_SCRIPT_START = (1 << 16), RPMCALLBACK_SCRIPT_STOP = (1 << 17), RPMCALLBACK_INST_STOP = (1 << 18), + RPMCALLBACK_ELEM_PROGRESS = (1 << 19), } rpmCallbackType; -/** +/** \ingroup rpmts + * Function pointer type for rpmtsSetNotifyCallback() triggered by + * rpmtsNotify() + * + * @param h related header or NULL + * @param what kind of notification (See RPMCALLBACK_ constants above) + * @param amount number of bytes/packages already processed or + * tag of the scriptlet involved + * or 0 or some other number + * @param total total number of bytes/packages to be processed or + * return code of the scriptlet or 0 + * @param key result of rpmteKey() of related rpmte or 0 + * @param data user data as passed to rpmtsSetNotifyCallback() */ typedef void * (*rpmCallbackFunction) (const void * h, diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c index 0d3e95ad7..196bb1ccb 100644 --- a/lib/rpmchecksig.c +++ b/lib/rpmchecksig.c @@ -11,7 +11,7 @@ #include <rpm/rpmpgp.h> #include <rpm/rpmcli.h> #include <rpm/rpmfileutil.h> /* rpmMkTemp() */ -#include <rpm/rpmdb.h> +#include <rpm/rpmsq.h> #include <rpm/rpmts.h> #include <rpm/rpmlog.h> #include <rpm/rpmstring.h> @@ -19,7 +19,8 @@ #include "rpmio/rpmio_internal.h" /* fdSetBundle() */ #include "lib/rpmlead.h" -#include "lib/signature.h" +#include "lib/header_internal.h" +#include "lib/rpmvs.h" #include "debug.h" @@ -35,14 +36,31 @@ static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen) do { uint8_t *pkt = NULL; + uint8_t *pkti = NULL; size_t pktlen = 0; + size_t certlen; /* Read pgp packet. */ if (pgpParsePkts(start, &pkt, &pktlen) == PGPARMOR_PUBKEY) { - /* Import pubkey packet(s). */ - if (rpmtsImportPubkey(ts, pkt, pktlen) != RPMRC_OK) { - rpmlog(RPMLOG_ERR, _("%s: key %d import failed.\n"), fn, keyno); - res++; + pkti = pkt; + + /* Iterate over certificates in pkt */ + while (pktlen > 0) { + if (pgpPubKeyCertLen(pkti, pktlen, &certlen)) { + rpmlog(RPMLOG_ERR, _("%s: key %d import failed.\n"), fn, + keyno); + res++; + break; + } + + /* Import pubkey certificate. */ + if (rpmtsImportPubkey(ts, pkti, certlen) != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("%s: key %d import failed.\n"), fn, + keyno); + res++; + } + pkti += certlen; + pktlen -= certlen; } } else { rpmlog(RPMLOG_ERR, _("%s: key %d not an armored public key.\n"), @@ -102,308 +120,155 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv) return res; } -/** - * @todo If the GPG key was known available, the md5 digest could be skipped. - */ -static int readFile(FD_t fd, const char * fn, - rpmDigestBundle plbundle, rpmDigestBundle hdrbundle) +static int readFile(FD_t fd, char **msg) { unsigned char buf[4*BUFSIZ]; ssize_t count; - int rc = 1; - Header h = NULL; - char *msg = NULL; - - /* Read the header from the package. */ - if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) { - rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), fn, msg); - goto exit; - } - - if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) { - struct rpmtd_s utd; - - if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){ - rpmlog(RPMLOG_ERR, - _("%s: Immutable header region could not be read. " - "Corrupted package?\n"), fn); - goto exit; - } - rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic)); - rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count); - rpmtdFreeData(&utd); - } /* Read the payload from the package. */ while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {} - if (count < 0) { - rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd)); - goto exit; - } + if (count < 0) + rasprintf(msg, _("Fread failed: %s"), Fstrerror(fd)); - rc = 0; - -exit: - free(msg); - headerFree(h); - return rc; + return (count != 0); } -/* - * Figure best available signature. - * XXX TODO: Similar detection in rpmReadPackageFile(), unify these. - */ -static rpmTagVal bestSig(Header sigh, int nosignatures, int nodigests) -{ - rpmTagVal sigtag = 0; - if (sigtag == 0 && !nosignatures) { - if (headerIsEntry(sigh, RPMSIGTAG_DSA)) - sigtag = RPMSIGTAG_DSA; - else if (headerIsEntry(sigh, RPMSIGTAG_RSA)) - sigtag = RPMSIGTAG_RSA; - else if (headerIsEntry(sigh, RPMSIGTAG_GPG)) - sigtag = RPMSIGTAG_GPG; - else if (headerIsEntry(sigh, RPMSIGTAG_PGP)) - sigtag = RPMSIGTAG_PGP; - } - if (sigtag == 0 && !nodigests) { - if (headerIsEntry(sigh, RPMSIGTAG_MD5)) - sigtag = RPMSIGTAG_MD5; - else if (headerIsEntry(sigh, RPMSIGTAG_SHA1)) - sigtag = RPMSIGTAG_SHA1; /* XXX never happens */ - } - return sigtag; -} +struct vfydata_s { + int seen; + int bad; +}; -static const char *sigtagname(rpmTagVal sigtag, int upper) +static rpmRC formatVerbose(struct rpmsinfo_s *sinfo, rpmRC sigres, const char *result, void *cbdata) { - const char *n = NULL; - - switch (sigtag) { - case RPMSIGTAG_SIZE: - n = (upper ? "SIZE" : "size"); - break; - case RPMSIGTAG_SHA1: - n = (upper ? "SHA1" : "sha1"); - break; - case RPMSIGTAG_MD5: - n = (upper ? "MD5" : "md5"); - break; - case RPMSIGTAG_RSA: - n = (upper ? "RSA" : "rsa"); - break; - case RPMSIGTAG_PGP5: /* XXX legacy */ - case RPMSIGTAG_PGP: - n = (upper ? "(MD5) PGP" : "(md5) pgp"); - break; - case RPMSIGTAG_DSA: - n = (upper ? "(SHA1) DSA" : "(sha1) dsa"); - break; - case RPMSIGTAG_GPG: - n = (upper ? "GPG" : "gpg"); - break; - default: - n = (upper ? "?UnknownSigatureType?" : "???"); - break; - } - return n; + char *vsmsg = rpmsinfoMsg(sinfo, sigres, result); + rpmlog(RPMLOG_NOTICE, " %s\n", vsmsg); + free(vsmsg); + return sigres; } -/* - * Format sigcheck result for output, appending the message spew to buf and - * bad/missing keyids to keyprob. - * - * In verbose mode, just dump it all. Otherwise ok signatures - * are dumped lowercase, bad sigs uppercase and for PGP/GPG - * if misssing/untrusted key it's uppercase in parenthesis - * and stash the key id as <SIGTYPE>#<keyid>. Pfft. - */ -static void formatResult(rpmTagVal sigtag, rpmRC sigres, const char *result, - int havekey, char **keyprob, char **buf) +/* Failures are uppercase, in parenthesis if NOKEY. Otherwise lowercase. */ +static rpmRC formatDefault(struct rpmsinfo_s *sinfo, rpmRC sigres, const char *result, void *cbdata) { - char *msg = NULL; - if (rpmIsVerbose()) { - rasprintf(&msg, " %s", result); - } else { - /* Check for missing / untrusted keys in result. */ - const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK)); - - if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) { - const char *tempKey = strstr(result, "ey ID"); - if (tempKey) { - char keyid[sizeof(pgpKeyID_t) + 1]; - rstrlcpy(keyid, tempKey + 6, sizeof(keyid)); - rstrscat(keyprob, " ", signame, "#", keyid, NULL); - } - } - rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame); - } - rstrcat(buf, msg); - free(msg); + struct vfydata_s *vd = cbdata; + vd->seen |= sinfo->type; + if (sigres != RPMRC_OK) + vd->bad |= sinfo->type; + return sigres; } -static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags, - FD_t fd, const char *fn) +rpmRC rpmpkgRead(rpmPlugins plugins, rpmKeyring keyring, rpmVSFlags flags, FD_t fd, + rpmsinfoCb cb, void *cbdata, Header *hdrp) { - char *buf = NULL; - char *missingKeys = NULL; - char *untrustedKeys = NULL; - struct rpmtd_s sigtd; - rpmTagVal sigtag; - pgpDigParams sig = NULL; - Header sigh = NULL; - HeaderIterator hi = NULL; char * msg = NULL; - int res = 1; /* assume failure */ - rpmRC rc; + rpmRC xx, rc = RPMRC_FAIL; /* assume failure */ int failed = 0; - int nodigests = !(flags & VERIFY_DIGEST); - int nosignatures = !(flags & VERIFY_SIGNATURE); - rpmDigestBundle plbundle = rpmDigestBundleNew(); - rpmDigestBundle hdrbundle = rpmDigestBundleNew(); - - if ((rc = rpmLeadRead(fd, NULL, NULL, &msg)) != RPMRC_OK) { - rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg); - free(msg); + int leadtype = -1; + struct hdrblob_s sigblob, blob; + struct rpmvs_s *sigset = NULL; + Header h = NULL; + Header sigh = NULL; + rpmDigestBundle bundle = fdGetBundle(fd, 1); /* freed with fd */ + + memset(&blob, 0, sizeof(blob)); + memset(&sigblob, 0, sizeof(sigblob)); + + if ((xx = rpmLeadRead(fd, &leadtype, &msg)) != RPMRC_OK) { + /* Avoid message spew on manifests */ + if (xx == RPMRC_NOTFOUND) + msg = _free(msg); + rc = xx; goto exit; } - rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg); - switch (rc) { - default: - rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn, - (msg && *msg ? msg : "\n")); - msg = _free(msg); + /* Read the signature header. Might not be in a contiguous region. */ + if (hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, &sigblob, &msg)) goto exit; - break; - case RPMRC_OK: - if (sigh == NULL) { - rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn); - goto exit; - } - break; - } - msg = _free(msg); - - /* Grab a hint of what needs doing to avoid duplication. */ - sigtag = bestSig(sigh, nosignatures, nodigests); - - /* XXX RSA needs the hash_algo, so decode early. */ - if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP || - sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) { - unsigned int hashalgo; - if (headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT)) { - parsePGPSig(&sigtd, "package", fn, &sig); - rpmtdFreeData(&sigtd); - } - if (sig == NULL) goto exit; - - /* XXX assume same hash_algo in header-only and header+payload */ - hashalgo = pgpDigParamsAlgo(sig, PGPVAL_HASHALGO); - rpmDigestBundleAdd(plbundle, hashalgo, RPMDIGEST_NONE); - rpmDigestBundleAdd(hdrbundle, hashalgo, RPMDIGEST_NONE); - } - if (headerIsEntry(sigh, RPMSIGTAG_PGP) || - headerIsEntry(sigh, RPMSIGTAG_PGP5) || - headerIsEntry(sigh, RPMSIGTAG_MD5)) { - rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE); - } - if (headerIsEntry(sigh, RPMSIGTAG_GPG)) { - rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE); - } + sigset = rpmvsCreate(&sigblob, flags); - /* always do sha1 hash of header */ - rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE); + /* Initialize digests ranging over the header */ + rpmvsInitDigests(sigset, RPMSIG_HEADER, bundle); - /* Read the file, generating digest(s) on the fly. */ - fdSetBundle(fd, plbundle); - if (readFile(fd, fn, plbundle, hdrbundle)) { + /* Read the header from the package. */ + if (hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, &blob, &msg)) goto exit; + + /* Fish interesting tags from the main header. This is a bit hacky... */ + if (!(flags & (RPMVSF_NOPAYLOAD|RPMVSF_NEEDPAYLOAD))) + rpmvsAppend(sigset, &blob, RPMTAG_PAYLOADDIGEST); + + /* Initialize digests ranging over the payload only */ + rpmvsInitDigests(sigset, RPMSIG_PAYLOAD, bundle); + + + /* Verify header signatures and digests */ + failed += rpmvsVerifyItems(plugins, sigset, (RPMSIG_HEADER), bundle, keyring, cb, cbdata); + + /* Unless disabled, read the file, generating digest(s) on the fly. */ + if (!(flags & RPMVSF_NEEDPAYLOAD)) { + if (readFile(fd, &msg)) + goto exit; } - rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') ); - - hi = headerInitIterator(sigh); - for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) { - char *result = NULL; - int havekey = 0; - DIGEST_CTX ctx = NULL; - if (sigtd.data == NULL) /* XXX can't happen */ - continue; - - /* Clean up parameters from previous sigtag. */ - sig = pgpDigParamsFree(sig); - - switch (sigtd.tag) { - case RPMSIGTAG_GPG: - case RPMSIGTAG_PGP5: /* XXX legacy */ - case RPMSIGTAG_PGP: - havekey = 1; - case RPMSIGTAG_RSA: - case RPMSIGTAG_DSA: - if (nosignatures) - continue; - if (parsePGPSig(&sigtd, "package", fn, &sig)) - goto exit; - ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle, - pgpDigParamsAlgo(sig, PGPVAL_HASHALGO)); - break; - case RPMSIGTAG_SHA1: - if (nodigests) - continue; - ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1); - break; - case RPMSIGTAG_MD5: - if (nodigests) - continue; - ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5); - break; - default: - continue; - break; - } + /* Verify signatures and digests ranging over the payload */ + failed += rpmvsVerifyItems(plugins, sigset, (RPMSIG_PAYLOAD), bundle, + keyring, cb, cbdata); + failed += rpmvsVerifyItems(plugins, sigset, (RPMSIG_HEADER|RPMSIG_PAYLOAD), bundle, + keyring, cb, cbdata); - rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, &result); - rpmDigestFinal(ctx, NULL, NULL, 0); + if (failed == 0) { + /* Finally import the headers and do whatever required retrofits etc */ + if (hdrp) { + if (hdrblobImport(&sigblob, 0, &sigh, &msg)) + goto exit; + if (hdrblobImport(&blob, 0, &h, &msg)) + goto exit; - formatResult(sigtd.tag, rc, result, havekey, - (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys), - &buf); - free(result); + /* Append (and remap) signature tags to the metadata. */ + headerMergeLegacySigs(h, sigh); + applyRetrofits(h, leadtype); - if (rc != RPMRC_OK) { - failed = 1; + /* Bump reference count for return. */ + *hdrp = headerLink(h); } - + rc = RPMRC_OK; } - res = failed; +exit: + if (rc && msg != NULL) + rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg); + free(msg); + free(sigblob.ei); + free(blob.ei); + headerFree(h); + headerFree(sigh); + rpmvsFree(sigset); + return rc; +} + +static int rpmpkgVerifySigs(rpmPlugins plugins, rpmKeyring keyring, rpmVSFlags flags, + FD_t fd, const char *fn) +{ + int rc; if (rpmIsVerbose()) { - rpmlog(RPMLOG_NOTICE, "%s", buf); + rpmlog(RPMLOG_NOTICE, "%s:\n", fn); + rc = rpmpkgRead(plugins, keyring, flags, fd, formatVerbose, NULL, NULL); } else { - const char *ok = (failed ? _("NOT OK") : _("OK")); - rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf, ok, - missingKeys ? _(" (MISSING KEYS:") : "", - missingKeys ? missingKeys : "", - missingKeys ? _(") ") : "", - untrustedKeys ? _(" (UNTRUSTED KEYS:") : "", - untrustedKeys ? untrustedKeys : "", - untrustedKeys ? _(")") : ""); + struct vfydata_s vd = { 0, 0 }; + rpmlog(RPMLOG_NOTICE, "%s:", fn); + rc = rpmpkgRead(plugins, keyring, flags, fd, formatDefault, &vd, NULL); + if (vd.seen & RPMSIG_DIGEST_TYPE) { + rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_DIGEST_TYPE) ? + _("DIGESTS") : _("digests")); + } + if (vd.seen & RPMSIG_SIGNATURE_TYPE) { + rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_SIGNATURE_TYPE) ? + _("SIGNATURES") : _("signatures")); + } + rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK")); } - free(missingKeys); - free(untrustedKeys); - -exit: - free(buf); - rpmDigestBundleFree(hdrbundle); - rpmDigestBundleFree(plbundle); - fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */ - sigh = rpmFreeSignature(sigh); - hi = headerFreeIterator(hi); - pgpDigParamsFree(sig); - return res; + return rc; } /* Wrapper around rpmkVerifySigs to preserve API */ @@ -412,7 +277,7 @@ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn) int rc = 1; /* assume failure */ if (ts && qva && fd && fn) { rpmKeyring keyring = rpmtsGetKeyring(ts, 1); - rc = rpmpkgVerifySigs(keyring, qva->qva_flags, fd, fn); + rc = rpmpkgVerifySigs(rpmtsPlugins(ts), keyring, qva->qva_flags, fd, fn); rpmKeyringFree(keyring); } return rc; @@ -423,9 +288,12 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) const char * arg; int res = 0; rpmKeyring keyring = rpmtsGetKeyring(ts, 1); - rpmVerifyFlags verifyFlags = (VERIFY_DIGEST|VERIFY_SIGNATURE); - - verifyFlags &= ~rpmcliQueryFlags; + rpmVSFlags vsflags = 0; + + if (rpmcliQueryFlags & QUERY_DIGEST) + vsflags |= _RPMVSF_NODIGESTS; + if (rpmcliQueryFlags & QUERY_SIGNATURE) + vsflags |= _RPMVSF_NOSIGNATURES; while ((arg = *argv++) != NULL) { FD_t fd = Fopen(arg, "r.ufdio"); @@ -433,12 +301,12 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), arg, Fstrerror(fd)); res++; - } else if (rpmpkgVerifySigs(keyring, verifyFlags, fd, arg)) { + } else if (rpmpkgVerifySigs(rpmtsPlugins(ts), keyring, vsflags, fd, arg)) { res++; } Fclose(fd); - rpmdbCheckSignals(); + rpmsqPoll(); } rpmKeyringFree(keyring); return res; diff --git a/lib/rpmcli.h b/lib/rpmcli.h index cdb1f8ab4..7a88f8791 100644 --- a/lib/rpmcli.h +++ b/lib/rpmcli.h @@ -3,6 +3,8 @@ /** \ingroup rpmcli rpmbuild * \file lib/rpmcli.h + * + * Parsing RPM command line arguments */ #include <popt.h> @@ -67,6 +69,8 @@ rpmcliFini(poptContext optCon); #define RPMCLI_POPT_NODIGEST -1030 #define RPMCLI_POPT_NOHDRCHK -1031 #define RPMCLI_POPT_NOCONTEXTS -1032 +#define RPMCLI_POPT_NOCAPS -1033 +#define RPMCLI_POPT_TARGETPLATFORM -1034 /* ==================================================================== */ /** \name RPMQV */ @@ -91,6 +95,11 @@ enum rpmQVSources_e { RPMQV_HDRID, /*!< ... from header id (immutable header SHA1). */ RPMQV_TID, /*!< ... from install transaction id (time stamp). */ RPMQV_SPECSRPM, /*!< ... from spec file source (query only). */ + RPMQV_WHATRECOMMENDS, /*!< ... from recommends db search. */ + RPMQV_WHATSUGGESTS, /*!< ... from suggests db search. */ + RPMQV_WHATSUPPLEMENTS, /*!< ... from supplements db search. */ + RPMQV_WHATENHANCES, /*!< ... from enhances db search. */ + RPMQV_SPECBUILTRPMS, /*!< ... from pkgs which would be built from spec */ }; typedef rpmFlags rpmQVSources; @@ -124,7 +133,8 @@ enum rpmQueryFlags_e { QUERY_FOR_DOCS = (1 << 25), /*!< query: from --docfiles */ QUERY_FOR_CONFIG = (1 << 26), /*!< query: from --configfiles */ QUERY_FOR_DUMPFILES = (1 << 27), /*!< query: from --dump */ - QUERY_FOR_LICENSE = (1 << 28) /*!< query: from --licensefiles */ + QUERY_FOR_LICENSE = (1 << 28), /*!< query: from --licensefiles */ + QUERY_FOR_ARTIFACT = (1 << 29), /*!< query: from --artifacts */ }; typedef rpmFlags rpmQueryFlags; @@ -282,7 +292,8 @@ enum rpmInstallFlags_e { INSTALL_FRESHEN = (1 << 6), /*!< from --freshen */ INSTALL_INSTALL = (1 << 7), /*!< from --install */ INSTALL_ERASE = (1 << 8), /*!< from --erase */ - INSTALL_ALLMATCHES = (1 << 9) /*!< from --allmatches */ + INSTALL_ALLMATCHES = (1 << 9), /*!< from --allmatches */ + INSTALL_REINSTALL = (1 << 10), /*!< from --reinstall */ }; typedef rpmFlags rpmInstallFlags; @@ -343,7 +354,7 @@ struct rpmInstallArguments_s { }; /** \ingroup rpmcli - * Install/upgrade/freshen binary rpm package. + * Install/upgrade/freshen/reinstall binary rpm package. * @param ts transaction set * @param ia mode flags and parameters * @param fileArgv array of package file names (NULL terminated) diff --git a/lib/rpmdb.c b/lib/rpmdb.c index c22c98aa8..4af21f69c 100644 --- a/lib/rpmdb.c +++ b/lib/rpmdb.c @@ -4,8 +4,6 @@ #include "system.h" -#define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */ - #include <sys/file.h> #include <utime.h> #include <errno.h> @@ -34,6 +32,8 @@ #include "lib/rpmdb_internal.h" #include "lib/fprint.h" #include "lib/header_internal.h" /* XXX for headerSetInstance() */ +#include "lib/backend/dbiset.h" +#include "lib/misc.h" #include "debug.h" #undef HASHTYPE @@ -48,39 +48,14 @@ #undef HTKEYTYPE #undef HTDATATYPE -static rpmDbiTag const dbiTags[] = { - RPMDBI_PACKAGES, - RPMDBI_NAME, - RPMDBI_BASENAMES, - RPMDBI_GROUP, - RPMDBI_REQUIRENAME, - RPMDBI_PROVIDENAME, - RPMDBI_CONFLICTNAME, - RPMDBI_OBSOLETENAME, - RPMDBI_TRIGGERNAME, - RPMDBI_DIRNAMES, - RPMDBI_INSTALLTID, - RPMDBI_SIGMD5, - RPMDBI_SHA1HEADER, -}; - -#define dbiTagsMax (sizeof(dbiTags) / sizeof(rpmDbiTag)) - -/* A single item from an index database (i.e. the "data returned"). */ -struct dbiIndexItem { - unsigned int hdrNum; /*!< header instance in db */ - unsigned int tagNum; /*!< tag index in header */ -}; +typedef rpmRC (*idxfunc)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec); -/* Items retrieved from the index database.*/ -typedef struct _dbiIndexSet { - struct dbiIndexItem * recs; /*!< array of records */ - unsigned int count; /*!< number of records */ - size_t alloced; /*!< alloced size */ -} * dbiIndexSet; +static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, + unsigned int hdrNum, Header h, + idxfunc idxupdate); -static int addToIndex(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); -static unsigned int pkgInstance(dbiIndex dbi, int alloc); +static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); static rpmdb rpmdbUnlink(rpmdb db); static int buildIndexes(rpmdb db) @@ -92,7 +67,7 @@ static int buildIndexes(rpmdb db) rc += rpmdbOpenAll(db); /* If the main db was just created, this is expected - dont whine */ - if (!(dbiFlags(db->_dbi[0]) & DBI_CREATED)) { + if (!(dbiFlags(db->db_pkgs) & DBI_CREATED)) { rpmlog(RPMLOG_WARNING, _("Generating %d missing index(es), please wait...\n"), db->db_buildindex); @@ -101,21 +76,27 @@ static int buildIndexes(rpmdb db) /* Don't call us again */ db->db_buildindex = 0; - dbSetFSync(db->db_dbenv, 0); + dbSetFSync(db, 0); + + dbCtrl(db, RPMDB_CTRL_LOCK_RW); mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0); while ((h = rpmdbNextIterator(mi))) { unsigned int hdrNum = headerGetInstance(h); /* Build all secondary indexes which were created on open */ - for (int dbix = 1; dbix < dbiTagsMax; dbix++) { - dbiIndex dbi = db->_dbi[dbix]; + for (int dbix = 0; dbix < db->db_ndbi; dbix++) { + dbiIndex dbi = db->db_indexes[dbix]; if (dbi && (dbiFlags(dbi) & DBI_CREATED)) { - rc += addToIndex(dbi, dbiTags[dbix], hdrNum, h); + rc += indexPut(dbi, db->db_tags[dbix], hdrNum, h); } } } rpmdbFreeIterator(mi); - dbSetFSync(db->db_dbenv, !db->cfg.db_no_fsync); + + dbCtrl(db, DB_CTRL_INDEXSYNC); + dbCtrl(db, DB_CTRL_UNLOCK_RW); + + dbSetFSync(db, !db->cfg.db_no_fsync); return rc; } @@ -154,395 +135,135 @@ static int64_t splitEpoch(const char *s, const char **version) return e; } -/** \ingroup dbi - * Return handle for an index database. - * @param db rpm database - * @param rpmtag rpm tag - * @param flags - * @return index database handle - */ -static dbiIndex rpmdbOpenIndex(rpmdb db, rpmDbiTagVal rpmtag, int flags) +static int pkgdbOpen(rpmdb db, int flags, dbiIndex *dbip) { - int dbix; - dbiIndex dbi = NULL; int rc = 0; + dbiIndex dbi = NULL; if (db == NULL) - return NULL; - - for (dbix = 0; dbix < dbiTagsMax; dbix++) { - if (rpmtag == dbiTags[dbix]) - break; - } - if (dbix >= dbiTagsMax) - return NULL; - - /* Is this index already open ? */ - if ((dbi = db->_dbi[dbix]) != NULL) - return dbi; - - errno = 0; - dbi = NULL; - rc = dbiOpen(db, rpmtag, &dbi, flags); - - if (rc) { - static int _printed[32]; - if (!_printed[dbix & 0x1f]++) - rpmlog(RPMLOG_ERR, _("cannot open %s index using db%d - %s (%d)\n"), - rpmTagGetName(rpmtag), db->db_ver, - (rc > 0 ? strerror(rc) : ""), rc); - } else { - db->_dbi[dbix] = dbi; - int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY); - int rebuild = (db->db_flags & RPMDB_FLAG_REBUILD); - if (dbiType(dbi) == DBI_PRIMARY) { - /* Allocate based on max header instance number + some reserve */ - if (!verifyonly && (db->db_checked == NULL)) { - db->db_checked = dbChkCreate(1024 + pkgInstance(dbi, 0) / 4, - uintId, uintCmp, NULL, NULL); - } - /* If primary got created, we can safely run without fsync */ - if ((!verifyonly && (dbiFlags(dbi) & DBI_CREATED)) || db->cfg.db_no_fsync) { - rpmlog(RPMLOG_DEBUG, "disabling fsync on database\n"); - db->cfg.db_no_fsync = 1; - dbSetFSync(db->db_dbenv, 0); - } - } else { /* secondary index */ - if (!rebuild && !verifyonly && (dbiFlags(dbi) & DBI_CREATED)) { - rpmlog(RPMLOG_DEBUG, "index %s needs creating\n", dbiName(dbi)); - db->db_buildindex++; - if (db->db_buildindex == 1) { - buildIndexes(db); - } - } - } - } - - return dbi; -} - -union _dbswap { - unsigned int ui; - unsigned char uc[4]; -}; - -#define _DBSWAP(_a) \ -\ - { unsigned char _b, *_c = (_a).uc; \ - _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \ - _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \ -\ - } - -/* - * Ensure sufficient memory for nrecs of new records in dbiIndexSet. - * Allocate in power of two sizes to avoid memory fragmentation, so - * realloc is not always needed. - */ -static inline void dbiGrowSet(dbiIndexSet set, unsigned int nrecs) -{ - size_t need = (set->count + nrecs) * sizeof(*(set->recs)); - size_t alloced = set->alloced ? set->alloced : 1 << 4; - - while (alloced < need) - alloced <<= 1; - - if (alloced != set->alloced) { - set->recs = xrealloc(set->recs, alloced); - set->alloced = alloced; - } -} - -/** - * Convert retrieved data to index set. - * @param dbi index database handle - * @param data retrieved data - * @retval setp (malloc'ed) index set - * @return 0 on success - */ -static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp) -{ - int _dbbyteswapped = dbiByteSwapped(dbi); - const char * sdbir; - dbiIndexSet set; - unsigned int i; - dbiIndexType itype = dbiType(dbi); - - if (dbi == NULL || data == NULL || setp == NULL) return -1; + /* Is this it already open ? */ + if ((dbi = db->db_pkgs) != NULL) + goto exit; - if ((sdbir = data->data) == NULL) { - *setp = NULL; - return 0; - } - - set = xcalloc(1, sizeof(*set)); - dbiGrowSet(set, data->size / itype); - set->count = data->size / itype; - - switch (itype) { - default: - case DBI_SECONDARY: - for (i = 0; i < set->count; i++) { - union _dbswap hdrNum, tagNum; - - memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); - sdbir += sizeof(hdrNum.ui); - memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui)); - sdbir += sizeof(tagNum.ui); - if (_dbbyteswapped) { - _DBSWAP(hdrNum); - _DBSWAP(tagNum); - } - /* remove tagged directory info */ - if (tagNum.ui & 0x80000000) - tagNum.ui &= 0x0000ffff; - set->recs[i].hdrNum = hdrNum.ui; - set->recs[i].tagNum = tagNum.ui; - } - break; - case DBI_PRIMARY: - for (i = 0; i < set->count; i++) { - union _dbswap hdrNum; - - memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); - sdbir += sizeof(hdrNum.ui); - if (_dbbyteswapped) { - _DBSWAP(hdrNum); - } - set->recs[i].hdrNum = hdrNum.ui; - set->recs[i].tagNum = 0; - } - break; - } - *setp = set; - return 0; -} - -/** - * Convert index set to database representation. - * @param dbi index database handle - * @param data retrieved data - * @param set index set - * @return 0 on success - */ -static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set) -{ - int _dbbyteswapped = dbiByteSwapped(dbi); - char * tdbir; - unsigned int i; - dbiIndexType itype = dbiType(dbi); - - if (dbi == NULL || data == NULL || set == NULL) - return -1; + rc = dbiOpen(db, RPMDBI_PACKAGES, &dbi, flags); - data->size = set->count * itype; - if (data->size == 0) { - data->data = NULL; - return 0; - } - tdbir = data->data = xmalloc(data->size); + if (rc == 0) { + int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY); - switch (itype) { - default: - case DBI_SECONDARY: - for (i = 0; i < set->count; i++) { - union _dbswap hdrNum, tagNum; - - memset(&hdrNum, 0, sizeof(hdrNum)); - memset(&tagNum, 0, sizeof(tagNum)); - hdrNum.ui = set->recs[i].hdrNum; - tagNum.ui = set->recs[i].tagNum; - if (_dbbyteswapped) { - _DBSWAP(hdrNum); - _DBSWAP(tagNum); - } - memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); - tdbir += sizeof(hdrNum.ui); - memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui)); - tdbir += sizeof(tagNum.ui); + db->db_pkgs = dbi; + /* Allocate header checking cache .. based on some random number */ + if (!verifyonly && (db->db_checked == NULL)) { + db->db_checked = dbChkCreate(567, uintId, uintCmp, NULL, NULL); } - break; - case DBI_PRIMARY: - for (i = 0; i < set->count; i++) { - union _dbswap hdrNum; - - memset(&hdrNum, 0, sizeof(hdrNum)); - hdrNum.ui = set->recs[i].hdrNum; - if (_dbbyteswapped) { - _DBSWAP(hdrNum); - } - memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); - tdbir += sizeof(hdrNum.ui); + /* If primary got created, we can safely run without fsync */ + if ((!verifyonly && (dbiFlags(dbi) & DBI_CREATED)) || db->cfg.db_no_fsync) { + rpmlog(RPMLOG_DEBUG, "disabling fsync on database\n"); + db->cfg.db_no_fsync = 1; + dbSetFSync(db, 0); } - break; + } else { + rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"), + rpmTagGetName(RPMDBI_PACKAGES), db->db_descr, + (rc > 0 ? strerror(rc) : ""), rc); } - return 0; -} +exit: + if (rc == 0 && dbip) + *dbip = dbi; -/* XXX assumes hdrNum is first int in dbiIndexItem */ -static int hdrNumCmp(const void * one, const void * two) -{ - const unsigned int * a = one, * b = two; - return (*a - *b); + return rc; } -/** - * Append element(s) to set of index database items. - * @param set set of index database items - * @param recs array of items to append to set - * @param nrecs number of items - * @param recsize size of an array item - * @param sortset should resulting set be sorted? - * @return 0 success, 1 failure (bad args) - */ -static int dbiAppendSet(dbiIndexSet set, const void * recs, - int nrecs, size_t recsize, int sortset) +static int indexOpen(rpmdb db, rpmDbiTagVal rpmtag, int flags, dbiIndex *dbip) { - const char * rptr = recs; - size_t rlen = (recsize < sizeof(*(set->recs))) - ? recsize : sizeof(*(set->recs)); - - if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0) - return 1; + int dbix, rc = 0; + dbiIndex dbi = NULL; - dbiGrowSet(set, nrecs); - memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs))); + if (db == NULL) + return -1; - while (nrecs-- > 0) { - memcpy(set->recs + set->count, rptr, rlen); - rptr += recsize; - set->count++; + for (dbix = 0; dbix < db->db_ndbi; dbix++) { + if (rpmtag == db->db_tags[dbix]) + break; } + if (dbix >= db->db_ndbi) + return -1; - if (sortset && set->count > 1) - qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp); - - return 0; -} + /* Is this index already open ? */ + if ((dbi = db->db_indexes[dbix]) != NULL) + goto exit; -/** - * Remove element(s) from set of index database items. - * @param set set of index database items - * @param recs array of items to remove from set - * @param nrecs number of items - * @param recsize size of an array item - * @param sorted array is already sorted? - * @return 0 success, 1 failure (no items found) - */ -static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs, - size_t recsize, int sorted) -{ - unsigned int from; - unsigned int to = 0; - unsigned int num = set->count; - unsigned int numCopied = 0; + rc = dbiOpen(db, rpmtag, &dbi, flags); - assert(set->count > 0); - if (nrecs > 1 && !sorted) - qsort(recs, nrecs, recsize, hdrNumCmp); + if (rc == 0) { + int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY); + int rebuild = (db->db_flags & RPMDB_FLAG_REBUILD); - for (from = 0; from < num; from++) { - if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) { - set->count--; - continue; + db->db_indexes[dbix] = dbi; + if (!rebuild && !verifyonly && (dbiFlags(dbi) & DBI_CREATED)) { + rpmlog(RPMLOG_DEBUG, "index %s needs creating\n", dbiName(dbi)); + db->db_buildindex++; + if (db->db_buildindex == 1) { + buildIndexes(db); + } } - if (from != to) - set->recs[to] = set->recs[from]; /* structure assignment */ - to++; - numCopied++; + } else { + rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"), + rpmTagGetName(rpmtag), db->db_descr, + (rc > 0 ? strerror(rc) : ""), rc); } - return (numCopied == num); -} - -/* Count items in index database set. */ -static unsigned int dbiIndexSetCount(dbiIndexSet set) -{ - return set->count; -} - -/* Return record offset of header from element in index database set. */ -static unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) -{ - return set->recs[recno].hdrNum; -} -/* Return file index from element in index database set. */ -static unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) -{ - return set->recs[recno].tagNum; -} - -/* Destroy set of index database items */ -static dbiIndexSet dbiIndexSetFree(dbiIndexSet set) -{ - if (set) { - free(set->recs); - memset(set, 0, sizeof(*set)); /* trash and burn */ - free(set); - } - return NULL; +exit: + if (rc == 0 && dbip) + *dbip = dbi; + return rc; } -static int dbiCursorGetToSet(dbiCursor dbc, const char *keyp, size_t keylen, - dbiIndexSet *set) +static rpmRC indexGet(dbiIndex dbi, const char *keyp, size_t keylen, + dbiIndexSet *set) { - int rc = EINVAL; - if (dbc != NULL && set != NULL) { - dbiIndex dbi = dbiCursorIndex(dbc); - int cflags = DB_NEXT; - DBT data, key; - memset(&data, 0, sizeof(data)); - memset(&key, 0, sizeof(key)); + rpmRC rc = RPMRC_FAIL; /* assume failure */ + if (dbi != NULL) { + dbiCursor dbc = dbiCursorInit(dbi, DBC_READ); if (keyp) { - key.data = (void *) keyp; /* discards const */ - key.size = keylen; - cflags = DB_SET; - } - - rc = dbiCursorGet(dbc, &key, &data, cflags); + if (keylen == 0) + keylen = strlen(keyp); + rc = idxdbGet(dbi, dbc, keyp, keylen, set, DBC_NORMAL_SEARCH); + } else { + do { + rc = idxdbGet(dbi, dbc, NULL, 0, set, DBC_NORMAL_SEARCH); + } while (rc == RPMRC_OK); - if (rc == 0) { - dbiIndexSet newset = NULL; - dbt2set(dbi, &data, &newset); - if (*set == NULL) { - *set = newset; - } else { - dbiAppendSet(*set, newset->recs, newset->count, - sizeof(*(newset->recs)), 0); - dbiIndexSetFree(newset); - } - } else if (rc != DB_NOTFOUND) { - rpmlog(RPMLOG_ERR, - _("error(%d) getting \"%s\" records from %s index: %s\n"), - rc, keyp ? keyp : "???", dbiName(dbi), db_strerror(rc)); + /* If we got some results, not found is not an error */ + if (rc == RPMRC_NOTFOUND && set != NULL) + rc = RPMRC_OK; } + + dbiCursorFree(dbi, dbc); } return rc; } -static int dbiGetToSet(dbiIndex dbi, const char *keyp, size_t keylen, - dbiIndexSet *set) +static rpmRC indexPrefixGet(dbiIndex dbi, const char *pfx, size_t plen, + dbiIndexSet *set) { - int rc = EINVAL; - if (dbi != NULL && keyp != NULL) { - dbiCursor dbc = dbiCursorInit(dbi, 0); + rpmRC rc = RPMRC_FAIL; /* assume failure */ - if (keylen == 0) { - keylen = strlen(keyp); - if (keylen == 0) - keylen++; /* XXX "/" fixup */ - } + if (dbi != NULL && pfx) { + dbiCursor dbc = dbiCursorInit(dbi, DBC_READ); - rc = dbiCursorGetToSet(dbc, keyp, keylen, set); + if (plen == 0) + plen = strlen(pfx); + rc = idxdbGet(dbi, dbc, pfx, plen, set, DBC_PREFIX_SEARCH); - dbiCursorFree(dbc); + dbiCursorFree(dbi, dbc); } return rc; } + typedef struct miRE_s { rpmTagVal tag; /*!< header tag */ rpmMireMode mode; /*!< pattern match mode */ @@ -556,8 +277,6 @@ typedef struct miRE_s { struct rpmdbMatchIterator_s { rpmdbMatchIterator mi_next; - void * mi_keyp; - size_t mi_keylen; rpmdb mi_db; rpmDbiTagVal mi_rpmtag; dbiIndexSet mi_set; @@ -583,92 +302,28 @@ struct rpmdbIndexIterator_s { dbiIndex ii_dbi; rpmDbiTag ii_rpmtag; dbiCursor ii_dbc; - DBT ii_key; dbiIndexSet ii_set; + unsigned int *ii_hdrNums; }; static rpmdb rpmdbRock; static rpmdbMatchIterator rpmmiRock; static rpmdbIndexIterator rpmiiRock; -int rpmdbCheckTerminate(int terminate) +void rpmAtExit(void) { - sigset_t newMask, oldMask; - static int terminating = 0; - - if (terminating) return 0; - - (void) sigfillset(&newMask); /* block all signals */ - (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask); - - if (rpmsqIsCaught(SIGINT) > 0 - || rpmsqIsCaught(SIGQUIT) > 0 - || rpmsqIsCaught(SIGHUP) > 0 - || rpmsqIsCaught(SIGTERM) > 0 - || rpmsqIsCaught(SIGPIPE) > 0 - || terminate) - terminating = 1; - - if (terminating) { - rpmdb db; - rpmdbMatchIterator mi; - rpmdbIndexIterator ii; - - while ((mi = rpmmiRock) != NULL) { - rpmmiRock = mi->mi_next; - mi->mi_next = NULL; - rpmdbFreeIterator(mi); - } - - while ((ii = rpmiiRock) != NULL) { - rpmiiRock = ii->ii_next; - ii->ii_next = NULL; - rpmdbIndexIteratorFree(ii); - } - - while ((db = rpmdbRock) != NULL) { - rpmdbRock = db->db_next; - db->db_next = NULL; - (void) rpmdbClose(db); - } - } - sigprocmask(SIG_SETMASK, &oldMask, NULL); - return terminating; -} - -int rpmdbCheckSignals(void) -{ - if (rpmdbCheckTerminate(0)) { - rpmlog(RPMLOG_DEBUG, "Exiting on signal...\n"); - exit(EXIT_FAILURE); - } - return 0; -} + rpmdb db; + rpmdbMatchIterator mi; + rpmdbIndexIterator ii; -/** - * Block all signals, returning previous signal mask. - */ -static int blockSignals(sigset_t * oldMask) -{ - sigset_t newMask; + while ((mi = rpmmiRock) != NULL) + rpmdbFreeIterator(mi); - (void) sigfillset(&newMask); /* block all signals */ - (void) sigprocmask(SIG_BLOCK, &newMask, oldMask); - (void) sigdelset(&newMask, SIGINT); - (void) sigdelset(&newMask, SIGQUIT); - (void) sigdelset(&newMask, SIGHUP); - (void) sigdelset(&newMask, SIGTERM); - (void) sigdelset(&newMask, SIGPIPE); - return sigprocmask(SIG_BLOCK, &newMask, NULL); -} + while ((ii = rpmiiRock) != NULL) + rpmdbIndexIteratorFree(ii); -/** - * Restore signal mask. - */ -static int unblockSignals(sigset_t * oldMask) -{ - (void) rpmdbCheckSignals(); - return sigprocmask(SIG_SETMASK, oldMask, NULL); + while ((db = rpmdbRock) != NULL) + (void) rpmdbClose(db); } rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx) @@ -705,20 +360,18 @@ int rpmdbOpenAll(rpmdb db) if (db == NULL) return -2; - for (int dbix = 0; dbix < dbiTagsMax; dbix++) { - dbiIndex dbi = db->_dbi[dbix]; - if (dbi == NULL) { - rc += (rpmdbOpenIndex(db, dbiTags[dbix], db->db_flags) == NULL); - } + rc = pkgdbOpen(db, db->db_flags, NULL); + for (int dbix = 0; dbix < db->db_ndbi; dbix++) { + rc += indexOpen(db, db->db_tags[dbix], db->db_flags, NULL); } return rc; } -static int dbiForeach(dbiIndex *dbis, +static int dbiForeach(dbiIndex *dbis, int ndbi, int (*func) (dbiIndex, unsigned int), int del) { int xx, rc = 0; - for (int dbix = dbiTagsMax; --dbix >= 0; ) { + for (int dbix = ndbi; --dbix >= 0; ) { if (dbis[dbix] == NULL) continue; xx = func(dbis[dbix], 0); @@ -733,12 +386,16 @@ int rpmdbClose(rpmdb db) { rpmdb * prev, next; int rc = 0; - int dbmode; if (db == NULL) goto exit; - dbmode = db->db_mode; + prev = &rpmdbRock; + while ((next = *prev) != NULL && next != db) + prev = &next->db_next; + if (!next) + goto exit; + (void) rpmdbUnlink(db); if (db->nrefs > 0) @@ -746,19 +403,19 @@ int rpmdbClose(rpmdb db) /* Always re-enable fsync on close of rw-database */ if ((db->db_mode & O_ACCMODE) != O_RDONLY) - dbSetFSync(db->db_dbenv, 1); + dbSetFSync(db, 1); - rc = dbiForeach(db->_dbi, dbiClose, 1); + if (db->db_pkgs) + rc = dbiClose(db->db_pkgs, 0); + rc += dbiForeach(db->db_indexes, db->db_ndbi, dbiClose, 1); db->db_root = _free(db->db_root); db->db_home = _free(db->db_home); db->db_fullpath = _free(db->db_fullpath); db->db_checked = dbChkFree(db->db_checked); - db->_dbi = _free(db->_dbi); + db->db_indexes = _free(db->db_indexes); + db->db_descr = _free(db->db_descr); - prev = &rpmdbRock; - while ((next = *prev) != NULL && next != db) - prev = &next->db_next; if (next) { *prev = next->db_next; next->db_next = NULL; @@ -766,36 +423,39 @@ int rpmdbClose(rpmdb db) db = _free(db); - if (rpmdbRock == NULL && (dbmode & (O_RDWR|O_WRONLY)) != 0) { - (void) rpmsqEnable(-SIGHUP, NULL); - (void) rpmsqEnable(-SIGINT, NULL); - (void) rpmsqEnable(-SIGTERM, NULL); - (void) rpmsqEnable(-SIGQUIT, NULL); - (void) rpmsqEnable(-SIGPIPE, NULL); + if (rpmdbRock == NULL) { + rpmsqActivate(0); } exit: return rc; } - -int rpmdbSync(rpmdb db) -{ - if (db == NULL) return 0; - - return dbiForeach(db->_dbi, dbiSync, 0); -} - -int rpmdbSuspendResumeDBLock(rpmdb db, int mode) -{ - if (db == NULL) return 0; - return dbiForeach(db->_dbi, mode ? dbiResumeDBLock : dbiSuspendDBLock, 0); -} - static rpmdb newRpmdb(const char * root, const char * home, int mode, int perms, int flags) { rpmdb db = NULL; char * db_home = rpmGetPath((home && *home) ? home : "%{_dbpath}", NULL); + static rpmDbiTag const dbiTags[] = { + RPMDBI_NAME, + RPMDBI_BASENAMES, + RPMDBI_GROUP, + RPMDBI_REQUIRENAME, + RPMDBI_PROVIDENAME, + RPMDBI_CONFLICTNAME, + RPMDBI_OBSOLETENAME, + RPMDBI_TRIGGERNAME, + RPMDBI_DIRNAMES, + RPMDBI_INSTALLTID, + RPMDBI_SIGMD5, + RPMDBI_SHA1HEADER, + RPMDBI_FILETRIGGERNAME, + RPMDBI_TRANSFILETRIGGERNAME, + RPMDBI_RECOMMENDNAME, + RPMDBI_SUGGESTNAME, + RPMDBI_SUPPLEMENTNAME, + RPMDBI_ENHANCENAME, + }; + if (!(db_home && db_home[0] != '%')) { rpmlog(RPMLOG_ERR, _("no dbpath has been set\n")); free(db_home); @@ -815,8 +475,10 @@ static rpmdb newRpmdb(const char * root, const char * home, db->db_fullpath = rpmGenPath(db->db_root, db->db_home, NULL); /* XXX remove environment after chrooted operations, for now... */ db->db_remove_env = (!rstreq(db->db_root, "/") ? 1 : 0); - db->_dbi = xcalloc(dbiTagsMax, sizeof(*db->_dbi)); - db->db_ver = DB_VERSION_MAJOR; /* XXX just to put something in messages */ + db->db_tags = dbiTags; + db->db_ndbi = sizeof(dbiTags) / sizeof(rpmDbiTag); + db->db_indexes = xcalloc(db->db_ndbi, sizeof(*db->db_indexes)); + db->db_descr = xstrdup("unknown db"); db->nrefs = 0; return rpmdbLink(db); } @@ -838,19 +500,15 @@ static int openDatabase(const char * prefix, if (db == NULL) return 1; - /* Try to ensure db home exists, error out if we cant even create */ + /* Try to ensure db home exists, error out if we can't even create */ rc = rpmioMkpath(rpmdbHome(db), 0755, getuid(), getgid()); if (rc == 0) { - if (rpmdbRock == NULL && (db->db_mode & (O_RDWR|O_WRONLY)) != 0) { - (void) rpmsqEnable(SIGHUP, NULL); - (void) rpmsqEnable(SIGINT, NULL); - (void) rpmsqEnable(SIGTERM, NULL); - (void) rpmsqEnable(SIGQUIT, NULL); - (void) rpmsqEnable(SIGPIPE, NULL); + if (rpmdbRock == NULL) { + rpmsqActivate(1); } /* Just the primary Packages database opened here */ - rc = (rpmdbOpenIndex(db, RPMDBI_PACKAGES, db->db_flags) != NULL) ? 0 : -2; + rc = pkgdbOpen(db, db->db_flags, NULL); } if (rc || justCheck || dbp == NULL) @@ -911,7 +569,10 @@ int rpmdbVerify(const char * prefix) int xx; rc = rpmdbOpenAll(db); - rc = dbiForeach(db->_dbi, dbiVerify, 0); + + if (db->db_pkgs) + rc += dbiVerify(db->db_pkgs, 0); + rc += dbiForeach(db->db_indexes, db->db_ndbi, dbiVerify, 0); xx = rpmdbClose(db); if (xx && rc == 0) rc = xx; @@ -920,7 +581,7 @@ int rpmdbVerify(const char * prefix) return rc; } -static Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset) +Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset) { rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset)); @@ -936,9 +597,9 @@ static Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset) * @param filespec * @param usestate take file state into account? * @retval matches - * @return 0 on success, 1 on not found, -2 on error + * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL */ -static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, +static rpmRC rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, int usestate, dbiIndexSet * matches) { char * dirName = NULL; @@ -947,7 +608,7 @@ static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, fingerPrint * fp1 = NULL; dbiIndexSet allMatches = NULL; unsigned int i; - int rc = -2; /* assume error */ + rpmRC rc = RPMRC_FAIL; /* assume error */ *matches = NULL; if (filespec == NULL) return rc; /* nothing alloced yet */ @@ -964,11 +625,11 @@ static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, if (baseName == NULL) goto exit; - rc = dbiGetToSet(dbi, baseName, 0, &allMatches); + rc = indexGet(dbi, baseName, 0, &allMatches); if (rc || allMatches == NULL) goto exit; - *matches = xcalloc(1, sizeof(**matches)); + *matches = dbiIndexSetNew(0); fpc = fpCacheCreate(allMatches->count, NULL); fpLookup(fpc, dirName, baseName, &fp1); @@ -996,7 +657,7 @@ static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, headerGet(h, RPMTAG_FILESTATES, &fs, HEADERGET_MINMEM); do { - int num = dbiIndexRecordFileNumber(allMatches, i); + unsigned int num = dbiIndexRecordFileNumber(allMatches, i); int skip = 0; if (usestate) { @@ -1009,11 +670,8 @@ static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, if (!skip) { const char *dirName = dirNames[dirIndexes[num]]; if (fpLookupEquals(fpc, fp1, dirName, baseNames[num])) { - struct dbiIndexItem rec = { - .hdrNum = dbiIndexRecordOffset(allMatches, i), - .tagNum = dbiIndexRecordFileNumber(allMatches, i), - }; - dbiAppendSet(*matches, &rec, 1, sizeof(rec), 0); + dbiIndexSetAppendOne(*matches, dbiIndexRecordOffset(allMatches, i), + dbiIndexRecordFileNumber(allMatches, i), 0); } } @@ -1036,9 +694,9 @@ static int rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec, if ((*matches)->count == 0) { *matches = dbiIndexSetFree(*matches); - rc = 1; + rc = RPMRC_NOTFOUND; } else { - rc = 0; + rc = RPMRC_OK; } exit: @@ -1049,29 +707,29 @@ exit: int rpmdbCountPackages(rpmdb db, const char * name) { - int rc = -1; - dbiIndex dbi = rpmdbOpenIndex(db, RPMDBI_NAME, 0); + int count = -1; + dbiIndex dbi = NULL; - if (dbi != NULL && name != NULL) { + if (name != NULL && indexOpen(db, RPMDBI_NAME, 0, &dbi) == 0) { dbiIndexSet matches = NULL; - rc = dbiGetToSet(dbi, name, strlen(name), &matches); + rpmRC rc = indexGet(dbi, name, strlen(name), &matches); - if (rc == 0) { - rc = dbiIndexSetCount(matches); + if (rc == RPMRC_OK) { + count = dbiIndexSetCount(matches); } else { - rc = (rc == DB_NOTFOUND) ? 0 : -1; + count = (rc == RPMRC_NOTFOUND) ? 0 : -1; } dbiIndexSetFree(matches); } - return rc; + return count; } /** * Attempt partial matches on name[-version[-release]][.arch] strings. * @param db rpmdb handle - * @param dbc index database cursor + * @param dbi index database * @param name package name * @param epoch package epoch (-1 for any epoch) * @param version package version (can be a pattern) @@ -1080,7 +738,7 @@ int rpmdbCountPackages(rpmdb db, const char * name) * @retval matches set of header instances that match * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL */ -static rpmRC dbiFindMatches(rpmdb db, dbiCursor dbc, +static rpmRC dbiFindMatches(rpmdb db, dbiIndex dbi, const char * name, int64_t epoch, const char * version, @@ -1089,16 +747,18 @@ static rpmRC dbiFindMatches(rpmdb db, dbiCursor dbc, dbiIndexSet * matches) { unsigned int gotMatches = 0; - int rc; + rpmRC rc; unsigned int i; - rc = dbiCursorGetToSet(dbc, name, strlen(name), matches); + rc = indexGet(dbi, name, strlen(name), matches); - if (rc != 0) { - return (rc == DB_NOTFOUND) ? RPMRC_NOTFOUND : RPMRC_FAIL; - } else if (epoch < 0 && version == NULL && release == NULL && arch == NULL) { - return RPMRC_OK; - } + /* No matches on the name, anything else wont match either */ + if (rc != RPMRC_OK) + goto exit; + + /* If we got matches on name and nothing else was specified, we're done */ + if (epoch < 0 && version == NULL && release == NULL && arch == NULL) + goto exit; /* Make sure the version and release match. */ for (i = 0; i < dbiIndexSetCount(*matches); i++) { @@ -1184,16 +844,14 @@ static rpmRC dbiFindByLabelArch(rpmdb db, dbiIndex dbi, char c; int brackets; rpmRC rc; - dbiCursor dbc; if (arglen == 0) return RPMRC_NOTFOUND; strncpy(localarg, arg, arglen); localarg[arglen] = '\0'; - dbc = dbiCursorInit(dbi, 0); /* did they give us just a name? */ - rc = dbiFindMatches(db, dbc, localarg, -1, NULL, NULL, arch, matches); + rc = dbiFindMatches(db, dbi, localarg, -1, NULL, NULL, arch, matches); if (rc != RPMRC_NOTFOUND) goto exit; @@ -1228,7 +886,7 @@ static rpmRC dbiFindByLabelArch(rpmdb db, dbiIndex dbi, *s = '\0'; epoch = splitEpoch(s + 1, &version); - rc = dbiFindMatches(db, dbc, localarg, epoch, version, NULL, arch, matches); + rc = dbiFindMatches(db, dbi, localarg, epoch, version, NULL, arch, matches); if (rc != RPMRC_NOTFOUND) goto exit; /* FIX: double indirection */ @@ -1262,9 +920,8 @@ static rpmRC dbiFindByLabelArch(rpmdb db, dbiIndex dbi, *s = '\0'; /* FIX: *matches may be NULL. */ epoch = splitEpoch(s + 1, &version); - rc = dbiFindMatches(db, dbc, localarg, epoch, version, release, arch, matches); + rc = dbiFindMatches(db, dbi, localarg, epoch, version, release, arch, matches); exit: - dbiCursorFree(dbc); return rc; } @@ -1298,22 +955,16 @@ static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi) return 0; if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) { - DBT key, data; - sigset_t signalMask; rpmRC rpmrc = RPMRC_NOTFOUND; - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = (void *) &mi->mi_prevoffset; - key.size = sizeof(mi->mi_prevoffset); - data.data = headerExport(mi->mi_h, &data.size); + unsigned int hdrLen = 0; + unsigned char *hdrBlob = headerExport(mi->mi_h, &hdrLen); /* Check header digest/signature on blob export (if requested). */ if (mi->mi_hdrchk && mi->mi_ts) { char * msg = NULL; int lvl; - rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data.data, data.size, &msg); + rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, hdrBlob, hdrLen, &msg); lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG); rpmlog(lvl, "%s h#%8u %s", (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"), @@ -1321,19 +972,22 @@ static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi) msg = _free(msg); } - if (data.data != NULL && rpmrc != RPMRC_FAIL) { - (void) blockSignals(&signalMask); - rc = dbiCursorPut(mi->mi_dbc, &key, &data, DB_KEYLAST); + if (hdrBlob != NULL && rpmrc != RPMRC_FAIL) { + rpmsqBlock(SIG_BLOCK); + dbCtrl(mi->mi_db, DB_CTRL_LOCK_RW); + rc = pkgdbPut(dbi, mi->mi_dbc, mi->mi_prevoffset, + hdrBlob, hdrLen); + dbCtrl(mi->mi_db, DB_CTRL_INDEXSYNC); + dbCtrl(mi->mi_db, DB_CTRL_UNLOCK_RW); + rpmsqBlock(SIG_UNBLOCK); + if (rc) { rpmlog(RPMLOG_ERR, _("error(%d) storing record #%d into %s\n"), rc, mi->mi_prevoffset, dbiName(dbi)); } - dbiSync(dbi, 0); - (void) unblockSignals(&signalMask); } - data.data = _free(data.data); - data.size = 0; + free(hdrBlob); } mi->mi_h = headerFree(mi->mi_h); @@ -1344,7 +998,7 @@ static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi) rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi) { rpmdbMatchIterator * prev, next; - dbiIndex dbi; + dbiIndex dbi = NULL; int i; if (mi == NULL) @@ -1356,13 +1010,14 @@ rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi) if (next) { *prev = next->mi_next; next->mi_next = NULL; - } + } else + return NULL; - dbi = rpmdbOpenIndex(mi->mi_db, RPMDBI_PACKAGES, 0); + pkgdbOpen(mi->mi_db, 0, &dbi); miFreeHeader(mi, dbi); - mi->mi_dbc = dbiCursorFree(mi->mi_dbc); + mi->mi_dbc = dbiCursorFree(dbi, mi->mi_dbc); if (mi->mi_re != NULL) for (i = 0; i < mi->mi_nre; i++) { @@ -1376,13 +1031,12 @@ rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi) mi->mi_re = _free(mi->mi_re); mi->mi_set = dbiIndexSetFree(mi->mi_set); - mi->mi_keyp = _free(mi->mi_keyp); rpmdbClose(mi->mi_db); mi->mi_ts = rpmtsFree(mi->mi_ts); mi = _free(mi); - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); return NULL; } @@ -1402,6 +1056,24 @@ int rpmdbGetIteratorCount(rpmdbMatchIterator mi) return (mi && mi->mi_set ? mi->mi_set->count : 0); } +int rpmdbGetIteratorIndex(rpmdbMatchIterator mi) +{ + return (mi ? mi->mi_setx : 0); +} + +void rpmdbSetIteratorIndex(rpmdbMatchIterator mi, unsigned int ix) +{ + if (mi) + mi->mi_setx = ix; +} + +unsigned int rpmdbGetIteratorOffsetFor(rpmdbMatchIterator mi, unsigned int ix) +{ + if (mi && mi->mi_set && ix < mi->mi_set->count) + return mi->mi_set->recs[ix].hdrNum; + return 0; +} + /** * Return pattern match. * @param mire match iterator regex @@ -1714,11 +1386,11 @@ int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite) int rc; if (mi == NULL) return 0; - rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0; + rc = (mi->mi_cflags & DBC_WRITE) ? 1 : 0; if (rewrite) - mi->mi_cflags |= DB_WRITECURSOR; + mi->mi_cflags |= DBC_WRITE; else - mi->mi_cflags &= ~DB_WRITECURSOR; + mi->mi_cflags &= ~DBC_WRITE; return rc; } @@ -1766,9 +1438,9 @@ static rpmRC miVerifyHeader(rpmdbMatchIterator mi, const void *uh, size_t uhlen) rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg); lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG); - rpmlog(lvl, "%s h#%8u %s", + rpmlog(lvl, "%s h#%8u %s\n", (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"), - mi->mi_offset, (msg ? msg : "\n")); + mi->mi_offset, (msg ? msg : "")); msg = _free(msg); /* Mark header checked. */ @@ -1782,20 +1454,16 @@ static rpmRC miVerifyHeader(rpmdbMatchIterator mi, const void *uh, size_t uhlen) /* FIX: mi->mi_key.data may be NULL */ Header rpmdbNextIterator(rpmdbMatchIterator mi) { - dbiIndex dbi; - void * uh; + dbiIndex dbi = NULL; + unsigned char * uh; unsigned int uhlen; - DBT key, data; - void * keyp; - size_t keylen; int rc; headerImportFlags importFlags = HEADERIMPORT_FAST; if (mi == NULL) return NULL; - dbi = rpmdbOpenIndex(mi->mi_db, RPMDBI_PACKAGES, 0); - if (dbi == NULL) + if (pkgdbOpen(mi->mi_db, 0, &dbi)) return NULL; #if defined(_USE_COPY_LOAD) @@ -1803,63 +1471,26 @@ Header rpmdbNextIterator(rpmdbMatchIterator mi) #endif /* * Cursors are per-iterator, not per-dbi, so get a cursor for the - * iterator on 1st call. If the iteration is to rewrite headers, and the - * CDB model is used for the database, then the cursor needs to - * marked with DB_WRITECURSOR as well. + * iterator on 1st call. If the iteration is to rewrite headers, + * then the cursor needs to marked with DBC_WRITE as well. */ if (mi->mi_dbc == NULL) mi->mi_dbc = dbiCursorInit(dbi, mi->mi_cflags); - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - top: uh = NULL; uhlen = 0; - union _dbswap mi_offset; do { - if (mi->mi_set) { if (!(mi->mi_setx < mi->mi_set->count)) return NULL; mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx); mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx); - mi_offset.ui = mi->mi_offset; - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(mi_offset); - keyp = &mi_offset; - keylen = sizeof(mi_offset.ui); } else { - - key.data = keyp = (void *)mi->mi_keyp; - key.size = keylen = mi->mi_keylen; - data.data = uh; - data.size = uhlen; -#if !defined(_USE_COPY_LOAD) - data.flags |= DB_DBT_MALLOC; -#endif - rc = dbiCursorGet(mi->mi_dbc, &key, &data, - (key.data == NULL ? DB_NEXT : DB_SET)); - data.flags = 0; - keyp = key.data; - keylen = key.size; - uh = data.data; - uhlen = data.size; - - /* - * If we got the next key, save the header instance number. - * - * Instance 0 (i.e. mi->mi_setx == 0) is the - * largest header instance in the database, and should be - * skipped. - */ - if (keyp && mi->mi_setx && rc == 0) { - memcpy(&mi_offset, keyp, sizeof(mi_offset.ui)); - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(mi_offset); - mi->mi_offset = mi_offset.ui; - } + rc = pkgdbGet(dbi, mi->mi_dbc, 0, &uh, &uhlen); + if (rc == 0) + mi->mi_offset = pkgdbKey(dbi, mi->mi_dbc); /* Terminate on error or end of keys */ if (rc || (mi->mi_setx && mi->mi_offset == 0)) @@ -1869,22 +1500,12 @@ top: } while (mi->mi_offset == 0); /* If next header is identical, return it now. */ - if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset) { - /* ...but rpmdb record numbers are unique, avoid endless loop */ - return (mi->mi_rpmtag == RPMDBI_PACKAGES) ? NULL : mi->mi_h; - } + if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset) + return mi->mi_h; /* Retrieve next header blob for index iterator. */ if (uh == NULL) { - key.data = keyp; - key.size = keylen; -#if !defined(_USE_COPY_LOAD) - data.flags |= DB_DBT_MALLOC; -#endif - rc = dbiCursorGet(mi->mi_dbc, &key, &data, DB_SET); - data.flags = 0; - uh = data.data; - uhlen = data.size; + rc = pkgdbGet(dbi, mi->mi_dbc, mi->mi_offset, &uh, &uhlen); if (rc) return NULL; } @@ -1914,10 +1535,7 @@ top: * Skip this header if iterator selector (if any) doesn't match. */ if (mireSkip(mi)) { - /* XXX hack, can't restart with Packages locked on single instance. */ - if (mi->mi_set || mi->mi_keyp == NULL) - goto top; - return NULL; + goto top; } headerSetInstance(mi->mi_h, mi->mi_offset); @@ -1934,22 +1552,19 @@ top: */ void rpmdbSortIterator(rpmdbMatchIterator mi) { - if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) { - /* - * mergesort is much (~10x with lots of identical basenames) faster - * than pure quicksort, but glibc uses msort_with_tmp() on stack. - */ -#if defined(__GLIBC__) - qsort(mi->mi_set->recs, mi->mi_set->count, - sizeof(*mi->mi_set->recs), hdrNumCmp); -#else - mergesort(mi->mi_set->recs, mi->mi_set->count, - sizeof(*mi->mi_set->recs), hdrNumCmp); -#endif + if (mi && mi->mi_set) { + dbiIndexSetSort(mi->mi_set); mi->mi_sorted = 1; } } +void rpmdbUniqIterator(rpmdbMatchIterator mi) +{ + if (mi && mi->mi_set) { + dbiIndexSetUniq(mi->mi_set, mi->mi_sorted); + } +} + int rpmdbExtendIterator(rpmdbMatchIterator mi, const void * keyp, size_t keylen) { @@ -1960,16 +1575,13 @@ int rpmdbExtendIterator(rpmdbMatchIterator mi, if (mi == NULL || keyp == NULL) return rc; - dbi = rpmdbOpenIndex(mi->mi_db, mi->mi_rpmtag, 0); + rc = indexOpen(mi->mi_db, mi->mi_rpmtag, 0, &dbi); - if (dbiGetToSet(dbi, keyp, keylen, &set) == 0) { + if (rc == 0 && indexGet(dbi, keyp, keylen, &set) == RPMRC_OK) { if (mi->mi_set == NULL) { mi->mi_set = set; } else { - dbiGrowSet(mi->mi_set, set->count); - memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs, - set->count * sizeof(*(mi->mi_set->recs))); - mi->mi_set->count += set->count; + dbiIndexSetAppendSet(mi->mi_set, set, 0); dbiIndexSetFree(set); } rc = 0; @@ -1978,22 +1590,31 @@ int rpmdbExtendIterator(rpmdbMatchIterator mi, return rc; } -int rpmdbPruneIterator(rpmdbMatchIterator mi, removedHash hdrNums) +int rpmdbFilterIterator(rpmdbMatchIterator mi, packageHash hdrNums, int neg) { - if (mi == NULL || hdrNums == NULL || removedHashNumKeys(hdrNums) <= 0) + if (mi == NULL || hdrNums == NULL) return 1; if (!mi->mi_set) - return 0; + return 0; + + if (packageHashNumKeys(hdrNums) == 0) { + if (!neg) + mi->mi_set->count = 0; + return 0; + } unsigned int from; unsigned int to = 0; unsigned int num = mi->mi_set->count; + int cond; assert(mi->mi_set->count > 0); for (from = 0; from < num; from++) { - if (removedHashHasEntry(hdrNums, mi->mi_set->recs[from].hdrNum)) { + cond = !packageHashHasEntry(hdrNums, mi->mi_set->recs[from].hdrNum); + cond = neg ? !cond : cond; + if (cond) { mi->mi_set->count--; continue; } @@ -2004,14 +1625,26 @@ int rpmdbPruneIterator(rpmdbMatchIterator mi, removedHash hdrNums) return 0; } -int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums) +int rpmdbPruneIterator(rpmdbMatchIterator mi, packageHash hdrNums) +{ + if (packageHashNumKeys(hdrNums) <= 0) + return 1; + + return rpmdbFilterIterator(mi, hdrNums, 1); +} + + +int rpmdbAppendIterator(rpmdbMatchIterator mi, + const unsigned int * hdrNums, unsigned int nHdrNums) { - if (mi == NULL || hdrNums == NULL || nHdrNums <= 0) + if (mi == NULL || hdrNums == NULL || nHdrNums == 0) return 1; if (mi->mi_set == NULL) - mi->mi_set = xcalloc(1, sizeof(*mi->mi_set)); - (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0); + mi->mi_set = dbiIndexSetNew(nHdrNums); + + for (unsigned int i = 0; i < nHdrNums; i++) + dbiIndexSetAppendOne(mi->mi_set, hdrNums[i], 0, 0); return 0; } @@ -2019,12 +1652,15 @@ rpmdbMatchIterator rpmdbNewIterator(rpmdb db, rpmDbiTagVal dbitag) { rpmdbMatchIterator mi = NULL; - if (rpmdbOpenIndex(db, dbitag, 0) == NULL) - return NULL; + if (dbitag == RPMDBI_PACKAGES) { + if (pkgdbOpen(db, 0, NULL)) + return NULL; + } else { + if (indexOpen(db, dbitag, 0, NULL)) + return NULL; + } mi = xcalloc(1, sizeof(*mi)); - mi->mi_keyp = NULL; - mi->mi_keylen = 0; mi->mi_set = NULL; mi->mi_db = rpmdbLink(db); mi->mi_rpmtag = dbitag; @@ -2051,19 +1687,32 @@ rpmdbMatchIterator rpmdbNewIterator(rpmdb db, rpmDbiTagVal dbitag) return mi; }; -rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag, - const void * keyp, size_t keylen) +static rpmdbMatchIterator pkgdbIterInit(rpmdb db, + const unsigned int * keyp, size_t keylen) { rpmdbMatchIterator mi = NULL; - dbiIndexSet set = NULL; - dbiIndex dbi; - void * mi_keyp = NULL; - rpmDbiTagVal dbtag = rpmtag; + rpmDbiTagVal dbtag = RPMDBI_PACKAGES; + dbiIndex pkgs = NULL; - if (db == NULL) + /* Require a sane keylen if one is specified */ + if (keyp && keylen != sizeof(*keyp)) return NULL; - (void) rpmdbCheckSignals(); + if (pkgdbOpen(db, 0, &pkgs) == 0) { + mi = rpmdbNewIterator(db, dbtag); + if (keyp) + rpmdbAppendIterator(mi, keyp, 1); + } + return mi; +} + +static rpmdbMatchIterator indexIterInit(rpmdb db, rpmDbiTagVal rpmtag, + const void * keyp, size_t keylen) +{ + rpmdbMatchIterator mi = NULL; + rpmDbiTagVal dbtag = rpmtag; + dbiIndex dbi = NULL; + dbiIndexSet set = NULL; /* Fixup the physical index for our pseudo indexes */ if (rpmtag == RPMDBI_LABEL) { @@ -2072,19 +1721,10 @@ rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag, dbtag = RPMDBI_BASENAMES; } - dbi = rpmdbOpenIndex(db, dbtag, 0); - if (dbi == NULL) - return NULL; - - /* - * Handle label and file name special cases. - * Otherwise, retrieve join keys for secondary lookup. - */ - if (rpmtag != RPMDBI_PACKAGES) { + if (indexOpen(db, dbtag, 0, &dbi) == 0) { int rc = 0; if (keyp) { - if (rpmtag == RPMDBI_LABEL) { rc = dbiFindByLabel(db, dbi, keyp, &set); } else if (rpmtag == RPMDBI_BASENAMES) { @@ -2092,117 +1732,130 @@ rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag, } else if (rpmtag == RPMDBI_INSTFILENAMES) { rc = rpmdbFindByFile(db, dbi, keyp, 1, &set); } else { - rc = dbiGetToSet(dbi, keyp, keylen, &set); + rc = indexGet(dbi, keyp, keylen, &set); } } else { /* get all entries from index */ - dbiCursor dbc = dbiCursorInit(dbi, 0); - - do { - rc = dbiCursorGetToSet(dbc, NULL, 0, &set); - } while (rc == 0); - - /* If we got some results, not found is not an error */ - if (rc == DB_NOTFOUND && set != NULL) - rc = 0; - - dbiCursorFree(dbc); + rc = indexGet(dbi, NULL, 0, &set); } if (rc) { /* error/not found */ set = dbiIndexSetFree(set); - goto exit; + } else { + mi = rpmdbNewIterator(db, dbtag); + mi->mi_set = set; + + if (keyp) { + rpmdbSortIterator(mi); + } } } + + return mi; +} - /* Copy the retrieval key, byte swapping header instance if necessary. */ - if (keyp) { - switch (dbtag) { - case RPMDBI_PACKAGES: - { union _dbswap *k; +rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag, + const void * keyp, size_t keylen) +{ + rpmdbMatchIterator mi = NULL; - assert(keylen == sizeof(k->ui)); /* xxx programmer error */ - k = xmalloc(sizeof(*k)); - memcpy(k, keyp, keylen); - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(*k); - mi_keyp = k; - } break; - default: - { char * k; - if (keylen == 0) - keylen = strlen(keyp); - k = xmalloc(keylen + 1); - memcpy(k, keyp, keylen); - k[keylen] = '\0'; /* XXX assumes strings */ - mi_keyp = k; - } break; - } + if (db != NULL) { + (void) rpmsqPoll(); + + if (rpmtag == RPMDBI_PACKAGES) + mi = pkgdbIterInit(db, keyp, keylen); + else + mi = indexIterInit(db, rpmtag, keyp, keylen); } - mi = rpmdbNewIterator(db, dbtag); - mi->mi_keyp = mi_keyp; - mi->mi_keylen = keylen; - mi->mi_set = set; + return mi; +} + +rpmdbMatchIterator rpmdbInitPrefixIterator(rpmdb db, rpmDbiTagVal rpmtag, + const void * pfx, size_t plen) +{ + rpmdbMatchIterator mi = NULL; + dbiIndexSet set = NULL; + dbiIndex dbi = NULL; + rpmDbiTagVal dbtag = rpmtag; + + if (!pfx) + return NULL; + + if (db != NULL && rpmtag != RPMDBI_PACKAGES) { + (void) rpmsqPoll(); + + + if (indexOpen(db, dbtag, 0, &dbi) == 0) { + int rc = 0; + + rc = indexPrefixGet(dbi, pfx, plen, &set); + + if (rc) { + set = dbiIndexSetFree(set); + } else { + mi = rpmdbNewIterator(db, dbtag); + mi->mi_set = set; + rpmdbSortIterator(mi); + } + } - if (dbtag != RPMDBI_PACKAGES && keyp == NULL) { - rpmdbSortIterator(mi); } -exit: return mi; } /* * Convert current tag data to db key * @param tagdata Tag data container - * @retval key DB key struct - * @retval freedata Should key.data be freed afterwards - * Return 0 to signal this item should be discarded (ie continue) + * @retval keylen Length of key + * @return Pointer to key value or NULL to signal skip */ -static int td2key(rpmtd tagdata, DBT *key, int *freedata) +static const void * td2key(rpmtd tagdata, unsigned int *keylen) { + const void * data = NULL; + unsigned int size = 0; const char *str = NULL; - *freedata = 0; switch (rpmtdType(tagdata)) { case RPM_CHAR_TYPE: case RPM_INT8_TYPE: - key->size = sizeof(uint8_t); - key->data = rpmtdGetChar(tagdata); + size = sizeof(uint8_t); + data = rpmtdGetChar(tagdata); break; case RPM_INT16_TYPE: - key->size = sizeof(uint16_t); - key->data = rpmtdGetUint16(tagdata); + size = sizeof(uint16_t); + data = rpmtdGetUint16(tagdata); break; case RPM_INT32_TYPE: - key->size = sizeof(uint32_t); - key->data = rpmtdGetUint32(tagdata); + size = sizeof(uint32_t); + data = rpmtdGetUint32(tagdata); break; case RPM_INT64_TYPE: - key->size = sizeof(uint64_t); - key->data = rpmtdGetUint64(tagdata); + size = sizeof(uint64_t); + data = rpmtdGetUint64(tagdata); break; case RPM_BIN_TYPE: - key->size = tagdata->count; - key->data = tagdata->data; + size = tagdata->count; + data = tagdata->data; break; case RPM_STRING_TYPE: case RPM_I18NSTRING_TYPE: case RPM_STRING_ARRAY_TYPE: - default: str = rpmtdGetString(tagdata); - key->data = (char *) str; /* XXX discards const */ - key->size = strlen(str); + if (str) { + size = strlen(str); + data = str; + } + break; + default: break; } - if (key->size == 0) - key->size = strlen((char *)key->data); - if (key->size == 0) - key->size++; /* XXX "/" fixup. */ + if (data && keylen) + *keylen = size; - return 1; + return data; } /* * rpmdbIndexIterator @@ -2216,10 +1869,9 @@ rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag) if (db == NULL) return NULL; - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); - dbi = rpmdbOpenIndex(db, rpmtag, 0); - if (dbi == NULL) + if (indexOpen(db, rpmtag, 0, &dbi)) return NULL; /* Chain cursors for teardown on abnormal exit. */ @@ -2238,37 +1890,69 @@ rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag) int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * keylen) { int rc; - DBT data; + unsigned int iikeylen = 0; /* argh, size_t vs uint pointer... */ if (ii == NULL) return -1; if (ii->ii_dbc == NULL) - ii->ii_dbc = dbiCursorInit(ii->ii_dbi, 0); + ii->ii_dbc = dbiCursorInit(ii->ii_dbi, DBC_READ); /* free old data */ ii->ii_set = dbiIndexSetFree(ii->ii_set); - memset(&data, 0, sizeof(data)); - rc = dbiCursorGet(ii->ii_dbc, &ii->ii_key, &data, DB_NEXT); + rc = idxdbGet(ii->ii_dbi, ii->ii_dbc, NULL, 0, &ii->ii_set, DBC_NORMAL_SEARCH); - if (rc != 0) { - *key = NULL; - *keylen = 0; + *key = idxdbKey(ii->ii_dbi, ii->ii_dbc, &iikeylen); + *keylen = iikeylen; - if (rc != DB_NOTFOUND) { - rpmlog(RPMLOG_ERR, - _("error(%d:%s) getting next key from %s index\n"), - rc, db_strerror(rc), rpmTagGetName(ii->ii_rpmtag)); - } - return -1; - } + return (rc == RPMRC_OK) ? 0 : -1; +} - (void) dbt2set(ii->ii_dbi, &data, &ii->ii_set); - *key = ii->ii_key.data; - *keylen = ii->ii_key.size; +int rpmdbIndexIteratorNextTd(rpmdbIndexIterator ii, rpmtd keytd) +{ + size_t keylen = 0; + const void * keyp = NULL; - return 0; + int rc = rpmdbIndexIteratorNext(ii, &keyp, &keylen); + + if (rc == 0) { + rpmTagVal tag = ii->ii_rpmtag; + rpmTagClass tagclass = rpmTagGetClass(tag); + + /* Set the common values, overridden below as necessary */ + keytd->type = rpmTagGetTagType(tag); + keytd->tag = tag; + keytd->flags = RPMTD_ALLOCED; + keytd->count = 1; + + switch (tagclass) { + case RPM_STRING_CLASS: { + /* + * XXX: We never return arrays here, so everything is a + * "simple" string. However this can disagree with the + * type of the index tag, eg requires are string arrays. + */ + char *key = memcpy(xmalloc(keylen + 1), keyp, keylen); + key[keylen] = '\0'; + keytd->data = key; + keytd->type = RPM_STRING_TYPE; + } break; + case RPM_BINARY_CLASS: + /* Binary types abuse count for data length */ + keytd->count = keylen; + /* fallthrough */ + case RPM_NUMERIC_CLASS: + keytd->data = memcpy(xmalloc(keylen), keyp, keylen); + break; + default: + rpmtdReset(keytd); + rc = -1; + break; + } + } + + return rc; } unsigned int rpmdbIndexIteratorNumPkgs(rpmdbIndexIterator ii) @@ -2285,6 +1969,24 @@ unsigned int rpmdbIndexIteratorPkgOffset(rpmdbIndexIterator ii, unsigned int nr) return dbiIndexRecordOffset(ii->ii_set, nr); } +unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii) +{ + int i; + + if (!ii || !ii->ii_set) + return NULL; + + if (ii->ii_hdrNums) + ii->ii_hdrNums = _free(ii->ii_hdrNums); + + ii->ii_hdrNums = xmalloc(sizeof(*ii->ii_hdrNums) * ii->ii_set->count); + for (i = 0; i < ii->ii_set->count; i++) { + ii->ii_hdrNums[i] = ii->ii_set->recs[i].hdrNum; + } + + return ii->ii_hdrNums; +} + unsigned int rpmdbIndexIteratorTagNum(rpmdbIndexIterator ii, unsigned int nr) { if (!ii || !ii->ii_set) @@ -2307,13 +2009,17 @@ rpmdbIndexIterator rpmdbIndexIteratorFree(rpmdbIndexIterator ii) if (next) { *prev = next->ii_next; next->ii_next = NULL; - } + } else + return NULL; - ii->ii_dbc = dbiCursorFree(ii->ii_dbc); + ii->ii_dbc = dbiCursorFree(ii->ii_dbi, ii->ii_dbc); ii->ii_dbi = NULL; rpmdbClose(ii->ii_db); ii->ii_set = dbiIndexSetFree(ii->ii_set); + if (ii->ii_hdrNums) + ii->ii_hdrNums = _free(ii->ii_hdrNums); + ii = _free(ii); return NULL; } @@ -2332,56 +2038,16 @@ static void logAddRemove(const char *dbiname, int removing, rpmtd tagdata) } } -/* Update primary Packages index. NULL hdr means remove */ -static int updatePackages(dbiIndex dbi, unsigned int hdrNum, DBT *hdr) +static rpmRC indexDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) { - union _dbswap mi_offset; - int rc = 0; - dbiCursor dbc; - DBT key; - - if (dbi == NULL || hdrNum == 0) - return 1; - - memset(&key, 0, sizeof(key)); - - dbc = dbiCursorInit(dbi, DB_WRITECURSOR); - - mi_offset.ui = hdrNum; - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(mi_offset); - key.data = (void *) &mi_offset; - key.size = sizeof(mi_offset.ui); - - if (hdr) { - rc = dbiCursorPut(dbc, &key, hdr, DB_KEYLAST); - if (rc) { - rpmlog(RPMLOG_ERR, - _("error(%d) adding header #%d record\n"), rc, hdrNum); - } - } else { - DBT data; - - memset(&data, 0, sizeof(data)); - rc = dbiCursorGet(dbc, &key, &data, DB_SET); - if (rc) { - rpmlog(RPMLOG_ERR, - _("error(%d) removing header #%d record\n"), rc, hdrNum); - } else - rc = dbiCursorDel(dbc, &key, &data, 0); - } - - dbiCursorFree(dbc); - dbiSync(dbi, 0); - - return rc; + return tag2index(dbi, rpmtag, hdrNum, h, idxdbDel); } int rpmdbRemove(rpmdb db, unsigned int hdrNum) { - dbiIndex dbi; + dbiIndex dbi = NULL; + dbiCursor dbc = NULL; Header h; - sigset_t signalMask; int ret = 0; if (db == NULL) @@ -2399,112 +2065,32 @@ int rpmdbRemove(rpmdb db, unsigned int hdrNum) free(nevra); } - (void) blockSignals(&signalMask); + if (pkgdbOpen(db, 0, &dbi)) + return 1; + + rpmsqBlock(SIG_BLOCK); + dbCtrl(db, DB_CTRL_LOCK_RW); - dbi = rpmdbOpenIndex(db, RPMDBI_PACKAGES, 0); /* Remove header from primary index */ - ret = updatePackages(dbi, hdrNum, NULL); + dbc = dbiCursorInit(dbi, DBC_WRITE); + ret = pkgdbDel(dbi, dbc, hdrNum); + dbiCursorFree(dbi, dbc); /* Remove associated data from secondary indexes */ if (ret == 0) { - struct dbiIndexItem rec = { .hdrNum = hdrNum, .tagNum = 0 }; - int rc = 0; - dbiCursor dbc = NULL; - DBT key, data; - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); + for (int dbix = 0; dbix < db->db_ndbi; dbix++) { + rpmDbiTag rpmtag = db->db_tags[dbix]; - for (int dbix = 1; dbix < dbiTagsMax; dbix++) { - rpmDbiTag rpmtag = dbiTags[dbix]; - struct rpmtd_s tagdata; - - if (!(dbi = rpmdbOpenIndex(db, rpmtag, 0))) - continue; - - if (!headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM)) + if (indexOpen(db, rpmtag, 0, &dbi)) continue; - dbc = dbiCursorInit(dbi, DB_WRITECURSOR); - - logAddRemove(dbiName(dbi), 1, &tagdata); - while (rpmtdNext(&tagdata) >= 0) { - dbiIndexSet set; - int freedata = 0; - - if (!td2key(&tagdata, &key, &freedata)) { - continue; - } - - /* XXX - * This is almost right, but, if there are duplicate tag - * values, there will be duplicate attempts to remove - * the header instance. It's faster to just ignore errors - * than to do things correctly. - */ - - /* - * XXX with duplicates, an accurate data value and - * DB_GET_BOTH is needed. - * */ - set = NULL; - - rc = dbiCursorGet(dbc, &key, &data, DB_SET); - if (rc == 0) { /* success */ - (void) dbt2set(dbi, &data, &set); - } else if (rc == DB_NOTFOUND) { /* not found */ - goto cont; - } else { /* error */ - rpmlog(RPMLOG_ERR, - _("error(%d) setting \"%s\" records from %s index\n"), - rc, (char*)key.data, dbiName(dbi)); - ret += 1; - goto cont; - } - - rc = dbiPruneSet(set, &rec, 1, sizeof(rec), 1); - - /* If nothing was pruned, then don't bother updating. */ - if (rc) { - set = dbiIndexSetFree(set); - goto cont; - } - - if (set->count > 0) { - (void) set2dbt(dbi, &data, set); - rc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST); - if (rc) { - rpmlog(RPMLOG_ERR, - _("error(%d) storing record \"%s\" into %s\n"), - rc, (char*)key.data, dbiName(dbi)); - ret += 1; - } - data.data = _free(data.data); - data.size = 0; - } else { - rc = dbiCursorDel(dbc, &key, &data, 0); - if (rc) { - rpmlog(RPMLOG_ERR, - _("error(%d) removing record \"%s\" from %s\n"), - rc, (char*)key.data, dbiName(dbi)); - ret += 1; - } - } - set = dbiIndexSetFree(set); -cont: - if (freedata) { - free(key.data); - } - } - - dbc = dbiCursorFree(dbc); - dbiSync(dbi, 0); - - rpmtdFreeData(&tagdata); + ret += indexDel(dbi, rpmtag, hdrNum, h); } } - (void) unblockSignals(&signalMask); + dbCtrl(db, DB_CTRL_INDEXSYNC); + dbCtrl(db, DB_CTRL_UNLOCK_RW); + rpmsqBlock(SIG_UNBLOCK); headerFree(h); @@ -2512,79 +2098,109 @@ cont: return 0; } -/* Get current header instance number or try to allocate a new one */ -static unsigned int pkgInstance(dbiIndex dbi, int alloc) -{ - unsigned int hdrNum = 0; - - if (dbi != NULL && dbiType(dbi) == DBI_PRIMARY) { - dbiCursor dbc; - DBT key, data; - unsigned int firstkey = 0; - union _dbswap mi_offset; - int ret; - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - - dbc = dbiCursorInit(dbi, alloc ? DB_WRITECURSOR : 0); - - /* Key 0 holds the current largest instance, fetch it */ - key.data = &firstkey; - key.size = sizeof(firstkey); - ret = dbiCursorGet(dbc, &key, &data, DB_SET); +struct updateRichDepData { + ARGV_t argv; + int nargv; + int neg; + int level; + int *nargv_level; +}; - if (ret == 0 && data.data) { - memcpy(&mi_offset, data.data, sizeof(mi_offset.ui)); - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(mi_offset); - hdrNum = mi_offset.ui; +static rpmRC updateRichDepCB(void *cbdata, rpmrichParseType type, + const char *n, int nl, const char *e, int el, rpmsenseFlags sense, + rpmrichOp op, char **emsg) { + struct updateRichDepData *data = cbdata; + if (type == RPMRICH_PARSE_ENTER) { + data->level++; + data->nargv_level = xrealloc(data->nargv_level, data->level * (sizeof(int))); + data->nargv_level[data->level - 1] = data->nargv; + } + if (type == RPMRICH_PARSE_LEAVE) { + data->level--; + } + if (type == RPMRICH_PARSE_SIMPLE && nl && !(nl > 7 && !strncmp(n, "rpmlib(", 7))) { + char *name = xmalloc(nl + 2); + *name = data->neg ? '!' : ' '; + strncpy(name + 1, n, nl); + name[1 + nl] = 0; + argvAdd(&data->argv, name); + data->nargv++; + _free(name); + } + if (type == RPMRICH_PARSE_OP && (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS)) { + /* save nargv in case of ELSE */ + data->nargv_level[data->level - 1] = data->nargv; + data->neg ^= 1; + } + if (type == RPMRICH_PARSE_OP && op == RPMRICHOP_ELSE) { + int i, nargv = data->nargv; + /* copy and invert condition block */ + for (i = data->nargv_level[data->level - 1]; i < nargv; i++) { + char *name = data->argv[i]; + *name ^= ' ' ^ '!'; + argvAdd(&data->argv, name); + *name ^= ' ' ^ '!'; + data->nargv++; } - - if (alloc) { - /* Rather complicated "increment by one", bswapping as needed */ - ++hdrNum; - mi_offset.ui = hdrNum; - if (dbiByteSwapped(dbi) == 1) - _DBSWAP(mi_offset); - if (ret == 0 && data.data) { - memcpy(data.data, &mi_offset, sizeof(mi_offset.ui)); - } else { - data.data = &mi_offset; - data.size = sizeof(mi_offset.ui); - } - - /* Unless we manage to insert the new instance number, we failed */ - ret = dbiCursorPut(dbc, &key, &data, DB_KEYLAST); - if (ret) { - hdrNum = 0; - rpmlog(RPMLOG_ERR, - _("error(%d) allocating new package instance\n"), ret); + data->neg ^= 1; + } + if (type == RPMRICH_PARSE_LEAVE && (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS)) { + data->neg ^= 1; + } + return RPMRC_OK; +} + +static rpmRC updateRichDep(dbiIndex dbi, dbiCursor dbc, const char *str, + struct dbiIndexItem_s *rec, + idxfunc idxupdate) +{ + int n, i, rc = 0; + struct updateRichDepData data; + + data.argv = argvNew(); + data.neg = 0; + data.nargv = 0; + data.level = 0; + data.nargv_level = xcalloc(1, sizeof(int)); + if (rpmrichParse(&str, NULL, updateRichDepCB, &data) == RPMRC_OK) { + n = argvCount(data.argv); + if (n) { + argvSort(data.argv, NULL); + for (i = 0; i < n; i++) { + char *name = data.argv[i]; + if (i && !strcmp(data.argv[i - 1], name)) + continue; /* ignore dups */ + if (*name == ' ') + name++; + rc += idxupdate(dbi, dbc, name, strlen(name), rec); } - - dbiSync(dbi, 0); } - dbiCursorFree(dbc); } - - return hdrNum; + _free(data.nargv_level); + argvFree(data.argv); + return rc; } -/* Add data to secondary index */ -static int addToIndex(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) +static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, + unsigned int hdrNum, Header h, + idxfunc idxupdate) { int i, rc = 0; - struct rpmtd_s tagdata, reqflags; + struct rpmtd_s tagdata, reqflags, trig_index; dbiCursor dbc = NULL; switch (rpmtag) { case RPMTAG_REQUIRENAME: headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM); - /* fallthrough */ - default: - headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM); + break; + case RPMTAG_FILETRIGGERNAME: + headerGet(h, RPMTAG_FILETRIGGERINDEX, &trig_index, HEADERGET_MINMEM); + break; + case RPMTAG_TRANSFILETRIGGERNAME: + headerGet(h, RPMTAG_TRANSFILETRIGGERINDEX, &trig_index, HEADERGET_MINMEM); break; } + headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM); if (rpmtdCount(&tagdata) == 0) { if (rpmtag != RPMTAG_GROUP) @@ -2596,15 +2212,29 @@ static int addToIndex(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Heade tagdata.count = 1; } - dbc = dbiCursorInit(dbi, DB_WRITECURSOR); + dbc = dbiCursorInit(dbi, DBC_WRITE); logAddRemove(dbiName(dbi), 0, &tagdata); while ((i = rpmtdNext(&tagdata)) >= 0) { - dbiIndexSet set; - int freedata = 0, j; - DBT key, data; - /* Include the tagNum in all indices (only files use though) */ - struct dbiIndexItem rec = { .hdrNum = hdrNum, .tagNum = i }; + const void * key = NULL; + unsigned int keylen = 0; + int j; + struct dbiIndexItem_s rec; + + switch (rpmtag) { + /* Include trigger index in db index for triggers */ + case RPMTAG_FILETRIGGERNAME: + case RPMTAG_TRANSFILETRIGGERNAME: + rec.hdrNum = hdrNum; + rec.tagNum = *rpmtdNextUint32(&trig_index); + break; + + /* Include the tagNum in the others indices (only files use though) */ + default: + rec.hdrNum = hdrNum; + rec.tagNum = i; + break; + } switch (rpmtag) { case RPMTAG_REQUIRENAME: { @@ -2631,107 +2261,90 @@ static int addToIndex(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Heade break; } - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - - if (!td2key(&tagdata, &key, &freedata)) { + if ((key = td2key(&tagdata, &keylen)) == NULL) continue; - } - - /* - * XXX with duplicates, an accurate data value and - * DB_GET_BOTH is needed. - */ - - set = NULL; - - rc = dbiCursorGet(dbc, &key, &data, DB_SET); - if (rc == 0) { /* success */ - /* With duplicates, cursor is positioned, discard the record. */ - if (!dbi->dbi_permit_dups) - (void) dbt2set(dbi, &data, &set); - } else if (rc != DB_NOTFOUND) { /* error */ - rpmlog(RPMLOG_ERR, - _("error(%d) getting \"%s\" records from %s index\n"), - rc, (char*)key.data, dbiName(dbi)); - rc += 1; - goto cont; - } - - if (set == NULL) /* not found or duplicate */ - set = xcalloc(1, sizeof(*set)); - (void) dbiAppendSet(set, &rec, 1, sizeof(rec), 0); - - (void) set2dbt(dbi, &data, set); - rc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST); - - if (rc) { - rpmlog(RPMLOG_ERR, - _("error(%d) storing record %s into %s\n"), - rc, (char*)key.data, dbiName(dbi)); - rc += 1; - } - data.data = _free(data.data); - data.size = 0; - set = dbiIndexSetFree(set); -cont: - if (freedata) { - free(key.data); + rc += idxupdate(dbi, dbc, key, keylen, &rec); + + if (*(char *)key == '(') { + switch (rpmtag) { + case RPMTAG_REQUIRENAME: + case RPMTAG_CONFLICTNAME: + case RPMTAG_SUGGESTNAME: + case RPMTAG_SUPPLEMENTNAME: + case RPMTAG_RECOMMENDNAME: + case RPMTAG_ENHANCENAME: + if (rpmtdType(&tagdata) == RPM_STRING_ARRAY_TYPE) { + rc += updateRichDep(dbi, dbc, rpmtdGetString(&tagdata), + &rec, idxupdate); + } + default: + break; + } } } - dbiCursorFree(dbc); - dbiSync(dbi, 0); + dbiCursorFree(dbi, dbc); exit: rpmtdFreeData(&tagdata); - return rc; + return (rc == 0) ? RPMRC_OK : RPMRC_FAIL; +} + +static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) +{ + return tag2index(dbi, rpmtag, hdrNum, h, idxdbPut); } int rpmdbAdd(rpmdb db, Header h) { - DBT hdr; - sigset_t signalMask; - dbiIndex dbi; + dbiIndex dbi = NULL; + dbiCursor dbc = NULL; unsigned int hdrNum = 0; + unsigned int hdrLen = 0; + unsigned char *hdrBlob = NULL; int ret = 0; - int hdrOk; if (db == NULL) return 0; - memset(&hdr, 0, sizeof(hdr)); - - hdr.data = headerExport(h, &hdr.size); - hdrOk = (hdr.data != NULL && hdr.size > 0); - - if (!hdrOk) { + hdrBlob = headerExport(h, &hdrLen); + if (hdrBlob == NULL || hdrLen == 0) { ret = -1; goto exit; } - (void) blockSignals(&signalMask); - - dbi = rpmdbOpenIndex(db, RPMDBI_PACKAGES, 0); - hdrNum = pkgInstance(dbi, 1); + ret = pkgdbOpen(db, 0, &dbi); + if (ret) + goto exit; + + rpmsqBlock(SIG_BLOCK); + dbCtrl(db, DB_CTRL_LOCK_RW); /* Add header to primary index */ - ret = updatePackages(dbi, hdrNum, &hdr); + dbc = dbiCursorInit(dbi, DBC_WRITE); + ret = pkgdbNew(dbi, dbc, &hdrNum); + if (ret == 0) + ret = pkgdbPut(dbi, dbc, hdrNum, hdrBlob, hdrLen); + dbiCursorFree(dbi, dbc); /* Add associated data to secondary indexes */ if (ret == 0) { - for (int dbix = 1; dbix < dbiTagsMax; dbix++) { - rpmDbiTag rpmtag = dbiTags[dbix]; + for (int dbix = 0; dbix < db->db_ndbi; dbix++) { + rpmDbiTag rpmtag = db->db_tags[dbix]; - if (!(dbi = rpmdbOpenIndex(db, rpmtag, 0))) + if (indexOpen(db, rpmtag, 0, &dbi)) continue; - ret += addToIndex(dbi, rpmtag, hdrNum, h); + ret += indexPut(dbi, rpmtag, hdrNum, h); } } - /* If everthing ok, mark header as installed now */ + dbCtrl(db, DB_CTRL_INDEXSYNC); + dbCtrl(db, DB_CTRL_UNLOCK_RW); + rpmsqBlock(SIG_UNBLOCK); + + /* If everything ok, mark header as installed now */ if (ret == 0) { headerSetInstance(h, hdrNum); /* Purge our verification cache on added public keys */ @@ -2741,24 +2354,15 @@ int rpmdbAdd(rpmdb db, Header h) } exit: - free(hdr.data); - (void) unblockSignals(&signalMask); + free(hdrBlob); return ret; } -/* - * Remove DB4 environment (and lock), ie the equivalent of - * rm -f <prefix>/<dbpath>/__db.??? - * Environment files not existing is not an error, failure to unlink is, - * return zero on success. - * TODO/FIX: push this down to db3.c where it belongs - */ -static int cleanDbenv(const char *prefix, const char *dbpath) +static int rpmdbRemoveFiles(char * pattern) { + int rc = 0; ARGV_t paths = NULL, p; - int rc = 0; - char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL); if (rpmGlob(pattern, NULL, &paths) == 0) { for (p = paths; *p; p++) { @@ -2766,86 +2370,114 @@ static int cleanDbenv(const char *prefix, const char *dbpath) } argvFree(paths); } + return rc; +} + +static int rpmdbRemoveDatabase(const char *dbpath) +{ + int rc = 0; + char *pattern; + + pattern = rpmGetPath(dbpath, "/*", NULL); + rc += rpmdbRemoveFiles(pattern); + free(pattern); + pattern = rpmGetPath(dbpath, "/.??*", NULL); + rc += rpmdbRemoveFiles(pattern); free(pattern); + + rc += rmdir(dbpath); return rc; } -static int rpmdbRemoveDatabase(const char * prefix, const char * dbpath) -{ - char *path; - int xx = 0; +static int rpmdbMoveDatabase(const char * prefix, const char * srcdbpath, + const char * dbpath, const char * tmppath) +{ + int rc = -1; + int xx; + char *src = rpmGetPath(prefix, "/", srcdbpath, NULL); + char *old = rpmGetPath(prefix, "/", tmppath, NULL); + char *dest = rpmGetPath(prefix, "/", dbpath, NULL); + + char * oldkeys = rpmGetPath(old, "/", "pubkeys", NULL); + char * destkeys = rpmGetPath(dest, "/", "pubkeys", NULL); - for (int i = 0; i < dbiTagsMax; i++) { - const char * base = rpmTagGetName(dbiTags[i]); - path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL); - if (access(path, F_OK) == 0) - xx += unlink(path); - free(path); + xx = rename(dest, old); + if (xx) { + goto exit; + } + xx = rename(src, dest); + if (xx) { + rpmlog(RPMLOG_ERR, _("could not move new database in place\n")); + xx = rename(old, dest); + if (xx) { + rpmlog(RPMLOG_ERR, _("could also not restore old database from %s\n"), + old); + rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s " + "to recover\n"), dest, old); + } + goto exit; + } + + if (access(oldkeys, F_OK ) != -1) { + xx = rename(oldkeys, destkeys); + if (xx) { + rpmlog(RPMLOG_ERR, _("Could not get public keys from %s\n"), oldkeys); + goto exit; + } + } + + xx = rpmdbRemoveDatabase(old); + if (xx) { + rpmlog(RPMLOG_ERR, _("could not delete old database at %s\n"), old); } - cleanDbenv(prefix, dbpath); - path = rpmGetPath(prefix, "/", dbpath, NULL); - xx += rmdir(path); - free(path); + rc = 0; - return (xx != 0); + exit: + _free(src); + _free(old); + _free(dest); + _free(oldkeys); + _free(destkeys); + return rc; } -static int rpmdbMoveDatabase(const char * prefix, - const char * olddbpath, const char * newdbpath) +static int rpmdbSetPermissions(char * src, char * dest) { - int i; + struct dirent *dp; + DIR *dfd; + struct stat st; - int rc = 0; - int xx; - int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1); - sigset_t sigMask; - - blockSignals(&sigMask); - for (i = 0; i < dbiTagsMax; i++) { - rpmDbiTag rpmtag = dbiTags[i]; - const char *base = rpmTagGetName(rpmtag); - char *src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL); - char *dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL); - - if (access(src, F_OK) != 0) - goto cont; - - /* - * Restore uid/gid/mode/security context if possible. - */ - if (stat(dest, &st) < 0) - if (stat(src, &st) < 0) - goto cont; - - if ((xx = rename(src, dest)) != 0) { - rc = 1; - goto cont; + int xx, rc = -1; + char * filepath; + + if (stat(dest, &st) < 0) + goto exit; + if (stat(src, &st) < 0) + goto exit; + + if ((dfd = opendir(dest)) == NULL) { + goto exit; + } + + rc = 0; + while ((dp = readdir(dfd)) != NULL) { + if (!strcmp(dp->d_name, "..")) { + continue; } - xx = chown(dest, st.st_uid, st.st_gid); - xx = chmod(dest, (st.st_mode & 07777)); - - if (selinux) { - security_context_t scon = NULL; - if (matchpathcon(dest, st.st_mode, &scon) != -1) { - (void) setfilecon(dest, scon); - freecon(scon); - } + filepath = rpmGetPath(dest, "/", dp->d_name, NULL); + xx = chown(filepath, st.st_uid, st.st_gid); + rc += xx; + if (!strcmp(dp->d_name, ".")) { + xx = chmod(filepath, (st.st_mode & 07777)); + } else { + xx = chmod(filepath, (st.st_mode & 07666)); } - -cont: - free(src); - free(dest); + rc += xx; + _free(filepath); } - cleanDbenv(prefix, olddbpath); - cleanDbenv(prefix, newdbpath); - - unblockSignals(&sigMask); - - if (selinux) { - (void) matchpathcon_fini(); - } + exit: return rc; } @@ -2855,12 +2487,12 @@ int rpmdbRebuild(const char * prefix, rpmts ts, rpmdb olddb; char * dbpath = NULL; char * rootdbpath = NULL; + char * tmppath = NULL; rpmdb newdb; char * newdbpath = NULL; char * newrootdbpath = NULL; int nocleanup = 1; int failed = 0; - int removedir = 0; int rc = 0; dbpath = rpmGetPath("%{?_dbpath}", NULL); @@ -2888,7 +2520,6 @@ int rpmdbRebuild(const char * prefix, rpmts ts, rc = 1; goto exit; } - removedir = 1; if (openDatabase(prefix, dbpath, &olddb, O_RDONLY, 0644, RPMDB_FLAG_REBUILD)) { @@ -2900,10 +2531,13 @@ int rpmdbRebuild(const char * prefix, rpmts ts, rc = 1; goto exit; } + if (rpmdbOpenAll(newdb)) { + rc = 1; + goto exit; + } { Header h = NULL; rpmdbMatchIterator mi; -#define _RECNUM rpmdbGetIteratorOffset(mi) mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0); if (ts && hdrchk) @@ -2919,20 +2553,22 @@ int rpmdbRebuild(const char * prefix, rpmts ts, { rpmlog(RPMLOG_ERR, _("header #%u in the database is bad -- skipping.\n"), - _RECNUM); + rpmdbGetIteratorOffset(mi)); continue; } /* Deleted entries are eliminated in legacy headers by copy. */ - { Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE) - ? headerCopy(h) : NULL); - rc = rpmdbAdd(newdb, (nh ? nh : h)); + if (headerIsEntry(h, RPMTAG_HEADERIMAGE)) { + Header nh = headerReload(headerCopy(h), RPMTAG_HEADERIMAGE); + rc = rpmdbAdd(newdb, h); headerFree(nh); + } else { + rc = rpmdbAdd(newdb, h); } if (rc) { - rpmlog(RPMLOG_ERR, - _("cannot add record originally at %u\n"), _RECNUM); + rpmlog(RPMLOG_ERR, _("cannot add record originally at %u\n"), + rpmdbGetIteratorOffset(mi)); failed = 1; break; } @@ -2950,15 +2586,20 @@ int rpmdbRebuild(const char * prefix, rpmts ts, _("failed to rebuild database: original database " "remains in place\n")); - rpmdbRemoveDatabase(prefix, newdbpath); + rpmdbRemoveDatabase(newrootdbpath); rc = 1; goto exit; - } else if (!nocleanup) { - if (rpmdbMoveDatabase(prefix, newdbpath, dbpath)) { + } else { + rpmdbSetPermissions(dbpath, newdbpath); + } + + if (!nocleanup) { + rasprintf(&tmppath, "%sold.%d", dbpath, (int) getpid()); + if (rpmdbMoveDatabase(prefix, newdbpath, dbpath, tmppath)) { rpmlog(RPMLOG_ERR, _("failed to replace old database with new " "database!\n")); rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s " - "to recover"), dbpath, newdbpath); + "to recover\n"), dbpath, newdbpath); rc = 1; goto exit; } @@ -2966,15 +2607,35 @@ int rpmdbRebuild(const char * prefix, rpmts ts, rc = 0; exit: - if (removedir && !(rc == 0 && nocleanup)) { - if (rmdir(newrootdbpath)) - rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"), - newrootdbpath, strerror(errno)); - } free(newdbpath); free(dbpath); + free(tmppath); free(newrootdbpath); free(rootdbpath); return rc; } + +int rpmdbCtrl(rpmdb db, rpmdbCtrlOp ctrl) +{ + dbCtrlOp dbctrl = 0; + switch (ctrl) { + case RPMDB_CTRL_LOCK_RO: + dbctrl = DB_CTRL_LOCK_RO; + break; + case RPMDB_CTRL_UNLOCK_RO: + dbctrl = DB_CTRL_UNLOCK_RO; + break; + case RPMDB_CTRL_LOCK_RW: + dbctrl = DB_CTRL_LOCK_RW; + break; + case RPMDB_CTRL_UNLOCK_RW: + dbctrl = DB_CTRL_UNLOCK_RW; + break; + case RPMDB_CTRL_INDEXSYNC: + dbctrl = DB_CTRL_INDEXSYNC; + break; + } + return dbctrl ? dbCtrl(db, dbctrl) : 1; +} + diff --git a/lib/rpmdb.h b/lib/rpmdb.h index 3c7aac2c8..78765bb15 100644 --- a/lib/rpmdb.h +++ b/lib/rpmdb.h @@ -3,7 +3,7 @@ /** \ingroup rpmdb dbi * \file lib/rpmdb.h - * Access RPM indices using Berkeley DB interface(s). + * RPM database API. */ #include <rpm/rpmtypes.h> @@ -30,6 +30,14 @@ typedef enum rpmdbOpX_e { RPMDB_OP_MAX = 4 } rpmdbOpX; +typedef enum rpmdbCtrlOp_e { + RPMDB_CTRL_LOCK_RO = 1, + RPMDB_CTRL_UNLOCK_RO = 2, + RPMDB_CTRL_LOCK_RW = 3, + RPMDB_CTRL_UNLOCK_RW = 4, + RPMDB_CTRL_INDEXSYNC = 5 +} rpmdbCtrlOp; + /** \ingroup rpmdb * Retrieve operation timestamp from rpm database. * @param db rpm database @@ -79,7 +87,7 @@ unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi); * @return 0 on success, 1 on failure (bad args) */ int rpmdbAppendIterator(rpmdbMatchIterator mi, - const int * hdrNums, int nHdrNums); + const unsigned int * hdrNums, unsigned int nHdrNums); /** \ingroup rpmdb * Add pattern to iterator selector. @@ -138,21 +146,6 @@ rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag, Header rpmdbNextIterator(rpmdbMatchIterator mi); /** \ingroup rpmdb - * Check for and exit on termination signals. - */ -int rpmdbCheckSignals(void); - -/** \ingroup rpmdb - * Check rpmdb signal handler for trapped signal and/or requested exit, - * clean up any open iterators and databases on termination condition. - * On non-zero exit any open references to rpmdb are invalid and cannot - * be accessed anymore, calling process should terminate immediately. - * @param terminate 0 to only check for signals, 1 to terminate anyway - * @return 0 to continue, 1 if termination cleanup was done. - */ -int rpmdbCheckTerminate(int terminate); - -/** \ingroup rpmdb * Destroy rpm database iterator. * @param mi rpm database iterator * @return NULL always @@ -171,13 +164,23 @@ rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag); * Get the next key - Warning! Keys are not zero terminated! * Binary tags may even contain zero bytes * @param ii index iterator - * @param key adress to save the pointer to the key - * @param keylen adress to save the length of the key to + * @param key address to save the pointer to the key + * @param keylen address to save the length of the key to * @return 0 on success; != 0 on error or end of index */ int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * keylen); /** \ingroup rpmdb + * Get the next key into a tag data container. + * Caller is responsible for calling rpmtdFreeData() to freeing the + * data returned in keytd once done with it. + * @param ii index iterator + * @param keytd tag container to store the key in + * @return 0 on success; != 0 on error or end of index + */ +int rpmdbIndexIteratorNextTd(rpmdbIndexIterator ii, rpmtd keytd); + +/** \ingroup rpmdb * Get number of entries for current key * @param ii index iterator * @return number of entries. 0 on error. @@ -207,6 +210,13 @@ unsigned int rpmdbIndexIteratorTagNum(rpmdbIndexIterator ii, unsigned int nr); */ rpmdbIndexIterator rpmdbIndexIteratorFree(rpmdbIndexIterator ii); +/** \ingroup rpmdb + * manipulate the rpm database + * @param db rpm database + * @param ctrl operation + * @return 0 on success; != 0 on error + */ +int rpmdbCtrl(rpmdb db, rpmdbCtrlOp ctrl); #ifdef __cplusplus } diff --git a/lib/rpmdb_internal.h b/lib/rpmdb_internal.h index ce95ce1b4..92848ab37 100644 --- a/lib/rpmdb_internal.h +++ b/lib/rpmdb_internal.h @@ -2,7 +2,6 @@ #define H_RPMDB_INTERNAL #include <assert.h> -#include <db.h> #include <rpm/rpmsw.h> #include <rpm/rpmtypes.h> @@ -16,7 +15,7 @@ extern "C" { #undef HASHTYPE #undef HTKEYTYPE #undef HTDATATYPE -#define HASHTYPE removedHash +#define HASHTYPE packageHash #define HTKEYTYPE unsigned int #define HTDATATYPE struct rpmte_s * #include "rpmhash.H" @@ -60,14 +59,6 @@ RPM_GNUC_INTERNAL int rpmdbClose (rpmdb db); /** \ingroup rpmdb - * Sync all database indices. - * @param db rpm database - * @return 0 on success - */ -RPM_GNUC_INTERNAL -int rpmdbSync (rpmdb db); - -/** \ingroup rpmdb * Rebuild database indices from package headers. * @param prefix path to top of install tree * @param ts transaction set (or NULL) @@ -130,13 +121,31 @@ int rpmdbExtendIterator(rpmdbMatchIterator mi, void rpmdbSortIterator(rpmdbMatchIterator mi); /** \ingroup rpmdb + * uniq the iterator by recnum + * Return database iterator. + * @param mi rpm database iterator + */ +void rpmdbUniqIterator(rpmdbMatchIterator mi); + +/** \ingroup rpmdb + * If neg equals to zero then it leaves in iterator only packages that + * header numbers are in hdrNums. If neg is not zero then removes from iterator + * all packages that header numbers are in hdrNums. + * @param mi rpm database iterator + * @param hdrNums hash of package numbers + * @param neg mode + * return 0 on success, 1 on failure (bad args) + */ +int rpmdbFilterIterator(rpmdbMatchIterator mi, packageHash hdrNums, int neg); + +/** \ingroup rpmdb * Remove items from set of package instances to iterate. * @note Sorted hdrNums are always passed in rpmlib. * @param mi rpm database iterator * @param hdrNums hash of package instances * @return 0 on success, 1 on failure (bad args) */ -int rpmdbPruneIterator(rpmdbMatchIterator mi, removedHash hdrNums); +int rpmdbPruneIterator(rpmdbMatchIterator mi, packageHash hdrNums); /** \ingroup rpmdb * Create a new, empty match iterator (for purposes of extending it @@ -148,16 +157,59 @@ int rpmdbPruneIterator(rpmdbMatchIterator mi, removedHash hdrNums); RPM_GNUC_INTERNAL rpmdbMatchIterator rpmdbNewIterator(rpmdb db, rpmDbiTagVal dbitag); -#ifndef __APPLE__ -/** - * * Mergesort, same arguments as qsort(2). - * */ +/** \ingroup rpmdb + * Return database iterator that iterates over database items + * starting with pfx. + * @param db rpm database + * @param rpmtag database index tag + * @param pfx prefix data + * @param plen prefix data length (0 will use strlen(keyp)) + * @return NULL on failure + */ +RPM_GNUC_INTERNAL +rpmdbMatchIterator rpmdbInitPrefixIterator(rpmdb db, rpmDbiTagVal rpmtag, + const void * pfx, size_t plen); +/** \ingroup rpmdb + * Get package offsets of entries + * @param ii index iterator + * @return db offsets of pkgs + */ +RPM_GNUC_INTERNAL +unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii); + +/** \ingroup rpmdb + * Return current index (position) in iterator. + * @param mi rpm database iterator + * @return current index + */ +RPM_GNUC_INTERNAL +int rpmdbGetIteratorIndex(rpmdbMatchIterator mi); + +/** \ingroup rpmdb + * Set iterator index. + * @param mi rpm database iterator + * @param ix index + */ +RPM_GNUC_INTERNAL +void rpmdbSetIteratorIndex(rpmdbMatchIterator mi, unsigned int ix); + +/** \ingroup rpmdb + * Return offset of package with given index. + * @param mi rpm database iterator + * @param ix index + * @return package offset + */ +RPM_GNUC_INTERNAL +unsigned int rpmdbGetIteratorOffsetFor(rpmdbMatchIterator mi, unsigned int ix); + +/** \ingroup rpmdb + * Return header located in rpmdb at given offset. + * @param db rpm database + * @param offset database offset + * @return header at given offset + */ RPM_GNUC_INTERNAL -int mergesort(void *base, size_t nmemb, size_t size, - int (*cmp) (const void *, const void *)); -#else -/* mergesort is defined in stdlib.h on Mac OS X */ -#endif /* __APPLE__ */ +Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset); #ifdef __cplusplus } diff --git a/lib/rpmds.c b/lib/rpmds.c index 1e6798648..823c722e8 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -32,19 +32,20 @@ struct rpmds_s { int32_t Count; /*!< No. of elements */ unsigned int instance; /*!< From rpmdb instance? */ int i; /*!< Element index. */ - unsigned l; /*!< Low element (bsearch). */ - unsigned u; /*!< High element (bsearch). */ int nopromote; /*!< Don't promote Epoch: in rpmdsCompare()? */ int nrefs; /*!< Reference count. */ + int *ti; /*!< Trigger index. */ }; static int dsType(rpmTagVal tag, - const char ** Type, rpmTagVal * tagEVR, rpmTagVal * tagF) + const char ** Type, rpmTagVal * tagEVR, rpmTagVal * tagF, + rpmTagVal * tagTi) { int rc = 0; const char *t = NULL; rpmTagVal evr = RPMTAG_NOT_FOUND; rpmTagVal f = RPMTAG_NOT_FOUND; + rpmTagVal ti = RPMTAG_NOT_FOUND; if (tag == RPMTAG_PROVIDENAME) { t = "Provides"; @@ -54,6 +55,22 @@ static int dsType(rpmTagVal tag, t = "Requires"; evr = RPMTAG_REQUIREVERSION; f = RPMTAG_REQUIREFLAGS; + } else if (tag == RPMTAG_SUPPLEMENTNAME) { + t = "Supplements"; + evr = RPMTAG_SUPPLEMENTVERSION; + f = RPMTAG_SUPPLEMENTFLAGS; + } else if (tag == RPMTAG_ENHANCENAME) { + t = "Enhances"; + evr = RPMTAG_ENHANCEVERSION; + f = RPMTAG_ENHANCEFLAGS; + } else if (tag == RPMTAG_RECOMMENDNAME) { + t = "Recommends"; + evr = RPMTAG_RECOMMENDVERSION; + f = RPMTAG_RECOMMENDFLAGS; + } else if (tag == RPMTAG_SUGGESTNAME) { + t = "Suggests"; + evr = RPMTAG_SUGGESTVERSION; + f = RPMTAG_SUGGESTFLAGS; } else if (tag == RPMTAG_CONFLICTNAME) { t = "Conflicts"; evr = RPMTAG_CONFLICTVERSION; @@ -70,19 +87,103 @@ static int dsType(rpmTagVal tag, t = "Trigger"; evr = RPMTAG_TRIGGERVERSION; f = RPMTAG_TRIGGERFLAGS; - } else if (tag == RPMTAG_ENHANCESNAME) { - t = "Enhances"; - evr = RPMTAG_ENHANCESVERSION; - f = RPMTAG_ENHANCESFLAGS; + ti = RPMTAG_TRIGGERINDEX; + } else if (tag == RPMTAG_OLDSUGGESTSNAME) { + t = "Oldsuggests"; + evr = RPMTAG_OLDSUGGESTSVERSION; + f = RPMTAG_OLDSUGGESTSFLAGS; + } else if (tag == RPMTAG_OLDENHANCESNAME) { + t = "Oldenhances"; + evr = RPMTAG_OLDENHANCESVERSION; + f = RPMTAG_OLDENHANCESFLAGS; + } else if (tag == RPMTAG_FILETRIGGERNAME) { + t = "FileTrigger"; + evr = RPMTAG_FILETRIGGERVERSION; + f = RPMTAG_FILETRIGGERFLAGS; + ti = RPMTAG_FILETRIGGERINDEX; + } else if (tag == RPMTAG_TRANSFILETRIGGERNAME) { + t = "TransFileTrigger"; + evr = RPMTAG_TRANSFILETRIGGERVERSION; + f = RPMTAG_TRANSFILETRIGGERFLAGS; + ti = RPMTAG_TRANSFILETRIGGERINDEX; } else { rc = 1; } if (Type) *Type = t; if (tagEVR) *tagEVR = evr; if (tagF) *tagF = f; + if (tagTi) *tagTi = ti; return rc; } +static char tagNToChar(rpmTagVal tagN) +{ + switch (tagN) { + default: + return 'R'; + break; + case RPMTAG_REQUIRENAME: + return 'R'; + break; + case RPMTAG_PROVIDENAME: + return 'P'; + break; + case RPMTAG_RECOMMENDNAME: + return 'r'; + break; + case RPMTAG_SUGGESTNAME: + return 's'; + break; + case RPMTAG_SUPPLEMENTNAME: + return 'S'; + break; + case RPMTAG_ENHANCENAME: + return 'e'; + break; + case RPMTAG_CONFLICTNAME: + return 'C'; + break; + case RPMTAG_OBSOLETENAME: + return 'O'; + break; + } +} + +rpmTagVal rpmdsDToTagN(char deptype) +{ + rpmTagVal tagN = RPMTAG_REQUIRENAME; + switch (deptype) { + default: + tagN = RPMTAG_NOT_FOUND; + break; + case 'P': + tagN = RPMTAG_PROVIDENAME; + break; + case 'R': + tagN = RPMTAG_REQUIRENAME; + break; + case 'r': + tagN = RPMTAG_RECOMMENDNAME; + break; + case 's': + tagN = RPMTAG_SUGGESTNAME; + break; + case 'S': + tagN = RPMTAG_SUPPLEMENTNAME; + break; + case 'e': + tagN = RPMTAG_ENHANCENAME; + break; + case 'C': + tagN = RPMTAG_CONFLICTNAME; + break; + case 'O': + tagN = RPMTAG_OBSOLETENAME; + break; + } + return tagN; +} + rpmsid rpmdsNIdIndex(rpmds ds, int i) { rpmsid id = 0; @@ -122,6 +223,14 @@ rpmsenseFlags rpmdsFlagsIndex(rpmds ds, int i) return Flags; } +int rpmdsTiIndex(rpmds ds, int i) +{ + int ti = -1; + if (ds != NULL && i >= 0 && i < ds->Count && ds->ti != NULL) + ti = ds->ti[i]; + return ti; +} + rpm_color_t rpmdsColorIndex(rpmds ds, int i) { rpm_color_t Color = 0; @@ -145,7 +254,7 @@ rpmds rpmdsLink(rpmds ds) rpmds rpmdsFree(rpmds ds) { - rpmTagVal tagEVR, tagF; + rpmTagVal tagEVR, tagF, tagTi; if (ds == NULL) return NULL; @@ -153,13 +262,14 @@ rpmds rpmdsFree(rpmds ds) if (ds->nrefs > 1) return rpmdsUnlink(ds); - if (dsType(ds->tagN, NULL, &tagEVR, &tagF)) + if (dsType(ds->tagN, NULL, &tagEVR, &tagF, &tagTi)) return NULL; if (ds->Count > 0) { ds->N = _free(ds->N); ds->EVR = _free(ds->EVR); ds->Flags = _free(ds->Flags); + ds->ti = _free(ds->ti); } ds->pool = rpmstrPoolFree(ds->pool); @@ -191,24 +301,46 @@ static rpmds rpmdsCreate(rpmstrPool pool, rpmds rpmdsNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, int flags) { - rpmTagVal tagEVR, tagF; + rpmTagVal tagEVR, tagF, tagTi; rpmds ds = NULL; const char * Type; struct rpmtd_s names; - if (dsType(tagN, &Type, &tagEVR, &tagF)) + if (dsType(tagN, &Type, &tagEVR, &tagF, &tagTi)) goto exit; if (headerGet(h, tagN, &names, HEADERGET_MINMEM)) { - struct rpmtd_s evr, flags; + struct rpmtd_s evr, flags, tindices; + rpm_count_t count = rpmtdCount(&names); - ds = rpmdsCreate(pool, tagN, Type, - rpmtdCount(&names), headerGetInstance(h)); - - ds->N = rpmtdToPool(&names, ds->pool); headerGet(h, tagEVR, &evr, HEADERGET_MINMEM); - ds->EVR = rpmtdToPool(&evr, ds->pool); + if (evr.count && evr.count != count) { + rpmtdFreeData(&evr); + return NULL; + } + headerGet(h, tagF, &flags, HEADERGET_ALLOC); + if (flags.count && flags.count != count) { + rpmtdFreeData(&flags); + return NULL; + } + + if (tagTi != RPMTAG_NOT_FOUND) { + headerGet(h, tagTi, &tindices, HEADERGET_ALLOC); + if (tindices.count && tindices.count != count) { + rpmtdFreeData(&tindices); + return NULL; + } + } + + ds = rpmdsCreate(pool, tagN, Type, count, headerGetInstance(h)); + + ds->N = names.count ? rpmtdToPool(&names, ds->pool) : NULL; + ds->EVR = evr.count ? rpmtdToPool(&evr, ds->pool): NULL; ds->Flags = flags.data; + if (tagTi != RPMTAG_NOT_FOUND) { + ds->ti = tindices.data; + } + /* ensure rpmlib() requires always have RPMSENSE_RPMLIB flag set */ if (tagN == RPMTAG_REQUIRENAME && ds->Flags) { for (int i = 0; i < ds->Count; i++) { @@ -285,12 +417,14 @@ char * rpmdsNewDNEVR(const char * dspfx, const rpmds ds) static rpmds singleDSPool(rpmstrPool pool, rpmTagVal tagN, rpmsid N, rpmsid EVR, rpmsenseFlags Flags, - unsigned int instance, rpm_color_t Color) + unsigned int instance, rpm_color_t Color, + int triggerIndex) { rpmds ds = NULL; const char * Type; + rpmTagVal tagTi; - if (dsType(tagN, &Type, NULL, NULL)) + if (dsType(tagN, &Type, NULL, NULL, &tagTi)) goto exit; ds = rpmdsCreate(pool, tagN, Type, 1, instance); @@ -301,6 +435,10 @@ static rpmds singleDSPool(rpmstrPool pool, rpmTagVal tagN, ds->EVR[0] = EVR; ds->Flags = xmalloc(sizeof(*ds->Flags)); ds->Flags[0] = Flags; + if (tagTi != RPMTAG_NOT_FOUND) { + ds->ti = xmalloc(sizeof(*ds->ti)); + ds->ti[0] = triggerIndex; + } ds->i = 0; if (Color) rpmdsSetColor(ds, Color); @@ -312,9 +450,10 @@ exit: static rpmds singleDS(rpmstrPool pool, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, unsigned int instance, - rpm_color_t Color) + rpm_color_t Color, int triggerIndex) { - rpmds ds = singleDSPool(pool, tagN, 0, 0, Flags, instance, Color); + rpmds ds = singleDSPool(pool, tagN, 0, 0, Flags, instance, Color, + triggerIndex); if (ds) { /* now that we have a pool, we can insert our N & EVR strings */ ds->N[0] = rpmstrPoolId(ds->pool, N ? N : "", 1); @@ -331,7 +470,7 @@ rpmds rpmdsThisPool(rpmstrPool pool, { char *evr = headerGetAsString(h, RPMTAG_EVR); rpmds ds = singleDS(pool, tagN, headerGetString(h, RPMTAG_NAME), - evr, Flags, headerGetInstance(h), 0); + evr, Flags, headerGetInstance(h), 0, 0); free(evr); return ds; } @@ -344,7 +483,14 @@ rpmds rpmdsThis(Header h, rpmTagVal tagN, rpmsenseFlags Flags) rpmds rpmdsSinglePool(rpmstrPool pool,rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags) { - return singleDS(pool, tagN, N, EVR, Flags, 0, 0); + return singleDS(pool, tagN, N, EVR, Flags, 0, 0, 0); +} + +rpmds rpmdsSinglePoolTix(rpmstrPool pool,rpmTagVal tagN, + const char * N, const char * EVR, + rpmsenseFlags Flags, int triggerIndex) +{ + return singleDS(pool, tagN, N, EVR, Flags, 0, 0, triggerIndex); } rpmds rpmdsSingle(rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags) @@ -355,14 +501,78 @@ rpmds rpmdsSingle(rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlag rpmds rpmdsCurrent(rpmds ds) { rpmds cds = NULL; + int ti = -1; if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) { + if (ds->ti) + ti = ds->ti[ds->i]; /* Using parent's pool so we can just use the same id's */ cds = singleDSPool(ds->pool, ds->tagN, ds->N[ds->i], ds->EVR[ds->i], - rpmdsFlags(ds), ds->instance, rpmdsColor(ds)); + rpmdsFlags(ds), ds->instance, rpmdsColor(ds), ti); } return cds; } +rpmds rpmdsFilterTi(rpmds ds, int ti) +{ + int i, i2, tiCount = 0; + rpmds fds; + + if (ds == NULL || !ds->ti || !ds->Count) + return NULL; + + for (i = 0; i < ds->Count; i++) { + if (ds->ti[i] == ti) + tiCount++; + } + + if (!tiCount) + return NULL; + + fds = rpmdsCreate(ds->pool, ds->tagN, ds->Type, tiCount, ds->instance); + + fds->N = xmalloc(tiCount * sizeof(*fds->N)); + fds->EVR = xmalloc(tiCount * sizeof(*fds->EVR)); + fds->Flags = xmalloc(tiCount * sizeof(*fds->Flags)); + fds->ti = xmalloc(tiCount * sizeof(*fds->ti)); + fds->i = -1; + + i2 = 0; + for (i = 0; i < ds->Count; i++) { + if (ds->ti[i] == ti) { + fds->N[i2] = ds->N[i]; + fds->EVR[i2] = ds->EVR[i]; + fds->Flags[i2] = ds->Flags[i]; + fds->ti[i2] = ds->ti[i]; + i2++; + } + } + + return fds; +} + +int rpmdsPutToHeader(rpmds ds, Header h) +{ + rpmTagVal tagN = rpmdsTagN(ds); + rpmTagVal tagEVR = rpmdsTagEVR(ds); + rpmTagVal tagF = rpmdsTagF(ds); + rpmTagVal tagTi = rpmdsTagTi(ds); + if (!tagN) + return -1; + + rpmds pi = rpmdsInit(ds); + while (rpmdsNext(pi) >= 0) { + rpmsenseFlags flags = rpmdsFlags(pi); + uint32_t index = rpmdsTi(pi); + headerPutString(h, tagN, rpmdsN(pi)); + headerPutString(h, tagEVR, rpmdsEVR(pi)); + headerPutUint32(h, tagF, &flags, 1); + if (tagTi != RPMTAG_NOT_FOUND) { + headerPutUint32(h, tagTi, &index, 1); + } + } + return 0; +} + int rpmdsCount(const rpmds ds) { return (ds != NULL ? ds->Count : 0); @@ -385,13 +595,22 @@ int rpmdsSetIx(rpmds ds, int ix) return i; } +char rpmdsD(const rpmds ds) +{ + if (ds != NULL) { + return tagNToChar(ds->tagN); + } else { + return '\0'; + } +} + const char * rpmdsDNEVR(const rpmds ds) { const char * DNEVR = NULL; if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) { if (ds->DNEVR == NULL) { - char t[2] = { ds->Type[0], '\0' }; + char t[2] = { tagNToChar(ds->tagN), '\0' }; ds->DNEVR = rpmdsNewDNEVR(t, ds); } DNEVR = ds->DNEVR; @@ -424,6 +643,11 @@ rpmsenseFlags rpmdsFlags(const rpmds ds) return (ds != NULL) ? rpmdsFlagsIndex(ds, ds->i) : 0; } +int rpmdsTi(const rpmds ds) +{ + return (ds != NULL) ? rpmdsTiIndex(ds, ds->i) : 0; +} + rpmTagVal rpmdsTagN(const rpmds ds) { rpmTagVal tagN = RPMTAG_NOT_FOUND; @@ -433,6 +657,33 @@ rpmTagVal rpmdsTagN(const rpmds ds) return tagN; } +rpmTagVal rpmdsTagEVR(const rpmds ds) +{ + rpmTagVal tagEVR = RPMTAG_NOT_FOUND; + + if (ds != NULL) + dsType(ds->tagN, NULL, &tagEVR, NULL, NULL); + return tagEVR; +} + +rpmTagVal rpmdsTagF(const rpmds ds) +{ + rpmTagVal tagF = RPMTAG_NOT_FOUND; + + if (ds != NULL) + dsType(ds->tagN, NULL, NULL, &tagF, NULL); + return tagF; +} + +rpmTagVal rpmdsTagTi(const rpmds ds) +{ + rpmTagVal tagTi = RPMTAG_NOT_FOUND; + + if (ds != NULL) + dsType(ds->tagN, NULL, NULL, NULL, &tagTi); + return tagTi; +} + unsigned int rpmdsInstance(rpmds ds) { return (ds != NULL) ? ds->instance : 0; @@ -529,8 +780,6 @@ static rpmds rpmdsDup(const rpmds ods) size_t nb; ds->i = ods->i; - ds->l = ods->l; - ds->u = ods->u; ds->nopromote = ods->nopromote; nb = ds->Count * sizeof(*ds->N); @@ -547,58 +796,77 @@ static rpmds rpmdsDup(const rpmds ods) ds->Flags = memcpy(xmalloc(nb), ods->Flags, nb); } + if (ods->ti) { + nb = ds->Count * sizeof(*ds->ti); + ds->ti = memcpy(xmalloc(nb), ods->ti, nb); + } + return ds; } -int rpmdsFind(rpmds ds, const rpmds ods) +static int doFind(rpmds ds, const rpmds ods, unsigned int *he) { int comparison; const char *N, *ON = rpmdsN(ods); const char *EVR, *OEVR = rpmdsEVR(ods); rpmsenseFlags Flags, OFlags = rpmdsFlags(ods); + int index, Oindex = rpmdsTi(ods); int rc = -1; /* assume not found */ if (ds == NULL || ods == NULL) return -1; - ds->l = 0; - ds->u = ds->Count; - while (ds->l < ds->u) { - ds->i = (ds->l + ds->u) / 2; + unsigned int l = 0; + unsigned int u = ds->Count; + while (l < u) { + ds->i = (l + u) / 2; N = rpmdsN(ds); EVR = rpmdsEVR(ds); Flags = rpmdsFlags(ds); + index = rpmdsTi(ds); comparison = strcmp(ON, N); /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */ if (comparison == 0 && OEVR && EVR) comparison = strcmp(OEVR, EVR); - if (comparison == 0 && OFlags && Flags) + if (comparison == 0) comparison = OFlags - Flags; + if (comparison == 0) + comparison = Oindex - index; if (comparison < 0) - ds->u = ds->i; + u = ds->i; else if (comparison > 0) - ds->l = ds->i + 1; + l = ds->i + 1; else { rc = ds->i; break; } } + if (he) + *he = u; return rc; } +int rpmdsFind(rpmds ds, const rpmds ods) +{ + return doFind(ds, ods, NULL); +} + int rpmdsMerge(rpmds * dsp, rpmds ods) { rpmds ds; int save; + int ocount; if (dsp == NULL || ods == NULL) return -1; + ocount = rpmdsCount(*dsp); + /* If not initialized yet, dup the 1st entry. */ if (*dsp == NULL) { save = ods->Count; @@ -615,6 +883,12 @@ int rpmdsMerge(rpmds * dsp, rpmds ods) ds->EVR = xcalloc(ds->Count, sizeof(*ds->EVR)); if (ds->Flags == NULL) ds->Flags = xcalloc(ds->Count, sizeof(*ds->Flags)); + if (ds->ti == NULL && ods->ti) { + int i; + ds->ti = xcalloc(ds->Count, sizeof(*ds->ti)); + for (i = 0; i < ds->Count; i++) + ds->ti[i] = -1; + } /* * Add new entries. @@ -623,10 +897,11 @@ int rpmdsMerge(rpmds * dsp, rpmds ods) ods = rpmdsInit(ods); while (rpmdsNext(ods) >= 0) { const char *OEVR; + unsigned int u; /* * If this entry is already present, don't bother. */ - if (rpmdsFind(ds, ods) >= 0) + if (doFind(ds, ods, &u) >= 0) continue; /* @@ -634,33 +909,42 @@ int rpmdsMerge(rpmds * dsp, rpmds ods) */ rpmstrPoolUnfreeze(ds->pool); ds->N = xrealloc(ds->N, (ds->Count+1) * sizeof(*ds->N)); - if (ds->u < ds->Count) { - memmove(ds->N + ds->u + 1, ds->N + ds->u, - (ds->Count - ds->u) * sizeof(*ds->N)); + if (u < ds->Count) { + memmove(ds->N + u + 1, ds->N + u, + (ds->Count - u) * sizeof(*ds->N)); } - ds->N[ds->u] = rpmstrPoolId(ds->pool, rpmdsN(ods), 1); + ds->N[u] = rpmstrPoolId(ds->pool, rpmdsN(ods), 1); ds->EVR = xrealloc(ds->EVR, (ds->Count+1) * sizeof(*ds->EVR)); - if (ds->u < ds->Count) { - memmove(ds->EVR + ds->u + 1, ds->EVR + ds->u, - (ds->Count - ds->u) * sizeof(*ds->EVR)); + if (u < ds->Count) { + memmove(ds->EVR + u + 1, ds->EVR + u, + (ds->Count - u) * sizeof(*ds->EVR)); } OEVR = rpmdsEVR(ods); - ds->EVR[ds->u] = rpmstrPoolId(ds->pool, OEVR ? OEVR : "", 1); + ds->EVR[u] = rpmstrPoolId(ds->pool, OEVR ? OEVR : "", 1); ds->Flags = xrealloc(ds->Flags, (ds->Count+1) * sizeof(*ds->Flags)); - if (ds->u < ds->Count) { - memmove(ds->Flags + ds->u + 1, ds->Flags + ds->u, - (ds->Count - ds->u) * sizeof(*ds->Flags)); + if (u < ds->Count) { + memmove(ds->Flags + u + 1, ds->Flags + u, + (ds->Count - u) * sizeof(*ds->Flags)); + } + ds->Flags[u] = rpmdsFlags(ods); + + if (ds->ti || ods->ti) { + ds->ti = xrealloc(ds->ti, (ds->Count+1) * sizeof(*ds->ti)); + if (u < ds->Count) { + memmove(ds->ti + u + 1, ds->ti + u, + (ds->Count - u) * sizeof(*ds->ti)); + } + ds->ti[u] = rpmdsTi(ods); } - ds->Flags[ds->u] = rpmdsFlags(ods); ds->i = ds->Count; ds->Count++; } ods->i = save; - return 0; + return (ds->Count - ocount); } @@ -988,6 +1272,17 @@ static const struct rpmlibProvides_s rpmlibProvides[] = { { "rpmlib(TildeInVersions)", "4.10.0-1", ( RPMSENSE_EQUAL), N_("dependency comparison supports versions with tilde.") }, + { "rpmlib(LargeFiles)", "4.12.0-1", + ( RPMSENSE_EQUAL), + N_("support files larger than 4GB") }, + { "rpmlib(RichDependencies)", "4.12.0-1", + ( RPMSENSE_EQUAL), + N_("support for rich dependencies.") }, +#ifdef HAVE_ZSTD + { "rpmlib(PayloadIsZstd)", "5.4.18-1", + (RPMSENSE_RPMLIB|RPMSENSE_EQUAL), + N_("package payload can be compressed using zstd.") }, +#endif { NULL, NULL, 0, NULL } }; @@ -1001,7 +1296,7 @@ int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp) if (rltblp == NULL) rltblp = rpmlibProvides; - for (rlp = rltblp; rlp->featureName != NULL && rc == 0; rlp++) { + for (rlp = rltblp; rlp->featureName != NULL && rc >= 0; rlp++) { rpmds ds = rpmdsSinglePool(pool, RPMTAG_PROVIDENAME, rlp->featureName, rlp->featureEVR, rlp->featureFlags); rc = rpmdsMerge(dsp, ds); @@ -1010,7 +1305,7 @@ int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp) /* freeze the pool to save memory, but only if private pool */ if (*dsp && (*dsp)->pool != pool) rpmstrPoolFreeze((*dsp)->pool, 0); - return rc; + return (rc < 0) ? -1 : 0; } int rpmdsRpmlib(rpmds * dsp, const void * tblp) @@ -1022,3 +1317,392 @@ rpmstrPool rpmdsPool(rpmds ds) { return (ds != NULL) ? ds->pool : NULL; } + +rpmsenseFlags rpmSanitizeDSFlags(rpmTagVal tagN, rpmsenseFlags Flags) +{ + rpmsenseFlags extra = RPMSENSE_ANY; + switch (tagN) { + case RPMTAG_PROVIDENAME: + extra = Flags & RPMSENSE_FIND_PROVIDES; + break; + case RPMTAG_TRIGGERNAME: + case RPMTAG_FILETRIGGERNAME: + case RPMTAG_TRANSFILETRIGGERNAME: + extra = Flags & RPMSENSE_TRIGGER; + break; + case RPMTAG_RECOMMENDNAME: + case RPMTAG_SUGGESTNAME: + case RPMTAG_SUPPLEMENTNAME: + case RPMTAG_ENHANCENAME: + case RPMTAG_REQUIRENAME: + extra = Flags & (_ALL_REQUIRES_MASK); + break; + case RPMTAG_CONFLICTNAME: + extra = Flags; + break; + default: + break; + } + return (Flags & RPMSENSE_SENSEMASK) | extra; +} + +static struct ReqComp { +const char * token; + rpmsenseFlags sense; +} const ReqComparisons[] = { + { "<=", RPMSENSE_LESS | RPMSENSE_EQUAL}, + { "=<", RPMSENSE_LESS | RPMSENSE_EQUAL}, + { "<", RPMSENSE_LESS}, + + { "==", RPMSENSE_EQUAL}, + { "=", RPMSENSE_EQUAL}, + + { ">=", RPMSENSE_GREATER | RPMSENSE_EQUAL}, + { "=>", RPMSENSE_GREATER | RPMSENSE_EQUAL}, + { ">", RPMSENSE_GREATER}, + + { NULL, 0 }, +}; + +rpmsenseFlags rpmParseDSFlags(const char *str, size_t len) +{ + const struct ReqComp *rc; + for (rc = ReqComparisons; rc->token != NULL; rc++) + if (len == strlen(rc->token) && rstreqn(str, rc->token, len)) + return rc->sense; + return 0; +} + +static struct RichOpComp { + const char * token; + rpmrichOp op; +} const RichOps[] = { + { "and", RPMRICHOP_AND}, + { "or", RPMRICHOP_OR}, + { "if", RPMRICHOP_IF}, + { "unless", RPMRICHOP_UNLESS}, + { "else", RPMRICHOP_ELSE}, + { "with", RPMRICHOP_WITH}, + { "without", RPMRICHOP_WITHOUT}, + { NULL, 0 }, +}; + +int rpmdsIsRich(rpmds dep) +{ + const char * n = rpmdsN(dep); + return (n && n[0] == '('); +} + +static rpmRC parseRichDepOp(const char **dstrp, rpmrichOp *opp, char **emsg) +{ + const char *p = *dstrp, *pe = p; + const struct RichOpComp *ro; + + while (*pe && !risspace(*pe) && *pe != ')') + pe++; + for (ro = RichOps; ro->token != NULL; ro++) + if (pe - p == strlen(ro->token) && rstreqn(p, ro->token, pe - p)) { + *opp = ro->op; + *dstrp = pe; + return RPMRC_OK; + } + if (emsg) + rasprintf(emsg, _("Unknown rich dependency op '%.*s'"), (int)(pe - p), p); + return RPMRC_FAIL; +} + +const char *rpmrichOpStr(rpmrichOp op) +{ + if (op == RPMRICHOP_SINGLE) + return "SINGLE"; + if (op == RPMRICHOP_AND) + return "and"; + if (op == RPMRICHOP_OR) + return "or"; + if (op == RPMRICHOP_IF) + return "if"; + if (op == RPMRICHOP_UNLESS) + return "unless"; + if (op == RPMRICHOP_ELSE) + return "else"; + if (op == RPMRICHOP_WITH) + return "with"; + if (op == RPMRICHOP_WITHOUT) + return "without"; + return NULL; +} + + +#define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} +#define SKIPNONWHITEX(_x){int bl = 0; while (*(_x) &&!(risspace(*_x) || *(_x) == ',' || (*(_x) == ')' && bl-- <= 0))) if (*(_x)++ == '(') bl++;} + +static rpmRC parseSimpleDep(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata) +{ + const char *p = *dstrp; + const char *n, *e = 0; + int nl, el = 0; + rpmsenseFlags sense = 0; + + n = p; + SKIPNONWHITEX(p); + nl = p - n; + if (nl == 0) { + if (emsg) + rasprintf(emsg, _("Name required")); + return RPMRC_FAIL; + } + SKIPWHITE(p); + if (*p) { + const char *pe = p; + + SKIPNONWHITEX(pe); + sense = rpmParseDSFlags(p, pe - p); + if (sense) { + p = pe; + SKIPWHITE(p); + e = p; + SKIPNONWHITEX(p); + el = p - e; + } + } + if (e && el == 0) { + if (emsg) + rasprintf(emsg, _("Version required")); + return RPMRC_FAIL; + } + if (cb && cb(cbdata, RPMRICH_PARSE_SIMPLE, n, nl, e, el, sense, RPMRICHOP_SINGLE, emsg) != RPMRC_OK) + return RPMRC_FAIL; + *dstrp = p; + return RPMRC_OK; +} + +#define RICHPARSE_CHECK (1 << 0) +#define RICHPARSE_NO_WITH (1 << 1) +#define RICHPARSE_NO_AND (1 << 2) +#define RICHPARSE_NO_OR (1 << 3) + +static rpmRC rpmrichParseCheck(rpmrichOp op, int check, char **emsg) +{ + if ((op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) && (check & RICHPARSE_NO_WITH) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal ops in with/without")); + return RPMRC_FAIL; + } + if ((check & RICHPARSE_CHECK) == 0) + return RPMRC_OK; + if ((op == RPMRICHOP_AND || op == RPMRICHOP_IF) && (check & RICHPARSE_NO_AND) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal context for 'unless', please use 'or' instead")); + return RPMRC_FAIL; + } + if ((op == RPMRICHOP_OR || op == RPMRICHOP_UNLESS) && (check & RICHPARSE_NO_OR) != 0) { + if (emsg) + rasprintf(emsg, _("Illegal context for 'if', please use 'and' instead")); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, int *checkp) +{ + const char *p = *dstrp, *pe; + rpmrichOp op = RPMRICHOP_SINGLE, firstop = RPMRICHOP_SINGLE, chainop = 0; + int check = checkp ? *checkp : 0; + + if (cb && cb(cbdata, RPMRICH_PARSE_ENTER, p, 0, 0, 0, 0, op, emsg) != RPMRC_OK) + return RPMRC_FAIL; + if (*p++ != '(') { + if (emsg) + rasprintf(emsg, _("Rich dependency does not start with '('")); + return RPMRC_FAIL; + } + for (;;) { + SKIPWHITE(p); + if (*p == ')') { + if (emsg) { + if (chainop) + rasprintf(emsg, _("Missing argument to rich dependency op")); + else + rasprintf(emsg, _("Empty rich dependency")); + } + return RPMRC_FAIL; + } + if (*p == '(') { + int subcheck = check & RICHPARSE_CHECK; + if (rpmrichParseInternal(&p, emsg, cb, cbdata, &subcheck) != RPMRC_OK) + return RPMRC_FAIL; + if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) + subcheck &= ~(RICHPARSE_NO_AND | RICHPARSE_NO_OR); + check |= subcheck; + } else { + if (parseSimpleDep(&p, emsg, cb, cbdata) != RPMRC_OK) + return RPMRC_FAIL; + } + SKIPWHITE(p); + if (!*p) { + if (emsg) + rasprintf(emsg, _("Unterminated rich dependency: %s"), *dstrp); + return RPMRC_FAIL; + } + if (*p == ')') + break; + pe = p; + if (parseRichDepOp(&pe, &op, emsg) != RPMRC_OK) + return RPMRC_FAIL; + if (firstop == RPMRICHOP_SINGLE) + firstop = op; + + if (op == RPMRICHOP_ELSE && (chainop == RPMRICHOP_IF || chainop == RPMRICHOP_UNLESS)) + chainop = 0; + if (chainop && op != chainop) { + if (emsg) + rasprintf(emsg, _("Cannot chain different ops")); + return RPMRC_FAIL; + } + if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR && op != RPMRICHOP_WITH) { + if (emsg) + rasprintf(emsg, _("Can only chain and/or/with ops")); + return RPMRC_FAIL; + } + if (cb && cb(cbdata, RPMRICH_PARSE_OP, p, pe - p, 0, 0, 0, op, emsg) != RPMRC_OK) + return RPMRC_FAIL; + chainop = op; + p = pe; + } + + /* check for illegal combinations */ + if (rpmrichParseCheck(firstop, check, emsg) != RPMRC_OK) + return RPMRC_FAIL; + + /* update check data */ + if (firstop == RPMRICHOP_IF) + check |= RICHPARSE_NO_OR; + if (firstop == RPMRICHOP_UNLESS) + check |= RICHPARSE_NO_AND; + if (op == RPMRICHOP_AND || op == RPMRICHOP_OR) + check &= ~(RICHPARSE_NO_AND | RICHPARSE_NO_OR); + if (op != RPMRICHOP_SINGLE && op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT && op != RPMRICHOP_OR) + check |= RICHPARSE_NO_WITH; + + p++; + if (cb && cb(cbdata, RPMRICH_PARSE_LEAVE, *dstrp, p - *dstrp , 0, 0, 0, op, emsg) != RPMRC_OK) + return RPMRC_FAIL; + *dstrp = p; + if (checkp) + *checkp |= check; + return RPMRC_OK; +} + +rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata) +{ + return rpmrichParseInternal(dstrp, emsg, cb, cbdata, NULL); +} + +rpmRC rpmrichParseForTag(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, rpmTagVal tagN) +{ + int check = RICHPARSE_CHECK; + if (rpmrichParseInternal(dstrp, emsg, cb, cbdata, &check) != RPMRC_OK) + return RPMRC_FAIL; + switch (tagN) { + case RPMTAG_CONFLICTNAME: + case RPMTAG_SUPPLEMENTNAME: + case RPMTAG_ENHANCENAME: + if (rpmrichParseCheck(RPMRICHOP_OR, check, emsg) != RPMRC_OK) + return RPMRC_FAIL; + break; + default: + if (rpmrichParseCheck(RPMRICHOP_AND, check, emsg) != RPMRC_OK) + return RPMRC_FAIL; + break; + } + return RPMRC_OK; +} + +struct rpmdsParseRichDepData { + rpmds dep; + rpmsenseFlags depflags; + + rpmds leftds; + rpmds rightds; + rpmrichOp op; + + int depth; + const char *rightstart; + int dochain; +}; + +static rpmRC rpmdsParseRichDepCB(void *cbdata, rpmrichParseType type, + const char *n, int nl, const char *e, int el, rpmsenseFlags sense, + rpmrichOp op, char **emsg) { + struct rpmdsParseRichDepData *data = cbdata; + rpmds ds = 0; + + if (type == RPMRICH_PARSE_ENTER) + data->depth++; + else if (type == RPMRICH_PARSE_LEAVE) { + if (--data->depth == 0 && data->dochain && data->rightstart) { + /* chain op hack, construct a sub-ds from the right side of the chain */ + char *right = xmalloc(n + nl - data->rightstart + 2); + right[0] = '('; + strncpy(right + 1, data->rightstart, n + nl - data->rightstart); + right[n + nl - data->rightstart + 1] = 0; + data->rightds = rpmdsFree(data->rightds); + ds = singleDS(data->dep->pool, data->dep->tagN, 0, 0, data->depflags, 0, 0, 0); + ds->N[0] = rpmstrPoolId(ds->pool, right, 1); + ds->EVR[0] = rpmstrPoolId(ds->pool, "", 1); + data->rightds = ds; + free(right); + } + } + if (data->depth != 1) + return RPMRC_OK; /* we're only interested in top-level parsing */ + if ((type == RPMRICH_PARSE_SIMPLE || type == RPMRICH_PARSE_LEAVE) && !data->dochain) { + if (type == RPMRICH_PARSE_SIMPLE && data->dep->tagN == RPMTAG_REQUIRENAME && nl > 7 && + rstreqn(n, "rpmlib(", sizeof("rpmlib(")-1)) + sense |= RPMSENSE_RPMLIB; + ds = singleDS(data->dep->pool, data->dep->tagN, 0, 0, sense | data->depflags, 0, 0, 0); + ds->N[0] = rpmstrPoolIdn(ds->pool, n, nl, 1); + ds->EVR[0] = rpmstrPoolIdn(ds->pool, e ? e : "", el, 1); + if (!data->leftds) + data->leftds = ds; + else { + data->rightds = ds; + data->rightstart = n; + } + } + if (type == RPMRICH_PARSE_OP) { + if (data->op != RPMRICHOP_SINGLE) + data->dochain = 1; /* this is a chained op */ + else + data->op = op; + } + return RPMRC_OK; +} + + +rpmRC rpmdsParseRichDep(rpmds dep, rpmds *leftds, rpmds *rightds, rpmrichOp *op, char **emsg) +{ + rpmRC rc; + struct rpmdsParseRichDepData data; + const char *depstr = rpmdsN(dep); + memset(&data, 0, sizeof(data)); + data.dep = dep; + data.op = RPMRICHOP_SINGLE; + data.depflags = rpmdsFlags(dep) & ~(RPMSENSE_SENSEMASK | RPMSENSE_MISSINGOK); + rc = rpmrichParse(&depstr, emsg, rpmdsParseRichDepCB, &data); + if (rc == RPMRC_OK && *depstr) { + if (emsg) + rasprintf(emsg, _("Junk after rich dependency")); + rc = RPMRC_FAIL; + } + if (rc != RPMRC_OK) { + rpmdsFree(data.leftds); + rpmdsFree(data.rightds); + } else { + *leftds = data.leftds; + *rightds = data.rightds; + *op = data.op; + } + return rc; +} + diff --git a/lib/rpmds.h b/lib/rpmds.h index bceed0081..463a82a96 100644 --- a/lib/rpmds.h +++ b/lib/rpmds.h @@ -86,6 +86,24 @@ typedef rpmFlags rpmsenseFlags; #define isInstallPreReq(_x) ((_x) & _INSTALL_ONLY_MASK) #define isErasePreReq(_x) ((_x) & _ERASE_ONLY_MASK) + + +/** \ingroup rpmds + * Return only those flags allowed for given type of dependencies + * @param tagN type of dependency + * @param Flags flags + * @return flags filtered to allowed bits + */ +rpmsenseFlags rpmSanitizeDSFlags(rpmTagVal tagN, rpmsenseFlags Flags); + +/** \ingroup rpmds + * Convert a string to the sense flags + * @param str the string + * @param len length of the string + * @return flags, zero for unknown relations + */ +rpmsenseFlags rpmParseDSFlags(const char *str, size_t len); + /** \ingroup rpmds * Reference a dependency set instance. * @param ds dependency set @@ -144,6 +162,14 @@ rpmds rpmdsSingle(rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlag rpmds rpmdsCurrent(rpmds ds); /** \ingroup rpmds + * Write content of the dependency set to the header + * @param ds dependency set + * @param h header + * @return 0 on success + */ +int rpmdsPutToHeader(rpmds ds, Header h); + +/** \ingroup rpmds * Return dependency set count. * @param ds dependency set * @return current count @@ -173,6 +199,20 @@ int rpmdsSetIx(rpmds ds, int ix); const char * rpmdsDNEVR(const rpmds ds); /** \ingroup rpmds + * Return one char indicating the type of the dependency. + * @param ds dependency set + * @return character + */ +char rpmdsD(const rpmds ds); + +/** \ingroup rpmds + * Return matching tagN for one char dependency type description. + * @param deptype character + * @return type of dependency + */ +rpmTagVal rpmdsDToTagN(char deptype); + +/** \ingroup rpmds * Return current dependency name. * @param ds dependency set * @return current dependency name, NULL on invalid @@ -187,6 +227,13 @@ const char * rpmdsN(const rpmds ds); const char * rpmdsEVR(const rpmds ds); /** \ingroup rpmds + * Return current dependency triggerindex. + * @param ds dependency set + * @return current dependency trigger index, 0 on invalid + */ +int rpmdsTi(const rpmds ds); + +/** \ingroup rpmds * Return current dependency flags. * @param ds dependency set * @return current dependency flags, 0 on invalid @@ -201,6 +248,27 @@ rpmsenseFlags rpmdsFlags(const rpmds ds); rpmTagVal rpmdsTagN(const rpmds ds); /** \ingroup rpmds + * Return current dependency type. + * @param ds dependency set + * @return current dependency type version tag, 0 on invalid + */ +rpmTagVal rpmdsTagEVR(const rpmds ds); + +/** \ingroup rpmds + * Return current dependency type. + * @param ds dependency set + * @return current dependency type flags tag, 0 on invalid + */ +rpmTagVal rpmdsTagF(const rpmds ds); + +/** \ingroup rpmds + * Return current dependency type. + * @param ds dependency set + * @return current dependency type trigger index tag, 0 on invalid + */ +rpmTagVal rpmdsTagTi(const rpmds ds); + +/** \ingroup rpmds * Return dependency header instance, ie whether the dependency comes from * an installed header or not. * @param ds dependency set @@ -247,15 +315,6 @@ rpm_color_t rpmdsColor(const rpmds ds); rpm_color_t rpmdsSetColor(const rpmds ds, rpm_color_t color); /** \ingroup rpmds - * Notify of results of dependency match. - * @param ds dependency set - * @param where where dependency was resolved (or NULL) - * @param rc 0 == YES, otherwise NO - */ -/* FIX: rpmMessage annotation is a lie */ -void rpmdsNotify(rpmds ds, const char * where, int rc); - -/** \ingroup rpmds * Return next dependency set iterator index. * @param ds dependency set * @return dependency set iterator index, -1 on termination @@ -281,7 +340,7 @@ int rpmdsFind(rpmds ds, const rpmds ods); * Merge a dependency set maintaining (N,EVR,Flags) sorted order. * @retval *dsp (merged) dependency set * @param ods dependency set to merge - * @return (merged) dependency index + * @return number of merged dependencies, -1 on error */ int rpmdsMerge(rpmds * dsp, rpmds ods); @@ -333,12 +392,138 @@ int rpmdsNVRMatchesDep(const Header h, const rpmds req, int nopromote); /** * Load rpmlib provides into a dependency set. - * @retval *dsp (loaded) depedency set + * @retval *dsp (loaded) dependency set * @param tblp rpmlib provides table (NULL uses internal table) * @return 0 on success */ int rpmdsRpmlib(rpmds * dsp, const void * tblp); +/** \ingroup rpmds + * Create and load a dependency set. + * @param pool shared string pool (or NULL for private pool) + * @param h header + * @param tagN type of dependency + * @param flags unused + * @return new dependency set + */ +rpmds rpmdsNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, int flags); + +/** \ingroup rpmds + * Create, load and initialize a dependency for this header. + * @param pool string pool (or NULL for private pool) + * @param h header + * @param tagN type of dependency + * @param Flags comparison flags + * @return new dependency set + */ +rpmds rpmdsThisPool(rpmstrPool pool, + Header h, rpmTagVal tagN, rpmsenseFlags Flags); + +/** \ingroup rpmds + * Create, load and initialize a dependency set of size 1. + * @param pool string pool (or NULL for private pool) + * @param tagN type of dependency + * @param N name + * @param EVR epoch:version-release + * @param Flags comparison flags + * @return new dependency set + */ +rpmds rpmdsSinglePool(rpmstrPool pool, rpmTagVal tagN, + const char * N, const char * EVR, rpmsenseFlags Flags); + +/** \ingroup rpmds + * Create, load and initialize a trigger dependency set of size 1. + * @param pool string pool (or NULL for private pool) + * @param tagN type of dependency + * @param N name + * @param EVR epoch:version-release + * @param Flags comparison flags + * @param triggerIndex trigger index + * @return new dependency set + */ +rpmds rpmdsSinglePoolTix(rpmstrPool pool, rpmTagVal tagN, + const char * N, const char * EVR, + rpmsenseFlags Flags, int triggerIndex); + +/** + * Load rpmlib provides into a dependency set. + * @param pool shared string pool (or NULL for private pool) + * @retval *dsp (loaded) dependency set + * @param tblp rpmlib provides table (NULL uses internal table) + * @return 0 on success + */ +int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp); + + +typedef enum rpmrichOp_e { + RPMRICHOP_SINGLE = 1, + RPMRICHOP_AND = 2, + RPMRICHOP_OR = 3, + RPMRICHOP_IF = 4, + RPMRICHOP_ELSE = 5, + RPMRICHOP_WITH = 6, + RPMRICHOP_WITHOUT = 7, + RPMRICHOP_UNLESS = 8 +} rpmrichOp; + +typedef enum rpmrichParseType_e { + RPMRICH_PARSE_SIMPLE = 1, /* standard N <=> EVR dep */ + RPMRICH_PARSE_ENTER = 2, /* entering sub-dependency */ + RPMRICH_PARSE_LEAVE = 3, /* leaving sub-dependency */ + RPMRICH_PARSE_OP = 4 /* parsed a rich dependency op */ +} rpmrichParseType; + +typedef rpmRC (*rpmrichParseFunction) (void *cbdata, rpmrichParseType type, + const char *n, int nl, const char *e, int el, rpmsenseFlags sense, + rpmrichOp op, char **emsg); + +/** + * Parse a rich dependency string + * @param dstrp pointer to sting, will be updated + * @param emsg returns the error string, can be NULL + * @param cb callback function + * @param cbdata callback function data + * @return RPMRC_OK on success + */ +rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata); + +/** + * Parse a rich dependency string for a specific tag + * @param dstrp pointer to sting, will be updated + * @param emsg returns the error string, can be NULL + * @param cb callback function + * @param cbdata callback function data + * @param tagN type of dependency + * @return RPMRC_OK on success + */ +rpmRC rpmrichParseForTag(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, rpmTagVal tagN); + + +/** + * Return if current depenency is rich + * @param dep the dependency + * @return 1 is dependency is a rich 0 otherwise + */ +int rpmdsIsRich(rpmds dep); + +/** + * Return a string representation of the rich dependency op + * @param op the dependency op + * @return constant string, do not free + */ +const char *rpmrichOpStr(rpmrichOp op); + +/** + * Parse a rich dependency string + * @param dep the dependency + * @param leftds returns the left dependency + * @param rightds returns the right dependency + * @param op returns the rich dep op + * @param emsg returns the error string + * @return RPMRC_OK on success + */ +rpmRC rpmdsParseRichDep(rpmds dep, rpmds *leftds, rpmds *rightds, rpmrichOp *op, char **emsg); + #ifdef __cplusplus } #endif diff --git a/lib/rpmds_internal.h b/lib/rpmds_internal.h index edd5a0268..6861fba0f 100644 --- a/lib/rpmds_internal.h +++ b/lib/rpmds_internal.h @@ -8,52 +8,6 @@ extern "C" { #endif /** \ingroup rpmds - * Create and load a dependency set. - * @param pool shared string pool (or NULL for private pool) - * @param h header - * @param tagN type of dependency - * @param flags unused - * @return new dependency set - */ -RPM_GNUC_INTERNAL -rpmds rpmdsNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, int flags); - -/** \ingroup rpmds - * Create, load and initialize a dependency for this header. - * @param pool string pool (or NULL for private pool) - * @param h header - * @param tagN type of dependency - * @param Flags comparison flags - * @return new dependency set - */ -RPM_GNUC_INTERNAL -rpmds rpmdsThisPool(rpmstrPool pool, - Header h, rpmTagVal tagN, rpmsenseFlags Flags); - -/** \ingroup rpmds - * Create, load and initialize a dependency set of size 1. - * @param pool string pool (or NULL for private pool) - * @param tagN type of dependency - * @param N name - * @param EVR epoch:version-release - * @param Flags comparison flags - * @return new dependency set - */ -RPM_GNUC_INTERNAL -rpmds rpmdsSinglePool(rpmstrPool pool, rpmTagVal tagN, - const char * N, const char * EVR, rpmsenseFlags Flags); - -/** - * Load rpmlib provides into a dependency set. - * @param pool shared string pool (or NULL for private pool) - * @retval *dsp (loaded) depedency set - * @param tblp rpmlib provides table (NULL uses internal table) - * @return 0 on success - */ -RPM_GNUC_INTERNAL -int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp); - -/** \ingroup rpmds * Swiss army knife dependency matching function. * @param pool string pool (or NULL for private pool) * @param h header @@ -68,6 +22,15 @@ int rpmdsMatches(rpmstrPool pool, Header h, int prix, rpmds req, int selfevr, int nopromote); /** \ingroup rpmds + * Notify of results of dependency match. + * @param ds dependency set + * @param where where dependency was resolved (or NULL) + * @param rc 0 == YES, otherwise NO + */ +RPM_GNUC_INTERNAL +void rpmdsNotify(rpmds ds, const char * where, int rc); + +/** \ingroup rpmds * Return current dependency name pool id. * @param ds dependency set * @return current dependency name id, 0 on invalid @@ -107,11 +70,23 @@ RPM_GNUC_INTERNAL rpmsenseFlags rpmdsFlagsIndex(rpmds ds, int i); RPM_GNUC_INTERNAL +int rpmdsTiIndex(rpmds ds, int i); + +RPM_GNUC_INTERNAL rpm_color_t rpmdsColorIndex(rpmds ds, int i); RPM_GNUC_INTERNAL int rpmdsCompareIndex(rpmds A, int aix, rpmds B, int bix); +/** \ingroup rpmds + * Filter dependency set and return new dependency set containing only items + * with given trigger index. + * @param ds dependency set + * @param ti trigger index + * @return new filtered dependency set + */ +RPM_GNUC_INTERNAL +rpmds rpmdsFilterTi(rpmds ds, int ti); #ifdef __cplusplus } #endif diff --git a/lib/rpmfi.c b/lib/rpmfi.c index 7f7ef2a7f..beb2ae72b 100644 --- a/lib/rpmfi.c +++ b/lib/rpmfi.c @@ -11,13 +11,137 @@ #include <rpm/rpmstring.h> #include <rpm/rpmmacro.h> /* XXX rpmCleanPath */ #include <rpm/rpmds.h> +#include <errno.h> #include "lib/rpmfi_internal.h" #include "lib/rpmte_internal.h" /* relocations */ #include "lib/cpio.h" /* XXX CPIO_FOO */ +#include "lib/fsm.h" /* rpmpsm stuff for now */ +#include "lib/rpmug.h" +#include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ #include "debug.h" +struct hardlinks_s { + int nlink; + int files[]; +}; + +typedef struct hardlinks_s * hardlinks_t; + +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE +#define HASHTYPE nlinkHash +#define HTKEYTYPE int +#define HTDATATYPE struct hardlinks_s * +#include "lib/rpmhash.H" +#include "lib/rpmhash.C" +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE + +typedef int (*iterfunc)(rpmfi fi); + +struct rpmfi_s { + int i; /*!< Current file index. */ + int j; /*!< Current directory index. */ + iterfunc next; /*!< Iterator function. */ + char * fn; /*!< File name buffer. */ + char * ofn; /*!< Original file name buffer. */ + + int intervalStart; /*!< Start of iterating interval. */ + int intervalEnd; /*!< End of iterating interval. */ + + rpmfiles files; /*!< File info set */ + rpmcpio_t archive; /*!< Archive with payload */ + unsigned char * found; /*!< Bit field of files found in the archive */ + int nrefs; /*!< Reference count */ +}; + +struct rpmfn_s { + rpm_count_t dc; /*!< No. of directories. */ + rpm_count_t fc; /*!< No. of files. */ + + rpmsid * bnid; /*!< Index to base name(s) (pool) */ + rpmsid * dnid; /*!< Index to directory name(s) (pool) */ + uint32_t * dil; /*!< Directory indice(s) (from header) */ +}; + +typedef struct rpmfn_s * rpmfn; + +/** + * A package filename set. + */ +struct rpmfiles_s { + Header h; /*!< Header for file info set (or NULL) */ + rpmstrPool pool; /*!< String pool of this file info set */ + + struct rpmfn_s fndata; /*!< File name data */ + struct rpmfn_s *ofndata; /*!< Original file name data */ + + rpmsid * flinks; /*!< Index to file link(s) (pool) */ + + rpm_flag_t * fflags; /*!< File flag(s) (from header) */ + rpm_off_t * fsizes; /*!< File size(s) (from header) */ + rpm_loff_t * lfsizes; /*!< File size(s) (from header) */ + rpm_time_t * fmtimes; /*!< File modification time(s) (from header) */ + rpm_mode_t * fmodes; /*!< File mode(s) (from header) */ + rpm_rdev_t * frdevs; /*!< File rdev(s) (from header) */ + rpm_ino_t * finodes; /*!< File inodes(s) (from header) */ + + rpmsid * fuser; /*!< Index to file owner(s) (misc pool) */ + rpmsid * fgroup; /*!< Index to file group(s) (misc pool) */ + rpmsid * flangs; /*!< Index to file lang(s) (misc pool) */ + + char * fstates; /*!< File state(s) (from header) */ + + rpm_color_t * fcolors; /*!< File color bits (header) */ + char ** fcaps; /*!< File capability strings (header) */ + + char ** cdict; /*!< File class dictionary (header) */ + rpm_count_t ncdict; /*!< No. of class entries. */ + uint32_t * fcdictx; /*!< File class dictionary index (header) */ + + uint32_t * ddict; /*!< File depends dictionary (header) */ + rpm_count_t nddict; /*!< No. of depends entries. */ + uint32_t * fddictx; /*!< File depends dictionary start (header) */ + uint32_t * fddictn; /*!< File depends dictionary count (header) */ + rpm_flag_t * vflags; /*!< File verify flag(s) (from header) */ + + rpmfiFlags fiflags; /*!< file info set control flags */ + + struct fingerPrint_s * fps; /*!< File fingerprint(s). */ + + int digestalgo; /*!< File digest algorithm */ + int signaturelength; /*!< File signature length */ + unsigned char * digests; /*!< File digests in binary. */ + unsigned char * signatures; /*!< File signatures in binary. */ + + struct nlinkHash_s * nlinks;/*!< Files connected by hardlinks */ + rpm_off_t * replacedSizes; /*!< (TR_ADDED) */ + rpm_loff_t * replacedLSizes;/*!< (TR_ADDED) */ + int magic; + int nrefs; /*!< Reference count. */ +}; + +static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd); +static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn); + +static rpmfiles rpmfilesUnlink(rpmfiles fi) +{ + if (fi) + fi->nrefs--; + return NULL; +} + +rpmfiles rpmfilesLink(rpmfiles fi) +{ + if (fi) + fi->nrefs++; + return fi; +} + static rpmfi rpmfiUnlink(rpmfi fi) { if (fi) @@ -32,14 +156,146 @@ rpmfi rpmfiLink(rpmfi fi) return fi; } +/* + * Collect and validate file path data from header. + * Return the number of files found (could be none) or -1 on error. + */ +static int rpmfnInit(rpmfn fndata, rpmTagVal bntag, Header h, rpmstrPool pool) +{ + struct rpmtd_s bn, dn, dx; + rpmTagVal dntag, ditag; + int rc = 0; + + if (bntag == RPMTAG_BASENAMES) { + dntag = RPMTAG_DIRNAMES; + ditag = RPMTAG_DIRINDEXES; + } else if (bntag == RPMTAG_ORIGBASENAMES) { + dntag = RPMTAG_ORIGDIRNAMES; + ditag = RPMTAG_ORIGDIRINDEXES; + } else { + return -1; + } + + /* Grab and validate file triplet data (if there is any) */ + if (headerGet(h, bntag, &bn, HEADERGET_MINMEM)) { + headerGet(h, dntag, &dn, HEADERGET_MINMEM); + headerGet(h, ditag, &dx, HEADERGET_ALLOC); + + if (indexSane(&bn, &dn, &dx)) { + /* Init the file triplet data */ + fndata->fc = rpmtdCount(&bn); + fndata->dc = rpmtdCount(&dn); + fndata->bnid = rpmtdToPool(&bn, pool); + fndata->dnid = rpmtdToPool(&dn, pool); + /* Steal index data from the td (pooh...) */ + fndata->dil = dx.data; + dx.data = NULL; + rc = fndata->fc; + } else { + memset(fndata, 0, sizeof(*fndata)); + rc = -1; + } + rpmtdFreeData(&bn); + rpmtdFreeData(&dn); + rpmtdFreeData(&dx); + } + + return rc; +} + +static void rpmfnClear(rpmfn fndata) +{ + if (fndata) { + free(fndata->bnid); + free(fndata->dnid); + free(fndata->dil); + memset(fndata, 0, sizeof(*fndata)); + } +} + +static rpm_count_t rpmfnFC(rpmfn fndata) +{ + return (fndata != NULL) ? fndata->fc :0; +} + +static rpm_count_t rpmfnDC(rpmfn fndata) +{ + return (fndata != NULL) ? fndata->dc : 0; +} + +static int rpmfnDI(rpmfn fndata, int ix) +{ + int j = -1; + if (ix >= 0 && ix < rpmfnFC(fndata)) { + if (fndata->dil != NULL) + j = fndata->dil[ix]; + } + return j; +} + +static rpmsid rpmfnBNId(rpmfn fndata, int ix) +{ + rpmsid id = 0; + if (ix >= 0 && ix < rpmfnFC(fndata)) { + if (fndata->bnid != NULL) + id = fndata->bnid[ix]; + } + return id; +} + +static rpmsid rpmfnDNId(rpmfn fndata, int ix) +{ + rpmsid id = 0; + if (ix >= 0 && ix < rpmfnDC(fndata)) { + if (fndata->dnid != NULL) + id = fndata->dnid[ix]; + } + return id; +} + +static const char * rpmfnBN(rpmstrPool pool, rpmfn fndata, int ix) +{ + return rpmstrPoolStr(pool, rpmfnBNId(fndata, ix)); +} + +static const char * rpmfnDN(rpmstrPool pool, rpmfn fndata, int ix) +{ + return rpmstrPoolStr(pool, rpmfnDNId(fndata, ix)); +} + +static char * rpmfnFN(rpmstrPool pool, rpmfn fndata, int ix) +{ + char *fn = NULL; + if (ix >= 0 && ix < rpmfnFC(fndata)) { + fn = rstrscat(NULL, rpmfnDN(pool, fndata, rpmfnDI(fndata, ix)), + rpmfnBN(pool, fndata, ix), NULL); + } + return fn; +} + +rpm_count_t rpmfilesFC(rpmfiles fi) +{ + return (fi != NULL ? rpmfnFC(&fi->fndata) : 0); +} + +rpm_count_t rpmfilesDC(rpmfiles fi) +{ + return (fi != NULL ? rpmfnDC(&fi->fndata) : 0); +} + +int rpmfilesDigestAlgo(rpmfiles fi) +{ + return (fi != NULL) ? fi->digestalgo : 0; +} + rpm_count_t rpmfiFC(rpmfi fi) { - return (fi != NULL ? fi->fc : 0); + return (fi != NULL ? rpmfilesFC(fi->files) : 0); } rpm_count_t rpmfiDC(rpmfi fi) { - return (fi != NULL ? fi->dc : 0); + return (fi != NULL ? rpmfilesDC(fi->files) : 0); } #ifdef NOTYET @@ -57,10 +313,10 @@ int rpmfiSetFX(rpmfi fi, int fx) { int i = -1; - if (fi != NULL && fx >= 0 && fx < fi->fc) { + if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) { i = fi->i; fi->i = fx; - fi->j = fi->dil[fi->i]; + fi->j = rpmfilesDI(fi->files, fi->i); } return i; } @@ -74,138 +330,222 @@ int rpmfiSetDX(rpmfi fi, int dx) { int j = -1; - if (fi != NULL && dx >= 0 && dx < fi->dc) { + if (fi != NULL && dx >= 0 && dx < rpmfiDC(fi)) { j = fi->j; fi->j = dx; } return j; } -int rpmfiDIIndex(rpmfi fi, int dx) +int rpmfilesDI(rpmfiles fi, int ix) { - int j = -1; - if (fi != NULL && dx >= 0 && dx < fi->fc) { - if (fi->dil != NULL) - j = fi->dil[dx]; - } - return j; + return (fi != NULL) ? rpmfnDI(&fi->fndata, ix) : -1; } -rpmsid rpmfiBNIdIndex(rpmfi fi, int ix) +int rpmfilesODI(rpmfiles fi, int ix) { - rpmsid id = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { - if (fi->bnid != NULL) - id = fi->bnid[ix]; - } - return id; + return (fi != NULL) ? rpmfnDI(fi->ofndata, ix) : -1; } -rpmsid rpmfiDNIdIndex(rpmfi fi, int jx) +rpmsid rpmfilesBNId(rpmfiles fi, int ix) { - rpmsid id = 0; - if (fi != NULL && jx >= 0 && jx < fi->fc) { - if (fi->dnid != NULL) - id = fi->dnid[jx]; - } - return id; + return (fi != NULL) ? rpmfnBNId(&fi->fndata, ix) : 0; +} + +rpmsid rpmfilesOBNId(rpmfiles fi, int ix) +{ + return (fi != NULL) ? rpmfnBNId(fi->ofndata, ix) : 0; +} + +rpmsid rpmfilesDNId(rpmfiles fi, int jx) +{ + return (fi != NULL) ? rpmfnDNId(&fi->fndata, jx) : 0; +} + +rpmsid rpmfilesODNId(rpmfiles fi, int jx) +{ + return (fi != NULL) ? rpmfnDNId(fi->ofndata, jx) : 0; +} + +const char * rpmfilesBN(rpmfiles fi, int ix) +{ + return (fi != NULL) ? rpmfnBN(fi->pool, &fi->fndata, ix) : NULL; +} + +const char * rpmfilesOBN(rpmfiles fi, int ix) +{ + return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesOBNId(fi, ix)) : NULL; +} + +const char * rpmfilesDN(rpmfiles fi, int jx) +{ + return (fi != NULL) ? rpmfnDN(fi->pool, &fi->fndata, jx) : NULL; +} + +const char * rpmfilesODN(rpmfiles fi, int jx) +{ + return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesODNId(fi, jx)) : NULL; +} + +char * rpmfilesFN(rpmfiles fi, int ix) +{ + return (fi != NULL) ? rpmfnFN(fi->pool, &fi->fndata, ix) : NULL; +} + +char * rpmfilesOFN(rpmfiles fi, int ix) +{ + return (fi != NULL) ? rpmfnFN(fi->pool, fi->ofndata, ix) : NULL; +} + +/* Fn is expected to be relative path, convert directory to relative too */ +static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn) +{ + rpmsid dnid = rpmfnDNId(files, rpmfnDI(files, ix)); + const char *dn = rpmstrPoolStr(pool, dnid); + const char *reldn = (dn[0] == '/') ? dn + 1 : dn; + size_t l = strlen(reldn); + int cmp = strncmp(reldn, fn, l); + if (cmp == 0) + cmp = strcmp(rpmfnBN(pool, files, ix), fn + l); + return cmp; } -const char * rpmfiBNIndex(rpmfi fi, int ix) +static int rpmfnFindFN(rpmstrPool pool, rpmfn files, const char * fn) { - const char * BN = NULL; + int fc = rpmfnFC(files); - if (fi != NULL && ix >= 0 && ix < fi->fc) { - if (fi->bnid != NULL) - BN = rpmstrPoolStr(fi->pool, fi->bnid[ix]); + /* + * Skip payload prefix, turn absolute paths into relative. This + * allows handling binary rpm payloads with and without ./ prefix and + * srpm payloads which only contain basenames. + */ + if (fn[0] == '.' && fn[1] == '/') + fn += 2; + if (fn[0] == '/') + fn += 1; + + /* try binary search */ + + int lo = 0; + int hi = fc; + int mid, cmp; + + while (hi > lo) { + mid = (hi + lo) / 2 ; + cmp = cmpPoolFn(pool, files, mid, fn); + if (cmp < 0) { + lo = mid+1; + } else if (cmp > 0) { + hi = mid; + } else { + return mid; + } + } + + /* not found: try linear search */ + for (int i=0; i < fc; i++) { + if (cmpPoolFn(pool, files, i, fn) == 0) + return i; } - return BN; + return -1; +} + +int rpmfilesFindFN(rpmfiles files, const char * fn) +{ + return (files && fn) ? rpmfnFindFN(files->pool, &files->fndata, fn) : -1; +} + +int rpmfilesFindOFN(rpmfiles files, const char * fn) +{ + return (files && fn) ? rpmfnFindFN(files->pool, files->ofndata, fn) : -1; } -const char * rpmfiDNIndex(rpmfi fi, int jx) +int rpmfiFindFN(rpmfi fi, const char * fn) { - const char * DN = NULL; + int ix = -1; - if (fi != NULL && jx >= 0 && jx < fi->dc) { - if (fi->dnid != NULL) - DN = rpmstrPoolStr(fi->pool, fi->dnid[jx]); + if (fi != NULL) { + ix = rpmfilesFindFN(fi->files, fn); } - return DN; + return ix; } -char * rpmfiFNIndex(rpmfi fi, int ix) +int rpmfiFindOFN(rpmfi fi, const char * fn) { - char *fn = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { - fn = rstrscat(NULL, rpmstrPoolStr(fi->pool, fi->dnid[fi->dil[ix]]), - rpmstrPoolStr(fi->pool, fi->bnid[ix]), NULL); + int ix = -1; + + if (fi != NULL) { + ix = rpmfilesFindOFN(fi->files, fn); } - return fn; + return ix; } -rpmfileAttrs rpmfiFFlagsIndex(rpmfi fi, int ix) +/* + * Dirnames are not sorted when separated from basenames, we need to assemble + * the whole path for search (binary or otherwise) purposes. + */ +static int cmpPfx(rpmfiles files, int ix, const char *pfx, size_t plen) +{ + char *fn = rpmfilesFN(files, ix); + int rc = strncmp(pfx, fn, plen); + free(fn); + return rc; +} + +rpmfileAttrs rpmfilesFFlags(rpmfiles fi, int ix) { rpmfileAttrs FFlags = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fflags != NULL) FFlags = fi->fflags[ix]; } return FFlags; } -rpmVerifyAttrs rpmfiVFlagsIndex(rpmfi fi, int ix) +rpmVerifyAttrs rpmfilesVFlags(rpmfiles fi, int ix) { rpmVerifyAttrs VFlags = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->vflags != NULL) VFlags = fi->vflags[ix]; } return VFlags; } -rpm_mode_t rpmfiFModeIndex(rpmfi fi, int ix) +rpm_mode_t rpmfilesFMode(rpmfiles fi, int ix) { rpm_mode_t fmode = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fmodes != NULL) fmode = fi->fmodes[ix]; } return fmode; } -rpmfileState rpmfiFStateIndex(rpmfi fi, int ix) +rpmfileState rpmfilesFState(rpmfiles fi, int ix) { rpmfileState fstate = RPMFILE_STATE_MISSING; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fstates != NULL) fstate = fi->fstates[ix]; } return fstate; } -const unsigned char * rpmfiMD5(rpmfi fi) -{ - const unsigned char *digest; - int algo = 0; - - digest = rpmfiFDigest(fi, &algo, NULL); - return (algo == PGPHASHALGO_MD5) ? digest : NULL; -} - int rpmfiDigestAlgo(rpmfi fi) { - return fi ? fi->digestalgo : 0; + return fi ? rpmfilesDigestAlgo(fi->files) : 0; } -const unsigned char * rpmfiFDigestIndex(rpmfi fi, int ix, int *algo, size_t *len) +const unsigned char * rpmfilesFDigest(rpmfiles fi, int ix, int *algo, size_t *len) { const unsigned char *digest = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { size_t diglen = rpmDigestLength(fi->digestalgo); if (fi->digests != NULL) digest = fi->digests + (diglen * ix); @@ -228,68 +568,89 @@ char * rpmfiFDigestHex(rpmfi fi, int *algo) return fdigest; } -const char * rpmfiFLinkIndex(rpmfi fi, int ix) +const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len) +{ + const unsigned char *signature = NULL; + + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { + if (fi->signatures != NULL) + signature = fi->signatures + (fi->signaturelength * ix); + if (len) + *len = fi->signaturelength; + } + return signature; +} + +const char * rpmfilesFLink(rpmfiles fi, int ix) { const char * flink = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->flinks != NULL) flink = rpmstrPoolStr(fi->pool, fi->flinks[ix]); } return flink; } -rpm_loff_t rpmfiFSizeIndex(rpmfi fi, int ix) +rpm_loff_t rpmfilesFSize(rpmfiles fi, int ix) { rpm_loff_t fsize = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fsizes != NULL) fsize = fi->fsizes[ix]; + else if (fi->lfsizes != NULL) + fsize = fi->lfsizes[ix]; } return fsize; } -rpm_rdev_t rpmfiFRdevIndex(rpmfi fi, int ix) +rpm_rdev_t rpmfilesFRdev(rpmfiles fi, int ix) { rpm_rdev_t frdev = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->frdevs != NULL) frdev = fi->frdevs[ix]; } return frdev; } -rpm_ino_t rpmfiFInodeIndex(rpmfi fi, int ix) +rpm_ino_t rpmfilesFInode(rpmfiles fi, int ix) { rpm_ino_t finode = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->finodes != NULL) finode = fi->finodes[ix]; } return finode; } -rpm_color_t rpmfiColor(rpmfi fi) +rpm_color_t rpmfilesColor(rpmfiles files) { rpm_color_t color = 0; - if (fi != NULL && fi->fcolors != NULL) { - for (int i = 0; i < fi->fc; i++) - color |= fi->fcolors[i]; + if (files != NULL && files->fcolors != NULL) { + int fc = rpmfilesFC(files); + for (int i = 0; i < fc; i++) + color |= files->fcolors[i]; /* XXX ignore all but lsnibble for now. */ color &= 0xf; } return color; } -rpm_color_t rpmfiFColorIndex(rpmfi fi, int ix) +rpm_color_t rpmfiColor(rpmfi fi) +{ + return (fi != NULL) ? rpmfilesColor(fi->files) : 0; +} + +rpm_color_t rpmfilesFColor(rpmfiles fi, int ix) { rpm_color_t fcolor = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fcolors != NULL) /* XXX ignore all but lsnibble for now. */ fcolor = (fi->fcolors[ix] & 0x0f); @@ -297,12 +658,12 @@ rpm_color_t rpmfiFColorIndex(rpmfi fi, int ix) return fcolor; } -const char * rpmfiFClassIndex(rpmfi fi, int ix) +const char * rpmfilesFClass(rpmfiles fi, int ix) { const char * fclass = NULL; int cdictx; - if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { cdictx = fi->fcdictx[ix]; if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict) fclass = fi->cdict[cdictx]; @@ -310,13 +671,13 @@ const char * rpmfiFClassIndex(rpmfi fi, int ix) return fclass; } -uint32_t rpmfiFDependsIndex(rpmfi fi, int ix, const uint32_t ** fddictp) +uint32_t rpmfilesFDepends(rpmfiles fi, int ix, const uint32_t ** fddictp) { int fddictx = -1; int fddictn = 0; const uint32_t * fddict = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fddictn != NULL) fddictn = fi->fddictn[ix]; if (fddictn > 0 && fi->fddictx != NULL) @@ -329,102 +690,189 @@ uint32_t rpmfiFDependsIndex(rpmfi fi, int ix, const uint32_t ** fddictp) return fddictn; } -uint32_t rpmfiFNlinkIndex(rpmfi fi, int ix) +uint32_t rpmfilesFLinks(rpmfiles fi, int ix, const int ** files) { uint32_t nlink = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { - /* XXX rpm-2.3.12 has not RPMTAG_FILEINODES */ - if (fi->finodes && fi->frdevs) { - rpm_ino_t finode = fi->finodes[ix]; - rpm_rdev_t frdev = fi->frdevs[ix]; - int j; - - for (j = 0; j < fi->fc; j++) { - if (fi->frdevs[j] == frdev && fi->finodes[j] == finode) - nlink++; + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { + nlink = 1; + if (fi->nlinks) { + struct hardlinks_s ** hardlinks = NULL; + nlinkHashGetEntry(fi->nlinks, ix, &hardlinks, NULL, NULL); + if (hardlinks) { + nlink = hardlinks[0]->nlink; + if (files) { + *files = hardlinks[0]->files; + } + } else if (files){ + *files = NULL; } } } return nlink; } -rpm_time_t rpmfiFMtimeIndex(rpmfi fi, int ix) +uint32_t rpmfiFLinks(rpmfi fi, const int ** files) +{ + return rpmfilesFLinks(fi->files, fi ? fi->i : -1, files); +} + +uint32_t rpmfilesFNlink(rpmfiles fi, int ix) +{ + return rpmfilesFLinks(fi, ix, NULL); +} + +rpm_time_t rpmfilesFMtime(rpmfiles fi, int ix) { rpm_time_t fmtime = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fmtimes != NULL) fmtime = fi->fmtimes[ix]; } return fmtime; } -const char * rpmfiFUserIndex(rpmfi fi, int ix) +const char * rpmfilesFUser(rpmfiles fi, int ix) { const char * fuser = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fuser != NULL) fuser = rpmstrPoolStr(fi->pool, fi->fuser[ix]); } return fuser; } -const char * rpmfiFGroupIndex(rpmfi fi, int ix) +const char * rpmfilesFGroup(rpmfiles fi, int ix) { const char * fgroup = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->fgroup != NULL) fgroup = rpmstrPoolStr(fi->pool, fi->fgroup[ix]); } return fgroup; } -const char * rpmfiFCapsIndex(rpmfi fi, int ix) +const char * rpmfilesFCaps(rpmfiles fi, int ix) { const char *fcaps = NULL; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { fcaps = fi->fcaps ? fi->fcaps[ix] : ""; } return fcaps; } -const char * rpmfiFLangsIndex(rpmfi fi, int ix) +const char * rpmfilesFLangs(rpmfiles fi, int ix) { const char *flangs = NULL; - if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { flangs = rpmstrPoolStr(fi->pool, fi->flangs[ix]); } return flangs; } -struct fingerPrint_s *rpmfiFps(rpmfi fi) +int rpmfilesStat(rpmfiles fi, int ix, int flags, struct stat *sb) +{ + int rc = -1; + if (fi && sb) { + /* XXX FIXME define proper flags with sane semantics... */ + int warn = flags & 0x1; + const char *user = rpmfilesFUser(fi, ix); + const char *group = rpmfilesFGroup(fi, ix); + const int * hardlinks = NULL; + uint32_t nlinks = rpmfilesFLinks(fi, ix, &hardlinks); + + memset(sb, 0, sizeof(*sb)); + sb->st_nlink = nlinks; + sb->st_ino = rpmfilesFInode(fi, ix); + sb->st_rdev = rpmfilesFRdev(fi, ix); + sb->st_mode = rpmfilesFMode(fi, ix); + sb->st_mtime = rpmfilesFMtime(fi, ix); + + /* Only regular files and symlinks have a size */ + if (S_ISREG(sb->st_mode)) { + /* Content and thus size comes with last hardlink */ + if (!(nlinks > 1 && hardlinks[nlinks-1] != ix)) + sb->st_size = rpmfilesFSize(fi, ix); + } else if (S_ISLNK(sb->st_mode)) { + /* + * Normally rpmfilesFSize() is correct for symlinks too, this is + * only needed for glob()'ed links from fakechroot environment. + */ + sb->st_size = strlen(rpmfilesFLink(fi, ix)); + } + + if (user && rpmugUid(user, &sb->st_uid)) { + if (warn) + rpmlog(RPMLOG_WARNING, + _("user %s does not exist - using %s\n"), user, UID_0_USER); + sb->st_mode &= ~S_ISUID; /* turn off suid bit */ + } + + if (group && rpmugGid(group, &sb->st_gid)) { + if (warn) + rpmlog(RPMLOG_WARNING, + _("group %s does not exist - using %s\n"), group, GID_0_GROUP); + sb->st_mode &= ~S_ISGID; /* turn off sgid bit */ + } + + rc = 0; + } + return rc; +} + +struct fingerPrint_s *rpmfilesFps(rpmfiles fi) { return (fi != NULL) ? fi->fps : NULL; } +static int iterFwd(rpmfi fi) +{ + return fi->i + 1; +} + +static int iterBack(rpmfi fi) +{ + return fi->i - 1; +} + +static int iterInterval(rpmfi fi) +{ + if (fi->i == -1) + return fi->intervalStart; + else if (fi->i + 1 < fi->intervalEnd) + return fi->i + 1; + else + return RPMERR_ITER_END; +} + int rpmfiNext(rpmfi fi) { - int i = -1; + int next = -1; + if (fi != NULL) { + do { + next = fi->next(fi); + } while (next == RPMERR_ITER_SKIP); - if (fi != NULL && ++fi->i >= 0) { - if (fi->i < fi->fc) { - i = fi->i; - if (fi->dil != NULL) - fi->j = fi->dil[fi->i]; - } else + if (next >= 0 && next < rpmfilesFC(fi->files)) { + fi->i = next; + fi->j = rpmfilesDI(fi->files, fi->i); + } else { fi->i = -1; + if (next >= 0) { + next = -1; + } + } } - - return i; + return next; } rpmfi rpmfiInit(rpmfi fi, int fx) { if (fi != NULL) { - if (fx >= 0 && fx < fi->fc) { + if (fx >= 0 && fx < rpmfilesFC(fi->files)) { fi->i = fx - 1; fi->j = -1; } @@ -438,7 +886,7 @@ int rpmfiNextD(rpmfi fi) int j = -1; if (fi != NULL && ++fi->j >= 0) { - if (fi->j < fi->dc) + if (fi->j < rpmfilesDC(fi->files)) j = fi->j; else fi->j = -1; @@ -450,7 +898,7 @@ int rpmfiNextD(rpmfi fi) rpmfi rpmfiInitD(rpmfi fi, int dx) { if (fi != NULL) { - if (dx >= 0 && dx < fi->fc) + if (dx >= 0 && dx < rpmfilesFC(fi->files)) fi->j = dx - 1; else fi = NULL; @@ -459,26 +907,6 @@ rpmfi rpmfiInitD(rpmfi fi, int dx) return fi; } -/** - * Identify a file type. - * @param ft file type - * @return string to identify a file type - */ -static -const char * ftstring (rpmFileTypes ft) -{ - switch (ft) { - case XDIR: return "directory"; - case CDEV: return "char dev"; - case BDEV: return "block dev"; - case LINK: return "link"; - case SOCK: return "sock"; - case PIPE: return "fifo/pipe"; - case REG: return "file"; - default: return "unknown file type"; - } -} - rpmFileTypes rpmfiWhatis(rpm_mode_t mode) { if (S_ISDIR(mode)) return XDIR; @@ -490,30 +918,32 @@ rpmFileTypes rpmfiWhatis(rpm_mode_t mode) return REG; } -int rpmfiCompareIndex(rpmfi afi, int aix, rpmfi bfi, int bix) +int rpmfilesCompare(rpmfiles afi, int aix, rpmfiles bfi, int bix) { - mode_t amode = rpmfiFModeIndex(afi, aix); - mode_t bmode = rpmfiFModeIndex(bfi, bix); + mode_t amode = rpmfilesFMode(afi, aix); + mode_t bmode = rpmfilesFMode(bfi, bix); rpmFileTypes awhat = rpmfiWhatis(amode); - if ((rpmfiFFlagsIndex(afi, aix) & RPMFILE_GHOST) || - (rpmfiFFlagsIndex(bfi, bix) & RPMFILE_GHOST)) return 0; + if ((rpmfilesFFlags(afi, aix) & RPMFILE_GHOST) || + (rpmfilesFFlags(bfi, bix) & RPMFILE_GHOST)) return 0; - if (amode != bmode) return 1; + /* Mode difference is a conflict, except for symlinks */ + if (!(awhat == LINK && rpmfiWhatis(bmode) == LINK) && amode != bmode) + return 1; if (awhat == LINK || awhat == REG) { - if (rpmfiFSizeIndex(afi, aix) != rpmfiFSizeIndex(bfi, bix)) + if (rpmfilesFSize(afi, aix) != rpmfilesFSize(bfi, bix)) return 1; } - if (!rstreq(rpmfiFUserIndex(afi, aix), rpmfiFUserIndex(bfi, bix))) + if (!rstreq(rpmfilesFUser(afi, aix), rpmfilesFUser(bfi, bix))) return 1; - if (!rstreq(rpmfiFGroupIndex(afi, aix), rpmfiFGroupIndex(bfi, bix))) + if (!rstreq(rpmfilesFGroup(afi, aix), rpmfilesFGroup(bfi, bix))) return 1; if (awhat == LINK) { - const char * alink = rpmfiFLinkIndex(afi, aix); - const char * blink = rpmfiFLinkIndex(bfi, bix); + const char * alink = rpmfilesFLink(afi, aix); + const char * blink = rpmfilesFLink(bfi, bix); if (alink == blink) return 0; if (alink == NULL) return 1; if (blink == NULL) return -1; @@ -522,8 +952,8 @@ int rpmfiCompareIndex(rpmfi afi, int aix, rpmfi bfi, int bix) size_t adiglen, bdiglen; int aalgo, balgo; const unsigned char * adigest, * bdigest; - adigest = rpmfiFDigestIndex(afi, aix, &aalgo, &adiglen); - bdigest = rpmfiFDigestIndex(bfi, bix, &balgo, &bdiglen); + adigest = rpmfilesFDigest(afi, aix, &aalgo, &adiglen); + bdigest = rpmfilesFDigest(bfi, bix, &balgo, &bdiglen); if (adigest == bdigest) return 0; if (adigest == NULL) return 1; if (bdigest == NULL) return -1; @@ -531,18 +961,88 @@ int rpmfiCompareIndex(rpmfi afi, int aix, rpmfi bfi, int bix) if (aalgo != balgo || adiglen != bdiglen) return -1; return memcmp(adigest, bdigest, adiglen); } else if (awhat == CDEV || awhat == BDEV) { - if (rpmfiFRdevIndex(afi, aix) != rpmfiFRdevIndex(bfi, bix)) + if (rpmfilesFRdev(afi, aix) != rpmfilesFRdev(bfi, bix)) return 1; } return 0; } -rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix, +int rpmfileContentsEqual(rpmfiles ofi, int oix, rpmfiles nfi, int nix) +{ + char * fn = rpmfilesFN(nfi, nix); + rpmFileTypes diskWhat, newWhat, oldWhat; + struct stat sb; + int equal = 0; + + if (fn == NULL || (lstat(fn, &sb))) { + goto exit; /* The file doesn't exist on the disk */ + } + + if (rpmfilesFSize(nfi, nix) != sb.st_size) { + goto exit; + } + + diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); + newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix)); + oldWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix)); + if ((diskWhat != newWhat) || (diskWhat != oldWhat)) { + goto exit; + } + + if (diskWhat == REG) { + int oalgo, nalgo; + size_t odiglen, ndiglen; + const unsigned char * odigest, * ndigest; + char buffer[1024]; + + odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen); + ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen); + /* See if the file in old pkg is identical to the one in new pkg */ + if ((oalgo != nalgo) || (odiglen != ndiglen) || (!ndigest) || + (memcmp(odigest, ndigest, ndiglen) != 0)) { + goto exit; + } + + if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL) != 0) { + goto exit; /* assume file has been removed */ + } + + /* See if the file on disk is identical to the one in new pkg */ + if (memcmp(ndigest, buffer, ndiglen) == 0) { + equal = 1; + goto exit; + } + } else if (diskWhat == LINK) { + const char * nFLink; + char buffer[1024]; + ssize_t link_len; + + nFLink = rpmfilesFLink(nfi, nix); + link_len = readlink(fn, buffer, sizeof(buffer) - 1); + if (link_len == -1) { + goto exit; /* assume file has been removed */ + } + buffer[link_len] = '\0'; + /* See if the link on disk is identical to the one in new pkg */ + if (nFLink && rstreq(nFLink, buffer)) { + equal = 1; + goto exit; + } + } + +exit: + free(fn); + return equal; +} + + +rpmFileAction rpmfilesDecideFate(rpmfiles ofi, int oix, + rpmfiles nfi, int nix, int skipMissing) { - char * fn = rpmfiFNIndex(nfi, nix); - rpmfileAttrs newFlags = rpmfiFFlagsIndex(nfi, nix); + char * fn = rpmfilesFN(nfi, nix); + rpmfileAttrs newFlags = rpmfilesFFlags(nfi, nix); char buffer[1024]; rpmFileTypes dbWhat, newWhat, diskWhat; struct stat sb; @@ -571,39 +1071,47 @@ rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix, } diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); - dbWhat = rpmfiWhatis(rpmfiFModeIndex(ofi, oix)); - newWhat = rpmfiWhatis(rpmfiFModeIndex(nfi, nix)); + dbWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix)); + newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix)); /* - * This order matters - we'd prefer to CREATE the file if at all + * This order matters - we'd prefer to TOUCH the file if at all * possible in case something else (like the timestamp) has changed. * Only regular files and symlinks might need a backup, everything * else falls through here with FA_CREATE. */ - memset(buffer, 0, sizeof(buffer)); if (dbWhat == REG) { int oalgo, nalgo; size_t odiglen, ndiglen; const unsigned char * odigest, * ndigest; + /* See if the file on disk is identical to the one in new pkg */ + ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen); + if (diskWhat == REG && newWhat == REG) { + if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL)) + goto exit; /* assume file has been removed */ + if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0) { + action = FA_TOUCH; + goto exit; /* unmodified config file, touch it. */ + } + } + /* See if the file on disk is identical to the one in old pkg */ - odigest = rpmfiFDigestIndex(ofi, oix, &oalgo, &odiglen); + odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen); if (diskWhat == REG) { - if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL)) - goto exit; /* assume file has been removed */ + /* hash algo changed or digest was not computed, recalculate it */ + if ((oalgo != nalgo) || (newWhat != REG)) { + if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL)) + goto exit; /* assume file has been removed */ + } if (odigest && memcmp(odigest, buffer, odiglen) == 0) goto exit; /* unmodified config file, replace. */ } - /* See if the file on disk is identical to the one in new pkg */ - ndigest = rpmfiFDigestIndex(nfi, nix, &nalgo, &ndiglen); - if (diskWhat == REG && newWhat == REG) { - /* hash algo changed in new, recalculate digest */ - if (oalgo != nalgo) - if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL)) - goto exit; /* assume file has been removed */ - if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0) - goto exit; /* file identical in new, replace. */ + /* if new file is no longer config, backup it and replace it */ + if (!(newFlags & RPMFILE_CONFIG)) { + action = FA_SAVE; + goto exit; } /* If file can be determined identical in old and new pkg, let it be */ @@ -619,24 +1127,36 @@ rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix, } else if (dbWhat == LINK) { const char * oFLink, * nFLink; - /* See if the link on disk is identical to the one in old pkg */ - oFLink = rpmfiFLinkIndex(ofi, oix); if (diskWhat == LINK) { + /* Read link from the disk */ ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1); if (link_len == -1) goto exit; /* assume file has been removed */ buffer[link_len] = '\0'; - if (oFLink && rstreq(oFLink, buffer)) - goto exit; /* unmodified config file, replace. */ } /* See if the link on disk is identical to the one in new pkg */ - nFLink = rpmfiFLinkIndex(nfi, nix); + nFLink = rpmfilesFLink(nfi, nix); if (diskWhat == LINK && newWhat == LINK) { - if (nFLink && rstreq(nFLink, buffer)) + if (nFLink && rstreq(nFLink, buffer)) { + action = FA_TOUCH; + goto exit; /* unmodified config file, touch it. */ + } + } + + /* See if the link on disk is identical to the one in old pkg */ + oFLink = rpmfilesFLink(ofi, oix); + if (diskWhat == LINK) { + if (oFLink && rstreq(oFLink, buffer)) goto exit; /* unmodified config file, replace. */ } + /* if new file is no longer config, backup it and replace it */ + if (!(newFlags & RPMFILE_CONFIG)) { + action = FA_SAVE; + goto exit; + } + /* If link is identical in old and new pkg, let it be */ if (newWhat == LINK && oFLink && nFLink && rstreq(oFLink, nFLink)) { action = FA_SKIP; /* identical file, don't bother. */ @@ -652,10 +1172,10 @@ exit: return action; } -int rpmfiConfigConflictIndex(rpmfi fi, int ix) +int rpmfilesConfigConflict(rpmfiles fi, int ix) { char * fn = NULL; - rpmfileAttrs flags = rpmfiFFlagsIndex(fi, ix); + rpmfileAttrs flags = rpmfilesFFlags(fi, ix); char buffer[1024]; rpmFileTypes newWhat, diskWhat; struct stat sb; @@ -667,12 +1187,12 @@ int rpmfiConfigConflictIndex(rpmfi fi, int ix) /* Only links and regular files can be %config, this is kinda moot */ /* XXX: Why are we returning 1 here? */ - newWhat = rpmfiWhatis(rpmfiFModeIndex(fi, ix)); + newWhat = rpmfiWhatis(rpmfilesFMode(fi, ix)); if (newWhat != LINK && newWhat != REG) return 1; /* If it's not on disk, there's nothing to be saved */ - fn = rpmfiFNIndex(fi, ix); + fn = rpmfilesFN(fi, ix); if (lstat(fn, &sb)) goto exit; @@ -697,7 +1217,7 @@ int rpmfiConfigConflictIndex(rpmfi fi, int ix) } /* Files of different sizes obviously are not identical */ - if (rpmfiFSizeIndex(fi, ix) != sb.st_size) { + if (rpmfilesFSize(fi, ix) != sb.st_size) { rc = 1; goto exit; } @@ -706,7 +1226,7 @@ int rpmfiConfigConflictIndex(rpmfi fi, int ix) if (newWhat == REG) { int algo; size_t diglen; - const unsigned char *ndigest = rpmfiFDigestIndex(fi,ix, &algo, &diglen); + const unsigned char *ndigest = rpmfilesFDigest(fi,ix, &algo, &diglen); if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL)) goto exit; /* assume file has been removed */ if (ndigest && memcmp(ndigest, buffer, diglen) == 0) @@ -717,7 +1237,7 @@ int rpmfiConfigConflictIndex(rpmfi fi, int ix) if (link_len == -1) goto exit; /* assume file has been removed */ buffer[link_len] = '\0'; - nFLink = rpmfiFLinkIndex(fi, ix); + nFLink = rpmfilesFLink(fi, ix); if (nFLink && rstreq(nFLink, buffer)) goto exit; /* unmodified config file */ } @@ -729,334 +1249,24 @@ exit: return rc; } -static char **duparray(char ** src, int size) -{ - char **dest = xmalloc((size+1) * sizeof(*dest)); - for (int i = 0; i < size; i++) { - dest[i] = xstrdup(src[i]); - } - free(src); - return dest; -} - -static int addPrefixes(Header h, rpmRelocation *relocations, int numRelocations) -{ - struct rpmtd_s validRelocs; - const char *validprefix; - const char ** actualRelocations; - int numActual = 0; - - headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM); - /* - * If no relocations are specified (usually the case), then return the - * original header. If there are prefixes, however, then INSTPREFIXES - * should be added for RPM_INSTALL_PREFIX environ variables in scriptlets, - * but, since relocateFileList() can be called more than once for - * the same header, don't bother if already present. - */ - if (relocations == NULL || numRelocations == 0) { - if (rpmtdCount(&validRelocs) > 0) { - if (!headerIsEntry(h, RPMTAG_INSTPREFIXES)) { - rpmtdSetTag(&validRelocs, RPMTAG_INSTPREFIXES); - headerPut(h, &validRelocs, HEADERPUT_DEFAULT); - } - rpmtdFreeData(&validRelocs); - } - return 0; - } - - actualRelocations = xmalloc(rpmtdCount(&validRelocs) * sizeof(*actualRelocations)); - rpmtdInit(&validRelocs); - while ((validprefix = rpmtdNextString(&validRelocs))) { - int j; - for (j = 0; j < numRelocations; j++) { - if (relocations[j].oldPath == NULL || /* XXX can't happen */ - !rstreq(validprefix, relocations[j].oldPath)) - continue; - /* On install, a relocate to NULL means skip the path. */ - if (relocations[j].newPath) { - actualRelocations[numActual] = relocations[j].newPath; - numActual++; - } - break; - } - if (j == numRelocations) { - actualRelocations[numActual] = validprefix; - numActual++; - } - } - rpmtdFreeData(&validRelocs); - - if (numActual) { - headerPutStringArray(h, RPMTAG_INSTPREFIXES, actualRelocations, numActual); - } - free(actualRelocations); - return numActual; -} - -static void saveRelocs(Header h, rpmtd bnames, rpmtd dnames, rpmtd dindexes) -{ - struct rpmtd_s td; - headerGet(h, RPMTAG_BASENAMES, &td, HEADERGET_MINMEM); - rpmtdSetTag(&td, RPMTAG_ORIGBASENAMES); - headerPut(h, &td, HEADERPUT_DEFAULT); - rpmtdFreeData(&td); - - headerGet(h, RPMTAG_DIRNAMES, &td, HEADERGET_MINMEM); - rpmtdSetTag(&td, RPMTAG_ORIGDIRNAMES); - headerPut(h, &td, HEADERPUT_DEFAULT); - rpmtdFreeData(&td); - - headerGet(h, RPMTAG_DIRINDEXES, &td, HEADERGET_MINMEM); - rpmtdSetTag(&td, RPMTAG_ORIGDIRINDEXES); - headerPut(h, &td, HEADERPUT_DEFAULT); - rpmtdFreeData(&td); - - headerMod(h, bnames); - headerMod(h, dnames); - headerMod(h, dindexes); -} - -void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations, - rpmfs fs, Header h) -{ - static int _printed = 0; - char ** baseNames; - char ** dirNames; - uint32_t * dirIndexes; - rpm_count_t fileCount, dirCount; - int nrelocated = 0; - int fileAlloced = 0; - char * fn = NULL; - int haveRelocatedBase = 0; - size_t maxlen = 0; - int i, j; - struct rpmtd_s bnames, dnames, dindexes, fmodes; - - addPrefixes(h, relocations, numRelocations); - - if (!_printed) { - _printed = 1; - rpmlog(RPMLOG_DEBUG, "========== relocations\n"); - for (i = 0; i < numRelocations; i++) { - if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */ - if (relocations[i].newPath == NULL) - rpmlog(RPMLOG_DEBUG, "%5d exclude %s\n", - i, relocations[i].oldPath); - else - rpmlog(RPMLOG_DEBUG, "%5d relocate %s -> %s\n", - i, relocations[i].oldPath, relocations[i].newPath); - } - } - - for (i = 0; i < numRelocations; i++) { - if (relocations[i].newPath == NULL) continue; - size_t len = strlen(relocations[i].newPath); - if (len > maxlen) maxlen = len; - } - - headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM); - headerGet(h, RPMTAG_DIRINDEXES, &dindexes, HEADERGET_ALLOC); - headerGet(h, RPMTAG_DIRNAMES, &dnames, HEADERGET_MINMEM); - headerGet(h, RPMTAG_FILEMODES, &fmodes, HEADERGET_MINMEM); - /* TODO XXX ugh.. use rpmtd iterators & friends instead */ - baseNames = bnames.data; - dirIndexes = dindexes.data; - fileCount = rpmtdCount(&bnames); - dirCount = rpmtdCount(&dnames); - /* XXX TODO: use rpmtdDup() instead */ - dirNames = dnames.data = duparray(dnames.data, dirCount); - dnames.flags |= RPMTD_PTR_ALLOCED; - - /* - * For all relocations, we go through sorted file/relocation lists - * backwards so that /usr/local relocations take precedence over /usr - * ones. - */ - - /* Relocate individual paths. */ - - for (i = fileCount - 1; i >= 0; i--) { - rpmFileTypes ft; - int fnlen; - - size_t len = maxlen + - strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1; - if (len >= fileAlloced) { - fileAlloced = len * 2; - fn = xrealloc(fn, fileAlloced); - } - -assert(fn != NULL); /* XXX can't happen */ - *fn = '\0'; - fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn; - - /* - * See if this file path needs relocating. - */ - /* - * XXX FIXME: Would a bsearch of the (already sorted) - * relocation list be a good idea? - */ - for (j = numRelocations - 1; j >= 0; j--) { - if (relocations[j].oldPath == NULL) /* XXX can't happen */ - continue; - len = !rstreq(relocations[j].oldPath, "/") - ? strlen(relocations[j].oldPath) - : 0; - - if (fnlen < len) - continue; - /* - * Only subdirectories or complete file paths may be relocated. We - * don't check for '\0' as our directory names all end in '/'. - */ - if (!(fn[len] == '/' || fnlen == len)) - continue; - - if (!rstreqn(relocations[j].oldPath, fn, len)) - continue; - break; - } - if (j < 0) continue; - - rpmtdSetIndex(&fmodes, i); - ft = rpmfiWhatis(rpmtdGetNumber(&fmodes)); - - /* On install, a relocate to NULL means skip the path. */ - if (relocations[j].newPath == NULL) { - if (ft == XDIR) { - /* Start with the parent, looking for directory to exclude. */ - for (j = dirIndexes[i]; j < dirCount; j++) { - len = strlen(dirNames[j]) - 1; - while (len > 0 && dirNames[j][len-1] == '/') len--; - if (fnlen != len) - continue; - if (!rstreqn(fn, dirNames[j], fnlen)) - continue; - break; - } - } - rpmfsSetAction(fs, i, FA_SKIPNSTATE); - rpmlog(RPMLOG_DEBUG, "excluding %s %s\n", - ftstring(ft), fn); - continue; - } - - /* Relocation on full paths only, please. */ - if (fnlen != len) continue; - - rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n", - fn, relocations[j].newPath); - nrelocated++; - - strcpy(fn, relocations[j].newPath); - { char * te = strrchr(fn, '/'); - if (te) { - if (te > fn) te++; /* root is special */ - fnlen = te - fn; - } else - te = fn + strlen(fn); - if (!rstreq(baseNames[i], te)) { /* basename changed too? */ - if (!haveRelocatedBase) { - /* XXX TODO: use rpmtdDup() instead */ - bnames.data = baseNames = duparray(baseNames, fileCount); - bnames.flags |= RPMTD_PTR_ALLOCED; - haveRelocatedBase = 1; - } - free(baseNames[i]); - baseNames[i] = xstrdup(te); - } - *te = '\0'; /* terminate new directory name */ - } - - /* Does this directory already exist in the directory list? */ - for (j = 0; j < dirCount; j++) { - if (fnlen != strlen(dirNames[j])) - continue; - if (!rstreqn(fn, dirNames[j], fnlen)) - continue; - break; - } - - if (j < dirCount) { - dirIndexes[i] = j; - continue; - } - - /* Creating new paths is a pita */ - dirNames = dnames.data = xrealloc(dnames.data, - sizeof(*dirNames) * (dirCount + 1)); - - dirNames[dirCount] = xstrdup(fn); - dirIndexes[i] = dirCount; - dirCount++; - dnames.count++; - } - - /* Finish off by relocating directories. */ - for (i = dirCount - 1; i >= 0; i--) { - for (j = numRelocations - 1; j >= 0; j--) { - - if (relocations[j].oldPath == NULL) /* XXX can't happen */ - continue; - size_t len = !rstreq(relocations[j].oldPath, "/") - ? strlen(relocations[j].oldPath) - : 0; - - if (len && !rstreqn(relocations[j].oldPath, dirNames[i], len)) - continue; - - /* - * Only subdirectories or complete file paths may be relocated. We - * don't check for '\0' as our directory names all end in '/'. - */ - if (dirNames[i][len] != '/') - continue; - - if (relocations[j].newPath) { /* Relocate the path */ - char *t = NULL; - rstrscat(&t, relocations[j].newPath, (dirNames[i] + len), NULL); - /* Unfortunatly rpmCleanPath strips the trailing slash.. */ - (void) rpmCleanPath(t); - rstrcat(&t, "/"); - - rpmlog(RPMLOG_DEBUG, - "relocating directory %s to %s\n", dirNames[i], t); - free(dirNames[i]); - dirNames[i] = t; - nrelocated++; - } - } - } - - /* Save original filenames in header and replace (relocated) filenames. */ - if (nrelocated) { - saveRelocs(h, &bnames, &dnames, &dindexes); - } - - rpmtdFreeData(&bnames); - rpmtdFreeData(&dnames); - rpmtdFreeData(&dindexes); - rpmtdFreeData(&fmodes); - free(fn); -} - -rpmfi rpmfiFree(rpmfi fi) +rpmfiles rpmfilesFree(rpmfiles fi) { if (fi == NULL) return NULL; if (fi->nrefs > 1) - return rpmfiUnlink(fi); + return rpmfilesUnlink(fi); - if (fi->fc > 0) { - fi->bnid = _free(fi->bnid); - fi->dnid = _free(fi->dnid); - fi->dil = _free(fi->dil); + if (rpmfilesFC(fi) > 0) { + if (fi->ofndata != &fi->fndata) { + rpmfnClear(fi->ofndata); + free(fi->ofndata); + } + rpmfnClear(&fi->fndata); fi->flinks = _free(fi->flinks); fi->flangs = _free(fi->flangs); fi->digests = _free(fi->digests); + fi->signatures = _free(fi->signatures); fi->fcaps = _free(fi->fcaps); fi->cdict = _free(fi->cdict); @@ -1067,8 +1277,6 @@ rpmfi rpmfiFree(rpmfi fi) fi->fstates = _free(fi->fstates); fi->fps = _free(fi->fps); - fi->pool = rpmstrPoolFree(fi->pool); - /* these point to header memory if KEEPHEADER is used, dont free */ if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) { fi->fmtimes = _free(fi->fmtimes); @@ -1076,6 +1284,7 @@ rpmfi rpmfiFree(rpmfi fi) fi->fflags = _free(fi->fflags); fi->vflags = _free(fi->vflags); fi->fsizes = _free(fi->fsizes); + fi->lfsizes = _free(fi->lfsizes); fi->frdevs = _free(fi->frdevs); fi->finodes = _free(fi->finodes); @@ -1088,26 +1297,46 @@ rpmfi rpmfiFree(rpmfi fi) } } - fi->fn = _free(fi->fn); - fi->apath = _free(fi->apath); - fi->replacedSizes = _free(fi->replacedSizes); + fi->replacedLSizes = _free(fi->replacedLSizes); fi->h = headerFree(fi->h); + fi->pool = rpmstrPoolFree(fi->pool); + + fi->nlinks = nlinkHashFree(fi->nlinks); - (void) rpmfiUnlink(fi); + (void) rpmfilesUnlink(fi); memset(fi, 0, sizeof(*fi)); /* XXX trash and burn */ fi = _free(fi); return NULL; } -static rpmsid * tag2pool(rpmstrPool pool, Header h, rpmTag tag) +rpmfi rpmfiFree(rpmfi fi) +{ + if (fi == NULL) return NULL; + + if (fi->nrefs > 1) + return rpmfiUnlink(fi); + + fi->files = rpmfilesFree(fi->files); + fi->fn = _free(fi->fn); + fi->ofn = _free(fi->ofn); + fi->found = _free(fi->found); + fi->archive = rpmcpioFree(fi->archive); + + free(fi); + return NULL; +} + +static rpmsid * tag2pool(rpmstrPool pool, Header h, rpmTag tag, rpm_count_t size) { rpmsid *sids = NULL; struct rpmtd_s td; if (headerGet(h, tag, &td, HEADERGET_MINMEM)) { - sids = rpmtdToPool(&td, pool); + if ((size >= 0) && (rpmtdCount(&td) == size)) { /* ensure right size */ + sids = rpmtdToPool(&td, pool); + } rpmtdFreeData(&td); } return sids; @@ -1122,30 +1351,182 @@ static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd) uint32_t zc = rpmtdCount(zd); /* check that the amount of data in each is sane */ - if (xc > 0 && yc > 0 && yc <= xc && zc == xc) { - uint32_t * i; + /* normally yc <= xc but larger values are not fatal (RhBug:1001553) */ + if (xc > 0 && yc > 0 && zc == xc) { + uint32_t * i, nvalid = 0; /* ...and that the indexes are within bounds */ while ((i = rpmtdNextUint32(zd))) { if (*i >= yc) break; + nvalid++; } /* unless the loop runs to finish, the data is broken */ - sane = (i == NULL); + sane = (nvalid == zc); } return sane; } +/* Get file data from header */ +/* Requires totalfc to be set and label err: to goto on error */ #define _hgfi(_h, _tag, _td, _flags, _data) \ - if (headerGet((_h), (_tag), (_td), (_flags))) \ - _data = (td.data) + if (headerGet((_h), (_tag), (_td), (_flags))) { \ + if (rpmtdCount(_td) != totalfc) { \ + rpmlog(RPMLOG_ERR, _("Wrong number of entries for tag %s: %u found but %u expected.\n"), rpmTagGetName(_tag), rpmtdCount(_td), totalfc); \ + goto err; \ + } \ + if (rpmTagGetTagType(_tag) != RPM_STRING_ARRAY_TYPE && rpmTagGetTagType(_tag) != RPM_I18NSTRING_TYPE && \ + (_td)->size < totalfc * sizeof(*(_data))) { \ + rpmlog(RPMLOG_ERR, _("Malformed data for tag %s: %u bytes found but %u expected.\n"), rpmTagGetName(_tag), (_td)->size, totalfc * sizeof(*(_data))); \ + goto err; \ + } \ + _data = ((_td)->data); \ + } +/* Get file data from header without checking number of entries */ +#define _hgfinc(_h, _tag, _td, _flags, _data) \ + if (headerGet((_h), (_tag), (_td), (_flags))) {\ + _data = ((_td)->data); \ + } + +/*** Hard link handling ***/ + +struct fileid_s { + rpm_dev_t id_dev; + rpm_ino_t id_ino; +}; + +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE +#define HASHTYPE fileidHash +#define HTKEYTYPE struct fileid_s +#define HTDATATYPE int +#include "lib/rpmhash.H" +#include "lib/rpmhash.C" +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE + +static unsigned int fidHashFunc(struct fileid_s a) +{ + return a.id_ino + (a.id_dev<<16) + (a.id_dev>>16); +} + +static int fidCmp(struct fileid_s a, struct fileid_s b) +{ + return !((a.id_dev == b.id_dev) && (a.id_ino == b.id_ino)); +} + +static unsigned int intHash(int a) +{ + return a < 0 ? UINT_MAX-a : a; +} + +static int intCmp(int a, int b) +{ + return a != b; +} + +static struct hardlinks_s * freeNLinks(struct hardlinks_s * nlinks) +{ + nlinks->nlink--; + if (!nlinks->nlink) { + nlinks = _free(nlinks); + } + return nlinks; +} -static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags) +static void rpmfilesBuildNLink(rpmfiles fi, Header h) +{ + struct fileid_s f_id; + fileidHash files; + rpm_dev_t * fdevs = NULL; + struct rpmtd_s td; + int fc = 0; + int totalfc = rpmfilesFC(fi); + + if (!fi->finodes) + return; + + _hgfi(h, RPMTAG_FILEDEVICES, &td, HEADERGET_ALLOC, fdevs); + if (!fdevs) + return; + + files = fileidHashCreate(totalfc, fidHashFunc, fidCmp, NULL, NULL); + for (int i=0; i < totalfc; i++) { + if (!S_ISREG(rpmfilesFMode(fi, i)) || + (rpmfilesFFlags(fi, i) & RPMFILE_GHOST) || + fi->finodes[i] <= 0) { + continue; + } + fc++; + f_id.id_dev = fdevs[i]; + f_id.id_ino = fi->finodes[i]; + fileidHashAddEntry(files, f_id, i); + } + if (fileidHashNumKeys(files) != fc) { + /* Hard links */ + fi->nlinks = nlinkHashCreate(2*(totalfc - fileidHashNumKeys(files)), + intHash, intCmp, NULL, freeNLinks); + for (int i=0; i < totalfc; i++) { + int fcnt; + int * data; + if (!S_ISREG(rpmfilesFMode(fi, i)) || + (rpmfilesFFlags(fi, i) & RPMFILE_GHOST)) { + continue; + } + f_id.id_dev = fdevs[i]; + f_id.id_ino = fi->finodes[i]; + fileidHashGetEntry(files, f_id, &data, &fcnt, NULL); + if (fcnt > 1 && !nlinkHashHasEntry(fi->nlinks, i)) { + struct hardlinks_s * hlinks; + hlinks = xmalloc(sizeof(struct hardlinks_s)+ + fcnt*sizeof(hlinks->files[0])); + hlinks->nlink = fcnt; + for (int j=0; j<fcnt; j++) { + hlinks->files[j] = data[j]; + nlinkHashAddEntry(fi->nlinks, data[j], hlinks); + } + } + } + } + _free(fdevs); + files = fileidHashFree(files); +err: + return; +} + +/* Convert a tag of hex strings to binary presentation */ +static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len) +{ + struct rpmtd_s td; + uint8_t *bin = NULL; + + if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) { + uint8_t *t = bin = xmalloc(num * len); + const char *s; + + while ((s = rpmtdNextString(&td))) { + if (*s == '\0') { + memset(t, 0, len); + t += len; + continue; + } + for (int j = 0; j < len; j++, t++, s += 2) + *t = (rnibble(s[0]) << 4) | rnibble(s[1]); + } + } + rpmtdFreeData(&td); + + return bin; +} + +static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags) { headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ? HEADERGET_MINMEM : HEADERGET_ALLOC; headerGetFlags defFlags = HEADERGET_ALLOC; - struct rpmtd_s fdigests, digalgo, td; - unsigned char * t; + struct rpmtd_s digalgo, td; + rpm_count_t totalfc = rpmfilesFC(fi); /* XXX TODO: all these should be sanity checked, ugh... */ if (!(flags & RPMFI_NOFILEMODES)) @@ -1154,22 +1535,23 @@ static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags) _hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags); if (!(flags & RPMFI_NOFILEVERIFYFLAGS)) _hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags); - if (!(flags & RPMFI_NOFILESIZES)) + if (!(flags & RPMFI_NOFILESIZES)) { _hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes); - + _hgfi(h, RPMTAG_LONGFILESIZES, &td, scareFlags, fi->lfsizes); + } if (!(flags & RPMFI_NOFILECOLORS)) _hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors); if (!(flags & RPMFI_NOFILECLASS)) { - _hgfi(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict); + _hgfinc(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict); fi->ncdict = rpmtdCount(&td); _hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx); } if (!(flags & RPMFI_NOFILEDEPS)) { - _hgfi(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict); + _hgfinc(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict); fi->nddict = rpmtdCount(&td); - _hgfi(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx); - _hgfi(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn); + _hgfinc(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx); + _hgfinc(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn); } if (!(flags & RPMFI_NOFILESTATES)) @@ -1179,10 +1561,10 @@ static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags) _hgfi(h, RPMTAG_FILECAPS, &td, defFlags, fi->fcaps); if (!(flags & RPMFI_NOFILELINKTOS)) - fi->flinks = tag2pool(fi->pool, h, RPMTAG_FILELINKTOS); + fi->flinks = tag2pool(fi->pool, h, RPMTAG_FILELINKTOS, totalfc); /* FILELANGS are only interesting when installing */ if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS)) - fi->flangs = tag2pool(fi->pool, h, RPMTAG_FILELANGS); + fi->flangs = tag2pool(fi->pool, h, RPMTAG_FILELANGS, totalfc); /* See if the package has non-md5 file digests */ fi->digestalgo = PGPHASHALGO_MD5; @@ -1196,22 +1578,17 @@ static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags) fi->digests = NULL; /* grab hex digests from header and store in binary format */ - if (!(flags & RPMFI_NOFILEDIGESTS) && - headerGet(h, RPMTAG_FILEDIGESTS, &fdigests, HEADERGET_MINMEM)) { - const char *fdigest; + if (!(flags & RPMFI_NOFILEDIGESTS)) { size_t diglen = rpmDigestLength(fi->digestalgo); - fi->digests = t = xmalloc(rpmtdCount(&fdigests) * diglen); + fi->digests = hex2bin(h, RPMTAG_FILEDIGESTS, totalfc, diglen); + } - while ((fdigest = rpmtdNextString(&fdigests))) { - if (!(fdigest && *fdigest != '\0')) { - memset(t, 0, diglen); - t += diglen; - continue; - } - for (int j = 0; j < diglen; j++, t++, fdigest += 2) - *t = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]); - } - rpmtdFreeData(&fdigests); + fi->signatures = NULL; + /* grab hex signatures from header and store in binary format */ + if (!(flags & RPMFI_NOFILESIGNATURES)) { + fi->signaturelength = headerGetNumber(h, RPMTAG_FILESIGNATURELENGTH); + fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES, + totalfc, fi->signaturelength); } /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */ @@ -1219,67 +1596,170 @@ static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags) _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes); if (!(flags & RPMFI_NOFILERDEVS)) _hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs); - if (!(flags & RPMFI_NOFILEINODES)) + if (!(flags & RPMFI_NOFILEINODES)) { _hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes); - - if (!(flags & RPMFI_NOFILEUSER)) - fi->fuser = tag2pool(fi->pool, h, RPMTAG_FILEUSERNAME); - if (!(flags & RPMFI_NOFILEGROUP)) - fi->fgroup = tag2pool(fi->pool, h, RPMTAG_FILEGROUPNAME); - + rpmfilesBuildNLink(fi, h); + } + if (!(flags & RPMFI_NOFILEUSER)) { + fi->fuser = tag2pool(fi->pool, h, RPMTAG_FILEUSERNAME, totalfc); + if (!fi->fuser) goto err; + } + if (!(flags & RPMFI_NOFILEGROUP)) { + fi->fgroup = tag2pool(fi->pool, h, RPMTAG_FILEGROUPNAME, totalfc); + if (!fi->fgroup) goto err; + } /* TODO: validate and return a real error */ return 0; + err: + return -1; } -rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags) +rpmfiles rpmfilesNew(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags) { - rpmfi fi = xcalloc(1, sizeof(*fi)); - struct rpmtd_s bn, dn, dx; + rpmfiles fi = xcalloc(1, sizeof(*fi)); + int fc; fi->magic = RPMFIMAGIC; - fi->i = -1; fi->fiflags = flags; + /* private or shared pool? */ + fi->pool = (pool != NULL) ? rpmstrPoolLink(pool) : rpmstrPoolCreate(); /* * Grab and validate file triplet data. Headers with no files simply * fall through here and an empty file set is returned. */ - if (headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM)) { - headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM); - headerGet(h, RPMTAG_DIRINDEXES, &dx, HEADERGET_ALLOC); + fc = rpmfnInit(&fi->fndata, RPMTAG_BASENAMES, h, fi->pool); + + /* Broken data, bail out */ + if (fc < 0) + goto err; + + /* populate the rest of the stuff if we have files */ + if (fc > 0) { + if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { + /* For relocated packages, grab the original paths too */ + int ofc; + fi->ofndata = xmalloc(sizeof(*fi->ofndata)); + ofc = rpmfnInit(fi->ofndata, RPMTAG_ORIGBASENAMES, h, fi->pool); + + if (ofc != 0 && ofc != fc) + goto err; + } else { + /* In the normal case, orig is the same as actual path data */ + fi->ofndata = &fi->fndata; + } + + if (rpmfilesPopulate(fi, h, flags)) + goto err; + } - if (indexSane(&bn, &dn, &dx)) { - /* private or shared pool? */ - fi->pool = (pool != NULL) ? rpmstrPoolLink(pool) : - rpmstrPoolCreate(); - - /* init the file triplet data */ - fi->fc = rpmtdCount(&bn); - fi->dc = rpmtdCount(&dn); - fi->bnid = rpmtdToPool(&bn, fi->pool); - fi->dnid = rpmtdToPool(&dn, fi->pool); - /* steal index data from the td (pooh...) */ - fi->dil = dx.data; - dx.data = NULL; + /* freeze the pool to save memory, but only if private pool */ + if (fi->pool != pool) + rpmstrPoolFreeze(fi->pool, 0); - /* populate the rest of the stuff */ - rpmfiPopulate(fi, h, flags); + fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL; - /* freeze the pool to save memory, but only if private pool */ - if (fi->pool != pool) - rpmstrPoolFreeze(fi->pool, 0); + return rpmfilesLink(fi); - fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL; - } else { - /* broken data, free and return NULL */ - fi = _free(fi); +err: + rpmfilesFree(fi); + return NULL; +} + +static int iterWriteArchiveNext(rpmfi fi); +static int iterReadArchiveNext(rpmfi fi); +static int iterReadArchiveNextContentFirst(rpmfi fi); +static int iterReadArchiveNextOmitHardlinks(rpmfi fi); + +static int (*nextfuncs[])(rpmfi fi) = { + iterFwd, + iterBack, + iterWriteArchiveNext, + iterReadArchiveNext, + iterReadArchiveNextContentFirst, + iterReadArchiveNextOmitHardlinks, + iterInterval, +}; + + +static rpmfi initIter(rpmfiles files, int itype, int link) +{ + rpmfi fi = NULL; + + if (files && itype>=0 && itype<=RPMFILEITERMAX) { + fi = xcalloc(1, sizeof(*fi)); + fi->i = -1; + fi->files = link ? rpmfilesLink(files) : files; + fi->next = nextfuncs[itype]; + fi->i = -1; + if (itype == RPMFI_ITER_BACK) { + fi->i = rpmfilesFC(fi->files); + } else if (itype >=RPMFI_ITER_READ_ARCHIVE + && itype <= RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS) { + + fi->found = xcalloc(1, (rpmfiFC(fi)>>3) + 1); } - rpmtdFreeData(&bn); - rpmtdFreeData(&dn); - rpmtdFreeData(&dx); + rpmfiLink(fi); + } + return fi; +} + +rpmfi rpmfilesIter(rpmfiles files, int itype) +{ + /* standalone iterators need to bump our refcount */ + return initIter(files, itype, 1); +} + +rpmfi rpmfilesFindPrefix(rpmfiles fi, const char *pfx) +{ + int l, u, c, comparison; + rpmfi iterator = NULL; + + if (!fi || !pfx) + return NULL; + + size_t plen = strlen(pfx); + l = 0; + u = rpmfilesFC(fi); + while (l < u) { + c = (l + u) / 2; + + comparison = cmpPfx(fi, c, pfx, plen); + + if (comparison < 0) + u = c; + else if (comparison > 0) + l = c + 1; + else { + if (cmpPfx(fi, l, pfx, plen)) + l = c; + while (l > 0 && !cmpPfx(fi, l - 1, pfx, plen)) + l--; + if ( u >= rpmfilesFC(fi) || cmpPfx(fi, u, pfx, plen)) + u = c; + while (++u < rpmfilesFC(fi)) { + if (cmpPfx(fi, u, pfx, plen)) + break; + } + break; + } + + } + + if (l < u) { + iterator = initIter(fi, RPMFI_ITER_INTERVAL, 1); + iterator->intervalStart = l; + iterator->intervalEnd = u; } - return rpmfiLink(fi); + return iterator; +} + +rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags) +{ + rpmfiles files = rpmfilesNew(pool, h, tagN, flags); + /* we already own rpmfiles, avoid extra refcount on it */ + return initIter(files, RPMFI_ITER_FWD, 0); } rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags) @@ -1287,36 +1767,52 @@ rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags) return rpmfiNewPool(NULL, h, tagN, flags); } -void rpmfiSetFReplacedSizeIndex(rpmfi fi, int ix, rpm_loff_t newsize) +void rpmfilesSetFReplacedSize(rpmfiles fi, int ix, rpm_loff_t newsize) { - if (fi != NULL && ix >= 0 && ix < fi->fc) { - if (fi->replacedSizes == NULL) { - fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes)); + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { + /* Switch over to 64 bit variant */ + int fc = rpmfilesFC(fi); + if (newsize > UINT32_MAX && fi->replacedLSizes == NULL) { + fi->replacedLSizes = xcalloc(fc, sizeof(*fi->replacedLSizes)); + /* copy 32 bit data */ + if (fi->replacedSizes) { + for (int i=0; i < fc; i++) + fi->replacedLSizes[i] = fi->replacedSizes[i]; + fi->replacedSizes = _free(fi->replacedSizes); + } + } + if (fi->replacedLSizes != NULL) { + fi->replacedLSizes[ix] = newsize; + } else { + if (fi->replacedSizes == NULL) + fi->replacedSizes = xcalloc(fc, sizeof(*fi->replacedSizes)); + fi->replacedSizes[ix] = (rpm_off_t) newsize; } - /* XXX watch out, replacedSizes is not rpm_loff_t (yet) */ - fi->replacedSizes[ix] = (rpm_off_t) newsize; } } -rpm_loff_t rpmfiFReplacedSizeIndex(rpmfi fi, int ix) +rpm_loff_t rpmfilesFReplacedSize(rpmfiles fi, int ix) { rpm_loff_t rsize = 0; - if (fi != NULL && ix >= 0 && ix < fi->fc) { + if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) { if (fi->replacedSizes) { rsize = fi->replacedSizes[ix]; + } else if (fi->replacedLSizes) { + rsize = fi->replacedLSizes[ix]; } } return rsize; } -void rpmfiFpLookup(rpmfi fi, fingerPrintCache fpc) +void rpmfilesFpLookup(rpmfiles fi, fingerPrintCache fpc) { /* This can get called twice (eg yum), scratch former results and redo */ - if (fi->fc > 0) { + if (rpmfilesFC(fi) > 0) { + rpmfn fn = &fi->fndata; if (fi->fps) free(fi->fps); fi->fps = fpLookupList(fpc, fi->pool, - fi->dnid, fi->bnid, fi->dil, fi->fc); + fn->dnid, fn->bnid, fn->dil, fn->fc); } } @@ -1326,12 +1822,14 @@ void rpmfiFpLookup(rpmfi fi, fingerPrintCache fpc) */ #define RPMFI_ITERFUNC(TYPE, NAME, IXV) \ - TYPE rpmfi ## NAME(rpmfi fi) { return rpmfi ## NAME ## Index(fi, fi ? fi->IXV : -1); } + TYPE rpmfi ## NAME(rpmfi fi) { return rpmfiles ## NAME(fi ? fi->files : NULL, fi ? fi->IXV : -1); } RPMFI_ITERFUNC(rpmsid, BNId, i) RPMFI_ITERFUNC(rpmsid, DNId, j) RPMFI_ITERFUNC(const char *, BN, i) RPMFI_ITERFUNC(const char *, DN, j) +RPMFI_ITERFUNC(const char *, OBN, i) +RPMFI_ITERFUNC(const char *, ODN, j) RPMFI_ITERFUNC(const char *, FLink, i) RPMFI_ITERFUNC(const char *, FUser, i) RPMFI_ITERFUNC(const char *, FGroup, i) @@ -1354,41 +1852,545 @@ const char * rpmfiFN(rpmfi fi) const char *fn = ""; /* preserve behavior on errors */ if (fi != NULL) { free(fi->fn); - fi->fn = rpmfiFNIndex(fi, fi->i); + fi->fn = rpmfilesFN(fi->files, fi->i); if (fi->fn != NULL) fn = fi->fn; } return fn; } +const char * rpmfiOFN(rpmfi fi) +{ + const char *fn = ""; /* preserve behavior on errors */ + if (fi != NULL) { + free(fi->ofn); + fi->ofn = rpmfilesOFN(fi->files, fi->i); + if (fi->ofn != NULL) + fn = fi->ofn; + } + return fn; +} + const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *len) { - return rpmfiFDigestIndex(fi, fi ? fi->i : -1, algo, len); + return rpmfilesFDigest(fi->files, fi ? fi->i : -1, algo, len); +} + +const unsigned char * rpmfiFSignature(rpmfi fi, size_t *len) +{ + return rpmfilesFSignature(fi->files, fi ? fi->i : -1, len); } uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp) { - return rpmfiFDependsIndex(fi, fi ? fi->i : -1, fddictp); + return rpmfilesFDepends(fi->files, fi ? fi->i : -1, fddictp); } -int rpmfiCompare(const rpmfi afi, const rpmfi bfi) +int rpmfiStat(rpmfi fi, int flags, struct stat *sb) { - return rpmfiCompareIndex(afi, afi ? afi->i : -1, bfi, bfi ? bfi->i : -1); + return rpmfilesStat(fi->files, fi->i, flags, sb); } -rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing) +int rpmfiCompare(const rpmfi afi, const rpmfi bfi) { - return rpmfiDecideFateIndex(ofi, ofi ? ofi->i : -1, - nfi, nfi ? nfi->i : -1, - skipMissing); + return rpmfilesCompare(afi->files, afi ? afi->i : -1, bfi->files, bfi ? bfi->i : -1); } -int rpmfiConfigConflict(const rpmfi fi) +rpmVerifyAttrs rpmfiVerify(rpmfi fi, rpmVerifyAttrs omitMask) { - return rpmfiConfigConflictIndex(fi, fi ? fi->i : -1); + return rpmfilesVerify(fi->files, fi->i, omitMask); } -rpmstrPool rpmfiPool(rpmfi fi) +rpmstrPool rpmfilesPool(rpmfiles fi) { return (fi != NULL) ? fi->pool : NULL; } + +rpmfiles rpmfiFiles(rpmfi fi) +{ + return (fi != NULL) ? fi->files : NULL; +} + +/******************************************************/ +/*** Archive handling *********************************/ +/******************************************************/ + +rpmfi rpmfiNewArchiveReader(FD_t fd, rpmfiles files, int itype) +{ + rpmcpio_t archive = rpmcpioOpen(fd, O_RDONLY); + rpmfi fi = NULL; + if (archive && itype >= RPMFI_ITER_READ_ARCHIVE) { + fi = rpmfilesIter(files, itype); + } + if (fi) { + fi->archive = archive; + } else { + rpmcpioFree(archive); + } + return fi; +} + +rpmfi rpmfiNewArchiveWriter(FD_t fd, rpmfiles files) +{ + rpmcpio_t archive = rpmcpioOpen(fd, O_WRONLY); + rpmfi fi = NULL; + if (archive) { + fi = rpmfilesIter(files, RPMFI_ITER_WRITE_ARCHIVE); + } + if (fi) { + fi->archive = archive; + } else { + rpmcpioFree(archive); + } + return fi; +} + +int rpmfiArchiveClose(rpmfi fi) +{ + if (fi == NULL) + return -1; + int rc = rpmcpioClose(fi->archive); + return rc; +} + +rpm_loff_t rpmfiArchiveTell(rpmfi fi) +{ + if (fi == NULL || fi->archive == NULL) + return 0; + return (rpm_loff_t) rpmcpioTell(fi->archive); +} + +static int rpmfiArchiveWriteHeader(rpmfi fi) +{ + int rc; + struct stat st; + + if (rpmfiStat(fi, 0, &st)) + return -1; + + rpmfiles files = fi->files; + + if (files->lfsizes) { + return rpmcpioStrippedHeaderWrite(fi->archive, rpmfiFX(fi), st.st_size); + } else { + const char * dn = rpmfiDN(fi); + char * path = rstrscat(NULL, (dn[0] == '/' && !rpmExpandNumeric("%{_noPayloadPrefix}")) ? "." : "", + dn, rpmfiBN(fi), NULL); + rc = rpmcpioHeaderWrite(fi->archive, path, &st); + free(path); + } + + return rc; +} + +static int iterWriteArchiveNextFile(rpmfi fi) +{ + rpmfiles files = rpmfiFiles(fi); + int fx = rpmfiFX(fi); + int fc = rpmfiFC(fi); + const int * hardlinks; + int numHardlinks = 0; + + /* already processing hard linked files */ + if (rpmfiFNlink(fi) > 1) { + /* search next hard linked file */ + fi->i = -1; + for (int i=fx+1; i<fc; i++) { + /* no ghosts */ + if (rpmfilesFFlags(files, i) & RPMFILE_GHOST) + continue; + numHardlinks = rpmfilesFLinks(files, i, &hardlinks); + if (numHardlinks > 1 && hardlinks[0] == i) { + rpmfiSetFX(fi, i); + break; + } + } + } else { + fi->i = -1; + /* search next non hardlinked file */ + for (int i=fx+1; i<fc; i++) { + /* no ghosts */ + if (rpmfilesFFlags(files, i) & RPMFILE_GHOST) + continue; + if (rpmfilesFNlink(files, i) < 2) { + rpmfiSetFX(fi, i); + break; + } + } + if (rpmfiFX(fi) == -1) { + /* continue with first hard linked file */ + for (int i=0; i<fc; i++) { + /* no ghosts */ + if (rpmfilesFFlags(files, i) & RPMFILE_GHOST) + continue; + numHardlinks = rpmfilesFLinks(files, i, &hardlinks); + if (numHardlinks > 1) { + rpmfiSetFX(fi, i); + break; + } + } + } + } + if (rpmfiFX(fi) == -1) + return -1; + + /* write header(s) */ + if (numHardlinks>1) { + for (int i=0; i<numHardlinks; i++) { + rpmfiSetFX(fi, hardlinks[i]); + int rc = rpmfiArchiveWriteHeader(fi); + if (rc) { + return rc; + } + } + rpmfiSetFX(fi, hardlinks[0]); + } else { + int rc = rpmfiArchiveWriteHeader(fi); + if (rc) { + return rc; + } + } + return rpmfiFX(fi); +} + +static int iterWriteArchiveNext(rpmfi fi) +{ + int fx; + /* loop over the files we can handle ourself */ + do { + fx = iterWriteArchiveNextFile(fi); + if (S_ISLNK(rpmfiFMode(fi))) { + /* write symlink target */ + const char *lnk = rpmfiFLink(fi); + size_t len = strlen(lnk); + if (rpmfiArchiveWrite(fi, lnk, len) != len) { + return RPMERR_WRITE_FAILED; + } + } else if (S_ISREG(rpmfiFMode(fi)) && rpmfiFSize(fi)) { + /* this file actually needs some content */ + return fx; + } + /* go on for special files, directories and empty files */ + } while (fx >= 0); + return fx; +} + +size_t rpmfiArchiveWrite(rpmfi fi, const void * buf, size_t size) +{ + if (fi == NULL || fi->archive == NULL) + return -1; + return rpmcpioWrite(fi->archive, buf, size); +} + +int rpmfiArchiveWriteFile(rpmfi fi, FD_t fd) +{ + rpm_loff_t left; + int rc = 0; + size_t len; + char buf[BUFSIZ*4]; + + if (fi == NULL || fi->archive == NULL || fd == NULL) + return -1; + + left = rpmfiFSize(fi); + + while (left) { + len = (left > sizeof(buf) ? sizeof(buf) : left); + if (Fread(buf, sizeof(*buf), len, fd) != len || Ferror(fd)) { + rc = RPMERR_READ_FAILED; + break; + } + + if (rpmcpioWrite(fi->archive, buf, len) != len) { + rc = RPMERR_WRITE_FAILED; + break; + } + left -= len; + } + return rc; +} + +static void rpmfiSetFound(rpmfi fi, int ix) +{ + fi->found[ix >> 3] |= (1 << (ix % 8)); +} + +static int rpmfiFound(rpmfi fi, int ix) +{ + return fi->found[ix >> 3] & (1 << (ix % 8)); +} + +static int iterReadArchiveNext(rpmfi fi) +{ + int rc; + int fx = -1; + int fc = rpmfilesFC(fi->files); + char * path = NULL; + + if (fi->archive == NULL) + return -1; + + /* Read next payload header. */ + rc = rpmcpioHeaderRead(fi->archive, &path, &fx); + + /* if archive ended, check if we found all files */ + if (rc == RPMERR_ITER_END) { + for (int i=0; i<fc; i++) { + if (!rpmfiFound(fi, i) && + !(rpmfilesFFlags(fi->files, i) & RPMFILE_GHOST)) { + rc = RPMERR_MISSING_FILE; + break; + } + } + } + if (rc) { + return rc; + } + + if (path) { + /* Regular cpio archive, identify mapping index. */ + fx = rpmfilesFindOFN(fi->files, path); + free(path); + } + + if (fx >= 0 && fx < fc) { + rpm_loff_t fsize = 0; + rpm_mode_t mode = rpmfilesFMode(fi->files, fx); + + /* %ghost in payload, should not be there but rpm < 4.11 sometimes did this */ + if (rpmfilesFFlags(fi->files, fx) & RPMFILE_GHOST) + return RPMERR_ITER_SKIP; + + if (S_ISREG(mode)) { + const int * links; + uint32_t numlinks = rpmfilesFLinks(fi->files, fx, &links); + if (!(numlinks > 1 && links[numlinks-1] != fx)) + fsize = rpmfilesFSize(fi->files, fx); + } else if (S_ISLNK(mode)) { + /* Skip over symlink target data in payload */ + rpm_loff_t lsize = rpmfilesFSize(fi->files, fx); + char *buf = xmalloc(lsize + 1); + if (rpmcpioRead(fi->archive, buf, lsize) != lsize) + rc = RPMERR_READ_FAILED; + /* XXX should we validate the payload matches? */ + free(buf); + } + rpmcpioSetExpectedFileSize(fi->archive, fsize); + rpmfiSetFound(fi, fx); + } else { + /* Mapping error */ + rc = RPMERR_UNMAPPED_FILE; + } + return (rc != 0) ? rc : fx; +} + + +static int iterReadArchiveNextOmitHardlinks(rpmfi fi) +{ + int fx; + const int * links; + int nlink; + do { + fx = iterReadArchiveNext(fi); + nlink = rpmfilesFLinks(fi->files, fx, &links); + } while (fx>=0 && nlink>1 && links[nlink-1]!=fx); + return fx; +} + +static int iterReadArchiveNextContentFirst(rpmfi fi) +{ + int fx = rpmfiFX(fi); + const int * links; + int nlink; + /* decide what to do on the basis of the last entry */ + nlink = rpmfilesFLinks(fi->files, fx, &links); + if (nlink > 1) { + /* currently reading through hard links */ + if (fx == links[nlink-1]) { + /* arrived back at last entry, read on */ + fx = iterReadArchiveNext(fi); + } else { + /* next hard link */ + /* scales poorly but shouldn't matter */ + for (int i=0; i<nlink; i++) { + if (links[i] == fx) { + fx = links[i+1]; + return fx; + } + } + /* should never happen */ + return -1; + } + } else { + fx = iterReadArchiveNext(fi); + } + + /* look at the new entry */ + nlink = rpmfilesFLinks(fi->files, fx, &links); + /* arrived at new set of hardlinks? */ + if (nlink > 1) { + /* read over all entries to the last one (containing the content) */ + do { + fx = iterReadArchiveNext(fi); + } while (fx >=0 && fx != links[nlink-1]); + /* rewind to the first entry */ + if (fx >= 0) { + fx = links[0]; + } + } + return fx; +} + +int rpmfiArchiveHasContent(rpmfi fi) +{ + int res = 0; + if (fi && S_ISREG(rpmfiFMode(fi))) { + const int * links; + int nlink = rpmfiFLinks(fi, &links); + if (nlink > 1) { + if (fi->next == iterReadArchiveNext || + fi->next == iterReadArchiveNextOmitHardlinks) { + res = rpmfiFX(fi) == links[nlink-1]; + } else if (fi->next == iterReadArchiveNextContentFirst) { + res = rpmfiFX(fi) == links[0]; + } + } else { + res = 1; + } + } + return res; +} + +size_t rpmfiArchiveRead(rpmfi fi, void * buf, size_t size) +{ + if (fi == NULL || fi->archive == NULL) + return -1; + return rpmcpioRead(fi->archive, buf, size); +} + +int rpmfiArchiveReadToFilePsm(rpmfi fi, FD_t fd, int nodigest, rpmpsm psm) +{ + if (fi == NULL || fi->archive == NULL || fd == NULL) + return -1; + + rpm_loff_t left = rpmfiFSize(fi); + const unsigned char * fidigest = NULL; + pgpHashAlgo digestalgo = 0; + int rc = 0; + char buf[BUFSIZ*4]; + + if (!nodigest) { + digestalgo = rpmfiDigestAlgo(fi); + fidigest = rpmfilesFDigest(fi->files, rpmfiFX(fi), NULL, NULL); + fdInitDigest(fd, digestalgo, 0); + } + + while (left) { + size_t len; + len = (left > sizeof(buf) ? sizeof(buf) : left); + if (rpmcpioRead(fi->archive, buf, len) != len) { + rc = RPMERR_READ_FAILED; + goto exit; + } + if ((Fwrite(buf, sizeof(*buf), len, fd) != len) || Ferror(fd)) { + rc = RPMERR_WRITE_FAILED; + goto exit; + } + + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi)); + left -= len; + } + + if (!nodigest) { + void * digest = NULL; + + (void) Fflush(fd); + fdFiniDigest(fd, digestalgo, &digest, NULL, 0); + + if (digest != NULL && fidigest != NULL) { + size_t diglen = rpmDigestLength(digestalgo); + if (memcmp(digest, fidigest, diglen)) { + rc = RPMERR_DIGEST_MISMATCH; + + /* ...but in old packages, empty files have zeros for digest */ + if (rpmfiFSize(fi) == 0 && digestalgo == PGPHASHALGO_MD5) { + uint8_t zeros[diglen]; + memset(&zeros, 0, diglen); + if (memcmp(zeros, fidigest, diglen) == 0) + rc = 0; + } + } + } else { + rc = RPMERR_DIGEST_MISMATCH; + } + free(digest); + } + +exit: + return rc; +} + +int rpmfiArchiveReadToFile(rpmfi fi, FD_t fd, int nodigest) +{ + return rpmfiArchiveReadToFilePsm(fi, fd, nodigest, NULL); +} + +char * rpmfileStrerror(int rc) +{ + char *msg = NULL; + const char *s = NULL; + const char *prefix = "cpio"; + int myerrno = errno; + + switch (rc) { + default: + break; + case RPMERR_BAD_MAGIC: s = _("Bad magic"); break; + case RPMERR_BAD_HEADER: s = _("Bad/unreadable header");break; + + case RPMERR_OPEN_FAILED: s = "open"; break; + case RPMERR_CHMOD_FAILED: s = "chmod"; break; + case RPMERR_CHOWN_FAILED: s = "chown"; break; + case RPMERR_WRITE_FAILED: s = "write"; break; + case RPMERR_UTIME_FAILED: s = "utime"; break; + case RPMERR_UNLINK_FAILED: s = "unlink"; break; + case RPMERR_RENAME_FAILED: s = "rename"; break; + case RPMERR_SYMLINK_FAILED: s = "symlink"; break; + case RPMERR_STAT_FAILED: s = "stat"; break; + case RPMERR_LSTAT_FAILED: s = "lstat"; break; + case RPMERR_MKDIR_FAILED: s = "mkdir"; break; + case RPMERR_RMDIR_FAILED: s = "rmdir"; break; + case RPMERR_MKNOD_FAILED: s = "mknod"; break; + case RPMERR_MKFIFO_FAILED: s = "mkfifo"; break; + case RPMERR_LINK_FAILED: s = "link"; break; + case RPMERR_READLINK_FAILED: s = "readlink"; break; + case RPMERR_READ_FAILED: s = "read"; break; + case RPMERR_COPY_FAILED: s = "copy"; break; + case RPMERR_LSETFCON_FAILED: s = "lsetfilecon"; break; + case RPMERR_SETCAP_FAILED: s = "cap_set_file"; break; + + case RPMERR_HDR_SIZE: s = _("Header size too big"); break; + case RPMERR_FILE_SIZE: s = _("File too large for archive"); break; + case RPMERR_UNKNOWN_FILETYPE: s = _("Unknown file type"); break; + case RPMERR_MISSING_FILE: s = _("Missing file(s)"); break; + case RPMERR_DIGEST_MISMATCH: s = _("Digest mismatch"); break; + case RPMERR_INTERNAL: s = _("Internal error"); break; + case RPMERR_UNMAPPED_FILE: s = _("Archive file not in header"); break; + case RPMERR_ENOENT: s = strerror(ENOENT); break; + case RPMERR_ENOTEMPTY: s = strerror(ENOTEMPTY); break; + case RPMERR_EXIST_AS_DIR: + s = _("File from package already exists as a directory in system"); + break; + } + + if (s != NULL) { + rasprintf(&msg, "%s: %s", prefix, s); + if ((rc <= RPMERR_CHECK_ERRNO) && myerrno) { + rstrscat(&msg, _(" failed - "), strerror(myerrno), NULL); + } + } else { + rasprintf(&msg, _("%s: (error 0x%x)"), prefix, rc); + } + + return msg; +} diff --git a/lib/rpmfi.h b/lib/rpmfi.h index b77c82895..cb22acae7 100644 --- a/lib/rpmfi.h +++ b/lib/rpmfi.h @@ -1,289 +1,248 @@ #ifndef H_RPMFI #define H_RPMFI -/** \ingroup rpmdep rpmtrans +/** \ingroup rpmfi * \file lib/rpmfi.h - * Structure(s) used for file info tag sets. + * File info set iterator API. */ #include <rpm/rpmtypes.h> -#include <rpm/rpmvf.h> -#include <rpm/rpmpgp.h> +#include <rpm/rpmfiles.h> +#include <rpm/rpmarchive.h> #ifdef __cplusplus extern "C" { #endif /** \ingroup rpmfi - * File types. - * These are the file types used internally by rpm. The file - * type is determined by applying stat(2) macros like S_ISDIR to - * the file mode tag from a header. The values are arbitrary, - * but are identical to the linux stat(2) file types. - */ -typedef enum rpmFileTypes_e { - PIPE = 1, /*!< pipe/fifo */ - CDEV = 2, /*!< character device */ - XDIR = 4, /*!< directory */ - BDEV = 6, /*!< block device */ - REG = 8, /*!< regular file */ - LINK = 10, /*!< hard link */ - SOCK = 12 /*!< socket */ -} rpmFileTypes; - -/** - * File States (when installed). - */ -typedef enum rpmfileState_e { - RPMFILE_STATE_MISSING = -1, /* used for unavailable data */ - RPMFILE_STATE_NORMAL = 0, - RPMFILE_STATE_REPLACED = 1, - RPMFILE_STATE_NOTINSTALLED = 2, - RPMFILE_STATE_NETSHARED = 3, - RPMFILE_STATE_WRONGCOLOR = 4 -} rpmfileState; - -#define RPMFILE_IS_INSTALLED(_x) ((_x) == RPMFILE_STATE_NORMAL || (_x) == RPMFILE_STATE_NETSHARED) - -/** - * Exported File Attributes (ie RPMTAG_FILEFLAGS) - */ -enum rpmfileAttrs_e { - RPMFILE_NONE = 0, - RPMFILE_CONFIG = (1 << 0), /*!< from %%config */ - RPMFILE_DOC = (1 << 1), /*!< from %%doc */ - RPMFILE_ICON = (1 << 2), /*!< from %%donotuse. */ - RPMFILE_MISSINGOK = (1 << 3), /*!< from %%config(missingok) */ - RPMFILE_NOREPLACE = (1 << 4), /*!< from %%config(noreplace) */ - RPMFILE_SPECFILE = (1 << 5), /*!< @todo (unnecessary) marks 1st file in srpm. */ - RPMFILE_GHOST = (1 << 6), /*!< from %%ghost */ - RPMFILE_LICENSE = (1 << 7), /*!< from %%license */ - RPMFILE_README = (1 << 8), /*!< from %%readme */ - /* bits 9-10 unused */ - RPMFILE_PUBKEY = (1 << 11), /*!< from %%pubkey */ - RPMFILE_SECMANIFEST = (1 << 12), /*!< from %%manifest */ -}; - -typedef rpmFlags rpmfileAttrs; - -#define RPMFILE_ALL ~(RPMFILE_NONE) - -/** \ingroup rpmfi - * File disposition(s) during package install/erase transaction. - */ -typedef enum rpmFileAction_e { - FA_UNKNOWN = 0, /*!< initial action for file ... */ - FA_CREATE, /*!< ... copy in from payload. */ - FA_COPYIN, /*!< ... copy in from payload. */ - FA_COPYOUT, /*!< ... copy out to payload. */ - FA_BACKUP, /*!< ... renamed with ".rpmorig" extension. */ - FA_SAVE, /*!< ... renamed with ".rpmsave" extension. */ - FA_SKIP, /*!< ... already replaced, don't remove. */ - FA_ALTNAME, /*!< ... create with ".rpmnew" extension. */ - FA_ERASE, /*!< ... to be removed. */ - FA_SKIPNSTATE, /*!< ... untouched, state "not installed". */ - FA_SKIPNETSHARED, /*!< ... untouched, state "netshared". */ - FA_SKIPCOLOR /*!< ... untouched, state "wrong color". */ -} rpmFileAction; - -#define XFA_SKIPPING(_a) \ - ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPCOLOR) - -/** - * We pass these around as an array with a sentinel. - */ -struct rpmRelocation_s { - char * oldPath; /*!< NULL here evals to RPMTAG_DEFAULTPREFIX, */ - char * newPath; /*!< NULL means to omit the file completely! */ -}; - -/** \ingroup rpmfi - * Reference a file info set instance. - * @param fi file info set - * @return new file info set reference + + * Reference a file info set iterator instance. + * @param fi file info set iterator + * @return new file info set iterator reference */ rpmfi rpmfiLink (rpmfi fi); /** \ingroup rpmfi - * Return file count from file info set. - * @param fi file info set + * Return file count from file info set iterator. + * @param fi file info set iterator * @return current file count */ rpm_count_t rpmfiFC(rpmfi fi); /** \ingroup rpmfi - * Return current file index from file info set. - * @param fi file info set + * Return current file index from file info set iterator. + * @param fi file info set iterator * @return current file index */ int rpmfiFX(rpmfi fi); /** \ingroup rpmfi - * Set current file index in file info set. - * @param fi file info set + * Set current file index in file info set iterator. + * @param fi file info set iterator * @param fx new file index * @return current file index */ int rpmfiSetFX(rpmfi fi, int fx); /** \ingroup rpmfi - * Return directory count from file info set. - * @param fi file info set + * Return directory count from file info set iterator. + * @param fi file info set iterator * @return current directory count */ rpm_count_t rpmfiDC(rpmfi fi); /** \ingroup rpmfi - * Return current directory index from file info set. - * @param fi file info set + * Return current directory index from file info set iterator. + * @param fi file info set iterator * @return current directory index */ int rpmfiDX(rpmfi fi); /** \ingroup rpmfi - * Set current directory index in file info set. - * @param fi file info set + * Set current directory index in file info set iterator. + * @param fi file info set iterator * @param dx new directory index * @return current directory index */ int rpmfiSetDX(rpmfi fi, int dx); /** \ingroup rpmfi - * Return current base name from file info set. - * @param fi file info set + * Return current base name from file info set iterator. + * @param fi file info set iterator * @return current base name, NULL on invalid */ const char * rpmfiBN(rpmfi fi); /** \ingroup rpmfi - * Return current directory name from file info set. - * @param fi file info set + * Return current directory name from file info set iterator. + * @param fi file info set iterator * @return current directory, NULL on invalid */ const char * rpmfiDN(rpmfi fi); /** \ingroup rpmfi - * Return current file name from file info set. - * @param fi file info set + * Return current file name from file info set iterator. + * @param fi file info set iterator * @return current file name */ const char * rpmfiFN(rpmfi fi); /** \ingroup rpmfi - * Return current file flags from file info set. - * @param fi file info set + * Return file index of the given file name or -1 if file is not in the rpmfi. + * The file name may have "." prefixed but is then interpreted as a global + * path without the prefixing "." + * @param fi file info set iterator + * @param fn file name + * @return file index or -1 + */ +int rpmfiFindFN(rpmfi fi, const char * fn); + +/** \ingroup rpmfi + * Return current original base name from file info set iterator. + * @param fi file info set iterator + * @return current base name, NULL on invalid + */ +const char * rpmfiOBN(rpmfi fi); + +/** \ingroup rpmfi + * Return current original directory name from file info set iterator. + * @param fi file info set iterator + * @return current directory, NULL on invalid + */ +const char * rpmfiODN(rpmfi fi); + +/** \ingroup rpmfi + * Return current original file name from file info set iterator. + * @param fi file info set iterator + * @return current file name + */ +const char * rpmfiOFN(rpmfi fi); + +/** \ingroup rpmfi + * Return file index of the given original file name or -1 if file is not + * in the rpmfi. The file name may have "." prefixed but is then interpreted + * as a global path without the prefixing "." + * @param fi file info set iterator + * @param fn file name + * @return file index or -1 + */ +int rpmfiFindOFN(rpmfi fi, const char * fn); + +/** \ingroup rpmfi + * Return current file flags from file info set iterator. + * @param fi file info set iterator * @return current file flags, 0 on invalid */ rpmfileAttrs rpmfiFFlags(rpmfi fi); /** \ingroup rpmfi - * Return current file verify flags from file info set. - * @param fi file info set + * Return current file verify flags from file info set iterator. + * @param fi file info set iterator * @return current file verify flags, 0 on invalid */ rpmVerifyAttrs rpmfiVFlags(rpmfi fi); /** \ingroup rpmfi - * Return current file mode from file info set. - * @param fi file info set + * Return current file mode from file info set iterator. + * @param fi file info set iterator * @return current file mode, 0 on invalid */ rpm_mode_t rpmfiFMode(rpmfi fi); /** \ingroup rpmfi - * Return current file state from file info set. - * @param fi file info set + * Return current file state from file info set iterator. + * @param fi file info set iterator * @return current file state, 0 on invalid */ rpmfileState rpmfiFState(rpmfi fi); /** \ingroup rpmfi - * Return digest algorithm of a file info set. - * @param fi file info set - * @return digest algorithm of file info set, 0 on invalid + * Return digest algorithm of a file info set iterator. + * @param fi file info set iterator + * @return digest algorithm of file info set iterator, 0 on invalid */ int rpmfiDigestAlgo(rpmfi fi); /** \ingroup rpmfi - * Return current file (binary) digest of file info set. - * @param fi file info set - * @retval algo digest hash algoritm used (pass NULL to ignore) + * Return current file (binary) digest of file info set iterator. + * @param fi file info set iterator + * @retval algo digest hash algorithm used (pass NULL to ignore) * @retval diglen digest hash length (pass NULL to ignore) * @return current file digest, NULL on invalid */ const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *diglen); /** \ingroup rpmfi - * Return current file (hex) digest of file info set. - * The file info set stores file digests in binary format to conserve + * Return current file (hex) digest of file info set iterator. + * The file info set iterator stores file digests in binary format to conserve * memory, this converts the binary data back to hex presentation used in * headers. - * @param fi file info set - * @retval algo digest hash algoritm used (pass NULL to ignore) + * @param fi file info set iterator + * @retval algo digest hash algorithm used (pass NULL to ignore) * @return current file digest (malloc'ed), NULL on invalid */ char * rpmfiFDigestHex(rpmfi fi, int *algo); /** \ingroup rpmfi - * Return current file (binary) md5 digest from file info set. - * @deprecated Use rpmfiFDigest() instead - * @param fi file info set - * @return current file md5 digest, NULL on invalid + * Return current file (binary) signature of file info set iterator. + * @param fi file info set iterator + * @retval siglen signature length (pass NULL to ignore) + * @return current file signature, NULL on invalid */ -const unsigned char * rpmfiMD5(rpmfi fi) RPM_GNUC_DEPRECATED; +const unsigned char * rpmfiFSignature(rpmfi fi, size_t *siglen); /** \ingroup rpmfi - * Return current file linkto (i.e. symlink(2) target) from file info set. - * @param fi file info set + * Return current file linkto (i.e. symlink(2) target) from file info set iterator. + * @param fi file info set iterator * @return current file linkto, NULL on invalid */ const char * rpmfiFLink(rpmfi fi); /** \ingroup rpmfi - * Return current file size from file info set. - * @param fi file info set + * Return current file size from file info set iterator. + * @param fi file info set iterator * @return current file size, 0 on invalid */ rpm_loff_t rpmfiFSize(rpmfi fi); /** \ingroup rpmfi - * Return current file rdev from file info set. - * @param fi file info set + * Return current file rdev from file info set iterator. + * @param fi file info set iterator * @return current file rdev, 0 on invalid */ rpm_rdev_t rpmfiFRdev(rpmfi fi); /** \ingroup rpmfi - * Return current file inode from file info set. - * @param fi file info set + * Return current file inode from file info set iterator. + * @param fi file info set iterator * @return current file inode, 0 on invalid */ rpm_ino_t rpmfiFInode(rpmfi fi); /** \ingroup rpmfi - * Return union of all file color bits from file info set. - * @param fi file info set + * Return union of all file color bits from file info set iterator. + * @param fi file info set iterator * @return current color */ rpm_color_t rpmfiColor(rpmfi fi); /** \ingroup rpmfi - * Return current file color bits from file info set. - * @param fi file info set + * Return current file color bits from file info set iterator. + * @param fi file info set iterator * @return current file color */ rpm_color_t rpmfiFColor(rpmfi fi); /** \ingroup rpmfi - * Return current file class from file info set. - * @param fi file info set + * Return current file class from file info set iterator. + * @param fi file info set iterator * @return current file class, 0 on invalid */ const char * rpmfiFClass(rpmfi fi); /** \ingroup rpmfi - * Return current file depends dictionary from file info set. - * @param fi file info set + * Return current file depends dictionary from file info set iterator. + * @param fi file info set iterator * @retval *fddictp file depends dictionary array (or NULL) * @return no. of file depends entries, 0 on invalid */ @@ -291,133 +250,121 @@ uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp); /** \ingroup rpmfi - * Return (calculated) current file nlink count from file info set. - * @param fi file info set + * Return (calculated) current file nlink count from file info set iterator. + * @param fi file info set iterator * @return current file nlink count, 0 on invalid */ uint32_t rpmfiFNlink(rpmfi fi); + +/** \ingroup rpmfi + * Return (calculated) current file nlink count from file info set iterator. + * @param fi file info set iterator + * @param files returns array of file ids hardlinked including ix, + NULL for nlink count == 1 + * @return current file nlink count, 0 on invalid + */ +uint32_t rpmfiFLinks(rpmfi fi, const int ** files); + /** \ingroup rpmfi - * Return current file modify time from file info set. - * @param fi file info set + * Return current file modify time from file info set iterator. + * @param fi file info set iterator * @return current file modify time, 0 on invalid */ rpm_time_t rpmfiFMtime(rpmfi fi); /** \ingroup rpmfi - * Return current file owner from file info set. - * @param fi file info set + * Return current file owner from file info set iterator. + * @param fi file info set iterator * @return current file owner, NULL on invalid */ const char * rpmfiFUser(rpmfi fi); /** \ingroup rpmfi - * Return current file group from file info set. - * @param fi file info set + * Return current file group from file info set iterator. + * @param fi file info set iterator * @return current file group, NULL on invalid */ const char * rpmfiFGroup(rpmfi fi); /** \ingroup rpmfi * Return textual representation of current file capabilities - * from file info set. See cap_from_text(3) for details. - * @param fi file info set + * from file info set iterator. See cap_from_text(3) for details. + * @param fi file info set iterator * @return file capability description, "" for no capabilities * and NULL on invalid */ const char * rpmfiFCaps(rpmfi fi); /** \ingroup rpmfi - * Return current file language(s) from file info set. - * @param fi file info set + * Return current file language(s) from file info set iterator. + * @param fi file info set iterator * @return current file language(s), NULL on invalid */ const char * rpmfiFLangs(rpmfi fi); /** \ingroup rpmfi + * Map file stat(2) info. + * @param fi file info iterator + * @param flags flags + * @retval sb mapped stat(2) data + */ +int rpmfiStat(rpmfi fi, int flags, struct stat *sb); + +/** \ingroup rpmfi * Return next file iterator index. - * @param fi file info set + * @param fi file info set iterator * @return file iterator index, -1 on termination */ int rpmfiNext(rpmfi fi); /** \ingroup rpmfi * Initialize file iterator index. - * @param fi file info set + * @param fi file info set iterator * @param fx file iterator index - * @return file info set + * @return file info set iterator */ rpmfi rpmfiInit(rpmfi fi, int fx); /** \ingroup rpmfi * Return next directory iterator index. - * @param fi file info set + * @param fi file info set iterator * @return directory iterator index, -1 on termination */ int rpmfiNextD(rpmfi fi); /** \ingroup rpmfi * Initialize directory iterator index. - * @param fi file info set + * @param fi file info set iterator * @param dx directory iterator index - * @return file info set, NULL if dx is out of range + * @return file info set iterator, NULL if dx is out of range */ rpmfi rpmfiInitD(rpmfi fi, int dx); /** \ingroup rpmfi - * Destroy a file info set. - * @param fi file info set + * Destroy a file info set iterator. + * @param fi file info set iterator * @return NULL always */ rpmfi rpmfiFree(rpmfi fi); -enum rpmfiFlags_e { - RPMFI_NOHEADER = 0, - RPMFI_KEEPHEADER = (1 << 0), - RPMFI_NOFILECLASS = (1 << 1), - RPMFI_NOFILEDEPS = (1 << 2), - RPMFI_NOFILELANGS = (1 << 3), - RPMFI_NOFILEUSER = (1 << 4), - RPMFI_NOFILEGROUP = (1 << 5), - RPMFI_NOFILEMODES = (1 << 6), - RPMFI_NOFILESIZES = (1 << 7), - RPMFI_NOFILECAPS = (1 << 8), - RPMFI_NOFILELINKTOS = (1 << 9), - RPMFI_NOFILEDIGESTS = (1 << 10), - RPMFI_NOFILEMTIMES = (1 << 11), - RPMFI_NOFILERDEVS = (1 << 12), - RPMFI_NOFILEINODES = (1 << 13), - RPMFI_NOFILESTATES = (1 << 14), - RPMFI_NOFILECOLORS = (1 << 15), - RPMFI_NOFILEVERIFYFLAGS = (1 << 16), - RPMFI_NOFILEFLAGS = (1 << 17), -}; - -typedef rpmFlags rpmfiFlags; - -#define RPMFI_FLAGS_ERASE \ - (RPMFI_NOFILECLASS | RPMFI_NOFILELANGS | \ - RPMFI_NOFILEMTIMES | RPMFI_NOFILERDEVS | RPMFI_NOFILEINODES | \ - RPMFI_NOFILEVERIFYFLAGS) - -#define RPMFI_FLAGS_INSTALL \ - (RPMFI_NOFILECLASS | RPMFI_NOFILEVERIFYFLAGS) - -#define RPMFI_FLAGS_VERIFY \ - (RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \ - RPMFI_NOFILECOLORS) - -#define RPMFI_FLAGS_QUERY \ - (RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \ - RPMFI_NOFILECOLORS | RPMFI_NOFILEVERIFYFLAGS) - -/** \ingroup rpmfi - * Create and load a file info set. +/** \ingroup rpmfi + * Create and load a file info set iterator. + * @param pool shared string pool (or NULL for private pool) + * @param h header + * @param tagN unused + * @param flags Flags to control what information is loaded. + * @return new file info set iterator + */ +rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags); + +/** \ingroup rpmfi + * Create and load a file info set iterator. * @param ts unused * @param h header * @param tagN unused * @param flags Flags to control what information is loaded. - * @return new file info set + * @return new file info set iterator */ rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags); @@ -437,20 +384,12 @@ rpmFileTypes rpmfiWhatis(rpm_mode_t mode); int rpmfiCompare(const rpmfi afi, const rpmfi bfi); /** \ingroup rpmfi - * Return file disposition. - * @param ofi old file info - * @param nfi new file info - * @param skipMissing OK to skip missing files? - * @return file dispostion - */ -rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing); - -/** \ingroup rpmfi - * Return whether file is conflicting config - * @param fi file info - * @return 1 if config file and file on disk conflicts + * Verify file attributes (including digest). + * @param fi file info iterator + * @param omitMask bit(s) to disable verify checks + * @return bit(s) to indicate failure (ie 0 for passed verify) */ -int rpmfiConfigConflict(const rpmfi fi); +rpmVerifyAttrs rpmfiVerify(rpmfi fi, rpmVerifyAttrs omitMask); #ifdef __cplusplus } diff --git a/lib/rpmfi_internal.h b/lib/rpmfi_internal.h index 19484ec5a..dccc6ccbe 100644 --- a/lib/rpmfi_internal.h +++ b/lib/rpmfi_internal.h @@ -5,91 +5,21 @@ #include <rpm/rpmfi.h> #include <rpm/rpmstrpool.h> #include "lib/fprint.h" +#include "lib/cpio.h" #define RPMFIMAGIC 0x09697923 -/** - * A package filename set. - */ -struct rpmfi_s { - int i; /*!< Current file index. */ - int j; /*!< Current directory index. */ - - Header h; /*!< Header for file info set (or NULL) */ - rpmstrPool pool; /*!< String pool of this file info set */ - - rpmsid * bnid; /*!< Index to base name(s) (pool) */ - rpmsid * dnid; /*!< Index to directory name(s) (pool) */ - - rpmsid * flinks; /*!< Index to file link(s) (pool) */ - - uint32_t * dil; /*!< Directory indice(s) (from header) */ - rpm_flag_t * fflags; /*!< File flag(s) (from header) */ - rpm_off_t * fsizes; /*!< File size(s) (from header) */ - rpm_time_t * fmtimes; /*!< File modification time(s) (from header) */ - rpm_mode_t * fmodes; /*!< File mode(s) (from header) */ - rpm_rdev_t * frdevs; /*!< File rdev(s) (from header) */ - rpm_ino_t * finodes; /*!< File inodes(s) (from header) */ - - rpmsid * fuser; /*!< Index to file owner(s) (misc pool) */ - rpmsid * fgroup; /*!< Index to file group(s) (misc pool) */ - rpmsid * flangs; /*!< Index to file lang(s) (misc pool) */ - - char * fstates; /*!< File state(s) (from header) */ - - rpm_color_t * fcolors; /*!< File color bits (header) */ - char ** fcaps; /*!< File capability strings (header) */ - - char ** cdict; /*!< File class dictionary (header) */ - rpm_count_t ncdict; /*!< No. of class entries. */ - uint32_t * fcdictx; /*!< File class dictionary index (header) */ - - uint32_t * ddict; /*!< File depends dictionary (header) */ - rpm_count_t nddict; /*!< No. of depends entries. */ - uint32_t * fddictx; /*!< File depends dictionary start (header) */ - uint32_t * fddictn; /*!< File depends dictionary count (header) */ - rpm_flag_t * vflags; /*!< File verify flag(s) (from header) */ - - rpm_count_t dc; /*!< No. of directories. */ - rpm_count_t fc; /*!< No. of files. */ - - rpmfiFlags fiflags; /*!< file info set control flags */ - - struct fingerPrint_s * fps; /*!< File fingerprint(s). */ - - int digestalgo; /*!< File digest algorithm */ - unsigned char * digests; /*!< File digests in binary. */ - - char * fn; /*!< File name buffer. */ - - char ** apath; - rpm_off_t * replacedSizes; /*!< (TR_ADDED) */ - int magic; - int nrefs; /*!< Reference count. */ -}; - #ifdef __cplusplus extern "C" { #endif /** \ingroup rpmfi - * Create and load a file info set. - * @param pool shared string pool (or NULL for private pool) - * @param h header - * @param tagN unused - * @param flags Flags to control what information is loaded. - * @return new file info set - */ -RPM_GNUC_INTERNAL -rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags); - -/** \ingroup rpmfi * Return file info set string pool handle * @param fi file info * @return string pool handle (weak reference) */ RPM_GNUC_INTERNAL -rpmstrPool rpmfiPool(rpmfi fi); +rpmstrPool rpmfilesPool(rpmfiles fi); /** \ingroup rpmfi * Return current base name pool id from file info set. @@ -108,99 +38,75 @@ RPM_GNUC_INTERNAL rpmsid rpmfiDNId(rpmfi fi); RPM_GNUC_INTERNAL -int rpmfiDIIndex(rpmfi fi, int dx); - -RPM_GNUC_INTERNAL -rpmsid rpmfiBNIdIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpmsid rpmfiDNIdIndex(rpmfi fi, int jx); - -RPM_GNUC_INTERNAL -const char * rpmfiBNIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -const char * rpmfiDNIndex(rpmfi fi, int jx); - -RPM_GNUC_INTERNAL -char * rpmfiFNIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpmVerifyAttrs rpmfiVFlagsIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpmfileState rpmfiFStateIndex(rpmfi fi, int ix); +rpmsid rpmfilesBNId(rpmfiles fi, int ix); RPM_GNUC_INTERNAL -const char * rpmfiFLinkIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpm_loff_t rpmfiFSizeIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpm_color_t rpmfiFColorIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -const char * rpmfiFClassIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -uint32_t rpmfiFDependsIndex(rpmfi fi, int ix, const uint32_t ** fddictp); - -RPM_GNUC_INTERNAL -uint32_t rpmfiFNlinkIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -const char * rpmfiFLangsIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpmfileAttrs rpmfiFFlagsIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -rpm_mode_t rpmfiFModeIndex(rpmfi fi, int ix); - -RPM_GNUC_INTERNAL -const unsigned char * rpmfiFDigestIndex(rpmfi fi, int ix, int *algo, size_t *len); +rpmsid rpmfilesDNId(rpmfiles fi, int jx); +/** \ingroup rpmfi + * Return current original base name pool id from file info set. + * @param fi file info set + * @return current base name id, 0 on invalid + */ RPM_GNUC_INTERNAL -rpm_rdev_t rpmfiFRdevIndex(rpmfi fi, int ix); +rpmsid rpmfiOBNId(rpmfi fi); +/** \ingroup rpmfi + * Return current original directory name pool id from file info set. + * @param fi file info set + * @return current base name id, 0 on invalid + */ RPM_GNUC_INTERNAL -rpm_ino_t rpmfiFInodeIndex(rpmfi fi, int ix); +rpmsid rpmfiODNId(rpmfi fi); RPM_GNUC_INTERNAL -rpm_time_t rpmfiFMtimeIndex(rpmfi fi, int ix); +rpmsid rpmfilesOBNId(rpmfiles fi, int ix); RPM_GNUC_INTERNAL -const char * rpmfiFUserIndex(rpmfi fi, int ix); +rpmsid rpmfilesODNId(rpmfiles fi, int jx); RPM_GNUC_INTERNAL -const char * rpmfiFGroupIndex(rpmfi fi, int ix); +struct fingerPrint_s *rpmfilesFps(rpmfiles fi); +/** \ingroup rpmfi + * Check if the file in new package, in old package and on the disk have the same contents. + * @param new file info set + * @param new file index + * @param old file info set + * @param old file index + * @return 1 if the condition is satisfied, 0 otherwise + */ RPM_GNUC_INTERNAL -const char * rpmfiFCapsIndex(rpmfi fi, int ix); +int rpmfileContentsEqual(rpmfiles ofi, int oix, rpmfiles nfi, int nix); -RPM_GNUC_INTERNAL -struct fingerPrint_s *rpmfiFps(rpmfi fi); RPM_GNUC_INTERNAL -rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix, +rpmFileAction rpmfilesDecideFate(rpmfiles ofi, int oix, + rpmfiles nfi, int nix, int skipMissing); RPM_GNUC_INTERNAL -int rpmfiCompareIndex(rpmfi afi, int aix, rpmfi bfi, int bix); +int rpmfilesConfigConflict(rpmfiles fi, int ix); RPM_GNUC_INTERNAL -int rpmfiConfigConflictIndex(rpmfi fi, int ix); +void rpmfilesSetFReplacedSize(rpmfiles fi, int ix, rpm_loff_t newsize); RPM_GNUC_INTERNAL -void rpmfiSetFReplacedSizeIndex(rpmfi fi, int ix, rpm_loff_t newsize); +rpm_loff_t rpmfilesFReplacedSize(rpmfiles fi, int ix); RPM_GNUC_INTERNAL -rpm_loff_t rpmfiFReplacedSizeIndex(rpmfi fi, int ix); +void rpmfilesFpLookup(rpmfiles fi, fingerPrintCache fpc); -RPM_GNUC_INTERNAL -void rpmfiFpLookup(rpmfi fi, fingerPrintCache fpc); +rpmfiles rpmfiFiles(rpmfi fi); +/** \ingroup rpmfi + * Return file iterator through files starting with given prefix. + * @param fi file info set + * @param pfx prefix + * @return file iterator + */ +RPM_GNUC_INTERNAL +rpmfi rpmfilesFindPrefix(rpmfiles fi, const char *pfx); #ifdef __cplusplus } #endif diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h new file mode 100644 index 000000000..4caef973b --- /dev/null +++ b/lib/rpmfiles.h @@ -0,0 +1,519 @@ +#ifndef _RPMFILES_H +#define _RPMFILES_H + +/** \ingroup rpmfilesles + * \file lib/rpmfiles.h + * File info set API. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <rpm/rpmtypes.h> +#include <rpm/rpmvf.h> +#include <rpm/rpmpgp.h> + +/** \ingroup rpmfiles + * File types. + * These are the file types used internally by rpm. The file + * type is determined by applying stat(2) macros like S_ISDIR to + * the file mode tag from a header. The values are arbitrary, + * but are identical to the linux stat(2) file types. + */ +typedef enum rpmFileTypes_e { + PIPE = 1, /*!< pipe/fifo */ + CDEV = 2, /*!< character device */ + XDIR = 4, /*!< directory */ + BDEV = 6, /*!< block device */ + REG = 8, /*!< regular file */ + LINK = 10, /*!< hard link */ + SOCK = 12 /*!< socket */ +} rpmFileTypes; + +/** + * File States (when installed). + */ +typedef enum rpmfileState_e { + RPMFILE_STATE_MISSING = -1, /* used for unavailable data */ + RPMFILE_STATE_NORMAL = 0, + RPMFILE_STATE_REPLACED = 1, + RPMFILE_STATE_NOTINSTALLED = 2, + RPMFILE_STATE_NETSHARED = 3, + RPMFILE_STATE_WRONGCOLOR = 4 +} rpmfileState; + +#define RPMFILE_IS_INSTALLED(_x) ((_x) == RPMFILE_STATE_NORMAL || (_x) == RPMFILE_STATE_NETSHARED) + +/** + * Exported File Attributes (ie RPMTAG_FILEFLAGS) + */ +enum rpmfileAttrs_e { + RPMFILE_NONE = 0, + RPMFILE_CONFIG = (1 << 0), /*!< from %%config */ + RPMFILE_DOC = (1 << 1), /*!< from %%doc */ + RPMFILE_ICON = (1 << 2), /*!< from %%donotuse. */ + RPMFILE_MISSINGOK = (1 << 3), /*!< from %%config(missingok) */ + RPMFILE_NOREPLACE = (1 << 4), /*!< from %%config(noreplace) */ + RPMFILE_SPECFILE = (1 << 5), /*!< @todo (unnecessary) marks 1st file in srpm. */ + RPMFILE_GHOST = (1 << 6), /*!< from %%ghost */ + RPMFILE_LICENSE = (1 << 7), /*!< from %%license */ + RPMFILE_README = (1 << 8), /*!< from %%readme */ + /* bits 9-10 unused */ + RPMFILE_PUBKEY = (1 << 11), /*!< from %%pubkey */ + RPMFILE_ARTIFACT = (1 << 12), /*!< from %%artifact */ + RPMFILE_SECMANIFEST = (1 << 13), /*!< from %%manifest */ +}; + +typedef rpmFlags rpmfileAttrs; + +#define RPMFILE_ALL ~(RPMFILE_NONE) + +/** \ingroup rpmfiles + * File disposition(s) during package install/erase transaction. + */ +typedef enum rpmFileAction_e { + FA_UNKNOWN = 0, /*!< initial action for file ... */ + FA_CREATE = 1, /*!< ... create from payload. */ + FA_COPYIN = 2, /*!< obsolete, unused. */ + FA_COPYOUT = 3, /*!< obsolete, unused. */ + FA_BACKUP = 4, /*!< ... renamed with ".rpmorig" extension. */ + FA_SAVE = 5, /*!< ... renamed with ".rpmsave" extension. */ + FA_SKIP = 6, /*!< ... already replaced, don't remove. */ + FA_ALTNAME = 7, /*!< ... create with ".rpmnew" extension. */ + FA_ERASE = 8, /*!< ... to be removed. */ + FA_SKIPNSTATE = 9, /*!< ... untouched, state "not installed". */ + FA_SKIPNETSHARED = 10, /*!< ... untouched, state "netshared". */ + FA_SKIPCOLOR = 11, /*!< ... untouched, state "wrong color". */ + FA_TOUCH = 12, /*!< ... change metadata only. */ + /* bits 16-31 reserved */ +} rpmFileAction; + +#define XFA_SKIPPING(_a) \ + ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPCOLOR) + +/** + * We pass these around as an array with a sentinel. + */ +struct rpmRelocation_s { + char * oldPath; /*!< NULL here evals to RPMTAG_DEFAULTPREFIX, */ + char * newPath; /*!< NULL means to omit the file completely! */ +}; + +enum rpmfiFlags_e { + RPMFI_NOHEADER = 0, + RPMFI_KEEPHEADER = (1 << 0), + RPMFI_NOFILECLASS = (1 << 1), + RPMFI_NOFILEDEPS = (1 << 2), + RPMFI_NOFILELANGS = (1 << 3), + RPMFI_NOFILEUSER = (1 << 4), + RPMFI_NOFILEGROUP = (1 << 5), + RPMFI_NOFILEMODES = (1 << 6), + RPMFI_NOFILESIZES = (1 << 7), + RPMFI_NOFILECAPS = (1 << 8), + RPMFI_NOFILELINKTOS = (1 << 9), + RPMFI_NOFILEDIGESTS = (1 << 10), + RPMFI_NOFILEMTIMES = (1 << 11), + RPMFI_NOFILERDEVS = (1 << 12), + RPMFI_NOFILEINODES = (1 << 13), + RPMFI_NOFILESTATES = (1 << 14), + RPMFI_NOFILECOLORS = (1 << 15), + RPMFI_NOFILEVERIFYFLAGS = (1 << 16), + RPMFI_NOFILEFLAGS = (1 << 17), + RPMFI_NOFILESIGNATURES = (1 << 18), +}; + +typedef rpmFlags rpmfiFlags; + +#define RPMFI_FLAGS_ERASE \ + (RPMFI_NOFILECLASS | RPMFI_NOFILELANGS | \ + RPMFI_NOFILEMTIMES | RPMFI_NOFILERDEVS | RPMFI_NOFILEINODES | \ + RPMFI_NOFILEVERIFYFLAGS) + +#define RPMFI_FLAGS_INSTALL \ + (RPMFI_NOFILECLASS | RPMFI_NOFILEVERIFYFLAGS) + +#define RPMFI_FLAGS_VERIFY \ + (RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \ + RPMFI_NOFILECOLORS) + +#define RPMFI_FLAGS_QUERY \ + (RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \ + RPMFI_NOFILECOLORS | RPMFI_NOFILEVERIFYFLAGS) + +#define RPMFI_FLAGS_FILETRIGGER \ + (RPMFI_NOFILECLASS | RPMFI_NOFILEDEPS | RPMFI_NOFILELANGS | \ + RPMFI_NOFILEUSER | RPMFI_NOFILEGROUP | RPMFI_NOFILEMODES | \ + RPMFI_NOFILESIZES | RPMFI_NOFILECAPS | RPMFI_NOFILELINKTOS | \ + RPMFI_NOFILEDIGESTS | RPMFI_NOFILEMTIMES | RPMFI_NOFILERDEVS | \ + RPMFI_NOFILEINODES | RPMFI_NOFILECOLORS | \ + RPMFI_NOFILEVERIFYFLAGS | RPMFI_NOFILEFLAGS) + +#define RPMFI_FLAGS_ONLY_FILENAMES \ + (RPMFI_FLAGS_FILETRIGGER | RPMFI_NOFILESTATES) + +typedef enum rpmFileIter_e { + RPMFI_ITER_FWD = 0, + RPMFI_ITER_BACK = 1, + RPMFI_ITER_WRITE_ARCHIVE = 2, + RPMFI_ITER_READ_ARCHIVE = 3, + RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST = 4, + RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS = 5, + RPMFI_ITER_INTERVAL = 6, +} rpmFileIter; + +#define RPMFILEITERMAX 6 + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmfiles + * Create and load a file info set. + * @param pool shared string pool (or NULL for private pool) + * @param h header + * @param tagN unused + * @param flags Flags to control what information is loaded. + * @return new file info set + */ +rpmfiles rpmfilesNew(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags); + +/** \ingroup rpmfiles + * Reference a file info set instance. + * @param fi file info set + * @return new file info set reference + */ +rpmfiles rpmfilesLink(rpmfiles fi); + +/** \ingroup rpmfiles + * Destroy a file info set. + * @param fi file info set + * @return NULL always + */ +rpmfiles rpmfilesFree(rpmfiles fi); + +/** \ingroup rpmfiles + * Return file count from file info set. + * @param fi file info set + * @return file count + */ +rpm_count_t rpmfilesFC(rpmfiles fi); + +/** \ingroup rpmfiles + * Return directory count from file info set. + * @param fi file info set + * @return directory count + */ +rpm_count_t rpmfilesDC(rpmfiles fi); + +/** \ingroup rpmfiles + * Return file index of the given file name or -1 if file is not in the rpmfi. + * The file name may have "." prefixed but is then interpreted as a global + * path without the prefixing "." + * @param files file info set + * @param fn file name + * @return file index or -1 + */ +int rpmfilesFindFN(rpmfiles files, const char * fn); + +/** \ingroup rpmfiles + * Return file index of the given original file name or -1 if file is not + * in the rpmfi. The file name may have "." prefixed but is then interpreted + * as a global path without the prefixing "." + * @param files file info set + * @param fn file name + * @return file index or -1 + */ +int rpmfilesFindOFN(rpmfiles files, const char * fn); + +rpmfi rpmfilesIter(rpmfiles files, int itype); + +/** \ingroup rpmfiles + * Return digest algorithm of a file info set. + * @param fi file info set + * @return digest algorithm of file info set, 0 on invalid + */ +int rpmfilesDigestAlgo(rpmfiles fi); + +/** \ingroup rpmfiles + * Return union of all file color bits from file info set. + * @param files file info set + * @return color + */ +rpm_color_t rpmfilesColor(rpmfiles files); + +/** \ingroup rpmfiles + * Return file info comparison. + * @param afi 1st file info + * @param aix index of the 1st file + * @param bfi 2nd file info + * @param bix index of the 2nd file + * @return 0 if identical + */ +int rpmfilesCompare(rpmfiles afi, int aix, rpmfiles bfi, int bix); + +/** \ingroup rpmfiles + * Return base name from file info set. + * @param fi file info set + * @param ix file index + * @return base name, NULL on invalid + */ +const char * rpmfilesBN(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return directory name from file info set. Note the index is on + * distinct directories within the file set, not a file index. The + * directory index associated with a given file index can be retrieved + * with rpmfilesDI(). Ie to constuct the full path of file index X + * you'd catenate the results of rpmfilesDN(f, rpmfilesDI(f, X)) and + * rpmfilesBN(f, X). + * @param fi file info set + * @param jx directory index + * @return directory, NULL on invalid + */ +const char * rpmfilesDN(rpmfiles fi, int jx); + +/** \ingroup rpmfiles + * Return directory index from file info set. + * @param fi file info set + * @param ix file index + * @return directory index, -1 on invalid + */ +int rpmfilesDI(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file name from file info set. + * @param fi file info set + * @param ix file index + * @return file name (malloced) + */ +char * rpmfilesFN(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return original directory index from file info set. + * @param fi file info set + * @param ix file index + * @return directory index, -1 on invalid + */ +int rpmfilesODI(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return original base name from file info set. + * @param fi file info set + * @param ix file index + * @return base name, NULL on invalid + */ +const char * rpmfilesOBN(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return original directory name from file info set. Note the index is on + * distinct directories within the file set, not a file index. The + * directory index associated with a given file index can be retrieved + * with rpmfilesODI(). Ie to constuct the full path of file index X + * you'd catenate the results of rpmfilesODN(f, rpmfilesODI(f, X)) and + * rpmfilesOBN(f, X). + * @param fi file info set + * @param jx directory index + * @return directory, NULL on invalid + */ +const char * rpmfilesODN(rpmfiles fi, int jx); + +/** \ingroup rpmfiles + * Return original file name from file info set. + * @param fi file info set + * @param ix file index + * @return file name + */ +char * rpmfilesOFN(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file verify flags from file info set. + * @param fi file info set + * @param ix file index + * @return file verify flags, 0 on invalid + */ +rpmVerifyAttrs rpmfilesVFlags(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file state from file info set. + * @param fi file info set + * @param ix file index + * @return file state, 0 on invalid + */ +rpmfileState rpmfilesFState(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file linkto (i.e. symlink(2) target) from file info set. + * @param fi file info set + * @param ix file index + * @return file linkto, NULL on invalid + */ +const char * rpmfilesFLink(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file size from file info set. + * @param fi file info set + * @param ix file index + * @return file size, 0 on invalid + */ +rpm_loff_t rpmfilesFSize(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file color bits from file info set. + * @param fi file info set + * @param ix file index + * @return file color + */ +rpm_color_t rpmfilesFColor(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file class from file info set. + * @param fi file info set + * @param ix file index + * @return file class, 0 on invalid + */ +const char * rpmfilesFClass(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file depends dictionary from file info set. + * @param fi file info set + * @param ix file index + * @retval *fddictp file depends dictionary array (or NULL) + * @return no. of file depends entries, 0 on invalid + */ +uint32_t rpmfilesFDepends(rpmfiles fi, int ix, const uint32_t ** fddictp); + +/** \ingroup rpmfiles + * Return (calculated) file nlink count from file info set. + * @param fi file info set + * @param ix file index + * @return file nlink count, 0 on invalid + */ +uint32_t rpmfilesFNlink(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return (calculated) file nlink count from file info set. + * @param fi file info set + * @param ix file index + * @param files returns array of file ids hardlinked including ix, + NULL for nlink count == 1 + * @return file nlink count, 0 on invalid + */ +uint32_t rpmfilesFLinks(rpmfiles fi, int ix, const int ** files); + +/** \ingroup rpmfiles + * Return file language(s) from file info set. + * @param fi file info set + * @param ix file index + * @return file language(s), NULL on invalid + */ +const char * rpmfilesFLangs(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file flags from file info set. + * @param fi file info set + * @param ix file index + * @return file flags, 0 on invalid + */ +rpmfileAttrs rpmfilesFFlags(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file mode from file info set. + * @param fi file info set + * @param ix file index + * @return file mode, 0 on invalid + */ +rpm_mode_t rpmfilesFMode(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file (binary) digest of file info set. + * @param fi file info set + * @param ix file index + * @retval algo digest hash algorithm used (pass NULL to ignore) + * @retval len digest hash length (pass NULL to ignore) + * @return file digest, NULL on invalid + */ +const unsigned char * rpmfilesFDigest(rpmfiles fi, int ix, int *algo, size_t *len); + +/** \ingroup rpmfiles + * Return file (binary) digest of file info set. + * @param fi file info set + * @param ix file index + * @retval len signature length (pass NULL to ignore) + * @return file signature, NULL on invalid + */ +const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len); + +/** \ingroup rpmfiles + * Return file rdev from file info set. + * @param fi file info set + * @param ix file index + * @return file rdev, 0 on invalid + */ +rpm_rdev_t rpmfilesFRdev(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file inode from file info set. + * @param fi file info set + * @param ix file index + * @return file inode, 0 on invalid + */ +rpm_ino_t rpmfilesFInode(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file modify time from file info set. + * @param fi file info set + * @param ix file index + * @return file modify time, 0 on invalid + */ +rpm_time_t rpmfilesFMtime(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file owner from file info set. + * @param fi file info set + * @param ix file index + * @return file owner, NULL on invalid + */ +const char * rpmfilesFUser(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return file group from file info set. + * @param fi file info set + * @param ix file index + * @return file group, NULL on invalid + */ +const char * rpmfilesFGroup(rpmfiles fi, int ix); + +/** \ingroup rpmfiles + * Return textual representation of file capabilities + * from file info set. See cap_from_text(3) for details. + * @param fi file info set + * @param ix file index + * @return file capability description, "" for no capabilities + * and NULL on invalid + */ +const char * rpmfilesFCaps(rpmfiles fi, int ix); + +/** \ingroup rpmfi + * Map file stat(2) info. + * @param fi file info set + * @param ix file index + * @param flags flags + * @retval sb mapped stat(2) data + * @return 0 on success + */ +int rpmfilesStat(rpmfiles fi, int ix, int flags, struct stat *sb); + +/** \ingroup rpmfiles + * Verify file attributes (including digest). + * @param fi file info set + * @param ix file index + * @param omitMask bit(s) to disable verify checks + * @return bit(s) to indicate failure (ie 0 for passed verify) + */ +rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMFILES_H */ diff --git a/lib/rpmfs.c b/lib/rpmfs.c index 764618d94..7d807e06b 100644 --- a/lib/rpmfs.c +++ b/lib/rpmfs.c @@ -18,7 +18,7 @@ rpmfs rpmfsNew(rpm_count_t fc, int initState) rpmfs fs = xcalloc(1, sizeof(*fs)); fs->fc = fc; fs->actions = xmalloc(fs->fc * sizeof(*fs->actions)); - memset(fs->actions, FA_UNKNOWN, fs->fc * sizeof(*fs->actions)); + rpmfsResetActions(fs); if (initState) { fs->states = xmalloc(sizeof(*fs->states) * fs->fc); memset(fs->states, RPMFILE_STATE_NORMAL, fs->fc); @@ -101,7 +101,7 @@ rpm_fstate_t * rpmfsGetStates(rpmfs fs) rpmFileAction rpmfsGetAction(rpmfs fs, unsigned int ix) { rpmFileAction action; - if (fs->actions != NULL && ix < fs->fc) { + if (fs && fs->actions != NULL && ix < fs->fc) { action = fs->actions[ix]; } else { action = FA_UNKNOWN; @@ -115,3 +115,10 @@ void rpmfsSetAction(rpmfs fs, unsigned int ix, rpmFileAction action) fs->actions[ix] = action; } } + +void rpmfsResetActions(rpmfs fs) +{ + if (fs && fs->actions) { + memset(fs->actions, FA_UNKNOWN, fs->fc * sizeof(*fs->actions)); + } +} diff --git a/lib/rpmfs.h b/lib/rpmfs.h index 5f747533c..83f99d15a 100644 --- a/lib/rpmfs.h +++ b/lib/rpmfs.h @@ -57,6 +57,9 @@ rpmFileAction rpmfsGetAction(rpmfs fs, unsigned int ix); RPM_GNUC_INTERNAL void rpmfsSetAction(rpmfs fs, unsigned int ix, rpmFileAction action); +RPM_GNUC_INTERNAL +void rpmfsResetActions(rpmfs fs); + #ifdef __cplusplus } #endif diff --git a/lib/rpmgi.c b/lib/rpmgi.c index 89707df64..c6663fde1 100644 --- a/lib/rpmgi.c +++ b/lib/rpmgi.c @@ -17,6 +17,8 @@ #include "debug.h" +#define MANIFEST_RECURSIONS 1000 /* Max. number of allowed manifest recursions */ + RPM_GNUC_INTERNAL rpmgiFlags giFlags = RPMGI_NONE; @@ -31,6 +33,10 @@ struct rpmgi_s { ARGV_t argv; int argc; + + int curLvl; /*!< Current recursion level */ + int recLvls[MANIFEST_RECURSIONS]; /*!< Reversed end index for given level */ + }; /** @@ -123,11 +129,24 @@ static Header rpmgiLoadReadHeader(rpmgi gi) if (gi->argv != NULL && gi->argv[gi->i] != NULL) do { char * fn = gi->argv[gi->i]; - int rc = rpmgiReadHeader(gi, fn, &h); + int rc; + + while (gi->recLvls[gi->curLvl] > gi->argc - gi->i) + gi->curLvl--; + + rc = rpmgiReadHeader(gi, fn, &h); if (h != NULL || (gi->flags & RPMGI_NOMANIFEST) || rc == 0) break; + if (gi->curLvl == MANIFEST_RECURSIONS - 1) { + rpmlog(RPMLOG_ERR, + _("Max level of manifest recursion exceeded: %s\n"), fn); + break; + } + gi->curLvl++; + gi->recLvls[gi->curLvl] = gi->argc - gi->i; + /* Not a header, so try for a manifest. */ gi->argv[gi->i] = NULL; /* Mark the insertion point */ if (rpmgiLoadManifest(gi, fn) != RPMRC_OK) { @@ -195,11 +214,13 @@ rpmgi rpmgiNew(rpmts ts, rpmgiFlags flags, ARGV_const_t argv) gi->i = -1; gi->errors = 0; - gi->flags = flags; gi->argv = argvNew(); gi->argc = 0; rpmgiGlobArgv(gi, argv); + gi->curLvl = 0; + gi->recLvls[gi->curLvl] = 1; + return gi; } diff --git a/lib/rpmhash.C b/lib/rpmhash.C index 98203272d..6875397aa 100644 --- a/lib/rpmhash.C +++ b/lib/rpmhash.C @@ -69,8 +69,8 @@ HASHTYPE HASHPREFIX(Create)(int numBuckets, HASHTYPE ht; ht = xmalloc(sizeof(*ht)); - ht->numBuckets = numBuckets; - ht->buckets = xcalloc(numBuckets, sizeof(*ht->buckets)); + ht->numBuckets = numBuckets > 11 ? numBuckets : 11; + ht->buckets = xcalloc(ht->numBuckets, sizeof(*ht->buckets)); ht->freeKey = freeKey; #ifdef HTDATATYPE ht->freeData = freeData; @@ -142,6 +142,8 @@ void HASHPREFIX(AddHEntry)(HASHTYPE ht, HTKEYTYPE key, unsigned int keyHash } #ifdef HTDATATYPE else { + if (ht->freeKey) + ht->freeKey(key); // resizing bucket TODO: increase exponentially // Bucket_s already contains space for one dataset b = *b_addr = xrealloc( diff --git a/lib/rpminstall.c b/lib/rpminstall.c index a3623fd3a..e10392e0f 100644 --- a/lib/rpminstall.c +++ b/lib/rpminstall.c @@ -10,6 +10,7 @@ #include <rpm/rpmdb.h> #include <rpm/rpmds.h> #include <rpm/rpmts.h> +#include <rpm/rpmsq.h> #include <rpm/rpmlog.h> #include <rpm/rpmfileutil.h> @@ -22,6 +23,7 @@ static int rpmcliHashesCurrent = 0; static int rpmcliHashesTotal = 0; static int rpmcliProgressCurrent = 0; static int rpmcliProgressTotal = 0; +static int rpmcliProgressState = 0; /** * Print a CLI progress bar. @@ -103,7 +105,6 @@ void * rpmShowProgress(const void * arg, void * rc = NULL; const char * filename = (const char *)key; static FD_t fd = NULL; - static int state = -1; switch (what) { case RPMCALLBACK_INST_OPEN_FILE: @@ -134,8 +135,8 @@ void * rpmShowProgress(const void * arg, case RPMCALLBACK_INST_START: case RPMCALLBACK_UNINST_START: - if (state != what) { - state = what; + if (rpmcliProgressState != what) { + rpmcliProgressState = what; if (flags & INSTALL_HASH) { if (what == RPMCALLBACK_INST_START) { fprintf(stdout, _("Updating / installing...\n")); @@ -185,7 +186,7 @@ void * rpmShowProgress(const void * arg, rpmcliProgressTotal = 1; rpmcliProgressCurrent = 0; rpmcliPackagesTotal = total; - state = what; + rpmcliProgressState = what; if (!(flags & INSTALL_LABEL)) break; if (flags & INSTALL_HASH) @@ -290,8 +291,8 @@ static int rpmcliTransaction(rpmts ts, struct rpmInstallArguments_s * ia, ps = rpmtsProblems(ts); - if ((rpmpsNumProblems(ps) > 0) && (eflags? 1 : (rc > 0))) - rpmpsPrint((eflags? NULL : stderr), ps); + if (rpmpsNumProblems(ps) > 0 && (eflags || rc > 0)) + rpmpsPrint(NULL, ps); ps = rpmpsFree(ps); } @@ -311,7 +312,8 @@ static int tryReadManifest(struct rpmEIU * eiu) Fclose(fd); fd = NULL; } - eiu->numFailed++; *eiu->fnp = NULL; + eiu->numFailed++; + *eiu->fnp = NULL; return RPMRC_FAIL; } @@ -323,8 +325,10 @@ static int tryReadManifest(struct rpmEIU * eiu) Fclose(fd); fd = NULL; - if (rc != RPMRC_OK) - eiu->numFailed++; *eiu->fnp = NULL; + if (rc != RPMRC_OK) { + eiu->numFailed++; + *eiu->fnp = NULL; + } return rc; } @@ -340,7 +344,8 @@ static int tryReadHeader(rpmts ts, struct rpmEIU * eiu, Header * hdrp) Fclose(fd); fd = NULL; } - eiu->numFailed++; *eiu->fnp = NULL; + eiu->numFailed++; + *eiu->fnp = NULL; return RPMRC_FAIL; } @@ -353,9 +358,10 @@ static int tryReadHeader(rpmts ts, struct rpmEIU * eiu, Header * hdrp) if (eiu->rpmrc == RPMRC_NOTFOUND && (giFlags & RPMGI_NOMANIFEST)) eiu->rpmrc = RPMRC_FAIL; - if(eiu->rpmrc == RPMRC_FAIL) { + if (eiu->rpmrc == RPMRC_FAIL) { rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), *eiu->fnp); - eiu->numFailed++; *eiu->fnp = NULL; + eiu->numFailed++; + *eiu->fnp = NULL; } return RPMRC_OK; @@ -385,6 +391,19 @@ static int checkFreshenStatus(rpmts ts, Header h) return (oldH != NULL); } +static int rpmNoGlob(const char *fn, int *argcPtr, ARGV_t * argvPtr) +{ + struct stat sb; + int rc = stat(fn, &sb); + if (rc == 0) { + argvAdd(argvPtr, fn); + *argcPtr = 1; + } else { + *argcPtr = 0; + } + return rc; +} + /** @todo Generalize --freshen policies. */ int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv) { @@ -417,13 +436,20 @@ int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv) for (eiu->fnp = fileArgv; *eiu->fnp != NULL; eiu->fnp++) { ARGV_t av = NULL; int ac = 0; - char * fn; - fn = rpmEscapeSpaces(*eiu->fnp); - rc = rpmGlob(fn, &ac, &av); - fn = _free(fn); + if (giFlags & RPMGI_NOGLOB) { + rc = rpmNoGlob(*eiu->fnp, &ac, &av); + } else { + char * fn = rpmEscapeSpaces(*eiu->fnp); + rc = rpmGlob(fn, &ac, &av); + fn = _free(fn); + } if (rc || ac == 0) { - rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), *eiu->fnp); + if (giFlags & RPMGI_NOGLOB) { + rpmlog(RPMLOG_ERR, _("File not found: %s\n"), *eiu->fnp); + } else { + rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), *eiu->fnp); + } eiu->numFailed++; continue; } @@ -520,6 +546,10 @@ restart: } if (headerIsSource(h)) { + if (ia->installInterfaceFlags & INSTALL_FRESHEN) { + headerFree(h); + continue; + } rpmlog(RPMLOG_DEBUG, "\tadded source package [%d]\n", eiu->numSRPMS); eiu->sourceURL = xrealloc(eiu->sourceURL, @@ -552,7 +582,10 @@ restart: continue; } - rc = rpmtsAddInstallElement(ts, h, (fnpyKey)fileName, + if (ia->installInterfaceFlags & INSTALL_REINSTALL) + rc = rpmtsAddReinstallElement(ts, h, (fnpyKey)fileName); + else + rc = rpmtsAddInstallElement(ts, h, (fnpyKey)fileName, (ia->installInterfaceFlags & INSTALL_UPGRADE) != 0, relocations); @@ -560,7 +593,7 @@ restart: if (eiu->relocations) eiu->relocations->oldPath = _free(eiu->relocations->oldPath); - switch(rc) { + switch (rc) { case 0: rpmlog(RPMLOG_DEBUG, "\tadded binary package [%d]\n", eiu->numRPMS); @@ -594,8 +627,11 @@ restart: } if (eiu->numSRPMS && (eiu->sourceURL != NULL)) { + rpmcliProgressState = 0; + rpmcliProgressTotal = 0; + rpmcliProgressCurrent = 0; for (i = 0; i < eiu->numSRPMS; i++) { - rpmdbCheckSignals(); + rpmsqPoll(); if (eiu->sourceURL[i] != NULL) { rc = RPMRC_OK; if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) diff --git a/lib/rpmlead.c b/lib/rpmlead.c index 8e56bfc38..981bc8f60 100644 --- a/lib/rpmlead.c +++ b/lib/rpmlead.c @@ -17,6 +17,9 @@ #include "debug.h" +/* A long time ago in a galaxy far far away, signatures were not in a header */ +#define RPMSIGTYPE_HEADERSIG 5 + static unsigned char const lead_magic[] = { RPMLEAD_MAGIC0, RPMLEAD_MAGIC1, RPMLEAD_MAGIC2, RPMLEAD_MAGIC3 }; @@ -39,10 +42,8 @@ struct rpmlead_s { char reserved[16]; /*!< Pad to 96 bytes -- 8 byte aligned! */ }; -rpmlead rpmLeadFromHeader(Header h) +static int rpmLeadFromHeader(Header h, struct rpmlead_s *l) { - rpmlead l = NULL; - if (h != NULL) { int archnum, osnum; char * nevr = headerGetAsString(h, RPMTAG_NEVR); @@ -51,7 +52,7 @@ rpmlead rpmLeadFromHeader(Header h) rpmGetArchInfo(NULL, &archnum); rpmGetOsInfo(NULL, &osnum); - l = xcalloc(1, sizeof(*l)); + memset(l, 0, sizeof(*l)); l->major = 3; l->minor = 0; l->archnum = archnum; @@ -65,36 +66,30 @@ rpmlead rpmLeadFromHeader(Header h) free(nevr); } - return l; -} - -rpmlead rpmLeadFree(rpmlead lead) -{ - free(lead); - return NULL; + return (h != NULL); } /* The lead needs to be 8 byte aligned */ -rpmRC rpmLeadWrite(FD_t fd, rpmlead lead) +rpmRC rpmLeadWrite(FD_t fd, Header h) { rpmRC rc = RPMRC_FAIL; + struct rpmlead_s l; - if (lead != NULL) { - struct rpmlead_s l; - memcpy(&l, lead, sizeof(l)); + if (rpmLeadFromHeader(h, &l)) { - l.type = htons(lead->type); - l.archnum = htons(lead->archnum); - l.osnum = htons(lead->osnum); - l.signature_type = htons(lead->signature_type); + l.type = htons(l.type); + l.archnum = htons(l.archnum); + l.osnum = htons(l.osnum); + l.signature_type = htons(l.signature_type); if (Fwrite(&l, 1, sizeof(l), fd) == sizeof(l)) rc = RPMRC_OK; } + return rc; } -static rpmRC rpmLeadCheck(rpmlead lead, char **msg) +static rpmRC rpmLeadCheck(struct rpmlead_s *lead, char **msg) { if (memcmp(lead->magic, lead_magic, sizeof(lead_magic))) { *msg = xstrdup(_("not an rpm package")); @@ -111,7 +106,7 @@ static rpmRC rpmLeadCheck(rpmlead lead, char **msg) return RPMRC_OK; } -rpmRC rpmLeadRead(FD_t fd, rpmlead *lead, int *type, char **emsg) +rpmRC rpmLeadRead(FD_t fd, int *type, char **emsg) { rpmRC rc = RPMRC_OK; struct rpmlead_s l; @@ -135,10 +130,6 @@ rpmRC rpmLeadRead(FD_t fd, rpmlead *lead, int *type, char **emsg) } if (rc == RPMRC_OK) { - if (lead != NULL) { - *lead = xmalloc(sizeof(l)); - memcpy(*lead, &l, sizeof(l)); - } if (type != NULL) *type = l.type; } else { diff --git a/lib/rpmlead.h b/lib/rpmlead.h index a7f4730ad..48d4df287 100644 --- a/lib/rpmlead.h +++ b/lib/rpmlead.h @@ -19,39 +19,22 @@ extern "C" { #define RPMLEAD_SIZE 96 /*!< Don't rely on sizeof(struct) */ -typedef struct rpmlead_s * rpmlead; - -/** \ingroup lead - * Initialize a lead structure from header - * param h Header - * @return Pointer to populated lead structure (malloced) - */ -rpmlead rpmLeadFromHeader(Header h); - -/** \ingroup lead - * Free a lead structure - * @param lead Pointer to lead structure - * @return NULL always - */ -rpmlead rpmLeadFree(rpmlead lead); - /** \ingroup lead * Write lead to file handle. * @param fd file handle - * @param lead package lead + * @param h package header * @return RPMRC_OK on success, RPMRC_FAIL on error */ -rpmRC rpmLeadWrite(FD_t fd, rpmlead lead); +rpmRC rpmLeadWrite(FD_t fd, Header h); /** \ingroup lead * Read lead from file handle. * @param fd file handle - * @retval lead pointer to package lead (malloced) * @retval type RPMLEAD_BINARY or RPMLEAD_SOURCE on success * @retval emsg failure message on error (malloced) * @return RPMRC_OK on success, RPMRC_FAIL/RPMRC_NOTFOUND on error */ -rpmRC rpmLeadRead(FD_t fd, rpmlead *lead, int *type, char **emsg); +rpmRC rpmLeadRead(FD_t fd, int *type, char **emsg); #ifdef __cplusplus } diff --git a/lib/rpmlib.h b/lib/rpmlib.h index 41a285d04..2e1da831a 100644 --- a/lib/rpmlib.h +++ b/lib/rpmlib.h @@ -15,8 +15,9 @@ #include <rpm/rpmtag.h> #include <rpm/rpmds.h> /* XXX move rpmlib provides to rpmds instead */ #include <rpm/rpmpgp.h> + #ifdef _RPM_4_4_COMPAT -#include <rpm/rpmlegacy.h> /* legacy compat definitions if enabled */ +#warning RPM 4.4.x compatibility layer has been removed in RPM >= 4.14 #endif #ifdef __cplusplus @@ -137,7 +138,7 @@ rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg); /** \ingroup header * Return checked and loaded header. - * @param ts transaction set + * @param ts unused * @param fd file handle * @retval hdrp address of header (or NULL) * @retval *msg verification error message (or NULL) diff --git a/lib/rpmlock.c b/lib/rpmlock.c index cf9947ea5..f27d8d9b3 100644 --- a/lib/rpmlock.c +++ b/lib/rpmlock.c @@ -12,18 +12,15 @@ /* Internal interface */ -enum { - RPMLOCK_READ = 1 << 0, - RPMLOCK_WRITE = 1 << 1, - RPMLOCK_WAIT = 1 << 2, -}; - struct rpmlock_s { int fd; int openmode; + char *path; + char *descr; + int fdrefs; }; -static rpmlock rpmlock_new(const char *lock_path) +static rpmlock rpmlock_new(const char *lock_path, const char *descr) { rpmlock lock = (rpmlock) malloc(sizeof(*lock)); @@ -33,7 +30,8 @@ static rpmlock rpmlock_new(const char *lock_path) (void) umask(oldmask); if (lock->fd == -1) { - lock->fd = open(lock_path, O_RDONLY); + if (errno == EACCES) + lock->fd = open(lock_path, O_RDONLY); if (lock->fd == -1) { free(lock); lock = NULL; @@ -43,13 +41,20 @@ static rpmlock rpmlock_new(const char *lock_path) } else { lock->openmode = RPMLOCK_WRITE | RPMLOCK_READ; } + if (lock) { + lock->path = xstrdup(lock_path); + lock->descr = xstrdup(descr); + lock->fdrefs = 1; + } } return lock; } static void rpmlock_free(rpmlock lock) { - if (lock) { + if (--lock->fdrefs == 0) { + free(lock->path); + free(lock->descr); (void) close(lock->fd); free(lock); } @@ -58,7 +63,13 @@ static void rpmlock_free(rpmlock lock) static int rpmlock_acquire(rpmlock lock, int mode) { int res = 0; - if (lock && (mode & lock->openmode)) { + + if (!(mode & lock->openmode)) + return res; + + if (lock->fdrefs > 1) { + res = 1; + } else { struct flock info; int cmd; if (mode & RPMLOCK_WAIT) @@ -76,12 +87,19 @@ static int rpmlock_acquire(rpmlock lock, int mode) if (fcntl(lock->fd, cmd, &info) != -1) res = 1; } + + lock->fdrefs += res; + return res; } static void rpmlock_release(rpmlock lock) { - if (lock) { + /* if not locked then we must not release */ + if (lock->fdrefs <= 1) + return; + + if (--lock->fdrefs == 1) { struct flock info; info.l_type = F_UNLCK; info.l_whence = SEEK_SET; @@ -94,31 +112,56 @@ static void rpmlock_release(rpmlock lock) /* External interface */ - -rpmlock rpmlockAcquire(const char *lock_path, const char *descr) +rpmlock rpmlockNew(const char *lock_path, const char *descr) { - rpmlock lock = rpmlock_new(lock_path); + rpmlock lock = rpmlock_new(lock_path, descr); if (!lock) { rpmlog(RPMLOG_ERR, _("can't create %s lock on %s (%s)\n"), descr, lock_path, strerror(errno)); - } else if (!rpmlock_acquire(lock, RPMLOCK_WRITE)) { - if (lock->openmode & RPMLOCK_WRITE) + } + return lock; +} + +int rpmlockAcquire(rpmlock lock) +{ + int locked = 0; /* assume failure */ + int maywait = isatty(STDIN_FILENO); /* dont wait within scriptlets */ + + if (lock) { + locked = rpmlock_acquire(lock, RPMLOCK_WRITE); + if (!locked && (lock->openmode & RPMLOCK_WRITE) && maywait) { rpmlog(RPMLOG_WARNING, _("waiting for %s lock on %s\n"), - descr, lock_path); - if (!rpmlock_acquire(lock, RPMLOCK_WRITE|RPMLOCK_WAIT)) { + lock->descr, lock->path); + locked = rpmlock_acquire(lock, (RPMLOCK_WRITE|RPMLOCK_WAIT)); + } + if (!locked) { rpmlog(RPMLOG_ERR, _("can't create %s lock on %s (%s)\n"), - descr, lock_path, strerror(errno)); - rpmlock_free(lock); - lock = NULL; + lock->descr, lock->path, strerror(errno)); } } + return locked; +} + +void rpmlockRelease(rpmlock lock) +{ + if (lock) + rpmlock_release(lock); +} + +rpmlock rpmlockNewAcquire(const char *lock_path, const char *descr) +{ + rpmlock lock = rpmlockNew(lock_path, descr); + if (!rpmlockAcquire(lock)) + lock = rpmlockFree(lock); return lock; } rpmlock rpmlockFree(rpmlock lock) { - rpmlock_release(lock); /* Not really needed here. */ - rpmlock_free(lock); + if (lock) { + rpmlock_release(lock); + rpmlock_free(lock); + } return NULL; } diff --git a/lib/rpmlock.h b/lib/rpmlock.h index aa85451d6..308794fea 100644 --- a/lib/rpmlock.h +++ b/lib/rpmlock.h @@ -5,12 +5,27 @@ typedef struct rpmlock_s * rpmlock; +enum { + RPMLOCK_READ = 1 << 0, + RPMLOCK_WRITE = 1 << 1, + RPMLOCK_WAIT = 1 << 2, +}; + #ifdef __cplusplus extern "C" { #endif RPM_GNUC_INTERNAL -rpmlock rpmlockAcquire(const char *lock_path, const char *descr); +rpmlock rpmlockNew(const char *lock_path, const char *descr); + +RPM_GNUC_INTERNAL +rpmlock rpmlockNewAcquire(const char *lock_path, const char *descr); + +RPM_GNUC_INTERNAL +int rpmlockAcquire(rpmlock lock); + +RPM_GNUC_INTERNAL +void rpmlockRelease(rpmlock lock); RPM_GNUC_INTERNAL rpmlock rpmlockFree(rpmlock lock); diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h new file mode 100644 index 000000000..d3cdecee2 --- /dev/null +++ b/lib/rpmplugin.h @@ -0,0 +1,145 @@ +#ifndef _RPMPLUGIN_H +#define _RPMPLUGIN_H + +#include <rpm/rpmtypes.h> +#include <rpm/rpmfi.h> + +/** \ingroup rpmplugin + * Rpm plugin API + */ + +/* indicates if a directory is part of rpm package or created by rpm itself */ +typedef enum rpmPluginDirType_e { + DIR_TYPE_NONE = 0, + DIR_TYPE_NORMAL = 1 << 0, + DIR_TYPE_UNOWNED = 1 << 1 +} rpmPluginDirType; + + +/* indicates the way the scriptlet is executed */ +typedef enum rpmScriptletExecutionFlow_e { + RPMSCRIPTLET_NONE = 0, + RPMSCRIPTLET_FORK = 1 << 0, + RPMSCRIPTLET_EXEC = 1 << 1 +} rpmScriptletExecutionFlow; + + +/** \ingroup rpmfi + * File disposition flags during package install/erase transaction. + * XXX: Move these to rpmfi.h once things stabilize. + */ +enum rpmFileActionFlags_e { + /* bits 0-15 reserved for actions */ + FAF_UNOWNED = (1 << 31) +}; +typedef rpmFlags rpmFileActionFlags; + +/** \ingroup rpmfi + * File action and associated flags on install/erase + */ +typedef rpmFlags rpmFsmOp; + +#define XFA_MASK 0x0000ffff +#define XFAF_MASK ~(XFA_MASK) +#define XFO_ACTION(_a) ((_a) & XFA_MASK) /*!< File op action part */ +#define XFO_FLAGS(_a) ((_a) & XFAF_MASK) /*!< File op flags part */ + +/* plugin hook typedefs */ +typedef rpmRC (*plugin_init_func)(rpmPlugin plugin, rpmts ts); +typedef void (*plugin_cleanup_func)(rpmPlugin plugin); +typedef rpmRC (*plugin_tsm_pre_func)(rpmPlugin plugin, rpmts ts); +typedef rpmRC (*plugin_tsm_post_func)(rpmPlugin plugin, rpmts ts, int res); +typedef rpmRC (*plugin_psm_pre_func)(rpmPlugin plugin, rpmte te); +typedef rpmRC (*plugin_psm_post_func)(rpmPlugin plugin, rpmte te, int res); +typedef rpmRC (*plugin_psm_verify_func)(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, DIGEST_CTX ctx, int res); + +typedef rpmRC (*plugin_scriptlet_pre_func)(rpmPlugin plugin, + const char *s_name, int type); +typedef rpmRC (*plugin_scriptlet_fork_post_func)(rpmPlugin plugin, + const char *path, int type); +typedef rpmRC (*plugin_scriptlet_post_func)(rpmPlugin plugin, + const char *s_name, int type, + int res); +typedef rpmRC (*plugin_fsm_file_pre_func)(rpmPlugin plugin, rpmfi fi, + const char* path, mode_t file_mode, + rpmFsmOp op); +typedef rpmRC (*plugin_fsm_file_post_func)(rpmPlugin plugin, rpmfi fi, + const char* path, mode_t file_mode, + rpmFsmOp op, int res); +typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi, + const char* path, + const char *dest, + mode_t file_mode, rpmFsmOp op); +typedef rpmRC (*plugin_fsm_file_init_func)(const char* path, mode_t mode); +typedef rpmRC (*plugin_fsm_file_commit_func)(const char* path, mode_t mode, int type); +typedef rpmRC (*plugin_fsm_file_conflict_func)(rpmts ts, char* path, Header oldHeader, rpmfi oldFi, int rpmrc); + +typedef struct rpmPluginHooks_s * rpmPluginHooks; +struct rpmPluginHooks_s { + /* plugin constructor and destructor hooks */ + plugin_init_func init; + plugin_cleanup_func cleanup; + /* per transaction plugin hooks */ + plugin_tsm_pre_func tsm_pre; + plugin_tsm_post_func tsm_post; + /* per transaction element hooks */ + plugin_psm_pre_func psm_pre; + plugin_psm_post_func psm_post; + plugin_psm_verify_func psm_verify; + /* per scriptlet hooks */ + plugin_scriptlet_pre_func scriptlet_pre; + plugin_scriptlet_fork_post_func scriptlet_fork_post; + plugin_scriptlet_post_func scriptlet_post; + /* per file hooks */ + plugin_fsm_file_pre_func fsm_file_pre; + plugin_fsm_file_post_func fsm_file_post; + plugin_fsm_file_prepare_func fsm_file_prepare; + plugin_fsm_file_init_func fsm_file_init; + plugin_fsm_file_commit_func fsm_file_commit; + plugin_fsm_file_conflict_func fsm_file_conflict; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** \ingroup rpmplugin + * Return plugin name + * @param plugin plugin handle + * @return plugin name string + */ +const char *rpmPluginName(rpmPlugin plugin); + +/** \ingroup rpmplugin + * Return plugin options + * @param plugin plugin handle + * @return plugin options string (or NULL if none) + */ +const char *rpmPluginOpts(rpmPlugin plugin); + +/** \ingroup rpmplugin + * Set plugin private data + * @param plugin plugin handle + * @param data pointer to plugin private data + */ +void rpmPluginSetData(rpmPlugin plugin, void *data); + +/** \ingroup rpmplugin + * Get plugin private data + * @param plugin plugin handle + * @return pointer to plugin private data + */ +void * rpmPluginGetData(rpmPlugin plugin); + +/** \ingroup rpmplugin + * Get plugin count + * @param plugins plugins handle + * @return count of plugins in the plugins handle + */ +int rpmPluginsGetCount(rpmPlugins plugin); + + +#ifdef __cplusplus +} +#endif +#endif /* _RPMPLUGIN_H */ diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c index 2d4fb01cd..4892bb18a 100644 --- a/lib/rpmplugins.c +++ b/lib/rpmplugins.c @@ -12,34 +12,37 @@ #define STR1(x) #x #define STR(x) STR1(x) +static rpmRC rpmpluginsCallInit(rpmPlugin plugin, rpmts ts); + +struct rpmPlugin_s { + char *name; + char *opts; + void *handle; + void *priv; + rpmPluginHooks hooks; +}; + struct rpmPlugins_s { - void **handles; - ARGV_t names; + rpmPlugin *plugins; int count; rpmts ts; }; -static int rpmpluginsGetPluginIndex(rpmPlugins plugins, const char *name) +static rpmPlugin rpmpluginsGetPlugin(rpmPlugins plugins, const char *name) { int i; for (i = 0; i < plugins->count; i++) { - if (rstreq(plugins->names[i], name)) { - return i; + rpmPlugin plugin = plugins->plugins[i]; + if (rstreq(plugin->name, name)) { + return plugin; } } - return -1; -} - -static int rpmpluginsHookIsSupported(void *handle, rpmPluginHook hook) -{ - rpmPluginHook *supportedHooks = - (rpmPluginHook *) dlsym(handle, STR(PLUGIN_HOOKS)); - return (*supportedHooks & hook); + return NULL; } int rpmpluginsPluginAdded(rpmPlugins plugins, const char *name) { - return (rpmpluginsGetPluginIndex(plugins, name) >= 0); + return (rpmpluginsGetPlugin(plugins, name) != NULL); } rpmPlugins rpmpluginsNew(rpmts ts) @@ -49,31 +52,100 @@ rpmPlugins rpmpluginsNew(rpmts ts) return plugins; } -rpmRC rpmpluginsAdd(rpmPlugins plugins, const char *name, const char *path, - const char *opts) +static rpmPlugin rpmPluginNew(const char *name, const char *path, + const char *opts) { + rpmPlugin plugin = NULL; + rpmPluginHooks hooks = NULL; char *error; + char *hooks_name = NULL; void *handle = dlopen(path, RTLD_LAZY); if (!handle) { rpmlog(RPMLOG_ERR, _("Failed to dlopen %s %s\n"), path, dlerror()); - return RPMRC_FAIL; + return NULL; } /* make sure the plugin has the supported hooks flag */ - (void) dlsym(handle, STR(PLUGIN_HOOKS)); + hooks_name = rstrscat(NULL, name, "_hooks", NULL); + hooks = dlsym(handle, hooks_name); if ((error = dlerror()) != NULL) { rpmlog(RPMLOG_ERR, _("Failed to resolve symbol %s: %s\n"), - STR(PLUGIN_HOOKS), error); - return RPMRC_FAIL; + hooks_name, error); + } else { + plugin = xcalloc(1, sizeof(*plugin)); + plugin->name = xstrdup(name); + plugin->handle = handle; + plugin->hooks = hooks; + if (opts) + plugin->opts = xstrdup(opts); } + free(hooks_name); - argvAdd(&plugins->names, name); - plugins->handles = xrealloc(plugins->handles, (plugins->count + 1) * sizeof(*plugins->handles)); - plugins->handles[plugins->count] = handle; - plugins->count++; + return plugin; +} - return rpmpluginsCallInit(plugins, name, opts); +static rpmPlugin rpmPluginFree(rpmPlugin plugin) +{ + if (plugin) { + rpmPluginHooks hooks = plugin->hooks; + if (hooks->cleanup) + hooks->cleanup(plugin); + dlclose(plugin->handle); + free(plugin->name); + free(plugin->opts); + free(plugin); + } + return NULL; +} + +const char *rpmPluginName(rpmPlugin plugin) +{ + return (plugin != NULL) ? plugin->name : NULL; +} + +const char *rpmPluginOpts(rpmPlugin plugin) +{ + return (plugin != NULL) ? plugin->opts : NULL; +} + +void * rpmPluginGetData(rpmPlugin plugin) +{ + return (plugin != NULL) ? plugin->priv : NULL; +} + +int rpmPluginsGetCount(rpmPlugins plugins) +{ + return (plugins != NULL) ? plugins->count : 0; +} + +void rpmPluginSetData(rpmPlugin plugin, void *data) +{ + if (plugin) + plugin->priv = data; +} + +rpmRC rpmpluginsAdd(rpmPlugins plugins, const char *name, const char *path, + const char *opts) +{ + rpmRC rc; + rpmPlugin plugin = rpmPluginNew(name, path, opts); + + if (plugin == NULL) + return RPMRC_FAIL; + + rc = rpmpluginsCallInit(plugin, plugins->ts); + + if (rc == RPMRC_OK) { + plugins->plugins = xrealloc(plugins->plugins, + (plugins->count + 1) * sizeof(*plugins->plugins)); + plugins->plugins[plugins->count] = plugin; + plugins->count++; + } else { + rpmPluginFree(plugin); + } + + return rc; } rpmRC rpmpluginsAddPlugin(rpmPlugins plugins, const char *type, const char *name) @@ -83,9 +155,11 @@ rpmRC rpmpluginsAddPlugin(rpmPlugins plugins, const char *type, const char *name rpmRC rc = RPMRC_FAIL; path = rpmExpand("%{?__", type, "_", name, "}", NULL); + //rpmlog(RPMLOG_WARNING, "rpmpluginsAddPlugin, path:%s\n", path); if (!path || rstreq(path, "")) { - rpmlog(RPMLOG_ERR, _("Failed to expand %%__%s_%s macro\n"), + rpmlog(RPMLOG_DEBUG, _("Plugin %%__%s_%s not configured\n"), type, name); + rc = RPMRC_NOTFOUND; goto exit; } @@ -112,222 +186,245 @@ rpmRC rpmpluginsAddPlugin(rpmPlugins plugins, const char *type, const char *name rpmPlugins rpmpluginsFree(rpmPlugins plugins) { - int i; - for (i = 0; i < plugins->count; i++) { - rpmpluginsCallCleanup(plugins, plugins->names[i]); - dlclose(plugins->handles[i]); + if (plugins) { + for (int i = 0; i < plugins->count; i++) { + rpmPlugin plugin = plugins->plugins[i]; + rpmPluginFree(plugin); + } + plugins->plugins = _free(plugins->plugins); + plugins->ts = NULL; + _free(plugins); } - plugins->handles = _free(plugins->handles); - plugins->names = argvFree(plugins->names); - plugins->ts = NULL; - _free(plugins); return NULL; } +#define RPMPLUGINS_GET_PLUGIN(name) \ + plugin = rpmpluginsGetPlugin(plugins, name); \ + if (plugin == NULL || plugin->handle == NULL) { \ + rpmlog(RPMLOG_ERR, _("Plugin %s not loaded\n"), name); \ + return RPMRC_FAIL; \ + } /* Common define for all rpmpluginsCall* hook functions */ #define RPMPLUGINS_SET_HOOK_FUNC(hook) \ - void *handle = NULL; \ - int index; \ - char * error; \ - index = rpmpluginsGetPluginIndex(plugins, name); \ - if (index < 0) { \ - rpmlog(RPMLOG_ERR, _("Plugin %s not loaded\n"), name); \ - return RPMRC_FAIL; \ - } \ - handle = plugins->handles[index]; \ - if (!handle) { \ - rpmlog(RPMLOG_ERR, _("Plugin %s not loaded\n"), name); \ - return RPMRC_FAIL; \ - } \ - if (!rpmpluginsHookIsSupported(handle, hook)) { \ - return RPMRC_OK; \ - } \ - *(void **)(&hookFunc) = dlsym(handle, STR(hook##_FUNC)); \ - if ((error = dlerror()) != NULL) { \ - rpmlog(RPMLOG_ERR, _("Failed to resolve %s plugin symbol %s: %s\n"), name, STR(hook##_FUNC), error); \ - return RPMRC_FAIL; \ - } \ - if (rpmtsFlags(plugins->ts) & (RPMTRANS_FLAG_TEST | RPMTRANS_FLAG_JUSTDB)) { \ - return RPMRC_OK; \ - } \ - rpmlog(RPMLOG_DEBUG, "Plugin: calling hook %s in %s plugin\n", STR(hook##_FUNC), name); + rpmPluginHooks hooks = (plugin != NULL) ? plugin->hooks : NULL; \ + hookFunc = (hooks != NULL) ? hooks->hook : NULL; \ + if (hookFunc) { \ + rpmlog(RPMLOG_DEBUG, "Plugin: calling hook %s in %s plugin\n", \ + STR(hook), plugin->name); \ + } -rpmRC rpmpluginsCallInit(rpmPlugins plugins, const char *name, const char *opts) +static rpmRC rpmpluginsCallInit(rpmPlugin plugin, rpmts ts) { - rpmRC (*hookFunc)(rpmts, const char *, const char *); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_INIT); - return hookFunc(plugins->ts, name, opts); + rpmRC rc = RPMRC_OK; + plugin_init_func hookFunc; + RPMPLUGINS_SET_HOOK_FUNC(init); + if (hookFunc) { + rc = hookFunc(plugin, ts); + if (rc != RPMRC_OK && rc != RPMRC_NOTFOUND) + rpmlog(RPMLOG_ERR, "Plugin %s: hook init failed\n", plugin->name); + } + return rc; } -rpmRC rpmpluginsCallCleanup(rpmPlugins plugins, const char *name) +rpmRC rpmpluginsCallTsmPre(rpmPlugins plugins, rpmts ts) { - rpmRC (*hookFunc)(void); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_CLEANUP); - return hookFunc(); -} + plugin_tsm_pre_func hookFunc; + int i; + rpmRC rc = RPMRC_OK; -rpmRC rpmpluginsCallOpenTE(rpmPlugins plugins, const char *name, rpmte te) -{ - rpmRC (*hookFunc)(rpmte); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_OPENTE); - return hookFunc(te); -} + for (i = 0; i < plugins->count; i++) { + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(tsm_pre); + if (hookFunc && hookFunc(plugin, ts) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook tsm_pre failed\n", plugin->name); + rc = RPMRC_FAIL; + } + } -rpmRC rpmpluginsCallCollectionPostAdd(rpmPlugins plugins, const char *name) -{ - rpmRC (*hookFunc)(void); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_COLL_POST_ADD); - return hookFunc(); + return rc; } -rpmRC rpmpluginsCallCollectionPostAny(rpmPlugins plugins, const char *name) +rpmRC rpmpluginsCallTsmPost(rpmPlugins plugins, rpmts ts, int res) { - rpmRC (*hookFunc)(void); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_COLL_POST_ANY); - return hookFunc(); + plugin_tsm_post_func hookFunc; + int i; + rpmRC rc = RPMRC_OK; + + for (i = 0; i < plugins->count; i++) { + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(tsm_post); + if (hookFunc && hookFunc(plugin, ts, res) == RPMRC_FAIL) { + rpmlog(RPMLOG_WARNING, "Plugin %s: hook tsm_post failed\n", plugin->name); + } + } + + return rc; } -rpmRC rpmpluginsCallCollectionPreRemove(rpmPlugins plugins, const char *name) +rpmRC rpmpluginsCallPsmPre(rpmPlugins plugins, rpmte te) { - rpmRC (*hookFunc)(void); - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_COLL_PRE_REMOVE); - return hookFunc(); + plugin_psm_pre_func hookFunc; + int i; + rpmRC rc = RPMRC_OK; + + for (i = 0; i < plugins->count; i++) { + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(psm_pre); + if (hookFunc && hookFunc(plugin, te) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook psm_pre failed\n", plugin->name); + rc = RPMRC_FAIL; + } + } + + return rc; } -rpmRC rpmpluginsCallTsmPre(rpmPlugins plugins, rpmts ts) +rpmRC rpmpluginsCallPsmPost(rpmPlugins plugins, rpmte te, int res) { - rpmRC (*hookFunc)(rpmts); + plugin_psm_post_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_TSM_PRE); - if (hookFunc(ts) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(psm_post); + if (hookFunc && hookFunc(plugin, te, res) == RPMRC_FAIL) { + rpmlog(RPMLOG_WARNING, "Plugin %s: hook psm_post failed\n", plugin->name); + } } return rc; } -rpmRC rpmpluginsCallTsmPost(rpmPlugins plugins, rpmts ts, int res) +rpmRC rpmpluginsCallScriptletPre(rpmPlugins plugins, const char *s_name, int type) { - rpmRC (*hookFunc)(rpmts, int); + plugin_scriptlet_pre_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_TSM_POST); - if (hookFunc(ts, res) == RPMRC_FAIL) + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(scriptlet_pre); + if (hookFunc && hookFunc(plugin, s_name, type) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook scriplet_pre failed\n", plugin->name); rc = RPMRC_FAIL; + } } return rc; } -rpmRC rpmpluginsCallPsmPre(rpmPlugins plugins, rpmte te) +rpmRC rpmpluginsCallScriptletForkPost(rpmPlugins plugins, const char *path, int type) { - rpmRC (*hookFunc)(rpmte); + plugin_scriptlet_fork_post_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_PSM_PRE); - if (hookFunc(te) == RPMRC_FAIL) + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(scriptlet_fork_post); + if (hookFunc && hookFunc(plugin, path, type) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook scriplet_fork_post failed\n", plugin->name); rc = RPMRC_FAIL; + } } return rc; } -rpmRC rpmpluginsCallPsmPost(rpmPlugins plugins, rpmte te, int res) +rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int type, int res) { - rpmRC (*hookFunc)(rpmte, int); + plugin_scriptlet_post_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_PSM_POST); - if (hookFunc(te, res) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(scriptlet_post); + if (hookFunc && hookFunc(plugin, s_name, type, res) == RPMRC_FAIL) { + rpmlog(RPMLOG_WARNING, "Plugin %s: hook scriplet_post failed\n", plugin->name); + } } return rc; } -rpmRC rpmpluginsCallScriptletPre(rpmPlugins plugins, const char *s_name, int type) +rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path, + mode_t file_mode, rpmFsmOp op) { - rpmRC (*hookFunc)(const char*, int); + plugin_fsm_file_pre_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_SCRIPTLET_PRE); - if (hookFunc(s_name, type) == RPMRC_FAIL) + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre); + if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name); rc = RPMRC_FAIL; + } } return rc; } -rpmRC rpmpluginsCallScriptletForkPost(rpmPlugins plugins, const char *path, int type) +rpmRC rpmpluginsCallFsmFilePost(rpmPlugins plugins, rpmfi fi, const char *path, + mode_t file_mode, rpmFsmOp op, int res) { - rpmRC (*hookFunc)(const char*, int); + plugin_fsm_file_post_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_SCRIPTLET_FORK_POST); - if (hookFunc(path, type) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_post); + if (hookFunc && hookFunc(plugin, fi, path, file_mode, op, res) == RPMRC_FAIL) { + rpmlog(RPMLOG_WARNING, "Plugin %s: hook fsm_file_post failed\n", plugin->name); + } } return rc; } -rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int type, int res) +rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, + const char *path, const char *dest, + mode_t file_mode, rpmFsmOp op) { - rpmRC (*hookFunc)(const char*, int, int); + plugin_fsm_file_prepare_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_SCRIPTLET_POST); - if (hookFunc(s_name, type, res) == RPMRC_FAIL) + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_prepare); + if (hookFunc && hookFunc(plugin, fi, path, dest, file_mode, op) == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_prepare failed\n", plugin->name); rc = RPMRC_FAIL; + } } return rc; } -rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd, +rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, int sigTagId, pgpDigParams sig, DIGEST_CTX ctx, int res) { - rpmRC (*hookFunc)(rpmKeyring, rpmtd, pgpDigParams, DIGEST_CTX, int); + plugin_psm_verify_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; + for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_VERIFY); - if (hookFunc(keyring, sigtd, sig, ctx, res) == RPMRC_FAIL) + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(psm_verify); + + if (hookFunc && hookFunc(keyring, sigTagId, sig, ctx, res) == RPMRC_FAIL) + { + rpmlog(RPMLOG_ERR, "Plugin %s: hook psm_verify failed\n", plugin->name); rc = RPMRC_FAIL; + } } return rc; @@ -336,16 +433,21 @@ rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd, rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path, mode_t mode) { - rpmRC (*hookFunc)(const char*, mode_t); + plugin_fsm_file_init_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; + for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FSM_INIT); - if (hookFunc(path, mode) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_init); + + if (hookFunc && hookFunc(path, mode) == RPMRC_FAIL) + { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_init failed\n", plugin->name); + rc = RPMRC_FAIL; + + } } return rc; @@ -354,16 +456,19 @@ rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path, rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path, mode_t mode, int type) { - rpmRC (*hookFunc)(const char*, mode_t, int); + plugin_fsm_file_commit_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FSM_COMMIT); - if (hookFunc(path, mode, type) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_commit); + + if (hookFunc && hookFunc(path, mode, type) == RPMRC_FAIL) + { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_commit failed\n", plugin->name); + rc = RPMRC_FAIL; + } } return rc; @@ -372,17 +477,22 @@ rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path, rpmRC rpmpluginsCallFileConflict(rpmPlugins plugins, rpmts ts, char* path, Header oldHeader, rpmfi oldFi, int res) { - rpmRC (*hookFunc)(rpmts, char*, Header, rpmfi, int); + plugin_fsm_file_conflict_func hookFunc; int i; rpmRC rc = RPMRC_OK; - const char *name = NULL; for (i = 0; i < plugins->count; i++) { - name = plugins->names[i]; - RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FILE_CONFLICT); - if (hookFunc(ts, path, oldHeader, oldFi, res) == RPMRC_FAIL) - rc = RPMRC_FAIL; + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(psm_verify); + + if (hookFunc && hookFunc(ts, path, oldHeader, oldFi, res) == RPMRC_FAIL) + { + rpmlog(RPMLOG_ERR, "Plugin %s: hook psm_verify failed\n", plugin->name); + rc = RPMRC_FAIL; + } } return rc; } + + diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h index 8e35d81ab..0ef6e3eb4 100644 --- a/lib/rpmplugins.h +++ b/lib/rpmplugins.h @@ -2,79 +2,19 @@ #define _PLUGINS_H #include <rpm/rpmtypes.h> +#include <rpm/rpmfi.h> +#include "lib/rpmplugin.h" #ifdef __cplusplus extern "C" { #endif -#define PLUGIN_HOOKS plugin_hooks - -#define PLUGINHOOK_INIT_FUNC pluginhook_init -#define PLUGINHOOK_CLEANUP_FUNC pluginhook_cleanup - -#define PLUGINHOOK_OPENTE_FUNC pluginhook_opente -#define PLUGINHOOK_COLL_POST_ADD_FUNC pluginhook_coll_post_add -#define PLUGINHOOK_COLL_POST_ANY_FUNC pluginhook_coll_post_any -#define PLUGINHOOK_COLL_PRE_REMOVE_FUNC pluginhook_coll_pre_remove - -#define PLUGINHOOK_TSM_PRE_FUNC pluginhook_tsm_pre -#define PLUGINHOOK_TSM_POST_FUNC pluginhook_tsm_post - -#define PLUGINHOOK_PSM_PRE_FUNC pluginhook_psm_pre -#define PLUGINHOOK_PSM_POST_FUNC pluginhook_psm_post -#define PLUGINHOOK_VERIFY_FUNC pluginhook_verify - -#define PLUGINHOOK_SCRIPTLET_PRE_FUNC pluginhook_scriptlet_pre -#define PLUGINHOOK_SCRIPTLET_FORK_POST_FUNC pluginhook_scriptlet_fork_post -#define PLUGINHOOK_SCRIPTLET_POST_FUNC pluginhook_scriptlet_post - -#define PLUGINHOOK_FSM_INIT_FUNC pluginhook_fsm_init -#define PLUGINHOOK_FSM_COMMIT_FUNC pluginhook_fsm_commit -#define PLUGINHOOK_FILE_CONFLICT pluginhook_file_conflict - -enum rpmPluginHook_e { - PLUGINHOOK_NONE = 0, - PLUGINHOOK_INIT = 1 << 0, - PLUGINHOOK_CLEANUP = 1 << 1, - PLUGINHOOK_OPENTE = 1 << 2, - PLUGINHOOK_COLL_POST_ADD = 1 << 3, - PLUGINHOOK_COLL_POST_ANY = 1 << 4, - PLUGINHOOK_COLL_PRE_REMOVE = 1 << 5, - PLUGINHOOK_TSM_PRE = 1 << 6, - PLUGINHOOK_TSM_POST = 1 << 7, - PLUGINHOOK_PSM_PRE = 1 << 8, - PLUGINHOOK_PSM_POST = 1 << 9, - PLUGINHOOK_SCRIPTLET_PRE = 1 << 10, - PLUGINHOOK_SCRIPTLET_FORK_POST = 1 << 11, - PLUGINHOOK_SCRIPTLET_POST = 1 << 12, - PLUGINHOOK_VERIFY = 1 << 13, - PLUGINHOOK_FSM_INIT = 1 << 14, - PLUGINHOOK_FSM_COMMIT = 1 << 15, - PLUGINHOOK_FILE_CONFLICT = 1 << 16 - -}; - -/* indicates if a directory is part of rpm package or created by rpm itself */ -typedef enum rpmPluginDirType_e { - DIR_TYPE_NONE = 0, - DIR_TYPE_NORMAL = 1 << 0, - DIR_TYPE_UNOWNED = 1 << 1 -} rpmPluginDirType; - -/* indicates the way the scriptlet is executed */ -typedef enum rpmScriptletExecutionFlow_e { - RPMSCRIPTLET_NONE = 0, - RPMSCRIPTLET_FORK = 1 << 0, - RPMSCRIPTLET_EXEC = 1 << 1 -} rpmScriptletExecutionFlow; - -typedef rpmFlags rpmPluginHook; - /** \ingroup rpmplugins * Create a new plugins structure * @param ts transaction set * @return new plugin structure */ +RPM_GNUC_INTERNAL rpmPlugins rpmpluginsNew(rpmts ts); /** \ingroup rpmplugins @@ -82,6 +22,7 @@ rpmPlugins rpmpluginsNew(rpmts ts); * @param plugins plugins structure to destroy * @return NULL always */ +RPM_GNUC_INTERNAL rpmPlugins rpmpluginsFree(rpmPlugins plugins); /** \ingroup rpmplugins @@ -92,15 +33,17 @@ rpmPlugins rpmpluginsFree(rpmPlugins plugins); * @param opts options to pass to the plugin * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsAdd(rpmPlugins plugins, const char *name, const char *path, const char *opts); /** \ingroup rpmplugins * Add and open a rpm plugin - * @param plugins plugins structure to add a collection plugin to + * @param plugins plugins structure to add a plugin to * @param type type of plugin * @param name name of plugin * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsAddPlugin(rpmPlugins plugins, const char *type, const char *name); /** \ingroup rpmplugins @@ -109,65 +52,16 @@ rpmRC rpmpluginsAddPlugin(rpmPlugins plugins, const char *type, const char *name * @param name name of plugin to check * @return 1 if plugin name has already been added, 0 otherwise */ +RPM_GNUC_INTERNAL int rpmpluginsPluginAdded(rpmPlugins plugins, const char *name); - -/** \ingroup rpmplugins - * Call the init plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @param opts plugin options - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallInit(rpmPlugins plugins, const char *name, const char *opts); - -/** \ingroup rpmplugins - * Call the cleanup plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallCleanup(rpmPlugins plugins, const char *name); - -/** \ingroup rpmplugins - * Call the open te plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @param te transaction element opened - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallOpenTE(rpmPlugins plugins, const char *name, rpmte te); - -/** \ingroup rpmplugins - * Call the collection post add plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallCollectionPostAdd(rpmPlugins plugins, const char *name); - -/** \ingroup rpmplugins - * Call the collection post any plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallCollectionPostAny(rpmPlugins plugins, const char *name); - -/** \ingroup rpmplugins - * Call the collection pre remove plugin hook - * @param plugins plugins structure - * @param name name of plugin - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmpluginsCallCollectionPreRemove(rpmPlugins plugins, const char *name); - /** \ingroup rpmplugins * Call the pre transaction plugin hook * @param plugins plugins structure * @param ts processed transaction * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallTsmPre(rpmPlugins plugins, rpmts ts); /** \ingroup rpmplugins @@ -177,6 +71,7 @@ rpmRC rpmpluginsCallTsmPre(rpmPlugins plugins, rpmts ts); * @param res transaction result code * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallTsmPost(rpmPlugins plugins, rpmts ts, int res); /** \ingroup rpmplugins @@ -185,6 +80,7 @@ rpmRC rpmpluginsCallTsmPost(rpmPlugins plugins, rpmts ts, int res); * @param te processed transaction element * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallPsmPre(rpmPlugins plugins, rpmte te); /** \ingroup rpmplugins @@ -194,6 +90,7 @@ rpmRC rpmpluginsCallPsmPre(rpmPlugins plugins, rpmte te); * @param res transaction element result code * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallPsmPost(rpmPlugins plugins, rpmte te, int res); /** \ingroup rpmplugins @@ -203,6 +100,7 @@ rpmRC rpmpluginsCallPsmPost(rpmPlugins plugins, rpmte te, int res); * @param type indicates the scriptlet execution flow, see rpmScriptletExecutionFlow * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallScriptletPre(rpmPlugins plugins, const char *s_name, int type); /** \ingroup rpmplugins @@ -212,6 +110,7 @@ rpmRC rpmpluginsCallScriptletPre(rpmPlugins plugins, const char *s_name, int typ * @param type indicates the scriptlet execution flow, see rpmScriptletExecutionFlow * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallScriptletForkPost(rpmPlugins plugins, const char *path, int type); /** \ingroup rpmplugins @@ -222,9 +121,52 @@ rpmRC rpmpluginsCallScriptletForkPost(rpmPlugins plugins, const char *path, int * @param res scriptlet execution result code * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int type, int res); /** \ingroup rpmplugins + * Call the fsm file pre plugin hook + * @param plugins plugins structure + * @param fi file info iterator (or NULL) + * @param path file object path + * @param file_mode file object mode + * @param op file operation + associated flags + * @return RPMRC_OK on success, RPMRC_FAIL otherwise + */ +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char* path, + mode_t file_mode, rpmFsmOp op); + +/** \ingroup rpmplugins + * Call the fsm file post plugin hook + * @param plugins plugins structure + * @param fi file info iterator (or NULL) + * @param path file object path + * @param file_mode file object mode + * @param op file operation + associated flags + * @param res fsm result code + * @return RPMRC_OK on success, RPMRC_FAIL otherwise + */ +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallFsmFilePost(rpmPlugins plugins, rpmfi fi, const char* path, + mode_t file_mode, rpmFsmOp op, int res); + +/** \ingroup rpmplugins + * Call the fsm file prepare plugin hook. Called after setting + * permissions etc, but before committing file to destination path. + * @param plugins plugins structure + * @param fi file info iterator (or NULL) + * @param path file object current path + * @param dest file object destination path + * @param mode file object mode + * @param op file operation + associated flags + * @return RPMRC_OK on success, RPMRC_FAIL otherwise + */ +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, + const char *path, const char *dest, + mode_t mode, rpmFsmOp op); +/** \ingroup rpmplugins * Call the verify hook * @param plugins plugins structure * @param keyring RPM keyring @@ -233,7 +175,8 @@ rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int ty * @param res scriptlet execution result code * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ -rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd, +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, int sigTagId, pgpDigParams sig, DIGEST_CTX ctx, int res); /** \ingroup rpmplugins @@ -243,8 +186,9 @@ rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd, * @param mode file mode * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path, mode_t mode); - + /** \ingroup rpmplugins * Call the fsm commit hook * @param plugins plugins structure @@ -253,6 +197,7 @@ rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path, mode_t mode); * @param type file type * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path, mode_t mode, int type); /** \ingroup rpmplugins @@ -265,8 +210,10 @@ rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path, mode_t mode, * @param res return code * @return RPMRC_OK on success, RPMRC_FAIL otherwise */ +RPM_GNUC_INTERNAL rpmRC rpmpluginsCallFileConflict(rpmPlugins plugins, rpmts ts, char* path, Header oldHeader, rpmfi oldFi, int res); + #ifdef __cplusplus } #endif diff --git a/lib/rpmprob.h b/lib/rpmprob.h index 2b89f15ef..ac57885c6 100644 --- a/lib/rpmprob.h +++ b/lib/rpmprob.h @@ -43,7 +43,7 @@ typedef enum rpmProblemType_e { RPMPROB_BADRELOCATE,/*!< path ... is not relocatable for package ... */ RPMPROB_REQUIRES, /*!< package ... has unsatisfied Requires: ... */ RPMPROB_CONFLICT, /*!< package ... has unsatisfied Conflicts: ... */ - RPMPROB_NEW_FILE_CONFLICT, /*!< file ... conflicts between attemped installs of ... */ + RPMPROB_NEW_FILE_CONFLICT, /*!< file ... conflicts between attempted installs of ... */ RPMPROB_FILE_CONFLICT,/*!< file ... from install of ... conflicts with file from package ... */ RPMPROB_OLDPACKAGE, /*!< package ... (which is newer than ...) is already installed */ RPMPROB_DISKSPACE, /*!< installing package ... needs ... on the ... filesystem */ diff --git a/lib/rpmrc.c b/lib/rpmrc.c index 7638e8114..cada3f788 100644 --- a/lib/rpmrc.c +++ b/lib/rpmrc.c @@ -1,6 +1,7 @@ #include "system.h" #include <stdarg.h> +#include <pthread.h> #if defined(__linux__) #include <elf.h> @@ -11,7 +12,6 @@ #if HAVE_SYS_UTSNAME_H #include <sys/utsname.h> #endif -#include <netdb.h> #include <ctype.h> /* XXX for /etc/rpm/platform contents */ #if HAVE_SYS_SYSTEMCFG_H @@ -20,32 +20,32 @@ #define __power_pc() 0 #endif +#ifdef HAVE_SYS_AUXV_H +#include <sys/auxv.h> +#endif + #include <rpm/rpmlib.h> /* RPM_MACTABLE*, Rc-prototypes */ #include <rpm/rpmmacro.h> #include <rpm/rpmfileutil.h> #include <rpm/rpmstring.h> #include <rpm/rpmlog.h> +#include <rpm/argv.h> #include "rpmio/rpmlua.h" #include "rpmio/rpmio_internal.h" /* XXX for rpmioSlurp */ #include "lib/misc.h" #include "lib/rpmliblua.h" +#include "lib/rpmug.h" #include "debug.h" static const char * defrcfiles = NULL; const char * macrofiles = NULL; -static const char * const platform = SYSCONFDIR "/rpm/platform"; -static char ** platpat = NULL; -static int nplatpat = 0; - -typedef char * cptr_t; - typedef struct machCacheEntry_s { char * name; int count; - cptr_t * equivs; + char ** equivs; int visited; } * machCacheEntry; @@ -111,13 +111,6 @@ typedef struct tableType_s { int canonsLength; } * tableType; -static struct tableType_s tables[RPM_MACHTABLE_COUNT] = { - { "arch", 1, 0 }, - { "os", 1, 0 }, - { "buildarch", 0, 1 }, - { "buildos", 0, 1 } -}; - /* XXX get rid of this stuff... */ /* Stuff for maintaining "variables" like SOURCEDIR, BUILDDIR, etc */ #define RPMVAR_OPTFLAGS 3 @@ -142,23 +135,58 @@ static const size_t optionTableSize = sizeof(optionTable) / sizeof(*optionTable) #define OS 0 #define ARCH 1 -static cptr_t current[2]; +typedef struct rpmrcCtx_s * rpmrcCtx; +struct rpmrcCtx_s { + ARGV_t platpat; + char *current[2]; + int currTables[2]; + struct rpmvarValue values[RPMVAR_NUM]; + struct tableType_s tables[RPM_MACHTABLE_COUNT]; + int machDefaults; + int pathDefaults; + pthread_rwlock_t lock; +}; -static int currTables[2] = { RPM_MACHTABLE_INSTOS, RPM_MACHTABLE_INSTARCH }; +/* prototypes */ +static rpmRC doReadRC(rpmrcCtx ctx, const char * urlfn); -static struct rpmvarValue values[RPMVAR_NUM]; +static void rpmSetVarArch(rpmrcCtx ctx, + int var, const char * val, const char * arch); -static int defaultsInitialized = 0; +static void rebuildCompatTables(rpmrcCtx ctx, int type, const char * name); -/* prototypes */ -static rpmRC doReadRC(const char * urlfn); +static void rpmRebuildTargetVars(rpmrcCtx ctx, const char **target, const char ** canontarget); -static void rpmSetVarArch(int var, const char * val, - const char * arch); +/* Force context (lock) acquisition through a function */ +static rpmrcCtx rpmrcCtxAcquire(int write) +{ + static struct rpmrcCtx_s _globalCtx = { + .lock = PTHREAD_RWLOCK_INITIALIZER, + .currTables = { RPM_MACHTABLE_INSTOS, RPM_MACHTABLE_INSTARCH }, + .tables = { + { "arch", 1, 0 }, + { "os", 1, 0 }, + { "buildarch", 0, 1 }, + { "buildos", 0, 1 } + }, + }; + rpmrcCtx ctx = &_globalCtx; + + /* XXX: errors should be handled */ + if (write) + pthread_rwlock_wrlock(&ctx->lock); + else + pthread_rwlock_rdlock(&ctx->lock); -static void rebuildCompatTables(int type, const char * name); + return ctx; +} -static void rpmRebuildTargetVars(const char **target, const char ** canontarget); +/* Release context (lock) */ +static rpmrcCtx rpmrcCtxRelease(rpmrcCtx ctx) +{ + pthread_rwlock_unlock(&ctx->lock); + return NULL; +} static int optionCompare(const void * a, const void * b) { @@ -452,7 +480,7 @@ static void setDefaults(void) } /* FIX: se usage inconsistent, W2DO? */ -static rpmRC doReadRC(const char * urlfn) +static rpmRC doReadRC(rpmrcCtx ctx, const char * urlfn) { char *s; char *se, *next, *buf = NULL, *fn; @@ -519,7 +547,7 @@ static rpmRC doReadRC(const char * urlfn) while (*se && !risspace(*se)) se++; if (*se != '\0') *se = '\0'; - if (doReadRC(s)) { + if (doReadRC(ctx, s)) { rpmlog(RPMLOG_ERR, _("cannot open %s at %s:%d: %m\n"), s, fn, linenum); goto exit; @@ -551,48 +579,49 @@ static rpmRC doReadRC(const char * urlfn) /* Only add macros if appropriate for this arch */ if (option->macroize && - (arch == NULL || rstreq(arch, current[ARCH]))) { + (arch == NULL || rstreq(arch, ctx->current[ARCH]))) { char *n, *name; n = name = xmalloc(strlen(option->name)+2); if (option->localize) *n++ = '_'; strcpy(n, option->name); - addMacro(NULL, name, NULL, val, RMIL_RPMRC); + rpmPushMacro(NULL, name, NULL, val, RMIL_RPMRC); free(name); } - rpmSetVarArch(option->var, val, arch); + rpmSetVarArch(ctx, option->var, val, arch); fn = _free(fn); - } else { /* For arch/os compatibilty tables ... */ + } else { /* For arch/os compatibility tables ... */ int gotit; int i; gotit = 0; for (i = 0; i < RPM_MACHTABLE_COUNT; i++) { - if (rstreqn(tables[i].key, s, strlen(tables[i].key))) + if (rstreqn(ctx->tables[i].key, s, strlen(ctx->tables[i].key))) break; } if (i < RPM_MACHTABLE_COUNT) { - const char *rest = s + strlen(tables[i].key); + const char *rest = s + strlen(ctx->tables[i].key); if (*rest == '_') rest++; if (rstreq(rest, "compat")) { if (machCompatCacheAdd(se, fn, linenum, - &tables[i].cache)) + &ctx->tables[i].cache)) goto exit; gotit = 1; - } else if (tables[i].hasTranslate && + } else if (ctx->tables[i].hasTranslate && rstreq(rest, "translate")) { - if (addDefault(&tables[i].defaults, - &tables[i].defaultsLength, + if (addDefault(&ctx->tables[i].defaults, + &ctx->tables[i].defaultsLength, se, fn, linenum)) goto exit; gotit = 1; - } else if (tables[i].hasCanon && + } else if (ctx->tables[i].hasCanon && rstreq(rest, "canon")) { - if (addCanon(&tables[i].canons, &tables[i].canonsLength, + if (addCanon(&ctx->tables[i].canons, + &ctx->tables[i].canonsLength, se, fn, linenum)) goto exit; gotit = 1; @@ -618,7 +647,7 @@ exit: /** */ -static rpmRC rpmPlatform(const char * platform) +static rpmRC rpmPlatform(rpmrcCtx ctx, const char * platform) { const char *cpu = NULL, *vendor = NULL, *os = NULL, *gnu = NULL; uint8_t * b = NULL; @@ -651,10 +680,7 @@ static rpmRC rpmPlatform(const char * platform) while (--t > p && isspace(*t)) *t = '\0'; if (t > p) { - platpat = xrealloc(platpat, (nplatpat + 2) * sizeof(*platpat)); - platpat[nplatpat] = xstrdup(p); - nplatpat++; - platpat[nplatpat] = NULL; + argvAdd(&ctx->platpat, p); } continue; } @@ -690,14 +716,14 @@ static rpmRC rpmPlatform(const char * platform) if (*p != '\0') *p = '\0'; } - addMacro(NULL, "_host_cpu", NULL, cpu, -1); - addMacro(NULL, "_host_vendor", NULL, vendor, -1); - addMacro(NULL, "_host_os", NULL, os, -1); + rpmPushMacro(NULL, "_host_cpu", NULL, cpu, -1); + rpmPushMacro(NULL, "_host_vendor", NULL, vendor, -1); + rpmPushMacro(NULL, "_host_os", NULL, os, -1); - platpat = xrealloc(platpat, (nplatpat + 2) * sizeof(*platpat)); - platpat[nplatpat] = rpmExpand("%{_host_cpu}-%{_host_vendor}-%{_host_os}", (gnu && *gnu ? "-" : NULL), gnu, NULL); - nplatpat++; - platpat[nplatpat] = NULL; + char *plat = rpmExpand("%{_host_cpu}-%{_host_vendor}-%{_host_os}", + (gnu && *gnu ? "-" : NULL), gnu, NULL); + argvAdd(&ctx->platpat, plat); + free(plat); init_platform++; } @@ -789,8 +815,21 @@ static inline int RPMClass(void) cpu = (tfms>>8)&15; + if (cpu == 5 + && cpuid_ecx(0) == '68xM' + && cpuid_edx(0) == 'Teni' + && (cpuid_edx(1) & ((1<<8)|(1<<15))) == ((1<<8)|(1<<15))) { + sigaction(SIGILL, &oldsa, NULL); + return 6; /* has CX8 and CMOV */ + } + sigaction(SIGILL, &oldsa, NULL); +#define USER686 ((1<<4) | (1<<8) | (1<<15)) + /* Transmeta Crusoe CPUs say that their CPU family is "5" but they have enough features for i686. */ + if (cpu == 5 && (cap & USER686) == USER686) + return 6; + if (cpu < 6) return cpu; @@ -912,13 +951,19 @@ static int is_geode(void) #if defined(__linux__) /** - * Populate rpmat structure with parsed info from /proc/self/auxv + * Populate rpmat structure with auxv values */ -static void parse_auxv(void) +static void read_auxv(void) { static int oneshot = 1; if (oneshot) { +#ifdef HAVE_GETAUXVAL + rpmat.platform = (char *) getauxval(AT_PLATFORM); + if (!rpmat.platform) + rpmat.platform = ""; + rpmat.hwcap = getauxval(AT_HWCAP); +#else rpmat.platform = ""; int fd = open("/proc/self/auxv", O_RDONLY); @@ -943,6 +988,7 @@ static void parse_auxv(void) } close(fd); } +#endif oneshot = 0; /* only try once even if it fails */ } return; @@ -951,22 +997,21 @@ static void parse_auxv(void) /** */ -static void defaultMachine(const char ** arch, - const char ** os) +static void defaultMachine(rpmrcCtx ctx, const char ** arch, const char ** os) { + const char * const platform_path = SYSCONFDIR "/rpm/platform"; static struct utsname un; - static int gotDefaults = 0; char * chptr; canonEntry canon; int rc; #if defined(__linux__) /* Populate rpmat struct with hw info */ - parse_auxv(); + read_auxv(); #endif - while (!gotDefaults) { - if (!rpmPlatform(platform)) { + while (!ctx->machDefaults) { + if (!rpmPlatform(ctx, platform_path)) { char * s = rpmExpand("%{_host_cpu}", NULL); if (s) { rstrlcpy(un.machine, s, sizeof(un.machine)); @@ -977,7 +1022,7 @@ static void defaultMachine(const char ** arch, rstrlcpy(un.sysname, s, sizeof(un.sysname)); free(s); } - gotDefaults = 1; + ctx->machDefaults = 1; break; } rc = uname(&un); @@ -988,11 +1033,15 @@ static void defaultMachine(const char ** arch, strcpy(un.machine, __power_pc() ? "ppc" : "rs6000"); sprintf(un.sysname,"aix%s.%s", un.version, un.release); } - else if(rstreq(un.sysname, "Darwin")) { -#ifdef __ppc__ + else if (rstreq(un.sysname, "Darwin")) { +#if defined(__ppc__) strcpy(un.machine, "ppc"); -#else ifdef __i386__ +#elif defined(__i386__) strcpy(un.machine, "i386"); +#elif defined(__x86_64__) + strcpy(un.machine, "x86_64"); +#else + #warning "No architecture defined! Automatic detection may not work!" #endif } else if (rstreq(un.sysname, "SunOS")) { @@ -1020,12 +1069,54 @@ static void defaultMachine(const char ** arch, # if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) /* little endian */ - strcpy(un.machine, "mipsel"); +# if defined(__mips64) + /* 64-bit */ +# if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + /* r1-r5 */ + strcpy(un.machine, "mips64el"); +# else + /* r6 */ + strcpy(un.machine, "mips64r6el"); +# endif +# else + /* 32-bit */ +# if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + /* r1-r5 */ + strcpy(un.machine, "mipsel"); +# else + /* r6 */ + strcpy(un.machine, "mipsr6el"); +# endif +# endif # elif defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) /* big endian */ - strcpy(un.machine, "mips"); +# if defined(__mips64) + /* 64-bit */ +# if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + /* r1-r5 */ + strcpy(un.machine, "mips64"); +# else + /* r6 */ + strcpy(un.machine, "mips64r6"); +# endif +# else + /* 32-bit */ +# if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + /* r1-r5 */ + strcpy(un.machine, "mips"); +# else + /* r6 */ + strcpy(un.machine, "mipsr6"); +# endif +# endif # endif +#if defined(__linux__) + /* in linux, lets rename parisc to hppa */ + if (rstreq(un.machine, "parisc")) + strcpy(un.machine, "hppa"); +#endif + # if defined(__hpux) && defined(_SC_CPU_VERSION) { # if !defined(CPU_PA_RISC1_2) @@ -1069,6 +1160,9 @@ static void defaultMachine(const char ** arch, # endif /* hpux */ # if defined(__linux__) && defined(__sparc__) +# if !defined(HWCAP_SPARC_BLKINIT) +# define HWCAP_SPARC_BLKINIT 0x00000040 +# endif if (rstreq(un.machine, "sparc")) { #define PERS_LINUX 0x00000000 #define PERS_LINUX_32BIT 0x00800000 @@ -1088,10 +1182,20 @@ static void defaultMachine(const char ** arch, } personality(oldpers); } + + /* This is how glibc detects Niagara so... */ + if (rpmat.hwcap & HWCAP_SPARC_BLKINIT) { + if (rstreq(un.machine, "sparcv9") || rstreq(un.machine, "sparc")) { + strcpy(un.machine, "sparcv9v"); + } else if (rstreq(un.machine, "sparc64")) { + strcpy(un.machine, "sparc64v"); + } + } } # endif /* sparc*-linux */ # if defined(__linux__) && defined(__powerpc__) +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ { int powerlvl; if (!rstreq(un.machine, "ppc") && @@ -1100,8 +1204,43 @@ static void defaultMachine(const char ** arch, strcpy(un.machine, "ppc64p7"); } } +# endif /* __ORDER_BIG_ENDIAN__ */ # endif /* ppc64*-linux */ +# if defined(__linux__) && defined(__arm__) && defined(__ARM_PCS_VFP) +# if !defined(HWCAP_ARM_VFP) +# define HWCAP_ARM_VFP (1 << 6) +# endif +# if !defined(HWCAP_ARM_NEON) +# define HWCAP_ARM_NEON (1 << 12) +# endif +# if !defined(HWCAP_ARM_VFPv3) +# define HWCAP_ARM_VFPv3 (1 << 13) +# endif + if (rstreq(un.machine, "armv7l")) { + if (rpmat.hwcap & HWCAP_ARM_VFPv3) { + if (rpmat.hwcap & HWCAP_ARM_NEON) + strcpy(un.machine, "armv7hnl"); + else + strcpy(un.machine, "armv7hl"); + } + } else if (rstreq(un.machine, "armv6l")) { + if (rpmat.hwcap & HWCAP_ARM_VFP) + strcpy(un.machine, "armv6hl"); + } +# endif /* arm*-linux */ + +# if defined(__linux__) && defined(__riscv__) + if (rstreq(un.machine, "riscv")) { + if (sizeof(long) == 4) + strcpy(un.machine, "riscv32"); + else if (sizeof(long) == 8) + strcpy(un.machine, "riscv64"); + else if (sizeof(long) == 16) + strcpy(un.machine, "riscv128"); + } +# endif /* riscv */ + # if defined(__GNUC__) && defined(__alpha__) { unsigned long amask, implver; @@ -1147,17 +1286,17 @@ static void defaultMachine(const char ** arch, /* the uname() result goes through the arch_canon table */ canon = lookupInCanonTable(un.machine, - tables[RPM_MACHTABLE_INSTARCH].canons, - tables[RPM_MACHTABLE_INSTARCH].canonsLength); + ctx->tables[RPM_MACHTABLE_INSTARCH].canons, + ctx->tables[RPM_MACHTABLE_INSTARCH].canonsLength); if (canon) rstrlcpy(un.machine, canon->short_name, sizeof(un.machine)); canon = lookupInCanonTable(un.sysname, - tables[RPM_MACHTABLE_INSTOS].canons, - tables[RPM_MACHTABLE_INSTOS].canonsLength); + ctx->tables[RPM_MACHTABLE_INSTOS].canons, + ctx->tables[RPM_MACHTABLE_INSTOS].canonsLength); if (canon) rstrlcpy(un.sysname, canon->short_name, sizeof(un.sysname)); - gotDefaults = 1; + ctx->machDefaults = 1; break; } @@ -1166,34 +1305,30 @@ static void defaultMachine(const char ** arch, } static -const char * rpmGetVarArch(int var, const char * arch) +const char * rpmGetVarArch(rpmrcCtx ctx, int var, const char * arch) { const struct rpmvarValue * next; - if (arch == NULL) arch = current[ARCH]; + if (arch == NULL) arch = ctx->current[ARCH]; if (arch) { - next = &values[var]; + next = &ctx->values[var]; while (next) { if (next->arch && rstreq(next->arch, arch)) return next->value; next = next->next; } } - next = values + var; + next = ctx->values + var; while (next && next->arch) next = next->next; return next ? next->value : NULL; } -static const char *rpmGetVar(int var) -{ - return rpmGetVarArch(var, NULL); -} - -static void rpmSetVarArch(int var, const char * val, const char * arch) +static void rpmSetVarArch(rpmrcCtx ctx, + int var, const char * val, const char * arch) { - struct rpmvarValue * next = values + var; + struct rpmvarValue * next = ctx->values + var; if (next->value) { if (arch) { @@ -1225,39 +1360,23 @@ static void rpmSetVarArch(int var, const char * val, const char * arch) next->arch = (arch ? xstrdup(arch) : NULL); } -static void rpmSetTables(int archTable, int osTable) +static void rpmSetTables(rpmrcCtx ctx, int archTable, int osTable) { const char * arch, * os; - defaultMachine(&arch, &os); + defaultMachine(ctx, &arch, &os); - if (currTables[ARCH] != archTable) { - currTables[ARCH] = archTable; - rebuildCompatTables(ARCH, arch); + if (ctx->currTables[ARCH] != archTable) { + ctx->currTables[ARCH] = archTable; + rebuildCompatTables(ctx, ARCH, arch); } - if (currTables[OS] != osTable) { - currTables[OS] = osTable; - rebuildCompatTables(OS, os); + if (ctx->currTables[OS] != osTable) { + ctx->currTables[OS] = osTable; + rebuildCompatTables(ctx, OS, os); } } -int rpmMachineScore(int type, const char * name) -{ - machEquivInfo info = NULL; - if (name) - info = machEquivSearch(&tables[type].equiv, name); - return info ? info->score : 0; -} - -int rpmIsKnownArch(const char *name) -{ - canonEntry canon = lookupInCanonTable(name, - tables[RPM_MACHTABLE_INSTARCH].canons, - tables[RPM_MACHTABLE_INSTARCH].canonsLength); - return (canon != NULL || rstreq(name, "noarch")); -} - /** \ingroup rpmrc * Set current arch/os names. * NULL as argument is set to the default value (munged uname()) @@ -1265,43 +1384,44 @@ int rpmIsKnownArch(const char *name) * @deprecated Use addMacro to set _target_* macros. * @todo Eliminate * + * @param ctx rpmrc context * @param arch arch name (or NULL) * @param os os name (or NULL) * */ -static void rpmSetMachine(const char * arch, const char * os) +static void rpmSetMachine(rpmrcCtx ctx, const char * arch, const char * os) { const char * host_cpu, * host_os; - defaultMachine(&host_cpu, &host_os); + defaultMachine(ctx, &host_cpu, &host_os); if (arch == NULL) { arch = host_cpu; - if (tables[currTables[ARCH]].hasTranslate) + if (ctx->tables[ctx->currTables[ARCH]].hasTranslate) arch = lookupInDefaultTable(arch, - tables[currTables[ARCH]].defaults, - tables[currTables[ARCH]].defaultsLength); + ctx->tables[ctx->currTables[ARCH]].defaults, + ctx->tables[ctx->currTables[ARCH]].defaultsLength); } if (arch == NULL) return; /* XXX can't happen */ if (os == NULL) { os = host_os; - if (tables[currTables[OS]].hasTranslate) + if (ctx->tables[ctx->currTables[OS]].hasTranslate) os = lookupInDefaultTable(os, - tables[currTables[OS]].defaults, - tables[currTables[OS]].defaultsLength); + ctx->tables[ctx->currTables[OS]].defaults, + ctx->tables[ctx->currTables[OS]].defaultsLength); } if (os == NULL) return; /* XXX can't happen */ - if (!current[ARCH] || !rstreq(arch, current[ARCH])) { - current[ARCH] = _free(current[ARCH]); - current[ARCH] = xstrdup(arch); - rebuildCompatTables(ARCH, host_cpu); + if (!ctx->current[ARCH] || !rstreq(arch, ctx->current[ARCH])) { + ctx->current[ARCH] = _free(ctx->current[ARCH]); + ctx->current[ARCH] = xstrdup(arch); + rebuildCompatTables(ctx, ARCH, host_cpu); } - if (!current[OS] || !rstreq(os, current[OS])) { + if (!ctx->current[OS] || !rstreq(os, ctx->current[OS])) { char * t = xstrdup(os); - current[OS] = _free(current[OS]); + ctx->current[OS] = _free(ctx->current[OS]); /* * XXX Capitalizing the 'L' is needed to insure that old * XXX os-from-uname (e.g. "Linux") is compatible with the new @@ -1312,79 +1432,49 @@ static void rpmSetMachine(const char * arch, const char * os) */ if (rstreq(t, "linux")) *t = 'L'; - current[OS] = t; + ctx->current[OS] = t; - rebuildCompatTables(OS, host_os); + rebuildCompatTables(ctx, OS, host_os); } } -static void rebuildCompatTables(int type, const char * name) +static void rebuildCompatTables(rpmrcCtx ctx, int type, const char * name) { - machFindEquivs(&tables[currTables[type]].cache, - &tables[currTables[type]].equiv, + machFindEquivs(&ctx->tables[ctx->currTables[type]].cache, + &ctx->tables[ctx->currTables[type]].equiv, name); } -static void getMachineInfo(int type, const char ** name, - int * num) +static void getMachineInfo(rpmrcCtx ctx, + int type, const char ** name, int * num) { canonEntry canon; - int which = currTables[type]; + int which = ctx->currTables[type]; /* use the normal canon tables, even if we're looking up build stuff */ if (which >= 2) which -= 2; - canon = lookupInCanonTable(current[type], - tables[which].canons, - tables[which].canonsLength); + canon = lookupInCanonTable(ctx->current[type], + ctx->tables[which].canons, + ctx->tables[which].canonsLength); if (canon) { if (num) *num = canon->num; if (name) *name = canon->short_name; } else { if (num) *num = 255; - if (name) *name = current[type]; + if (name) *name = ctx->current[type]; - if (tables[currTables[type]].hasCanon) { - rpmlog(RPMLOG_WARNING, _("Unknown system: %s\n"), current[type]); + if (ctx->tables[ctx->currTables[type]].hasCanon) { + rpmlog(RPMLOG_WARNING, _("Unknown system: %s\n"), + ctx->current[type]); rpmlog(RPMLOG_WARNING, _("Please contact %s\n"), PACKAGE_BUGREPORT); } } } -void rpmGetArchInfo(const char ** name, int * num) -{ - getMachineInfo(ARCH, name, num); -} - -int rpmGetArchColor(const char *arch) -{ - const char *color; - char *e; - int color_i; - - arch = lookupInDefaultTable(arch, - tables[currTables[ARCH]].defaults, - tables[currTables[ARCH]].defaultsLength); - color = rpmGetVarArch(RPMVAR_ARCHCOLOR, arch); - if (color == NULL) { - return -1; - } - - color_i = strtol(color, &e, 10); - if (!(e && *e == '\0')) { - return -1; - } - - return color_i; -} - -void rpmGetOsInfo(const char ** name, int * num) -{ - getMachineInfo(OS, name, num); -} - -static void rpmRebuildTargetVars(const char ** target, const char ** canontarget) +static void rpmRebuildTargetVars(rpmrcCtx ctx, + const char ** target, const char ** canontarget) { char *ca = NULL, *co = NULL, *ct = NULL; @@ -1392,9 +1482,9 @@ static void rpmRebuildTargetVars(const char ** target, const char ** canontarget /* Rebuild the compat table to recalculate the current target arch. */ - rpmSetMachine(NULL, NULL); - rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS); - rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS); + rpmSetMachine(ctx, NULL, NULL); + rpmSetTables(ctx, RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS); + rpmSetTables(ctx, RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS); if (target && *target) { char *c; @@ -1419,16 +1509,16 @@ static void rpmRebuildTargetVars(const char ** target, const char ** canontarget const char *a = NULL; const char *o = NULL; /* Set build target from rpm arch and os */ - rpmGetArchInfo(&a, NULL); + getMachineInfo(ctx, ARCH, &a, NULL); ca = (a) ? xstrdup(a) : NULL; - rpmGetOsInfo(&o, NULL); + getMachineInfo(ctx, OS, &o, NULL); co = (o) ? xstrdup(o) : NULL; } /* If still not set, Set target arch/os from default uname(2) values */ if (ca == NULL) { const char *a = NULL; - defaultMachine(&a, NULL); + defaultMachine(ctx, &a, NULL); ca = xstrdup(a ? a : "(arch)"); } for (x = 0; ca[x] != '\0'; x++) @@ -1436,7 +1526,7 @@ static void rpmRebuildTargetVars(const char ** target, const char ** canontarget if (co == NULL) { const char *o = NULL; - defaultMachine(NULL, &o); + defaultMachine(ctx, NULL, &o); co = xstrdup(o ? o : "(os)"); } for (x = 0; co[x] != '\0'; x++) @@ -1451,19 +1541,19 @@ static void rpmRebuildTargetVars(const char ** target, const char ** canontarget * XXX All this macro pokery/jiggery could be achieved by doing a delayed * rpmInitMacros(NULL, PER-PLATFORM-MACRO-FILE-NAMES); */ - delMacro(NULL, "_target"); - addMacro(NULL, "_target", NULL, ct, RMIL_RPMRC); - delMacro(NULL, "_target_cpu"); - addMacro(NULL, "_target_cpu", NULL, ca, RMIL_RPMRC); - delMacro(NULL, "_target_os"); - addMacro(NULL, "_target_os", NULL, co, RMIL_RPMRC); + rpmPopMacro(NULL, "_target"); + rpmPushMacro(NULL, "_target", NULL, ct, RMIL_RPMRC); + rpmPopMacro(NULL, "_target_cpu"); + rpmPushMacro(NULL, "_target_cpu", NULL, ca, RMIL_RPMRC); + rpmPopMacro(NULL, "_target_os"); + rpmPushMacro(NULL, "_target_os", NULL, co, RMIL_RPMRC); /* * XXX Make sure that per-arch optflags is initialized correctly. */ - { const char *optflags = rpmGetVarArch(RPMVAR_OPTFLAGS, ca); + { const char *optflags = rpmGetVarArch(ctx, RPMVAR_OPTFLAGS, ca); if (optflags != NULL) { - delMacro(NULL, "optflags"); - addMacro(NULL, "optflags", NULL, optflags, RMIL_RPMRC); + rpmPopMacro(NULL, "optflags"); + rpmPushMacro(NULL, "optflags", NULL, optflags, RMIL_RPMRC); } } @@ -1475,97 +1565,20 @@ static void rpmRebuildTargetVars(const char ** target, const char ** canontarget free(co); } -void rpmFreeRpmrc(void) -{ - int i, j, k; - - if (platpat) - for (i = 0; i < nplatpat; i++) - platpat[i] = _free(platpat[i]); - platpat = _free(platpat); - nplatpat = 0; - - for (i = 0; i < RPM_MACHTABLE_COUNT; i++) { - tableType t; - t = tables + i; - if (t->equiv.list) { - for (j = 0; j < t->equiv.count; j++) - t->equiv.list[j].name = _free(t->equiv.list[j].name); - t->equiv.list = _free(t->equiv.list); - t->equiv.count = 0; - } - if (t->cache.cache) { - for (j = 0; j < t->cache.size; j++) { - machCacheEntry e; - e = t->cache.cache + j; - if (e == NULL) - continue; - e->name = _free(e->name); - if (e->equivs) { - for (k = 0; k < e->count; k++) - e->equivs[k] = _free(e->equivs[k]); - e->equivs = _free(e->equivs); - } - } - t->cache.cache = _free(t->cache.cache); - t->cache.size = 0; - } - if (t->defaults) { - for (j = 0; j < t->defaultsLength; j++) { - t->defaults[j].name = _free(t->defaults[j].name); - t->defaults[j].defName = _free(t->defaults[j].defName); - } - t->defaults = _free(t->defaults); - t->defaultsLength = 0; - } - if (t->canons) { - for (j = 0; j < t->canonsLength; j++) { - t->canons[j].name = _free(t->canons[j].name); - t->canons[j].short_name = _free(t->canons[j].short_name); - } - t->canons = _free(t->canons); - t->canonsLength = 0; - } - } - - for (i = 0; i < RPMVAR_NUM; i++) { - struct rpmvarValue * vp; - while ((vp = values[i].next) != NULL) { - values[i].next = vp->next; - vp->value = _free(vp->value); - vp->arch = _free(vp->arch); - vp = _free(vp); - } - values[i].value = _free(values[i].value); - values[i].arch = _free(values[i].arch); - } - current[OS] = _free(current[OS]); - current[ARCH] = _free(current[ARCH]); - defaultsInitialized = 0; -/* FIX: platpat/current may be NULL */ - - /* XXX doesn't really belong here but... */ - rpmFreeCrypto(); -#ifdef WITH_LUA - rpmLuaFree(); -#endif - - return; -} - /** \ingroup rpmrc * Read rpmrc (and macro) configuration file(s). + * @param ctx rpmrc context * @param rcfiles colon separated files to read (NULL uses default) * @return RPMRC_OK on success */ -static rpmRC rpmReadRC(const char * rcfiles) +static rpmRC rpmReadRC(rpmrcCtx ctx, const char * rcfiles) { ARGV_t p, globs = NULL, files = NULL; rpmRC rc = RPMRC_FAIL; - if (!defaultsInitialized) { + if (!ctx->pathDefaults) { setDefaults(); - defaultsInitialized = 1; + ctx->pathDefaults = 1; } if (rcfiles == NULL) @@ -1592,29 +1605,47 @@ static rpmRC rpmReadRC(const char * rcfiles) goto exit; break; } else { - rc = doReadRC(*p); + rc = doReadRC(ctx, *p); } } rc = RPMRC_OK; - rpmSetMachine(NULL, NULL); /* XXX WTFO? Why bother? */ + rpmSetMachine(ctx, NULL, NULL); /* XXX WTFO? Why bother? */ exit: argvFree(files); return rc; } +static void register_atexit(void) +{ + if (atexit(rpmAtExit) != 0) + rpmlog(RPMLOG_WARNING, _("failed to register exit handler")); +} + +/* External interfaces */ + int rpmReadConfigFiles(const char * file, const char * target) { + static pthread_once_t atexit_registered = PTHREAD_ONCE_INIT; + int rc = -1; /* assume failure */ + rpmrcCtx ctx = rpmrcCtxAcquire(1); + + pthread_once(&atexit_registered, register_atexit); + /* Force preloading of dlopen()'ed libraries in case we go chrooting */ - (void) gethostbyname("localhost"); - (void) rpmInitCrypto(); + if (rpmugInit()) + goto exit; + + if (rpmInitCrypto()) + goto exit; /* Preset target macros */ /* FIX: target can be NULL */ - rpmRebuildTargetVars(&target, NULL); + rpmRebuildTargetVars(ctx, &target, NULL); /* Read the files */ - if (rpmReadRC(file)) return -1; + if (rpmReadRC(ctx, file)) + goto exit; if (macrofiles != NULL) { char *mf = rpmGetPath(macrofiles, NULL); @@ -1623,12 +1654,12 @@ int rpmReadConfigFiles(const char * file, const char * target) } /* Reset target macros */ - rpmRebuildTargetVars(&target, NULL); + rpmRebuildTargetVars(ctx, &target, NULL); /* Finally set target platform */ { char *cpu = rpmExpand("%{_target_cpu}", NULL); char *os = rpmExpand("%{_target_os}", NULL); - rpmSetMachine(cpu, os); + rpmSetMachine(ctx, cpu, os); free(cpu); free(os); } @@ -1637,12 +1668,93 @@ int rpmReadConfigFiles(const char * file, const char * target) /* Force Lua state initialization */ rpmLuaInit(); #endif + rc = 0; - return 0; +exit: + rpmrcCtxRelease(ctx); + return rc; +} + +void rpmFreeRpmrc(void) +{ + rpmrcCtx ctx = rpmrcCtxAcquire(1); + int i, j, k; + + ctx->platpat = argvFree(ctx->platpat); + + for (i = 0; i < RPM_MACHTABLE_COUNT; i++) { + tableType t; + t = ctx->tables + i; + if (t->equiv.list) { + for (j = 0; j < t->equiv.count; j++) + t->equiv.list[j].name = _free(t->equiv.list[j].name); + t->equiv.list = _free(t->equiv.list); + t->equiv.count = 0; + } + if (t->cache.cache) { + for (j = 0; j < t->cache.size; j++) { + machCacheEntry e; + e = t->cache.cache + j; + if (e == NULL) + continue; + e->name = _free(e->name); + if (e->equivs) { + for (k = 0; k < e->count; k++) + e->equivs[k] = _free(e->equivs[k]); + e->equivs = _free(e->equivs); + } + } + t->cache.cache = _free(t->cache.cache); + t->cache.size = 0; + } + if (t->defaults) { + for (j = 0; j < t->defaultsLength; j++) { + t->defaults[j].name = _free(t->defaults[j].name); + t->defaults[j].defName = _free(t->defaults[j].defName); + } + t->defaults = _free(t->defaults); + t->defaultsLength = 0; + } + if (t->canons) { + for (j = 0; j < t->canonsLength; j++) { + t->canons[j].name = _free(t->canons[j].name); + t->canons[j].short_name = _free(t->canons[j].short_name); + } + t->canons = _free(t->canons); + t->canonsLength = 0; + } + } + + for (i = 0; i < RPMVAR_NUM; i++) { + struct rpmvarValue * vp; + while ((vp = ctx->values[i].next) != NULL) { + ctx->values[i].next = vp->next; + vp->value = _free(vp->value); + vp->arch = _free(vp->arch); + vp = _free(vp); + } + ctx->values[i].value = _free(ctx->values[i].value); + ctx->values[i].arch = _free(ctx->values[i].arch); + } + ctx->current[OS] = _free(ctx->current[OS]); + ctx->current[ARCH] = _free(ctx->current[ARCH]); + ctx->machDefaults = 0; + ctx->pathDefaults = 0; + + /* XXX doesn't really belong here but... */ + rpmFreeCrypto(); +#ifdef WITH_LUA + rpmLuaFree(); +#endif + + rpmrcCtxRelease(ctx); + return; } int rpmShowRC(FILE * fp) { + /* Write-context necessary as this calls rpmSetTables(), ugh */ + rpmrcCtx ctx = rpmrcCtxAcquire(1); const struct rpmOption *opt; rpmds ds = NULL; int i; @@ -1650,43 +1762,43 @@ int rpmShowRC(FILE * fp) /* the caller may set the build arch which should be printed here */ fprintf(fp, "ARCHITECTURE AND OS:\n"); - fprintf(fp, "build arch : %s\n", current[ARCH]); + fprintf(fp, "build arch : %s\n", ctx->current[ARCH]); fprintf(fp, "compatible build archs:"); - equivTable = &tables[RPM_MACHTABLE_BUILDARCH].equiv; + equivTable = &ctx->tables[RPM_MACHTABLE_BUILDARCH].equiv; for (i = 0; i < equivTable->count; i++) fprintf(fp," %s", equivTable->list[i].name); fprintf(fp, "\n"); - fprintf(fp, "build os : %s\n", current[OS]); + fprintf(fp, "build os : %s\n", ctx->current[OS]); fprintf(fp, "compatible build os's :"); - equivTable = &tables[RPM_MACHTABLE_BUILDOS].equiv; + equivTable = &ctx->tables[RPM_MACHTABLE_BUILDOS].equiv; for (i = 0; i < equivTable->count; i++) fprintf(fp," %s", equivTable->list[i].name); fprintf(fp, "\n"); - rpmSetTables(RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS); - rpmSetMachine(NULL, NULL); /* XXX WTFO? Why bother? */ + rpmSetTables(ctx, RPM_MACHTABLE_INSTARCH, RPM_MACHTABLE_INSTOS); + rpmSetMachine(ctx, NULL, NULL); /* XXX WTFO? Why bother? */ - fprintf(fp, "install arch : %s\n", current[ARCH]); - fprintf(fp, "install os : %s\n", current[OS]); + fprintf(fp, "install arch : %s\n", ctx->current[ARCH]); + fprintf(fp, "install os : %s\n", ctx->current[OS]); fprintf(fp, "compatible archs :"); - equivTable = &tables[RPM_MACHTABLE_INSTARCH].equiv; + equivTable = &ctx->tables[RPM_MACHTABLE_INSTARCH].equiv; for (i = 0; i < equivTable->count; i++) fprintf(fp," %s", equivTable->list[i].name); fprintf(fp, "\n"); fprintf(fp, "compatible os's :"); - equivTable = &tables[RPM_MACHTABLE_INSTOS].equiv; + equivTable = &ctx->tables[RPM_MACHTABLE_INSTOS].equiv; for (i = 0; i < equivTable->count; i++) fprintf(fp," %s", equivTable->list[i].name); fprintf(fp, "\n"); fprintf(fp, "\nRPMRC VALUES:\n"); for (i = 0, opt = optionTable; i < optionTableSize; i++, opt++) { - const char *s = rpmGetVar(opt->var); + const char *s = rpmGetVarArch(ctx, opt->var, NULL); if (s != NULL || rpmIsVerbose()) fprintf(fp, "%-21s : %s\n", opt->name, s ? s : "(not set)"); } @@ -1703,7 +1815,74 @@ int rpmShowRC(FILE * fp) ds = rpmdsFree(ds); fprintf(fp, "\n"); + fprintf(fp, "Macro path: %s\n", macrofiles); + fprintf(fp, "\n"); + rpmDumpMacroTable(NULL, fp); + /* XXX: Move this earlier eventually... */ + rpmrcCtxRelease(ctx); + return 0; } + +int rpmMachineScore(int type, const char * name) +{ + int score = 0; + if (name) { + rpmrcCtx ctx = rpmrcCtxAcquire(0); + machEquivInfo info = machEquivSearch(&ctx->tables[type].equiv, name); + if (info) + score = info->score; + rpmrcCtxRelease(ctx); + } + return score; +} + +int rpmIsKnownArch(const char *name) +{ + rpmrcCtx ctx = rpmrcCtxAcquire(0); + canonEntry canon = lookupInCanonTable(name, + ctx->tables[RPM_MACHTABLE_INSTARCH].canons, + ctx->tables[RPM_MACHTABLE_INSTARCH].canonsLength); + int known = (canon != NULL || rstreq(name, "noarch")); + rpmrcCtxRelease(ctx); + return known; +} + +void rpmGetArchInfo(const char ** name, int * num) +{ + rpmrcCtx ctx = rpmrcCtxAcquire(0); + getMachineInfo(ctx, ARCH, name, num); + rpmrcCtxRelease(ctx); +} + +int rpmGetArchColor(const char *arch) +{ + rpmrcCtx ctx = rpmrcCtxAcquire(0); + const char *color; + char *e; + int color_i = -1; /* assume failure */ + + arch = lookupInDefaultTable(arch, + ctx->tables[ctx->currTables[ARCH]].defaults, + ctx->tables[ctx->currTables[ARCH]].defaultsLength); + color = rpmGetVarArch(ctx, RPMVAR_ARCHCOLOR, arch); + if (color) { + color_i = strtol(color, &e, 10); + if (!(e && *e == '\0')) { + color_i = -1; + } + } + rpmrcCtxRelease(ctx); + + return color_i; +} + +void rpmGetOsInfo(const char ** name, int * num) +{ + rpmrcCtx ctx = rpmrcCtxAcquire(0); + getMachineInfo(ctx, OS, name, num); + rpmrcCtxRelease(ctx); +} + diff --git a/lib/rpmscript.c b/lib/rpmscript.c index a27251c9a..cc98c4885 100644 --- a/lib/rpmscript.c +++ b/lib/rpmscript.c @@ -10,6 +10,7 @@ #include <rpm/rpmio.h> #include <rpm/rpmlog.h> #include <rpm/header.h> +#include <rpm/rpmds.h> #include "rpmio/rpmlua.h" #include "lib/rpmscript.h" @@ -18,20 +19,73 @@ #include "debug.h" +struct scriptNextFileFunc_s { + char *(*func)(void *); /* function producing input for script */ + void *param; /* parameter for func */ +}; + +typedef struct scriptNextFileFunc_s *scriptNextFileFunc; + struct rpmScript_s { + rpmscriptTypes type; /* script type */ rpmTagVal tag; /* script tag */ char **args; /* scriptlet call arguments */ char *body; /* script body */ char *descr; /* description for logging */ rpmscriptFlags flags; /* flags to control operation */ + struct scriptNextFileFunc_s nextFileFunc; /* input function */ +}; + +struct scriptInfo_s { + rpmscriptTypes type; + const char *desc; + rpmsenseFlags sense; + rpmTagVal tag; + rpmTagVal progtag; + rpmTagVal flagtag; +}; + +static const struct scriptInfo_s scriptInfo[] = { + { RPMSCRIPT_PREIN, "%prein", 0, + RPMTAG_PREIN, RPMTAG_PREINPROG, RPMTAG_PREINFLAGS }, + { RPMSCRIPT_PREUN, "%preun", 0, + RPMTAG_PREUN, RPMTAG_PREUNPROG, RPMTAG_PREUNFLAGS }, + { RPMSCRIPT_POSTIN, "%post", 0, + RPMTAG_POSTIN, RPMTAG_POSTINPROG, RPMTAG_POSTINFLAGS }, + { RPMSCRIPT_POSTUN, "%postun", 0, + RPMTAG_POSTUN, RPMTAG_POSTUNPROG, RPMTAG_POSTUNFLAGS }, + { RPMSCRIPT_PRETRANS, "%pretrans", 0, + RPMTAG_PRETRANS, RPMTAG_PRETRANSPROG, RPMTAG_PRETRANSFLAGS }, + { RPMSCRIPT_POSTTRANS, "%posttrans", 0, + RPMTAG_POSTTRANS, RPMTAG_POSTTRANSPROG, RPMTAG_POSTTRANSFLAGS }, + { RPMSCRIPT_TRIGGERPREIN, "%triggerprein", RPMSENSE_TRIGGERPREIN, + RPMTAG_TRIGGERPREIN, 0, 0 }, + { RPMSCRIPT_TRIGGERUN, "%triggerun", RPMSENSE_TRIGGERUN, + RPMTAG_TRIGGERUN, 0, 0 }, + { RPMSCRIPT_TRIGGERIN, "%triggerin", RPMSENSE_TRIGGERIN, + RPMTAG_TRIGGERIN, 0, 0 }, + { RPMSCRIPT_TRIGGERPOSTUN, "%triggerpostun", RPMSENSE_TRIGGERPOSTUN, + RPMTAG_TRIGGERPOSTUN, 0, 0 }, + { RPMSCRIPT_VERIFY, "%verify", 0, + RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS}, + { 0, "unknown", 0, + RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND } }; +static const struct scriptInfo_s * findTag(rpmTagVal tag) +{ + const struct scriptInfo_s * si = scriptInfo; + while (si->type && si->tag != tag) + si++; + return si; +} /** * Run internal Lua script. */ -static rpmRC runLuaScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes, +static rpmRC runLuaScript(rpmPlugins plugins, ARGV_const_t prefixes, const char *sname, rpmlogLvl lvl, FD_t scriptFd, - ARGV_t * argvp, const char *script, int arg1, int arg2) + ARGV_t * argvp, const char *script, int arg1, int arg2, + scriptNextFileFunc nextFileFunc) { rpmRC rc = RPMRC_FAIL; #ifdef WITH_LUA @@ -45,6 +99,7 @@ static rpmRC runLuaScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes /* Create arg variable */ rpmluaPushTable(lua, "arg"); rpmluavSetListMode(var, 1); + rpmluaSetNextFileFunc(nextFileFunc->func, nextFileFunc->param); if (argv) { char **p; for (p = argv; *p; p++) { @@ -68,10 +123,16 @@ static rpmRC runLuaScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes if (cwd != -1) { mode_t oldmask = umask(0); umask(oldmask); + pid_t pid = getpid(); if (chdir("/") == 0 && rpmluaRunScript(lua, script, sname) == 0) { rc = RPMRC_OK; } + if (pid != getpid()) { + /* Terminate child process forked in lua scriptlet */ + rpmlog(RPMLOG_ERR, _("No exec() called after fork() in lua scriptlet\n")); + _exit(EXIT_FAILURE); + } /* This failing would be fatal, return something different for it... */ if (fchdir(cwd)) { rpmlog(RPMLOG_ERR, _("Unable to restore current directory: %m")); @@ -93,22 +154,16 @@ static rpmRC runLuaScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin"; -static void doScriptExec(int selinux, ARGV_const_t argv, ARGV_const_t prefixes, +static void doScriptExec(ARGV_const_t argv, ARGV_const_t prefixes, FD_t scriptFd, FD_t out) { - int pipes[2]; int flag; int fdno; int xx; int open_max; + /* SIGPIPE is ignored in rpm, reset to default for the scriptlet */ (void) signal(SIGPIPE, SIG_DFL); - pipes[0] = pipes[1] = 0; - /* make stdin inaccessible */ - xx = pipe(pipes); - xx = close(pipes[1]); - xx = dup2(pipes[0], STDIN_FILENO); - xx = close(pipes[0]); /* XXX Force FD_CLOEXEC on all inherited fdno's. */ open_max = sysconf(_SC_OPEN_MAX); @@ -165,11 +220,6 @@ static void doScriptExec(int selinux, ARGV_const_t argv, ARGV_const_t prefixes, /* XXX Don't mtrace into children. */ unsetenv("MALLOC_CHECK_"); - /* Permit libselinux to do the scriptlet exec. */ - if (selinux == 1) { - xx = rpm_execcon(0, argv[0], argv, environ); - } - if (xx == 0) { xx = execv(argv[0], argv); } @@ -204,14 +254,19 @@ exit: /** * Run an external script. */ -static rpmRC runExtScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes, +static rpmRC runExtScript(rpmPlugins plugins, ARGV_const_t prefixes, const char *sname, rpmlogLvl lvl, FD_t scriptFd, - ARGV_t * argvp, const char *script, int arg1, int arg2) + ARGV_t * argvp, const char *script, int arg1, int arg2, + scriptNextFileFunc nextFileFunc) { FD_t out = NULL; char * fn = NULL; pid_t pid, reaped; int status; + int inpipe[2]; + FILE *in = NULL; + const char *line; + char *mline = NULL; rpmRC rc = RPMRC_FAIL; rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname); @@ -234,6 +289,14 @@ static rpmRC runExtScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes } } + if (pipe(inpipe) < 0) { + rpmlog(RPMLOG_ERR, + ("Couldn't create pipe: %s\n"), strerror(errno)); + goto exit; + } + in = fdopen(inpipe[1], "w"); + inpipe[1] = 0; + if (scriptFd != NULL) { if (rpmIsVerbose()) { out = fdDup(Fileno(scriptFd)); @@ -261,13 +324,41 @@ static rpmRC runExtScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n", sname, *argvp[0], (unsigned)getpid()); + fclose(in); + dup2(inpipe[0], STDIN_FILENO); + /* Run scriptlet post fork hook for all plugins */ if (rpmpluginsCallScriptletForkPost(plugins, *argvp[0], RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC) != RPMRC_FAIL) { - doScriptExec(selinux, *argvp, prefixes, scriptFd, out); + doScriptExec(*argvp, prefixes, scriptFd, out); } else { _exit(126); /* exit 126 for compatibility with bash(1) */ } } + close(inpipe[0]); + inpipe[0] = 0; + + if (nextFileFunc->func) { + while ((line = nextFileFunc->func(nextFileFunc->param)) != NULL) { + size_t size = strlen(line); + size_t ret_size; + mline = xstrdup(line); + mline[size] = '\n'; + + ret_size = fwrite(mline, size + 1, 1, in); + mline = _free(mline); + if (ret_size != 1) { + if (errno == EPIPE) { + break; + } else { + rpmlog(RPMLOG_ERR, _("Fwrite failed: %s"), strerror(errno)); + rc = RPMRC_FAIL; + goto exit; + } + } + } + } + fclose(in); + in = NULL; do { reaped = waitpid(pid, &status, 0); @@ -293,6 +384,12 @@ static rpmRC runExtScript(rpmPlugins plugins, int selinux, ARGV_const_t prefixes } exit: + if (in) + fclose(in); + + if (inpipe[0]) + close(inpipe[0]); + if (out) Fclose(out); /* XXX dup'd STDOUT_FILENO */ @@ -301,11 +398,13 @@ exit: unlink(fn); free(fn); } + free(mline); + return rc; } rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd, - ARGV_const_t prefixes, int warn_only, int selinux, rpmPlugins plugins) + ARGV_const_t prefixes, int warn_only, rpmPlugins plugins) { ARGV_t args = NULL; rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR; @@ -329,9 +428,9 @@ rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd, if (rc != RPMRC_FAIL) { if (script_type & RPMSCRIPTLET_EXEC) { - rc = runExtScript(plugins, selinux, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2); + rc = runExtScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc); } else { - rc = runLuaScript(plugins, selinux, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2); + rc = runLuaScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc); } } @@ -343,54 +442,24 @@ rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd, return rc; } +static rpmscriptTypes getScriptType(rpmTagVal scriptTag) +{ + return findTag(scriptTag)->type; +} + static rpmTagVal getProgTag(rpmTagVal scriptTag) { - switch (scriptTag) { - case RPMTAG_PREIN: return RPMTAG_PREINPROG; - case RPMTAG_POSTIN: return RPMTAG_POSTINPROG; - case RPMTAG_PREUN: return RPMTAG_PREUNPROG; - case RPMTAG_POSTUN: return RPMTAG_POSTUNPROG; - case RPMTAG_PRETRANS: return RPMTAG_PRETRANSPROG; - case RPMTAG_POSTTRANS: return RPMTAG_POSTTRANSPROG; - case RPMTAG_VERIFYSCRIPT: return RPMTAG_VERIFYSCRIPTPROG; - default: return RPMTAG_NOT_FOUND; - } + return findTag(scriptTag)->progtag; } static rpmTagVal getFlagTag(rpmTagVal scriptTag) { - switch (scriptTag) { - case RPMTAG_PRETRANS: return RPMTAG_PRETRANSFLAGS; - case RPMTAG_POSTTRANS: return RPMTAG_POSTTRANSFLAGS; - case RPMTAG_PREUN: return RPMTAG_PREUNFLAGS; - case RPMTAG_POSTUN: return RPMTAG_POSTUNFLAGS; - case RPMTAG_PREIN: return RPMTAG_PREINFLAGS; - case RPMTAG_POSTIN: return RPMTAG_POSTINFLAGS; - case RPMTAG_VERIFYSCRIPT: return RPMTAG_VERIFYSCRIPTFLAGS; - case RPMTAG_TRIGGERSCRIPTS: return RPMTAG_TRIGGERSCRIPTFLAGS; - default: - break; - } - return RPMTAG_NOT_FOUND; + return findTag(scriptTag)->flagtag; } static const char * tag2sln(rpmTagVal tag) { - switch (tag) { - case RPMTAG_PRETRANS: return "%pretrans"; - case RPMTAG_TRIGGERPREIN: return "%triggerprein"; - case RPMTAG_PREIN: return "%pre"; - case RPMTAG_POSTIN: return "%post"; - case RPMTAG_TRIGGERIN: return "%triggerin"; - case RPMTAG_TRIGGERUN: return "%triggerun"; - case RPMTAG_PREUN: return "%preun"; - case RPMTAG_POSTUN: return "%postun"; - case RPMTAG_POSTTRANS: return "%posttrans"; - case RPMTAG_TRIGGERPOSTUN: return "%triggerpostun"; - case RPMTAG_VERIFYSCRIPT: return "%verify"; - default: break; - } - return "%unknownscript"; + return findTag(tag)->desc; } static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body, @@ -399,6 +468,7 @@ static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body, char *nevra = headerGetAsString(h, RPMTAG_NEVRA); rpmScript script = xcalloc(1, sizeof(*script)); script->tag = tag; + script->type = getScriptType(tag); script->flags = flags; script->body = (body != NULL) ? xstrdup(body) : NULL; rasprintf(&script->descr, "%s(%s)", tag2sln(tag), nevra); @@ -416,20 +486,101 @@ static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body, script->body = body; } + script->nextFileFunc.func = NULL; + script->nextFileFunc.param = NULL; + free(nevra); return script; } -rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag, uint32_t ix) +void rpmScriptSetNextFileFunc(rpmScript script, char *(*func)(void *), + void *param) +{ + script->nextFileFunc.func = func; + script->nextFileFunc.param = param; +} + +rpmTagVal triggerDsTag(rpmscriptTriggerModes tm) +{ + rpmTagVal tag = RPMTAG_NOT_FOUND; + switch (tm) { + case RPMSCRIPT_NORMALTRIGGER: + tag = RPMTAG_TRIGGERNAME; + break; + case RPMSCRIPT_FILETRIGGER: + tag = RPMTAG_FILETRIGGERNAME; + break; + case RPMSCRIPT_TRANSFILETRIGGER: + tag = RPMTAG_TRANSFILETRIGGERNAME; + break; + } + return tag; +} + +rpmscriptTriggerModes triggerMode(rpmTagVal tag) +{ + rpmscriptTriggerModes tm = 0; + switch (tag) { + case RPMTAG_TRIGGERNAME: + tm = RPMSCRIPT_NORMALTRIGGER; + break; + case RPMTAG_FILETRIGGERNAME: + tm = RPMSCRIPT_FILETRIGGER; + break; + case RPMTAG_TRANSFILETRIGGERNAME: + tm = RPMSCRIPT_TRANSFILETRIGGER; + break; + } + return tm; +} + +rpmTagVal triggertag(rpmsenseFlags sense) +{ + rpmTagVal tag = RPMTAG_NOT_FOUND; + switch (sense) { + case RPMSENSE_TRIGGERIN: + tag = RPMTAG_TRIGGERIN; + break; + case RPMSENSE_TRIGGERUN: + tag = RPMTAG_TRIGGERUN; + break; + case RPMSENSE_TRIGGERPOSTUN: + tag = RPMTAG_TRIGGERPOSTUN; + break; + case RPMSENSE_TRIGGERPREIN: + tag = RPMTAG_TRIGGERPREIN; + break; + default: + break; + } + return tag; +} + +rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag, + rpmscriptTriggerModes tm, uint32_t ix) { rpmScript script = NULL; struct rpmtd_s tscripts, tprogs, tflags; headerGetFlags hgflags = HEADERGET_MINMEM; - headerGet(h, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags); - headerGet(h, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags); - headerGet(h, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags); - + switch (tm) { + case RPMSCRIPT_NORMALTRIGGER: + headerGet(h, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags); + headerGet(h, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags); + headerGet(h, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags); + break; + case RPMSCRIPT_FILETRIGGER: + headerGet(h, RPMTAG_FILETRIGGERSCRIPTS, &tscripts, hgflags); + headerGet(h, RPMTAG_FILETRIGGERSCRIPTPROG, &tprogs, hgflags); + headerGet(h, RPMTAG_FILETRIGGERSCRIPTFLAGS, &tflags, hgflags); + break; + case RPMSCRIPT_TRANSFILETRIGGER: + headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTS, &tscripts, hgflags); + headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTPROG, &tprogs, hgflags); + headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS, &tflags, hgflags); + break; + } + if (rpmtdSetIndex(&tscripts, ix) >= 0 && rpmtdSetIndex(&tprogs, ix) >= 0) { rpmscriptFlags sflags = 0; const char *prog = rpmtdGetString(&tprogs); @@ -487,3 +638,8 @@ rpmTagVal rpmScriptTag(rpmScript script) { return (script != NULL) ? script->tag : RPMTAG_NOT_FOUND; } + +rpmscriptTypes rpmScriptType(rpmScript script) +{ + return (script != NULL) ? script->type : 0; +} diff --git a/lib/rpmscript.h b/lib/rpmscript.h index 852735b8d..3768077c7 100644 --- a/lib/rpmscript.h +++ b/lib/rpmscript.h @@ -3,6 +3,33 @@ #include <rpm/rpmtypes.h> #include <rpm/argv.h> +#include <rpm/rpmds.h> + +/* Rpm scriptlet types */ +enum rpmscriptTypes_e { + RPMSCRIPT_PREIN = (1 << 0), + RPMSCRIPT_PREUN = (1 << 1), + RPMSCRIPT_POSTIN = (1 << 2), + RPMSCRIPT_POSTUN = (1 << 3), + RPMSCRIPT_TRIGGERPREIN = (1 << 4), + RPMSCRIPT_TRIGGERUN = (1 << 6), + RPMSCRIPT_TRIGGERIN = (1 << 5), + RPMSCRIPT_TRIGGERPOSTUN = (1 << 7), + RPMSCRIPT_PRETRANS = (1 << 8), + RPMSCRIPT_POSTTRANS = (1 << 9), + /* ... */ + RPMSCRIPT_VERIFY = (1 << 24), +}; + +typedef rpmFlags rpmscriptTypes; + +enum rpmscriptTriggerMode_e { + RPMSCRIPT_NORMALTRIGGER = (1 << 0), + RPMSCRIPT_FILETRIGGER = (1 << 1), + RPMSCRIPT_TRANSFILETRIGGER = (1 << 2), +}; + +typedef rpmFlags rpmscriptTriggerModes; enum rpmscriptFlags_e { RPMSCRIPT_FLAG_NONE = 0, @@ -19,20 +46,37 @@ extern "C" { #endif RPM_GNUC_INTERNAL +rpmTagVal triggerDsTag(rpmscriptTriggerModes tm); + +RPM_GNUC_INTERNAL +rpmscriptTriggerModes triggerMode(rpmTagVal tag); + +RPM_GNUC_INTERNAL +rpmTagVal triggertag(rpmsenseFlags sense); + +RPM_GNUC_INTERNAL rpmScript rpmScriptFromTag(Header h, rpmTagVal scriptTag); RPM_GNUC_INTERNAL -rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag, uint32_t ix); +rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag, + rpmscriptTriggerModes tm, uint32_t ix); RPM_GNUC_INTERNAL rpmScript rpmScriptFree(rpmScript script); RPM_GNUC_INTERNAL rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd, - ARGV_const_t prefixes, int warn_only, int selinux, rpmPlugins plugins); + ARGV_const_t prefixes, int warn_only, rpmPlugins plugins); RPM_GNUC_INTERNAL rpmTagVal rpmScriptTag(rpmScript script); + +RPM_GNUC_INTERNAL +rpmscriptTypes rpmScriptType(rpmScript script); + +RPM_GNUC_INTERNAL +void rpmScriptSetNextFileFunc(rpmScript script, char *(*func)(void *), + void *param); #ifdef __cplusplus } #endif diff --git a/lib/rpmtag.h b/lib/rpmtag.h index 51691018e..276df7a23 100644 --- a/lib/rpmtag.h +++ b/lib/rpmtag.h @@ -1,6 +1,12 @@ #ifndef _RPMTAG_H #define _RPMTAG_H +/** \ingroup rpmtag + * \file lib/rpmtag.h + * + * Accessing RPM tags: values, types, ... + */ + #include <rpm/rpmtypes.h> #ifdef __cplusplus @@ -57,6 +63,8 @@ typedef enum rpmTag_e { #define RPMTAG_HDRID RPMTAG_SHA1HEADER /* s */ RPMTAG_LONGSIGSIZE = RPMTAG_SIG_BASE+14, /* l */ RPMTAG_LONGARCHIVESIZE = RPMTAG_SIG_BASE+15, /* l */ + /* RPMTAG_SIG_BASE+16 reserved */ + RPMTAG_SHA256HEADER = RPMTAG_SIG_BASE+17, /* s */ RPMTAG_NAME = 1000, /* s */ #define RPMTAG_N RPMTAG_NAME /* s */ @@ -117,8 +125,8 @@ typedef enum rpmTag_e { RPMTAG_REQUIRENAME = 1049, /* s[] */ #define RPMTAG_REQUIRES RPMTAG_REQUIRENAME /* s[] */ RPMTAG_REQUIREVERSION = 1050, /* s[] */ - RPMTAG_NOSOURCE = 1051, /* i */ - RPMTAG_NOPATCH = 1052, /* i */ + RPMTAG_NOSOURCE = 1051, /* i[] */ + RPMTAG_NOPATCH = 1052, /* i[] */ RPMTAG_CONFLICTFLAGS = 1053, /* i[] */ RPMTAG_CONFLICTNAME = 1054, /* s[] */ #define RPMTAG_CONFLICTS RPMTAG_CONFLICTNAME /* s[] */ @@ -217,14 +225,14 @@ typedef enum rpmTag_e { RPMTAG_PRETRANSPROG = 1153, /* s[] */ RPMTAG_POSTTRANSPROG = 1154, /* s[] */ RPMTAG_DISTTAG = 1155, /* s */ - RPMTAG_SUGGESTSNAME = 1156, /* s[] extension */ -#define RPMTAG_SUGGESTS RPMTAG_SUGGESTSNAME /* s[] */ - RPMTAG_SUGGESTSVERSION = 1157, /* s[] extension */ - RPMTAG_SUGGESTSFLAGS = 1158, /* i[] extension */ - RPMTAG_ENHANCESNAME = 1159, /* s[] extension */ -#define RPMTAG_ENHANCES RPMTAG_ENHANCESNAME /* s[] */ - RPMTAG_ENHANCESVERSION = 1160, /* s[] extension */ - RPMTAG_ENHANCESFLAGS = 1161, /* i[] extension */ + RPMTAG_OLDSUGGESTSNAME = 1156, /* s[] - obsolete */ +#define RPMTAG_OLDSUGGESTS RPMTAG_OLDSUGGESTSNAME /* s[] - obsolete */ + RPMTAG_OLDSUGGESTSVERSION = 1157, /* s[] - obsolete */ + RPMTAG_OLDSUGGESTSFLAGS = 1158, /* i[] - obsolete */ + RPMTAG_OLDENHANCESNAME = 1159, /* s[] - obsolete */ +#define RPMTAG_OLDENHANCES RPMTAG_OLDENHANCESNAME /* s[] - obsolete */ + RPMTAG_OLDENHANCESVERSION = 1160, /* s[] - obsolete */ + RPMTAG_OLDENHANCESFLAGS = 1161, /* i[] - obsolete */ RPMTAG_PRIORITY = 1162, /* i[] extension placeholder (unimplemented) */ RPMTAG_CVSID = 1163, /* s (unimplemented) */ #define RPMTAG_SVNID RPMTAG_CVSID /* s (unimplemented) */ @@ -261,6 +269,7 @@ typedef enum rpmTag_e { RPMTAG_BUILDOBSOLETES = 1194, /* internal (unimplemented) */ RPMTAG_DBINSTANCE = 1195, /* i extension */ RPMTAG_NVRA = 1196, /* s extension */ + /* tags 1997-4999 reserved */ RPMTAG_FILENAMES = 5000, /* s[] extension */ RPMTAG_FILEPROVIDE = 5001, /* s[] extension */ @@ -290,7 +299,7 @@ typedef enum rpmTag_e { RPMTAG_POSTTRANSFLAGS = 5025, /* i */ RPMTAG_VERIFYSCRIPTFLAGS = 5026, /* i */ RPMTAG_TRIGGERSCRIPTFLAGS = 5027, /* i[] */ - RPMTAG_COLLECTIONS = 5029, /* s[] list of collections */ + RPMTAG_COLLECTIONS = 5029, /* s[] list of collections (unimplemented) */ RPMTAG_POLICYNAMES = 5030, /* s[] */ RPMTAG_POLICYTYPES = 5031, /* s[] */ RPMTAG_POLICYTYPESINDEXES = 5032, /* i[] */ @@ -307,8 +316,60 @@ typedef enum rpmTag_e { RPMTAG_OBSOLETENEVRS = 5043, /* s[] extension */ RPMTAG_CONFLICTNEVRS = 5044, /* s[] extension */ RPMTAG_FILENLINKS = 5045, /* i[] extension */ - /* Skip numbers which might be used in new RPM versions */ - RPMTAG_BUILDINFO = 7000, /* s[] information about build */ + RPMTAG_RECOMMENDNAME = 5046, /* s[] */ +#define RPMTAG_RECOMMENDS RPMTAG_RECOMMENDNAME /* s[] */ + RPMTAG_RECOMMENDVERSION = 5047, /* s[] */ + RPMTAG_RECOMMENDFLAGS = 5048, /* i[] */ + RPMTAG_SUGGESTNAME = 5049, /* s[] */ +#define RPMTAG_SUGGESTS RPMTAG_SUGGESTNAME /* s[] */ + RPMTAG_SUGGESTVERSION = 5050, /* s[] extension */ + RPMTAG_SUGGESTFLAGS = 5051, /* i[] extension */ + RPMTAG_SUPPLEMENTNAME = 5052, /* s[] */ +#define RPMTAG_SUPPLEMENTS RPMTAG_SUPPLEMENTNAME /* s[] */ + RPMTAG_SUPPLEMENTVERSION = 5053, /* s[] */ + RPMTAG_SUPPLEMENTFLAGS = 5054, /* i[] */ + RPMTAG_ENHANCENAME = 5055, /* s[] */ +#define RPMTAG_ENHANCES RPMTAG_ENHANCENAME /* s[] */ + RPMTAG_ENHANCEVERSION = 5056, /* s[] */ + RPMTAG_ENHANCEFLAGS = 5057, /* i[] */ + RPMTAG_RECOMMENDNEVRS = 5058, /* s[] extension */ + RPMTAG_SUGGESTNEVRS = 5059, /* s[] extension */ + RPMTAG_SUPPLEMENTNEVRS = 5060, /* s[] extension */ + RPMTAG_ENHANCENEVRS = 5061, /* s[] extension */ + RPMTAG_ENCODING = 5062, /* s */ + RPMTAG_FILETRIGGERIN = 5063, /* internal */ + RPMTAG_FILETRIGGERUN = 5064, /* internal */ + RPMTAG_FILETRIGGERPOSTUN = 5065, /* internal */ + RPMTAG_FILETRIGGERSCRIPTS = 5066, /* s[] */ + RPMTAG_FILETRIGGERSCRIPTPROG = 5067, /* s[] */ + RPMTAG_FILETRIGGERSCRIPTFLAGS = 5068, /* i[] */ + RPMTAG_FILETRIGGERNAME = 5069, /* s[] */ + RPMTAG_FILETRIGGERINDEX = 5070, /* i[] */ + RPMTAG_FILETRIGGERVERSION = 5071, /* s[] */ + RPMTAG_FILETRIGGERFLAGS = 5072, /* i[] */ + RPMTAG_TRANSFILETRIGGERIN = 5073, /* internal */ + RPMTAG_TRANSFILETRIGGERUN = 5074, /* internal */ + RPMTAG_TRANSFILETRIGGERPOSTUN = 5075, /* internal */ + RPMTAG_TRANSFILETRIGGERSCRIPTS = 5076, /* s[] */ + RPMTAG_TRANSFILETRIGGERSCRIPTPROG = 5077, /* s[] */ + RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS = 5078, /* i[] */ + RPMTAG_TRANSFILETRIGGERNAME = 5079, /* s[] */ + RPMTAG_TRANSFILETRIGGERINDEX = 5080, /* i[] */ + RPMTAG_TRANSFILETRIGGERVERSION = 5081, /* s[] */ + RPMTAG_TRANSFILETRIGGERFLAGS = 5082, /* i[] */ + RPMTAG_REMOVEPATHPOSTFIXES = 5083, /* s internal */ + RPMTAG_FILETRIGGERPRIORITIES = 5084, /* i[] */ + RPMTAG_TRANSFILETRIGGERPRIORITIES = 5085, /* i[] */ + RPMTAG_FILETRIGGERCONDS = 5086, /* s[] extension */ + RPMTAG_FILETRIGGERTYPE = 5087, /* s[] extension */ + RPMTAG_TRANSFILETRIGGERCONDS = 5088, /* s[] extension */ + RPMTAG_TRANSFILETRIGGERTYPE = 5089, /* s[] extension */ + RPMTAG_FILESIGNATURES = 5090, /* s[] */ + RPMTAG_FILESIGNATURELENGTH = 5091, /* i */ + RPMTAG_PAYLOADDIGEST = 5092, /* s[] */ + RPMTAG_PAYLOADDIGESTALGO = 5093, /* i */ + /* Skip numbers which might be used in new RPM versions */ + RPMTAG_BUILDINFO = 7000, /* s[] information about build */ RPMTAG_FIRSTFREE_TAG /*!< internal */ } rpmTag; @@ -334,6 +395,12 @@ typedef enum rpmDbiTag_e { RPMDBI_SIGMD5 = RPMTAG_SIGMD5, RPMDBI_SHA1HEADER = RPMTAG_SHA1HEADER, RPMDBI_INSTFILENAMES = RPMTAG_INSTFILENAMES, + RPMDBI_FILETRIGGERNAME = RPMTAG_FILETRIGGERNAME, + RPMDBI_TRANSFILETRIGGERNAME = RPMTAG_TRANSFILETRIGGERNAME, + RPMDBI_RECOMMENDNAME = RPMTAG_RECOMMENDNAME, + RPMDBI_SUGGESTNAME = RPMTAG_SUGGESTNAME, + RPMDBI_SUPPLEMENTNAME = RPMTAG_SUPPLEMENTNAME, + RPMDBI_ENHANCENAME = RPMTAG_ENHANCENAME, } rpmDbiTag; /** \ingroup signature @@ -348,13 +415,15 @@ typedef enum rpmSigTag_e { RPMSIGTAG_GPG = 1005, /*!< internal GnuPG signature. */ RPMSIGTAG_PGP5 = 1006, /*!< internal PGP5 signature @deprecated legacy. */ RPMSIGTAG_PAYLOADSIZE = 1007,/*!< internal uncompressed payload size (32bit) in bytes. */ + RPMSIGTAG_RESERVEDSPACE = 1008,/*!< internal space reserved for signatures */ RPMSIGTAG_BADSHA1_1 = RPMTAG_BADSHA1_1, /*!< internal Broken SHA1, take 1. */ RPMSIGTAG_BADSHA1_2 = RPMTAG_BADSHA1_2, /*!< internal Broken SHA1, take 2. */ - RPMSIGTAG_SHA1 = RPMTAG_SHA1HEADER, /*!< internal sha1 header digest. */ RPMSIGTAG_DSA = RPMTAG_DSAHEADER, /*!< internal DSA header signature. */ RPMSIGTAG_RSA = RPMTAG_RSAHEADER, /*!< internal RSA header signature. */ + RPMSIGTAG_SHA1 = RPMTAG_SHA1HEADER, /*!< internal sha1 header digest. */ RPMSIGTAG_LONGSIZE = RPMTAG_LONGSIGSIZE, /*!< internal Header+Payload size (64bit) in bytes. */ RPMSIGTAG_LONGARCHIVESIZE = RPMTAG_LONGARCHIVESIZE, /*!< internal uncompressed payload size (64bit) in bytes. */ + RPMSIGTAG_SHA256 = RPMTAG_SHA256HEADER, } rpmSigTag; diff --git a/lib/rpmtd.c b/lib/rpmtd.c index 550d733ab..e33c8cb53 100644 --- a/lib/rpmtd.c +++ b/lib/rpmtd.c @@ -19,7 +19,7 @@ rpmtd rpmtdFree(rpmtd td) { /* permit free on NULL td */ if (td != NULL) { - /* XXX should we free data too - a flag maybe? */ + rpmtdFreeData(td); free(td); } return NULL; @@ -27,19 +27,16 @@ rpmtd rpmtdFree(rpmtd td) void rpmtdReset(rpmtd td) { - assert(td != NULL); - - memset(td, 0, sizeof(*td)); - td->ix = -1; + if (td) { + memset(td, 0, sizeof(*td)); + td->ix = -1; + } } void rpmtdFreeData(rpmtd td) { - assert(td != NULL); - - if (td->flags & RPMTD_ALLOCED) { + if (td && td->data && td->flags & RPMTD_ALLOCED) { if (td->flags & RPMTD_PTR_ALLOCED) { - assert(td->data != NULL); char **data = td->data; for (int i = 0; i < td->count; i++) { free(data[i]); @@ -52,27 +49,32 @@ void rpmtdFreeData(rpmtd td) rpm_count_t rpmtdCount(rpmtd td) { - assert(td != NULL); - /* fix up for binary type abusing count as data length */ - return (td->type == RPM_BIN_TYPE) ? 1 : td->count; + rpm_count_t count = 0; + if (td != NULL) { + /* fix up for binary type abusing count as data length */ + count = (td->type == RPM_BIN_TYPE) ? 1 : td->count; + } + return count; +} + +rpm_count_t rpmtdSize(rpmtd td) +{ + return (td != NULL) ? td->size : 0; } rpmTagVal rpmtdTag(rpmtd td) { - assert(td != NULL); - return td->tag; + return (td != NULL) ? td->tag : 0; } rpmTagType rpmtdType(rpmtd td) { - assert(td != NULL); - return td->type; + return (td != NULL) ? td->type : 0; } rpmTagClass rpmtdClass(rpmtd td) { - assert(td != NULL); - return rpmTagTypeGetClass(td->type); + return (td != NULL) ? rpmTagTypeGetClass(td->type) : 0; } rpmtdFlags rpmtdGetFlags(rpmtd td) @@ -82,15 +84,12 @@ rpmtdFlags rpmtdGetFlags(rpmtd td) int rpmtdGetIndex(rpmtd td) { - assert(td != NULL); - return td->ix; + return (td != NULL) ? td->ix : -1; } int rpmtdSetIndex(rpmtd td, int index) { - assert(td != NULL); - - if (index < 0 || index >= rpmtdCount(td)) { + if (td == NULL || index < 0 || index >= rpmtdCount(td)) { return -1; } td->ix = index; @@ -99,7 +98,8 @@ int rpmtdSetIndex(rpmtd td, int index) int rpmtdInit(rpmtd td) { - assert(td != NULL); + if (td == NULL) + return -1; /* XXX check that this is an array type? */ td->ix = -1; @@ -108,11 +108,9 @@ int rpmtdInit(rpmtd td) int rpmtdNext(rpmtd td) { - assert(td != NULL); - int i = -1; - if (++td->ix >= 0) { + if (td != NULL && ++td->ix >= 0) { if (td->ix < rpmtdCount(td)) { i = td->ix; } else { @@ -124,7 +122,6 @@ int rpmtdNext(rpmtd td) uint32_t *rpmtdNextUint32(rpmtd td) { - assert(td != NULL); uint32_t *res = NULL; if (rpmtdNext(td) >= 0) { res = rpmtdGetUint32(td); @@ -134,7 +131,6 @@ uint32_t *rpmtdNextUint32(rpmtd td) uint64_t *rpmtdNextUint64(rpmtd td) { - assert(td != NULL); uint64_t *res = NULL; if (rpmtdNext(td) >= 0) { res = rpmtdGetUint64(td); @@ -144,7 +140,6 @@ uint64_t *rpmtdNextUint64(rpmtd td) const char *rpmtdNextString(rpmtd td) { - assert(td != NULL); const char *res = NULL; if (rpmtdNext(td) >= 0) { res = rpmtdGetString(td); @@ -156,9 +151,7 @@ char * rpmtdGetChar(rpmtd td) { char *res = NULL; - assert(td != NULL); - - if (td->type == RPM_CHAR_TYPE) { + if (td != NULL && td->type == RPM_CHAR_TYPE) { int ix = (td->ix >= 0 ? td->ix : 0); res = (char *) td->data + ix; } @@ -168,9 +161,7 @@ uint16_t * rpmtdGetUint16(rpmtd td) { uint16_t *res = NULL; - assert(td != NULL); - - if (td->type == RPM_INT16_TYPE) { + if (td != NULL && td->type == RPM_INT16_TYPE) { int ix = (td->ix >= 0 ? td->ix : 0); res = (uint16_t *) td->data + ix; } @@ -181,9 +172,7 @@ uint32_t * rpmtdGetUint32(rpmtd td) { uint32_t *res = NULL; - assert(td != NULL); - - if (td->type == RPM_INT32_TYPE) { + if (td != NULL && td->type == RPM_INT32_TYPE) { int ix = (td->ix >= 0 ? td->ix : 0); res = (uint32_t *) td->data + ix; } @@ -194,9 +183,7 @@ uint64_t * rpmtdGetUint64(rpmtd td) { uint64_t *res = NULL; - assert(td != NULL); - - if (td->type == RPM_INT64_TYPE) { + if (td != NULL && td->type == RPM_INT64_TYPE) { int ix = (td->ix >= 0 ? td->ix : 0); res = (uint64_t *) td->data + ix; } @@ -207,7 +194,8 @@ const char * rpmtdGetString(rpmtd td) { const char *str = NULL; - assert(td != NULL); + if (td == NULL) + return NULL; if (td->type == RPM_STRING_TYPE) { str = (const char *) td->data; @@ -222,10 +210,12 @@ const char * rpmtdGetString(rpmtd td) uint64_t rpmtdGetNumber(rpmtd td) { - assert(td != NULL); uint64_t val = 0; int ix = (td->ix >= 0 ? td->ix : 0); + if (td == NULL) + return 0; + switch (td->type) { case RPM_INT64_TYPE: val = *((uint64_t *) td->data + ix); @@ -248,12 +238,12 @@ uint64_t rpmtdGetNumber(rpmtd td) char *rpmtdFormat(rpmtd td, rpmtdFormats fmt, const char *errmsg) { - headerTagFormatFunction func = rpmHeaderFormatFuncByValue(fmt); + headerFmt ext = rpmHeaderFormatByValue(fmt); const char *err = NULL; char *str = NULL; - if (func) { - str = func(td); + if (ext) { + str = rpmHeaderFormatCall(ext, td); } else { err = _("Unknown format"); } @@ -267,10 +257,12 @@ char *rpmtdFormat(rpmtd td, rpmtdFormats fmt, const char *errmsg) int rpmtdSetTag(rpmtd td, rpmTagVal tag) { - assert(td != NULL); rpmTagType newtype = rpmTagGetTagType(tag); int rc = 0; + if (td == NULL) + goto exit; + /* * Sanity checks: * - is the new tag valid at all @@ -425,8 +417,10 @@ rpmtd rpmtdDup(rpmtd td) rpmtd newtd = NULL; char **data = NULL; int i; + + if (td == NULL) + return NULL; - assert(td != NULL); /* TODO: permit other types too */ if (td->type != RPM_STRING_ARRAY_TYPE && td->type != RPM_I18NSTRING_TYPE) { return NULL; diff --git a/lib/rpmtd.h b/lib/rpmtd.h index de7101123..040ddb340 100644 --- a/lib/rpmtd.h +++ b/lib/rpmtd.h @@ -1,6 +1,12 @@ #ifndef _RPMTD_H #define _RPMTD_H +/** \ingroup rpmtd + * \file lib/rpmtd.h + * + * RPM Tag Data Container API + */ + #include <rpm/rpmtypes.h> #include <rpm/argv.h> @@ -30,6 +36,7 @@ struct rpmtd_s { rpm_data_t data; /* pointer to actual data */ rpmtdFlags flags; /* flags on memory allocation etc */ int ix; /* iteration index */ + rpm_count_t size; /* size of data (only works for RPMTD_IMMUTABLE atm) */ }; /** \ingroup rpmtd @@ -67,6 +74,14 @@ void rpmtdFreeData(rpmtd td); rpm_count_t rpmtdCount(rpmtd td); /** \ingroup rpmtd + * Retrieve container data size (eg required for allocation). + * Note this currently only works for RPMTD_IMMUTABLE data. + * @param td Tag data container + * @return Data size in bytes. + */ +rpm_count_t rpmtdSize(rpmtd td); + +/** \ingroup rpmtd * Retrieve tag of the container. * @param td Tag data container * @return Rpm tag. diff --git a/lib/rpmte.c b/lib/rpmte.c index da6f48c5c..66194dc3b 100644 --- a/lib/rpmte.c +++ b/lib/rpmte.c @@ -13,6 +13,7 @@ #include <rpm/rpmdb.h> #include <rpm/rpmlog.h> +#include "lib/misc.h" #include "lib/rpmplugins.h" #include "lib/rpmte_internal.h" /* strpool-related interfaces */ @@ -50,7 +51,8 @@ struct rpmte_s { rpmds conflicts; /*!< Conflicts: dependencies. */ rpmds obsoletes; /*!< Obsoletes: dependencies. */ rpmds order; /*!< Order: dependencies. */ - rpmfi fi; /*!< File information. */ + rpmfiles files; /*!< File information. */ + rpmfi fi; /*!< File iterator (backwards compat) */ rpmps probs; /*!< Problems (relocations) */ rpmts ts; /*!< Parent transaction */ @@ -70,11 +72,6 @@ struct rpmte_s { int failed; /*!< (parent) install/erase failed */ rpmfs fs; - - ARGV_t lastInCollectionsAny; /*!< list of collections this te is the last to be installed or removed */ - ARGV_t lastInCollectionsAdd; /*!< list of collections this te is the last to be only installed */ - ARGV_t firstInCollectionsRemove; /*!< list of collections this te is the first to be only removed */ - ARGV_t collections; /*!< list of collections */ }; /* forward declarations */ @@ -91,111 +88,26 @@ void rpmteCleanDS(rpmte te) te->order = rpmdsFree(te->order); } -static rpmfi getFI(rpmte p, Header h) +static rpmfiles getFiles(rpmte p, Header h) { rpmfiFlags fiflags; fiflags = (p->type == TR_ADDED) ? (RPMFI_NOHEADER | RPMFI_FLAGS_INSTALL) : (RPMFI_NOHEADER | RPMFI_FLAGS_ERASE); /* relocate stuff in header if necessary */ - if (rpmteType(p) == TR_ADDED && rpmfsFC(p->fs) > 0 && p->nrelocs) { - if (!headerIsSource(h) && !headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { - rpmRelocateFileList(p->relocs, p->nrelocs, p->fs, h); - } - } - return rpmfiNewPool(rpmtsPool(p->ts), h, RPMTAG_BASENAMES, fiflags); -} - -/* stupid bubble sort, but it's probably faster here */ -static void sortRelocs(rpmRelocation *relocations, int numRelocations) -{ - for (int i = 0; i < numRelocations; i++) { - int madeSwap = 0; - for (int j = 1; j < numRelocations; j++) { - rpmRelocation tmpReloc; - if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */ - relocations[j ].oldPath == NULL || /* XXX can't happen */ - strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0) - continue; - tmpReloc = relocations[j - 1]; - relocations[j - 1] = relocations[j]; - relocations[j] = tmpReloc; - madeSwap = 1; - } - if (!madeSwap) break; - } -} - -static char * stripTrailingChar(char * s, char c) -{ - char * t; - for (t = s + strlen(s) - 1; *t == c && t >= s; t--) - *t = '\0'; - return s; -} - -static void buildRelocs(rpmte p, Header h, rpmRelocation *relocs) -{ - int i; - struct rpmtd_s validRelocs; - - for (rpmRelocation *r = relocs; r->oldPath || r->newPath; r++) - p->nrelocs++; - - headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM); - p->relocs = xmalloc(sizeof(*p->relocs) * (p->nrelocs+1)); - - /* Build sorted relocation list from raw relocations. */ - for (i = 0; i < p->nrelocs; i++) { - char * t; - - /* - * Default relocations (oldPath == NULL) are handled in the UI, - * not rpmlib. - */ - if (relocs[i].oldPath == NULL) continue; /* XXX can't happen */ - - /* FIXME: Trailing /'s will confuse us greatly. Internal ones will - too, but those are more trouble to fix up. :-( */ - t = xstrdup(relocs[i].oldPath); - p->relocs[i].oldPath = (t[0] == '/' && t[1] == '\0') - ? t - : stripTrailingChar(t, '/'); - - /* An old path w/o a new path is valid, and indicates exclusion */ - if (relocs[i].newPath) { - int valid = 0; - const char *validprefix; - - t = xstrdup(relocs[i].newPath); - p->relocs[i].newPath = (t[0] == '/' && t[1] == '\0') - ? t - : stripTrailingChar(t, '/'); - - /* FIX: relocations[i].oldPath == NULL */ - /* Verify that the relocation's old path is in the header. */ - rpmtdInit(&validRelocs); - while ((validprefix = rpmtdNextString(&validRelocs))) { - if (rstreq(validprefix, p->relocs[i].oldPath)) { - valid = 1; - break; + if (rpmteType(p) == TR_ADDED && rpmfsFC(p->fs) > 0) { + if (!headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { + if (rpmteIsSource(p)) { + /* Unlike binary packages, source relocation can fail */ + if (rpmRelocateSrpmFileList(h, rpmtsRootDir(p->ts)) < 0) { + return NULL; } + } else { + rpmRelocateFileList(p->relocs, p->nrelocs, p->fs, h); } - - if (!valid) { - if (p->badrelocs == NULL) - p->badrelocs = xcalloc(p->nrelocs, sizeof(*p->badrelocs)); - p->badrelocs[i] = 1; - } - } else { - p->relocs[i].newPath = NULL; } } - p->relocs[i].oldPath = NULL; - p->relocs[i].newPath = NULL; - sortRelocs(p->relocs, p->nrelocs); - - rpmtdFreeData(&validRelocs); + return rpmfilesNew(rpmtsPool(p->ts), h, RPMTAG_BASENAMES, fiflags); } /** @@ -208,7 +120,7 @@ static void buildRelocs(rpmte p, Header h, rpmRelocation *relocs) static int addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) { rpmstrPool tspool = rpmtsPool(p->ts); - struct rpmtd_s colls, bnames; + struct rpmtd_s bnames; int rc = 1; /* assume failure */ p->name = headerGetAsString(h, RPMTAG_NAME); @@ -237,7 +149,7 @@ static int addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) p->relocs = NULL; p->badrelocs = NULL; if (relocs != NULL) - buildRelocs(p, h, relocs); + rpmRelocationBuild(h, relocs, &p->nrelocs, &p->relocs, &p->badrelocs); p->db_instance = headerGetInstance(h); p->key = key; @@ -258,10 +170,10 @@ static int addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) p->fs = rpmfsNew(rpmtdCount(&bnames), (p->type == TR_ADDED)); rpmtdFreeData(&bnames); - p->fi = getFI(p, h); + p->files = getFiles(p, h); /* Packages with no files return an empty file info set, NULL is an error */ - if (p->fi == NULL) + if (p->files == NULL) goto exit; /* See if we have pre/posttrans scripts. */ @@ -272,19 +184,6 @@ static int addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) headerIsEntry(h, RPMTAG_POSTTRANSPROG)) ? RPMTE_HAVE_POSTTRANS : 0; - p->lastInCollectionsAny = NULL; - p->lastInCollectionsAdd = NULL; - p->firstInCollectionsRemove = NULL; - p->collections = NULL; - if (headerGet(h, RPMTAG_COLLECTIONS, &colls, HEADERGET_MINMEM)) { - const char *collname; - while ((collname = rpmtdNextString(&colls))) { - argvAdd(&p->collections, collname); - } - argvSort(p->collections, NULL); - rpmtdFreeData(&colls); - } - rpmteColorDS(p, RPMTAG_PROVIDENAME); rpmteColorDS(p, RPMTAG_REQUIRENAME); @@ -320,16 +219,12 @@ rpmte rpmteFree(rpmte te) fdFree(te->fd); rpmfiFree(te->fi); + rpmfilesFree(te->files); headerFree(te->h); rpmfsFree(te->fs); rpmpsFree(te->probs); rpmteCleanDS(te); - argvFree(te->collections); - argvFree(te->lastInCollectionsAny); - argvFree(te->lastInCollectionsAdd); - argvFree(te->firstInCollectionsRemove); - memset(te, 0, sizeof(*te)); /* XXX trash and burn */ free(te); } @@ -433,46 +328,6 @@ rpm_color_t rpmteSetColor(rpmte te, rpm_color_t color) return ocolor; } -ARGV_const_t rpmteCollections(rpmte te) -{ - return (te != NULL) ? te->collections : NULL; -} - -int rpmteHasCollection(rpmte te, const char *collname) -{ - return (argvSearch(rpmteCollections(te), collname, NULL) != NULL); -} - -int rpmteAddToLastInCollectionAdd(rpmte te, const char *collname) -{ - if (te != NULL) { - argvAdd(&te->lastInCollectionsAdd, collname); - argvSort(te->lastInCollectionsAdd, NULL); - return 0; - } - return -1; -} - -int rpmteAddToLastInCollectionAny(rpmte te, const char *collname) -{ - if (te != NULL) { - argvAdd(&te->lastInCollectionsAny, collname); - argvSort(te->lastInCollectionsAny, NULL); - return 0; - } - return -1; -} - -int rpmteAddToFirstInCollectionRemove(rpmte te, const char *collname) -{ - if (te != NULL) { - argvAdd(&te->firstInCollectionsRemove, collname); - argvSort(te->firstInCollectionsRemove, NULL); - return 0; - } - return -1; -} - rpm_loff_t rpmtePkgFileSize(rpmte te) { return (te != NULL ? te->pkgFileSize : 0); @@ -571,14 +426,12 @@ rpmds rpmteDS(rpmte te, rpmTagVal tag) return NULL; } -rpmfi rpmteSetFI(rpmte te, rpmfi fi) +void rpmteCleanFiles(rpmte te) { if (te != NULL) { te->fi = rpmfiFree(te->fi); - if (fi != NULL) - te->fi = rpmfiLink(fi); + te->files = rpmfilesFree(te->files); } - return NULL; } rpmfi rpmteFI(rpmte te) @@ -586,12 +439,20 @@ rpmfi rpmteFI(rpmte te) if (te == NULL) return NULL; + if (te->fi == NULL) + te->fi = rpmfilesIter(te->files, RPMFI_ITER_FWD); + return te->fi; /* XXX take fi reference here? */ } +rpmfiles rpmteFiles(rpmte te) +{ + return (te != NULL) ? rpmfilesLink(te->files) : NULL; +} + static void rpmteColorDS(rpmte te, rpmTag tag) { - rpmfi fi = rpmteFI(te); + rpmfi fi; rpmds ds = rpmteDS(te, tag); char deptype = 'R'; char mydt; @@ -602,7 +463,7 @@ static void rpmteColorDS(rpmte te, rpmTag tag) unsigned ix; int ndx, i; - if (!(te && (Count = rpmdsCount(ds)) > 0 && rpmfiFC(fi) > 0)) + if (!(te && (Count = rpmdsCount(ds)) > 0 && rpmfilesFC(te->files) > 0)) return; switch (tag) { @@ -620,8 +481,7 @@ static void rpmteColorDS(rpmte te, rpmTag tag) colors = xcalloc(Count, sizeof(*colors)); /* Calculate dependency color. */ - fi = rpmfiInit(fi, 0); - if (fi != NULL) + fi = rpmfilesIter(te->files, RPMFI_ITER_FWD); while (rpmfiNext(fi) >= 0) { val = rpmfiFColor(fi); ddict = NULL; @@ -646,6 +506,7 @@ assert (ix < Count); (void) rpmdsSetColor(ds, val); } free(colors); + rpmfiFree(fi); } static Header rpmteDBHeader(rpmte te) @@ -707,8 +568,8 @@ static int rpmteOpen(rpmte te, int reload_fi) if (h != NULL) { if (reload_fi) { /* This can fail if we get a different, bad header from callback */ - te->fi = getFI(te, h); - rc = (te->fi != NULL); + te->files = getFiles(te, h); + rc = (te->files != NULL); } else { rc = 1; } @@ -739,7 +600,7 @@ static int rpmteClose(rpmte te, int reset_fi) } rpmteSetHeader(te, NULL); if (reset_fi) { - rpmteSetFI(te, NULL); + rpmteCleanFiles(te); } return 1; } @@ -864,7 +725,7 @@ void rpmteAddRelocProblems(rpmte te) const char * rpmteTypeString(rpmte te) { - switch(rpmteType(te)) { + switch (rpmteType(te)) { case TR_ADDED: return _("install"); case TR_REMOVED: return _("erase"); default: return "???"; @@ -876,70 +737,7 @@ rpmfs rpmteGetFileStates(rpmte te) return te->fs; } -rpmRC rpmteSetupCollectionPlugins(rpmte te) -{ - ARGV_const_t colls = rpmteCollections(te); - rpmPlugins plugins = rpmtsPlugins(te->ts); - rpmRC rc = RPMRC_OK; - - if (!colls) { - return rc; - } - - rpmteOpen(te, 0); - for (; colls && *colls; colls++) { - if (!rpmpluginsPluginAdded(plugins, *colls)) { - rc = rpmpluginsAddPlugin(plugins, "collection", *colls); - if (rc != RPMRC_OK) { - break; - } - } - rc = rpmpluginsCallOpenTE(plugins, *colls, te); - if (rc != RPMRC_OK) { - break; - } - } - rpmteClose(te, 0); - - return rc; -} - -static rpmRC rpmteRunAllCollections(rpmte te, rpmPluginHook hook) -{ - ARGV_const_t colls; - rpmRC(*collHook) (rpmPlugins, const char *); - rpmRC rc = RPMRC_OK; - - if (rpmtsFlags(te->ts) & RPMTRANS_FLAG_NOCOLLECTIONS) { - goto exit; - } - - switch (hook) { - case PLUGINHOOK_COLL_POST_ADD: - colls = te->lastInCollectionsAdd; - collHook = rpmpluginsCallCollectionPostAdd; - break; - case PLUGINHOOK_COLL_POST_ANY: - colls = te->lastInCollectionsAny; - collHook = rpmpluginsCallCollectionPostAny; - break; - case PLUGINHOOK_COLL_PRE_REMOVE: - colls = te->firstInCollectionsRemove; - collHook = rpmpluginsCallCollectionPreRemove; - break; - default: - goto exit; - } - - for (; colls && *colls; colls++) { - rc = collHook(rpmtsPlugins(te->ts), *colls); - } - - exit: - return rc; -} - -int rpmteProcess(rpmte te, pkgGoal goal) +int rpmteProcess(rpmte te, pkgGoal goal, int num) { /* Only install/erase resets pkg file info */ int scriptstage = (goal != PKG_INSTALL && goal != PKG_ERASE); @@ -954,20 +752,16 @@ int rpmteProcess(rpmte te, pkgGoal goal) } } - if (!scriptstage) { - rpmteRunAllCollections(te, PLUGINHOOK_COLL_PRE_REMOVE); - } - if (rpmteOpen(te, reset_fi)) { + if (!scriptstage) { + rpmtsNotify(te->ts, te, RPMCALLBACK_ELEM_PROGRESS, num, + rpmtsMembers(te->ts)->orderCount); + } + failed = rpmpsmRun(te->ts, te, goal); rpmteClose(te, reset_fi); } - if (!scriptstage) { - rpmteRunAllCollections(te, PLUGINHOOK_COLL_POST_ADD); - rpmteRunAllCollections(te, PLUGINHOOK_COLL_POST_ANY); - } - if (failed) { failed = rpmteMarkFailed(te); } diff --git a/lib/rpmte.h b/lib/rpmte.h index a66c1e91f..c52c44a09 100644 --- a/lib/rpmte.h +++ b/lib/rpmte.h @@ -233,27 +233,19 @@ int rpmteFailed(rpmte te); rpmds rpmteDS(rpmte te, rpmTagVal tag); /** \ingroup rpmte - * Retrieve file info tag set from transaction element. + * Retrieve file info set from transaction element. * @param te transaction element - * @return file info tag set + * @return file info set (refcounted) */ -rpmfi rpmteFI(rpmte te); +rpmfiles rpmteFiles(rpmte te); /** \ingroup rpmte - * Retrieve list of collections + * Retrieve file info iterator from transaction element. + * @deprecated use rpmteFiles() instead * @param te transaction element - * @return list of collections - */ -ARGV_const_t rpmteCollections(rpmte te); - -/** \ingroup rpmte - * Determine a transaction element is part of a collection - * @param te transaction element - * @param collname collection name - * @return 1 if collname is part of a collection, 0 if not + * @return file info tag set */ -int rpmteHasCollection(rpmte te, const char * collname); - +rpmfi rpmteFI(rpmte te); #ifdef __cplusplus } diff --git a/lib/rpmte_internal.h b/lib/rpmte_internal.h index abcdb9020..a5a991ec5 100644 --- a/lib/rpmte_internal.h +++ b/lib/rpmte_internal.h @@ -14,6 +14,8 @@ typedef enum pkgGoal_e { PKG_VERIFY = RPMTAG_VERIFYSCRIPT, PKG_PRETRANS = RPMTAG_PRETRANS, PKG_POSTTRANS = RPMTAG_POSTTRANS, + PKG_TRANSFILETRIGGERIN = RPMTAG_TRANSFILETRIGGERIN, + PKG_TRANSFILETRIGGERUN = RPMTAG_TRANSFILETRIGGERUN, } pkgGoal; /** \ingroup rpmte @@ -47,7 +49,7 @@ RPM_GNUC_INTERNAL rpmte rpmteFree(rpmte te); RPM_GNUC_INTERNAL -rpmfi rpmteSetFI(rpmte te, rpmfi fi); +void rpmteCleanFiles(rpmte te); RPM_GNUC_INTERNAL FD_t rpmteSetFd(rpmte te, FD_t fd); @@ -56,7 +58,7 @@ RPM_GNUC_INTERNAL FD_t rpmtePayload(rpmte te); RPM_GNUC_INTERNAL -int rpmteProcess(rpmte te, pkgGoal goal); +int rpmteProcess(rpmte te, pkgGoal goal, int num); RPM_GNUC_INTERNAL void rpmteAddProblem(rpmte te, rpmProblemType type, @@ -84,18 +86,6 @@ int rpmteHaveTransScript(rpmte te, rpmTagVal tag); /* XXX should be internal too but build code needs for now... */ rpmfs rpmteGetFileStates(rpmte te); -/* XXX here for now... */ -/** - * Relocate files in header. - * @todo multilib file dispositions need to be checked. - * @param relocations relocations - * @param numRelocations number of relocations - * @param fs file state set - * @param h package header to relocate - */ -RPM_GNUC_INTERNAL -void rpmRelocateFileList(rpmRelocation *relocs, int numRelocations, rpmfs fs, Header h); - /** \ingroup rpmte * Retrieve size in bytes of package header. * @param te transaction element @@ -114,44 +104,6 @@ unsigned int rpmteHeaderSize(rpmte te); RPM_GNUC_INTERNAL rpmRC rpmpsmRun(rpmts ts, rpmte te, pkgGoal goal); -/** \ingroup rpmte - * Add a collection to the list of last collections for the installation - * section of a transaction element - * @param te transaction element - * @param collname collection name - * @return 0 on success, non-zero on error - */ -RPM_GNUC_INTERNAL -int rpmteAddToLastInCollectionAdd(rpmte te, const char * collname); - -/** \ingroup rpmte - * Add a collection to the list of last collections for the installation - * or removal section of a transaction element - * @param te transaction element - * @param collname collection name - * @return 0 on success, non-zero on error - */ -RPM_GNUC_INTERNAL -int rpmteAddToLastInCollectionAny(rpmte te, const char * collname); - -/** \ingroup rpmte - * Add a collection to the list of first collections for the removal - * section of a transaction element - * @param te transaction element - * @param collname collection name - * @return 0 on success, non-zero on error - */ -RPM_GNUC_INTERNAL -int rpmteAddToFirstInCollectionRemove(rpmte te, const char * collname); - -/** \ingroup rpmte - * Sends the open te plugin hook for each plugins with the transaction element open - * @param te transaction element - * @return 0 on success, non-zero on error - */ -RPM_GNUC_INTERNAL -rpmRC rpmteSetupCollectionPlugins(rpmte te); - #ifdef __cplusplus } #endif diff --git a/lib/rpmtriggers.c b/lib/rpmtriggers.c new file mode 100644 index 000000000..23a913d35 --- /dev/null +++ b/lib/rpmtriggers.c @@ -0,0 +1,632 @@ +#include "system.h" + +#include <rpm/rpmts.h> +#include <rpm/rpmdb.h> +#include <rpm/rpmds.h> +#include <rpm/rpmfi.h> +#include <stdlib.h> + +#include "lib/rpmtriggers.h" +#include "lib/rpmts_internal.h" +#include "lib/rpmdb_internal.h" +#include "lib/rpmds_internal.h" +#include "lib/rpmfi_internal.h" +#include "lib/rpmte_internal.h" +#include "lib/rpmchroot.h" + +#define TRIGGER_PRIORITY_BOUND 10000 + +rpmtriggers rpmtriggersCreate(unsigned int hint) +{ + rpmtriggers triggers = xmalloc(sizeof(struct rpmtriggers_s)); + triggers->count = 0; + triggers->alloced = hint; + triggers->triggerInfo = xmalloc(sizeof(struct triggerInfo_s) * + triggers->alloced); + return triggers; +} + +rpmtriggers rpmtriggersFree(rpmtriggers triggers) +{ + _free(triggers->triggerInfo); + _free(triggers); + + return NULL; +} + +static void rpmtriggersAdd(rpmtriggers trigs, unsigned int hdrNum, + unsigned int tix, unsigned int priority) +{ + if (trigs->count == trigs->alloced) { + trigs->alloced <<= 1; + trigs->triggerInfo = xrealloc(trigs->triggerInfo, + sizeof(struct triggerInfo_s) * trigs->alloced); + } + + trigs->triggerInfo[trigs->count].hdrNum = hdrNum; + trigs->triggerInfo[trigs->count].tix = tix; + trigs->triggerInfo[trigs->count].priority = priority; + trigs->count++; +} + +static int trigCmp(const void *a, const void *b) +{ + const struct triggerInfo_s *trigA = a, *trigB = b; + + if (trigA->priority < trigB->priority) + return 1; + + if (trigA->priority > trigB->priority) + return -1; + + if (trigA->hdrNum < trigB->hdrNum) + return -1; + + if (trigA->hdrNum > trigB->hdrNum) + return 1; + + if (trigA->tix < trigB->tix) + return -1; + + if (trigA->tix > trigB->tix) + return 1; + + return 0; +} + +static void rpmtriggersSortAndUniq(rpmtriggers trigs) +{ + unsigned int from; + unsigned int to = 0; + unsigned int count = trigs->count; + + if (count > 1) + qsort(trigs->triggerInfo, count, sizeof(struct triggerInfo_s), trigCmp); + + for (from = 0; from < count; from++) { + if (from > 0 && + !trigCmp((const void *) &trigs->triggerInfo[from - 1], + (const void *) &trigs->triggerInfo[from])) { + + trigs->count--; + continue; + } + if (from != to) + trigs->triggerInfo[to] = trigs->triggerInfo[from]; + to++; + } +} + +void rpmtriggersPrepPostUnTransFileTrigs(rpmts ts, rpmte te) +{ + rpmdbMatchIterator mi; + rpmdbIndexIterator ii; + Header trigH; + const void *key; + size_t keylen; + rpmfiles files; + rpmds rpmdsTriggers; + rpmds rpmdsTrigger; + + ii = rpmdbIndexIteratorInit(rpmtsGetRdb(ts), RPMDBI_TRANSFILETRIGGERNAME); + mi = rpmdbNewIterator(rpmtsGetRdb(ts), RPMDBI_PACKAGES); + files = rpmteFiles(te); + + /* Iterate over file triggers in rpmdb */ + while ((rpmdbIndexIteratorNext(ii, &key, &keylen)) == 0) { + char pfx[keylen + 1]; + memcpy(pfx, key, keylen); + pfx[keylen] = '\0'; + /* Check if file trigger matches any installed file in this te */ + rpmfi fi = rpmfilesFindPrefix(files, pfx); + while (rpmfiNext(fi) >= 0) { + if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) { + /* If yes then store it */ + rpmdbAppendIterator(mi, rpmdbIndexIteratorPkgOffsets(ii), + rpmdbIndexIteratorNumPkgs(ii)); + break; + } + } + rpmfiFree(fi); + } + rpmdbIndexIteratorFree(ii); + + if (rpmdbGetIteratorCount(mi)) { + /* Filter triggers and save only trans postun triggers into ts */ + while ((trigH = rpmdbNextIterator(mi)) != NULL) { + int tix = 0; + rpmdsTriggers = rpmdsNew(trigH, RPMTAG_TRANSFILETRIGGERNAME, 0); + while ((rpmdsTrigger = rpmdsFilterTi(rpmdsTriggers, tix))) { + if ((rpmdsNext(rpmdsTrigger) >= 0) && + (rpmdsFlags(rpmdsTrigger) & RPMSENSE_TRIGGERPOSTUN)) { + struct rpmtd_s priorities; + + headerGet(trigH, RPMTAG_TRANSFILETRIGGERPRIORITIES, + &priorities, HEADERGET_MINMEM); + rpmtdSetIndex(&priorities, tix); + rpmtriggersAdd(ts->trigs2run, rpmdbGetIteratorOffset(mi), + tix, *rpmtdGetUint32(&priorities)); + } + rpmdsFree(rpmdsTrigger); + tix++; + } + rpmdsFree(rpmdsTriggers); + } + } + rpmdbFreeIterator(mi); +} + +int runPostUnTransFileTrigs(rpmts ts) +{ + int i; + Header trigH; + struct rpmtd_s installPrefixes; + rpmScript script; + rpmtriggers trigs = ts->trigs2run; + int nerrors = 0; + + if (rpmChrootIn() != 0) + return -1; + + rpmtriggersSortAndUniq(trigs); + /* Iterate over stored triggers */ + for (i = 0; i < trigs->count; i++) { + /* Get header containing trigger script */ + trigH = rpmdbGetHeaderAt(rpmtsGetRdb(ts), + trigs->triggerInfo[i].hdrNum); + + /* Maybe package with this trigger is already uninstalled */ + if (trigH == NULL) + continue; + + /* Prepare and run script */ + script = rpmScriptFromTriggerTag(trigH, + triggertag(RPMSENSE_TRIGGERPOSTUN), + RPMSCRIPT_TRANSFILETRIGGER, trigs->triggerInfo[i].tix); + + headerGet(trigH, RPMTAG_INSTPREFIXES, &installPrefixes, + HEADERGET_ALLOC|HEADERGET_ARGV); + + nerrors += runScript(ts, NULL, trigH, installPrefixes.data, script, 0, 0); + rpmtdFreeData(&installPrefixes); + rpmScriptFree(script); + headerFree(trigH); + } + + rpmChrootOut(); + + return nerrors; +} + +/* + * Get files from next package from match iterator. If files are + * available in memory then don't read them from rpmdb. + */ +static rpmfiles rpmtsNextFiles(rpmts ts, rpmdbMatchIterator mi) +{ + Header h; + rpmte *te; + rpmfiles files = NULL; + rpmstrPool pool = ts->members->pool; + int ix; + unsigned int offset; + + ix = rpmdbGetIteratorIndex(mi); + if (ix < rpmdbGetIteratorCount(mi)) { + offset = rpmdbGetIteratorOffsetFor(mi, ix); + if (packageHashGetEntry(ts->members->removedPackages, offset, + &te, NULL, NULL)) { + /* Files are available in memory */ + files = rpmteFiles(te[0]); + } + + if (packageHashGetEntry(ts->members->installedPackages, offset, + &te, NULL, NULL)) { + /* Files are available in memory */ + files = rpmteFiles(te[0]); + } + } + + if (files) { + rpmdbSetIteratorIndex(mi, ix + 1); + } else { + /* Files are not available in memory. Read them from rpmdb */ + h = rpmdbNextIterator(mi); + if (h) { + files = rpmfilesNew(pool, h, RPMTAG_BASENAMES, + RPMFI_FLAGS_FILETRIGGER); + } + } + + return files; +} + + +typedef struct matchFilesIter_s { + rpmts ts; + rpmds rpmdsTrigger; + rpmfiles files; + rpmfi fi; + rpmfs fs; + const char *pfx; + rpmdbMatchIterator pi; + packageHash tranPkgs; +} *matchFilesIter; + +static matchFilesIter matchFilesIterator(rpmds trigger, rpmfiles files, rpmte te) +{ + matchFilesIter mfi = xcalloc(1, sizeof(*mfi)); + rpmdsInit(trigger); + mfi->rpmdsTrigger = trigger; + mfi->files = rpmfilesLink(files); + mfi->fs = rpmteGetFileStates(te); + return mfi; +} + +static matchFilesIter matchDBFilesIterator(rpmds trigger, rpmts ts, + int inTransaction) +{ + matchFilesIter mfi = xcalloc(1, sizeof(*mfi)); + rpmsenseFlags sense; + + rpmdsSetIx(trigger, 0); + sense = rpmdsFlags(trigger); + rpmdsInit(trigger); + + mfi->rpmdsTrigger = trigger; + mfi->ts = ts; + + /* If inTransaction is set then filter out packages that aren't in transaction */ + if (inTransaction) { + if (sense & RPMSENSE_TRIGGERIN) + mfi->tranPkgs = ts->members->installedPackages; + else + mfi->tranPkgs = ts->members->removedPackages; + } + return mfi; +} + +static const char *matchFilesNext(matchFilesIter mfi) +{ + const char *matchFile = NULL; + int fx = 0; + + /* Decide if we iterate over given files (mfi->files) */ + if (!mfi->ts) + do { + /* Get next file from mfi->fi */ + matchFile = NULL; + while (matchFile == NULL && rpmfiNext(mfi->fi) >= 0) { + if (!XFA_SKIPPING(rpmfsGetAction(mfi->fs, rpmfiFX(mfi->fi)))) + matchFile = rpmfiFN(mfi->fi); + } + if (matchFile) + break; + + /* If we are done with current mfi->fi, create mfi->fi for next prefix */ + fx = rpmdsNext(mfi->rpmdsTrigger); + mfi->pfx = rpmdsN(mfi->rpmdsTrigger); + rpmfiFree(mfi->fi); + mfi->fi = rpmfilesFindPrefix(mfi->files, mfi->pfx); + + } while (fx >= 0); + /* or we iterate over files in rpmdb */ + else + do { + matchFile = NULL; + while (matchFile == NULL && rpmfiNext(mfi->fi) >= 0) { + if (RPMFILE_IS_INSTALLED(rpmfiFState(mfi->fi))) + matchFile = rpmfiFN(mfi->fi); + } + if (matchFile) + break; + + /* If we are done with current mfi->fi, create mfi->fi for next package */ + rpmfilesFree(mfi->files); + rpmfiFree(mfi->fi); + mfi->files = rpmtsNextFiles(mfi->ts, mfi->pi); + mfi->fi = rpmfilesFindPrefix(mfi->files, mfi->pfx); + if (mfi->files) + continue; + + /* If we are done with all packages, go through packages with new prefix */ + fx = rpmdsNext(mfi->rpmdsTrigger); + mfi->pfx = rpmdsN(mfi->rpmdsTrigger); + rpmdbFreeIterator(mfi->pi); + mfi->pi = rpmdbInitPrefixIterator(rpmtsGetRdb(mfi->ts), + RPMDBI_DIRNAMES, mfi->pfx, 0); + + rpmdbFilterIterator(mfi->pi, mfi->tranPkgs, 0); + /* Only walk through each header with matches once */ + rpmdbUniqIterator(mfi->pi); + + } while (fx >= 0); + + + return matchFile; +} + +static int matchFilesEmpty(matchFilesIter mfi) +{ + const char *matchFile; + + /* Try to get the first file */ + matchFile = matchFilesNext(mfi); + + /* Rewind back this file */ + rpmfiInit(mfi->fi, 0); + + if (matchFile) + /* We have at least one file so iterator is not empty */ + return 0; + else + /* No file in iterator */ + return 1; +} + +static matchFilesIter matchFilesIteratorFree(matchFilesIter mfi) +{ + rpmfiFree(mfi->fi); + rpmfilesFree(mfi->files); + rpmdbFreeIterator(mfi->pi); + free(mfi); + return NULL; +} + +/* + * Run all file triggers in header h + * @param searchMode 0 match trigger prefixes against files in te + * 1 match trigger prefixes against files in whole ts + * 2 match trigger prefixes against files in whole + * rpmdb + */ +static int runHandleTriggersInPkg(rpmts ts, rpmte te, Header h, + rpmsenseFlags sense, rpmscriptTriggerModes tm, + int searchMode, int ti) +{ + int nerrors = 0; + rpmds rpmdsTriggers, rpmdsTrigger; + rpmfiles files = NULL; + matchFilesIter mfi = NULL; + rpmScript script; + struct rpmtd_s installPrefixes; + char *(*inputFunc)(void *); + + rpmdsTriggers = rpmdsNew(h, triggerDsTag(tm), 0); + rpmdsTrigger = rpmdsFilterTi(rpmdsTriggers, ti); + /* + * Now rpmdsTrigger contains all dependencies belonging to one trigger + * with trigger index tix. Have a look at the first one to check flags. + */ + if ((rpmdsNext(rpmdsTrigger) >= 0) && + (rpmdsFlags(rpmdsTrigger) & sense)) { + + switch (searchMode) { + case 0: + /* Create iterator over files in te that this trigger matches */ + files = rpmteFiles(te); + mfi = matchFilesIterator(rpmdsTrigger, files, te); + break; + case 1: + /* Create iterator over files in ts that this trigger matches */ + mfi = matchDBFilesIterator(rpmdsTrigger, ts, 1); + break; + case 2: + /* Create iterator over files in whole rpmd that this trigger matches */ + mfi = matchDBFilesIterator(rpmdsTrigger, ts, 0); + break; + } + + /* If this trigger matches any file then run trigger script */ + if (!matchFilesEmpty(mfi)) { + script = rpmScriptFromTriggerTag(h, triggertag(sense), tm, ti); + + headerGet(h, RPMTAG_INSTPREFIXES, &installPrefixes, + HEADERGET_ALLOC|HEADERGET_ARGV); + + + /* + * As input function set function to get next file from + * matching file iterator. As parameter for this function + * set matching file iterator. Input function will be called + * during execution of trigger script in order to get data + * that will be passed as stdin to trigger script. To get + * these data from lua script function rpm.input() can be used. + */ + inputFunc = (char *(*)(void *)) matchFilesNext; + rpmScriptSetNextFileFunc(script, inputFunc, mfi); + + nerrors += runScript(ts, te, h, installPrefixes.data, + script, 0, 0); + rpmtdFreeData(&installPrefixes); + rpmScriptFree(script); + } + rpmfilesFree(files); + matchFilesIteratorFree(mfi); + } + rpmdsFree(rpmdsTrigger); + rpmdsFree(rpmdsTriggers); + + return nerrors; +} + +/* Return true if any file in package (te) starts with pfx */ +static int matchFilesInPkg(rpmts ts, rpmte te, const char *pfx, + rpmsenseFlags sense) +{ + int rc; + rpmfiles files = rpmteFiles(te); + rpmfi fi = rpmfilesFindPrefix(files, pfx); + + rc = (fi != NULL); + rpmfilesFree(files); + rpmfiFree(fi); + return rc; +} + +/* Return number of added/removed files starting with pfx in transaction */ +static int matchFilesInTran(rpmts ts, rpmte te, const char *pfx, + rpmsenseFlags sense) +{ + int rc = 1; + rpmdbMatchIterator pi; + + /* Get all files from rpmdb starting with pfx */ + pi = rpmdbInitPrefixIterator(rpmtsGetRdb(ts), RPMDBI_DIRNAMES, pfx, 0); + + if (sense & RPMSENSE_TRIGGERIN) + /* Leave in pi only files installed in ts */ + rpmdbFilterIterator(pi, ts->members->installedPackages, 0); + else + /* Leave in pi only files removed in ts */ + rpmdbFilterIterator(pi, ts->members->removedPackages, 0); + + rc = rpmdbGetIteratorCount(pi); + rpmdbFreeIterator(pi); + + return rc; +} + +rpmRC runFileTriggers(rpmts ts, rpmte te, rpmsenseFlags sense, + rpmscriptTriggerModes tm, int priorityClass) +{ + int nerrors = 0, i; + rpmdbIndexIterator ii; + const void *key; + char *pfx; + size_t keylen; + Header trigH; + int (*matchFunc)(rpmts, rpmte, const char*, rpmsenseFlags sense); + rpmTagVal priorityTag; + rpmtriggers triggers = rpmtriggersCreate(10); + + /* Decide if we match triggers against files in te or in whole ts */ + if (tm == RPMSCRIPT_FILETRIGGER) { + matchFunc = matchFilesInPkg; + priorityTag = RPMTAG_FILETRIGGERPRIORITIES; + } else { + matchFunc = matchFilesInTran; + priorityTag = RPMTAG_TRANSFILETRIGGERPRIORITIES; + } + + ii = rpmdbIndexIteratorInit(rpmtsGetRdb(ts), triggerDsTag(tm)); + + /* Loop over all file triggers in rpmdb */ + while ((rpmdbIndexIteratorNext(ii, &key, &keylen)) == 0) { + pfx = xmalloc(keylen + 1); + memcpy(pfx, key, keylen); + pfx[keylen] = '\0'; + + /* Check if file trigger is fired by any file in ts/te */ + if (matchFunc(ts, te, pfx, sense)) { + for (i = 0; i < rpmdbIndexIteratorNumPkgs(ii); i++) { + struct rpmtd_s priorities; + unsigned int priority; + unsigned int offset = rpmdbIndexIteratorPkgOffset(ii, i); + unsigned int tix = rpmdbIndexIteratorTagNum(ii, i); + + /* + * Don't handle transaction triggers installed in current + * transaction to avoid executing the same script two times. + * These triggers are handled in runImmedFileTriggers(). + */ + if (tm == RPMSCRIPT_TRANSFILETRIGGER && + (packageHashHasEntry(ts->members->removedPackages, offset) || + packageHashHasEntry(ts->members->installedPackages, offset))) + continue; + + /* Get priority of trigger from header */ + trigH = rpmdbGetHeaderAt(rpmtsGetRdb(ts), offset); + headerGet(trigH, priorityTag, &priorities, HEADERGET_MINMEM); + rpmtdSetIndex(&priorities, tix); + priority = *rpmtdGetUint32(&priorities); + headerFree(trigH); + + /* Store file trigger in array */ + rpmtriggersAdd(triggers, offset, tix, priority); + } + } + free(pfx); + } + rpmdbIndexIteratorFree(ii); + + /* Sort triggers by priority, offset, trigger index */ + rpmtriggersSortAndUniq(triggers); + + if (rpmChrootIn() != 0) { + rpmtriggersFree(triggers); + return RPMRC_FAIL; + } + + /* Handle stored triggers */ + for (i = 0; i < triggers->count; i++) { + if (priorityClass == 1) { + if (triggers->triggerInfo[i].priority < TRIGGER_PRIORITY_BOUND) + continue; + } else if (priorityClass == 2) { + if (triggers->triggerInfo[i].priority >= TRIGGER_PRIORITY_BOUND) + continue; + } + + trigH = rpmdbGetHeaderAt(rpmtsGetRdb(ts), triggers->triggerInfo[i].hdrNum); + if (tm == RPMSCRIPT_FILETRIGGER) + nerrors += runHandleTriggersInPkg(ts, te, trigH, sense, tm, 0, + triggers->triggerInfo[i].tix); + else + nerrors += runHandleTriggersInPkg(ts, te, trigH, sense, tm, 1, + triggers->triggerInfo[i].tix); + headerFree(trigH); + } + rpmtriggersFree(triggers); + /* XXX an error here would require a full abort */ + (void) rpmChrootOut(); + + return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL; +} + +rpmRC runImmedFileTriggers(rpmts ts, rpmte te, rpmsenseFlags sense, + rpmscriptTriggerModes tm, int priorityClass) +{ + int nerrors = 0; + int triggersCount, i; + Header trigH = rpmteHeader(te); + struct rpmtd_s priorities; + rpmTagVal priorityTag; + rpmtriggers triggers; + + if (tm == RPMSCRIPT_FILETRIGGER) { + priorityTag = RPMTAG_FILETRIGGERPRIORITIES; + } else { + priorityTag = RPMTAG_TRANSFILETRIGGERPRIORITIES; + } + headerGet(trigH, priorityTag, &priorities, HEADERGET_MINMEM); + + triggersCount = rpmtdCount(&priorities); + triggers = rpmtriggersCreate(triggersCount); + + for (i = 0; i < triggersCount; i++) { + rpmtdSetIndex(&priorities, i); + /* Offset is not important, all triggers are from the same package */ + rpmtriggersAdd(triggers, 0, i, *rpmtdGetUint32(&priorities)); + } + + /* Sort triggers by priority, offset, trigger index */ + rpmtriggersSortAndUniq(triggers); + + for (i = 0; i < triggersCount; i++) { + if (priorityClass == 1) { + if (triggers->triggerInfo[i].priority < TRIGGER_PRIORITY_BOUND) + continue; + } else if (priorityClass == 2) { + if (triggers->triggerInfo[i].priority >= TRIGGER_PRIORITY_BOUND) + continue; + } + + nerrors += runHandleTriggersInPkg(ts, te, trigH, sense, tm, 2, + triggers->triggerInfo[i].tix); + } + rpmtriggersFree(triggers); + headerFree(trigH); + + return (nerrors == 0) ? RPMRC_OK : RPMRC_FAIL; +} diff --git a/lib/rpmtriggers.h b/lib/rpmtriggers.h new file mode 100644 index 000000000..5ae9edd00 --- /dev/null +++ b/lib/rpmtriggers.h @@ -0,0 +1,84 @@ +#ifndef _RPMTRIGGERS_H +#define _RPMTRIGGERS_H + +#include <rpm/rpmutil.h> +#include "lib/rpmscript.h" + +struct triggerInfo_s { + unsigned int hdrNum; + unsigned int tix; + unsigned int priority; +}; + +typedef struct rpmtriggers_s { + struct triggerInfo_s *triggerInfo; + int count; + int alloced; +} *rpmtriggers; + +#ifdef __cplusplus +extern "C" { +#endif + + +RPM_GNUC_INTERNAL +rpmtriggers rpmtriggersCreate(unsigned int hint); + +RPM_GNUC_INTERNAL +rpmtriggers rpmtriggersFree(rpmtriggers triggers); + +/* + * Prepare post trans uninstall file triggers. After transcation uninstalled + * files are not saved anywhere. So we need during uninstalation of every + * package, in time when the files to uninstall are still available, + * to determine and store triggers that should be set off after transaction. + */ +RPM_GNUC_INTERNAL +void rpmtriggersPrepPostUnTransFileTrigs(rpmts ts, rpmte te); + +/* Run triggers stored in ts */ +RPM_GNUC_INTERNAL +int runPostUnTransFileTrigs(rpmts ts); + +/* + * It runs file triggers in other package(s) this package/transaction sets off. + * If tm is RPMSCRIPT_FILETRIGGERSCRIPT then it runs file triggers that are + * fired by files in transaction entry. If tm is RPMSCRIPT_TRANSFILETRIGGERSCRIPT + * then it runs file triggers that are fired by all files in transaction set. + * In that case te can be NULL. + * + * @param ts transaction set + * @param te transaction entry + * @param sense defines which triggers should be set off (triggerin, + * triggerun, triggerpostun) + * @param tm trigger mode, (filetrigger/transfiletrigger) + * @param priorityClass 1 to run triggers that should be executed before + * standard scriptlets + * 2 to run triggers that should be executed after + * standard scriptlets + * 0 to run all triggers + */ +RPM_GNUC_INTERNAL +rpmRC runFileTriggers(rpmts ts, rpmte te, rpmsenseFlags sense, + rpmscriptTriggerModes tm, int priorityClass); + +/* Run file triggers in this te other package(s) set off. + * @param ts transaction set + * @param te transaction entry + * @param sense defines which triggers should be set off (triggerin, + * triggerun, triggerpostun) + * @param tm trigger mode, (filetrigger/transfiletrigger) + * @param priorityClass 1 to run triggers that should be executed before + * standard scriptlets + * 2 to run triggers that should be executed after + * standard scriptlets + * 0 to run all triggers + */ +RPM_GNUC_INTERNAL +rpmRC runImmedFileTriggers(rpmts ts, rpmte te, rpmsenseFlags sense, + rpmscriptTriggerModes tm, int priorityClass); +#ifdef __cplusplus +} +#endif +#endif /* _RPMTRIGGERS_H */ + diff --git a/lib/rpmts.c b/lib/rpmts.c index d3b893a1d..49f1a369b 100644 --- a/lib/rpmts.c +++ b/lib/rpmts.c @@ -19,6 +19,7 @@ #include <rpm/rpmds.h> #include <rpm/rpmfi.h> #include <rpm/rpmlog.h> +#include <rpm/rpmsq.h> #include <rpm/rpmte.h> #include "rpmio/digest.h" @@ -28,6 +29,7 @@ #include "lib/rpmts_internal.h" #include "lib/rpmte_internal.h" #include "lib/misc.h" +#include "lib/rpmtriggers.h" #include "debug.h" @@ -39,6 +41,12 @@ struct rpmtsi_s { int oc; /*!< iterator index. */ }; +struct rpmtxn_s { + rpmlock lock; /* transaction lock */ + rpmtxnFlags flags; /* transaction flags */ + rpmts ts; /* parent transaction set reference */ +}; + static void loadKeyring(rpmts ts); int _rpmts_stats = 0; @@ -97,16 +105,16 @@ int rpmtsOpenDB(rpmts ts, int dbmode) int rpmtsSuspendResumeDBLock(rpmts ts, int mode) { - return rpmdbSuspendResumeDBLock(ts->rdb, mode); + return 0;//rpmdbSuspendResumeDBLock(ts->rdb, mode); } int rpmtsInitDB(rpmts ts, int dbmode) { - rpmlock lock = rpmtsAcquireLock(ts); + rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE); int rc = -1; - if (lock) + if (txn) rc = rpmdbInit(ts->rootDir, dbmode); - rpmlockFree(lock); + rpmtxnEnd(txn); return rc; } @@ -131,19 +139,19 @@ int rpmtsSetDBMode(rpmts ts, int dbmode) int rpmtsRebuildDB(rpmts ts) { int rc = -1; - rpmlock lock = NULL; + rpmtxn txn = NULL; /* Cannot do this on a populated transaction set */ if (rpmtsNElements(ts) > 0) return -1; - lock = rpmtsAcquireLock(ts); - if (lock) { + txn = rpmtxnBegin(ts, RPMTXN_WRITE); + if (txn) { if (!(ts->vsflags & RPMVSF_NOHDRCHK)) rc = rpmdbRebuild(ts->rootDir, ts, headerCheck); else rc = rpmdbRebuild(ts->rootDir, NULL, NULL); - rpmlockFree(lock); + rpmtxnEnd(txn); } return rc; } @@ -151,10 +159,10 @@ int rpmtsRebuildDB(rpmts ts) int rpmtsVerifyDB(rpmts ts) { int rc = -1; - rpmlock lock = rpmtsAcquireLock(ts); - if (lock) { + rpmtxn txn = rpmtxnBegin(ts, RPMTXN_READ); + if (txn) { rc = rpmdbVerify(ts->rootDir); - rpmlockFree(lock); + rpmtxnEnd(txn); } return rc; } @@ -273,7 +281,10 @@ static int loadKeyringFromFiles(rpmts ts) } for (char **f = files; *f; f++) { + int subkeysCount, i; + rpmPubkey *subkeys; rpmPubkey key = rpmPubkeyRead(*f); + if (!key) { rpmlog(RPMLOG_ERR, _("%s: reading of public key failed.\n"), *f); continue; @@ -282,7 +293,22 @@ static int loadKeyringFromFiles(rpmts ts) nkeys++; rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", *f); } + subkeys = rpmGetSubkeys(key, &subkeysCount); rpmPubkeyFree(key); + + for (i = 0; i < subkeysCount; i++) { + rpmPubkey subkey = subkeys[i]; + + if (rpmKeyringAddKey(ts->keyring, subkey) == 0) { + rpmlog(RPMLOG_DEBUG, + "added subkey %d of main key %s to keyring\n", + i, *f); + + nkeys++; + } + rpmPubkeyFree(subkey); + } + free(subkeys); } exit: free(pkpath); @@ -311,6 +337,9 @@ static int loadKeyringFromDB(rpmts ts) if (rpmBase64Decode(key, (void **) &pkt, &pktlen) == 0) { rpmPubkey key = rpmPubkeyNew(pkt, pktlen); + int subkeysCount, i; + rpmPubkey *subkeys = rpmGetSubkeys(key, &subkeysCount); + if (rpmKeyringAddKey(ts->keyring, key) == 0) { char *nvr = headerGetAsString(h, RPMTAG_NVR); rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", nvr); @@ -318,6 +347,22 @@ static int loadKeyringFromDB(rpmts ts) nkeys++; } rpmPubkeyFree(key); + + for (i = 0; i < subkeysCount; i++) { + rpmPubkey subkey = subkeys[i]; + + if (rpmKeyringAddKey(ts->keyring, subkey) == 0) { + char *nvr = headerGetAsString(h, RPMTAG_NVR); + rpmlog(RPMLOG_DEBUG, + "added subkey %d of main key %s to keyring\n", + i, nvr); + + free(nvr); + nkeys++; + } + rpmPubkeyFree(subkey); + } + free(subkeys); free(pkt); } } @@ -343,7 +388,8 @@ static void loadKeyring(rpmts ts) } /* Build pubkey header. */ -static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp) +static int makePubkeyHeader(rpmts ts, rpmPubkey key, rpmPubkey *subkeys, + int subkeysCount, Header * hdrp) { Header h = headerNew(); const char * afmt = "%{pubkeys:armor}"; @@ -364,6 +410,7 @@ static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp) char * r = NULL; char * evr = NULL; int rc = -1; + int i; if ((enc = rpmPubkeyBase64(key)) == NULL) goto exit; @@ -374,12 +421,12 @@ static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp) /* Build header elements. */ v = pgpHexStr(pubp->signid, sizeof(pubp->signid)); - r = pgpHexStr(pubp->time, sizeof(pubp->time)); userid = pubp->userid ? pubp->userid : "none"; - keytime = pgpGrab(pubp->time, sizeof(pubp->time)); + keytime = pubp->time; rasprintf(&n, "gpg(%s)", v+8); rasprintf(&u, "gpg(%s)", userid); + rasprintf(&r, "%x", keytime); rasprintf(&evr, "%d:%s-%s", pubp->version, v, r); headerPutString(h, RPMTAG_PUBKEYS, enc); @@ -411,6 +458,27 @@ static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header * hdrp) headerPutUint32(h, RPMTAG_BUILDTIME, &keytime, 1); headerPutString(h, RPMTAG_SOURCERPM, "(none)"); + for (i = 0; i < subkeysCount; i++) { + char *v, *r, *n, *evr; + pgpDigParams pgpkey; + + pgpkey = rpmPubkeyPgpDigParams(subkeys[i]); + v = pgpHexStr(pgpkey->signid, sizeof(pgpkey->signid)); + + rasprintf(&n, "gpg(%s)", v+8); + rasprintf(&r, "%x", pgpkey->time); + rasprintf(&evr, "%d:%s-%s", pubp->version, v, r); + + headerPutString(h, RPMTAG_PROVIDENAME, n); + headerPutString(h, RPMTAG_PROVIDEVERSION, evr); + headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1); + + free(v); + free(r); + free(n); + free(evr); + } + /* Reload the lot to immutable region and stomp sha1 digest on it */ h = headerReload(h, RPMTAG_HEADERIMMUTABLE); if (h != NULL) { @@ -446,14 +514,32 @@ exit: return rc; } +rpmRC rpmtsImportHeader(rpmtxn txn, Header h, rpmFlags flags) +{ + rpmRC rc = RPMRC_FAIL; + + if (txn && h && rpmtsOpenDB(txn->ts, (O_RDWR|O_CREAT)) == 0) { + if (rpmdbAdd(rpmtsGetRdb(txn->ts), h) == 0) { + rc = RPMRC_OK; + } + } + return rc; +} + rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen) { Header h = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ rpmPubkey pubkey = NULL; + rpmPubkey *subkeys = NULL; + int subkeysCount = 0; rpmVSFlags oflags = rpmtsVSFlags(ts); rpmKeyring keyring; - int krc; + rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE); + int krc, i; + + if (txn == NULL) + return rc; /* XXX keyring wont load if sigcheck disabled, force it temporarily */ rpmtsSetVSFlags(ts, (oflags & ~_RPMVSF_NOSIGNATURES)); @@ -462,6 +548,10 @@ rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen if ((pubkey = rpmPubkeyNew(pkt, pktlen)) == NULL) goto exit; + + if ((subkeys = rpmGetSubkeys(pubkey, &subkeysCount)) == NULL) + goto exit; + krc = rpmKeyringAddKey(keyring, pubkey); if (krc < 0) goto exit; @@ -470,7 +560,7 @@ rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen if (krc == 0) { rpm_tid_t tid = rpmtsGetTid(ts); - if (makePubkeyHeader(ts, pubkey, &h) != 0) + if (makePubkeyHeader(ts, pubkey, subkeys, subkeysCount, &h) != 0) goto exit; headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1); @@ -478,10 +568,7 @@ rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen /* Add header to database. */ if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) { - if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT))) - goto exit; - if (rpmdbAdd(rpmtsGetRdb(ts), h) != 0) - goto exit; + rc = rpmtsImportHeader(txn, h, 0); } } rc = RPMRC_OK; @@ -490,7 +577,12 @@ exit: /* Clean up. */ headerFree(h); rpmPubkeyFree(pubkey); + for (i = 0; i < subkeysCount; i++) + rpmPubkeyFree(subkeys[i]); + free(subkeys); + rpmKeyringFree(keyring); + rpmtxnEnd(txn); return rc; } @@ -593,7 +685,7 @@ void rpmtsEmpty(rpmts ts) tsmem->orderCount = 0; /* The pool cannot be emptied, there might be references to its contents */ tsmem->pool = rpmstrPoolFree(tsmem->pool); - removedHashEmpty(tsmem->removedPackages); + packageHashEmpty(tsmem->removedPackages); return; } @@ -642,7 +734,8 @@ rpmts rpmtsFree(rpmts ts) (void) rpmtsCloseDB(ts); - tsmem->removedPackages = removedHashFree(tsmem->removedPackages); + tsmem->removedPackages = packageHashFree(tsmem->removedPackages); + tsmem->installedPackages = packageHashFree(tsmem->installedPackages); tsmem->order = _free(tsmem->order); ts->members = _free(ts->members); @@ -654,6 +747,7 @@ rpmts rpmtsFree(rpmts ts) } ts->rootDir = _free(ts->rootDir); ts->lockPath = _free(ts->lockPath); + ts->lock = rpmlockFree(ts->lock); ts->keyring = rpmKeyringFree(ts->keyring); ts->netsharedPaths = argvFree(ts->netsharedPaths); @@ -661,6 +755,8 @@ rpmts rpmtsFree(rpmts ts) ts->plugins = rpmpluginsFree(ts->plugins); + rpmtriggersFree(ts->trigs2run); + if (_rpmts_stats) rpmtsPrintStats(ts); @@ -731,63 +827,6 @@ void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd) } } -struct selabel_handle * rpmtsSELabelHandle(rpmts ts) -{ -#if WITH_SELINUX - if (ts != NULL) { - return ts->selabelHandle; - } -#endif - return NULL; -} - -rpmRC rpmtsSELabelInit(rpmts ts, int open_status) -{ -#if WITH_SELINUX - const char * path = selinux_file_context_path(); - - if (ts == NULL || path == NULL) { - return RPMRC_FAIL; - } - - if (open_status) { - selinux_status_close(); - if (selinux_status_open(0) < 0) { - return RPMRC_FAIL; - } - } else if (!selinux_status_updated() && ts->selabelHandle) { - return RPMRC_OK; - } - - struct selinux_opt opts[] = { - { .type = SELABEL_OPT_PATH, .value = path} - }; - - if (ts->selabelHandle) { - rpmtsSELabelFini(ts, 0); - } - ts->selabelHandle = selabel_open(SELABEL_CTX_FILE, opts, 1); - - if (!ts->selabelHandle) { - return RPMRC_FAIL; - } -#endif - return RPMRC_OK; -} - -void rpmtsSELabelFini(rpmts ts, int close_status) -{ -#if WITH_SELINUX - if (ts && ts->selabelHandle) { - selabel_close(ts->selabelHandle); - ts->selabelHandle = NULL; - } - if (close_status) { - selinux_status_close(); - } -#endif -} - rpm_tid_t rpmtsGetTid(rpmts ts) { rpm_tid_t tid = (rpm_tid_t)-1; /* XXX -1 is time(2) error return. */ @@ -918,7 +957,14 @@ rpmop rpmtsOp(rpmts ts, rpmtsOpX opx) rpmPlugins rpmtsPlugins(rpmts ts) { - return (ts != NULL ? ts->plugins : NULL); + rpmPlugins plugins = NULL; + + if (ts != NULL) { + if (ts->plugins == NULL) + ts->plugins = rpmpluginsNew(ts); + plugins = ts->plugins; + } + return plugins; } int rpmtsSetNotifyCallback(rpmts ts, @@ -999,7 +1045,8 @@ rpmts rpmtsCreate(void) tsmem->pool = NULL; tsmem->delta = 5; tsmem->addedPackages = NULL; - tsmem->removedPackages = removedHashCreate(128, uintId, uintCmp, NULL, NULL); + tsmem->removedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL); + tsmem->installedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL); tsmem->orderAlloced = 0; tsmem->orderCount = 0; tsmem->order = NULL; @@ -1008,11 +1055,13 @@ rpmts rpmtsCreate(void) ts->rootDir = NULL; ts->keyring = NULL; - ts->selabelHandle = NULL; - ts->nrefs = 0; - ts->plugins = rpmpluginsNew(ts); + ts->plugins = NULL; + + ts->trigs2run = rpmtriggersCreate(10); + + ts->min_writes = rpmExpandNumeric("%{_minimize_writes}"); return rpmtsLink(ts); } @@ -1069,9 +1118,13 @@ rpmte rpmtsiNext(rpmtsi tsi, rpmElementTypes types) } #define RPMLOCK_PATH LOCALSTATEDIR "/rpm/.rpm.lock" -rpmlock rpmtsAcquireLock(rpmts ts) +rpmtxn rpmtxnBegin(rpmts ts, rpmtxnFlags flags) { static const char * const rpmlock_path_default = "%{?_rpmlock_path}"; + rpmtxn txn = NULL; + + if (ts == NULL) + return NULL; if (ts->lockPath == NULL) { const char *rootDir = rpmtsRootDir(ts); @@ -1089,6 +1142,30 @@ rpmlock rpmtsAcquireLock(rpmts ts) (void) rpmioMkpath(dirname(t), 0755, getuid(), getgid()); free(t); } - return rpmlockAcquire(ts->lockPath, _("transaction")); + + if (ts->lock == NULL) + ts->lock = rpmlockNew(ts->lockPath, _("transaction")); + + if (rpmlockAcquire(ts->lock)) { + txn = xcalloc(1, sizeof(*txn)); + txn->lock = ts->lock; + txn->flags = flags; + txn->ts = rpmtsLink(ts); + if (txn->flags & RPMTXN_WRITE) + rpmsqBlock(SIG_BLOCK); + } + + return txn; } +rpmtxn rpmtxnEnd(rpmtxn txn) +{ + if (txn) { + rpmlockRelease(txn->lock); + if (txn->flags & RPMTXN_WRITE) + rpmsqBlock(SIG_UNBLOCK); + rpmtsFree(txn->ts); + free(txn); + } + return NULL; +} diff --git a/lib/rpmts.h b/lib/rpmts.h index 770504443..e09569a90 100644 --- a/lib/rpmts.h +++ b/lib/rpmts.h @@ -34,9 +34,10 @@ enum rpmtransFlags_e { RPMTRANS_FLAG_NOTRIGGERS = (1 << 4), /*!< from --notriggers */ RPMTRANS_FLAG_NODOCS = (1 << 5), /*!< from --excludedocs */ RPMTRANS_FLAG_ALLFILES = (1 << 6), /*!< from --allfiles */ - /* bit 7 unused */ + RPMTRANS_FLAG_NOPLUGINS = (1 << 7), /*!< from --noplugins */ RPMTRANS_FLAG_NOCONTEXTS = (1 << 8), /*!< from --nocontexts */ - /* bits 9-15 unused */ + RPMTRANS_FLAG_NOCAPS = (1 << 9), /*!< from --nocaps */ + /* bits 10-15 unused */ RPMTRANS_FLAG_NOTRIGGERPREIN= (1 << 16), /*!< from --notriggerprein */ RPMTRANS_FLAG_NOPRE = (1 << 17), /*!< from --nopre */ RPMTRANS_FLAG_NOPOST = (1 << 18), /*!< from --nopost */ @@ -47,7 +48,7 @@ enum rpmtransFlags_e { RPMTRANS_FLAG_NOTRIGGERPOSTUN = (1 << 23), /*!< from --notriggerpostun */ RPMTRANS_FLAG_NOPRETRANS = (1 << 24), /*!< from --nopretrans */ RPMTRANS_FLAG_NOPOSTTRANS = (1 << 25), /*!< from --noposttrans */ - RPMTRANS_FLAG_NOCOLLECTIONS = (1 << 26), /*!< from --nocollections */ + /* bit 26 unused */ RPMTRANS_FLAG_NOMD5 = (1 << 27), /*!< from --nomd5 */ RPMTRANS_FLAG_NOFILEDIGEST = (1 << 27), /*!< from --nofiledigest (alias to --nomd5) */ /* bits 28-29 unused */ @@ -96,11 +97,11 @@ enum rpmVSFlags_e { RPMVSF_NEEDPAYLOAD = (1 << 1), /* bit(s) 2-7 unused */ RPMVSF_NOSHA1HEADER = (1 << 8), - RPMVSF_NOMD5HEADER = (1 << 9), /* unimplemented */ + RPMVSF_NOSHA256HEADER = (1 << 9), RPMVSF_NODSAHEADER = (1 << 10), - RPMVSF_NORSAHEADER = (1 << 11), /* unimplemented */ + RPMVSF_NORSAHEADER = (1 << 11), /* bit(s) 12-15 unused */ - RPMVSF_NOSHA1 = (1 << 16), /* unimplemented */ + RPMVSF_NOPAYLOAD = (1 << 16), RPMVSF_NOMD5 = (1 << 17), RPMVSF_NODSA = (1 << 18), RPMVSF_NORSA = (1 << 19) @@ -111,8 +112,8 @@ typedef rpmFlags rpmVSFlags; #define _RPMVSF_NODIGESTS \ ( RPMVSF_NOSHA1HEADER | \ - RPMVSF_NOMD5HEADER | \ - RPMVSF_NOSHA1 | \ + RPMVSF_NOSHA256HEADER | \ + RPMVSF_NOPAYLOAD | \ RPMVSF_NOMD5 ) #define _RPMVSF_NOSIGNATURES \ @@ -123,13 +124,13 @@ typedef rpmFlags rpmVSFlags; #define _RPMVSF_NOHEADER \ ( RPMVSF_NOSHA1HEADER | \ - RPMVSF_NOMD5HEADER | \ + RPMVSF_NOSHA256HEADER | \ RPMVSF_NODSAHEADER | \ RPMVSF_NORSAHEADER ) #define _RPMVSF_NOPAYLOAD \ - ( RPMVSF_NOSHA1 | \ - RPMVSF_NOMD5 | \ + ( RPMVSF_NOMD5 | \ + RPMVSF_NOPAYLOAD | \ RPMVSF_NODSA | \ RPMVSF_NORSA ) @@ -156,6 +157,12 @@ typedef enum rpmtsOpX_e { RPMTS_OP_MAX = 17 } rpmtsOpX; +enum rpmtxnFlags_e { + RPMTXN_READ = (1 << 0), + RPMTXN_WRITE = (1 << 1), +}; +typedef rpmFlags rpmtxnFlags; + /** \ingroup rpmts * Perform dependency resolution on the transaction set. * @@ -278,6 +285,15 @@ rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmDbiTagVal rpmtag, const void * keyp, size_t keylen); /** \ingroup rpmts + * Import a header into the rpmdb + * @param txn transaction handle + * @param h header + * @param flags (unused) + * @return RPMRC_OK/RPMRC_FAIL + */ +rpmRC rpmtsImportHeader(rpmtxn txn, Header h, rpmFlags flags); + +/** \ingroup rpmts * Import public key packet(s). * @todo Implicit --update policy for gpg-pubkey headers. * @param ts transaction set @@ -473,7 +489,7 @@ rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags); rpm_color_t rpmtsColor(rpmts ts); /** \ingroup rpmts - * Retrieve prefered file color + * Retrieve preferred file color * @param ts transaction set * @return color bits */ @@ -488,7 +504,7 @@ rpm_color_t rpmtsPrefColor(rpmts ts); rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color); /** \ingroup rpmts - * Set prefered file color + * Set preferred file color * @param ts transaction set * @param color new color bits * @return previous color bits @@ -549,6 +565,16 @@ int rpmtsAddInstallElement(rpmts ts, Header h, rpmRelocation * relocs); /** \ingroup rpmts + * Add package to be reinstalled to transaction set. + * + * @param ts transaction set + * @param h header + * @param key package retrieval key (e.g. file name) + * @return 0 on success + */ +int rpmtsAddReinstallElement(rpmts ts, Header h, const fnpyKey key); + +/** \ingroup rpmts * Add package to be erased to transaction set. * @param ts transaction set * @param h header @@ -557,6 +583,21 @@ int rpmtsAddInstallElement(rpmts ts, Header h, */ int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset); +/** \ingroup rpmts + * Create a transaction (lock) handle + * @param ts transaction set + * @param flags flags + * @return transaction handle + */ +rpmtxn rpmtxnBegin(rpmts ts, rpmtxnFlags flags); + +/** \ingroup rpmts + * Destroy transaction (lock) handle + * @param txn transaction handle + * @return NULL always + */ +rpmtxn rpmtxnEnd(rpmtxn txn); + /** \ingroup rpmte * Destroy transaction element iterator. * @param tsi transaction element iterator diff --git a/lib/rpmts_internal.h b/lib/rpmts_internal.h index d4f25e17e..1e166d632 100644 --- a/lib/rpmts_internal.h +++ b/lib/rpmts_internal.h @@ -8,13 +8,16 @@ #include "lib/fprint.h" #include "lib/rpmlock.h" #include "lib/rpmdb_internal.h" +#include "lib/rpmscript.h" +#include "lib/rpmtriggers.h" typedef struct diskspaceInfo_s * rpmDiskSpaceInfo; /* Transaction set elements information */ typedef struct tsMembers_s { rpmstrPool pool; /*!< Global string pool */ - removedHash removedPackages; /*!< Set of packages being removed. */ + packageHash removedPackages; /*!< Set of packages being removed. */ + packageHash installedPackages; /*!< Set of installed packages */ rpmal addedPackages; /*!< Set of packages being installed. */ rpmds rpmlib; /*!< rpmlib() dependency set. */ @@ -24,6 +27,17 @@ typedef struct tsMembers_s { int delta; /*!< Delta for reallocation. */ } * tsMembers; +typedef struct tsTrigger_s { + unsigned int hdrNum; + int index; +} tsTrigger; + +typedef struct tsTriggers_s { + tsTrigger *trigger; + int count; + int alloced; +} tsTriggers; + /** \ingroup rpmts * The set of packages to be installed/removed atomically. */ @@ -47,10 +61,9 @@ struct rpmts_s { tsMembers members; /*!< Transaction set member info (order etc) */ - struct selabel_handle * selabelHandle; /*!< Handle to selabel */ - char * rootDir; /*!< Path to top of install tree. */ char * lockPath; /*!< Transaction lock path */ + rpmlock lock; /*!< Transaction lock file */ FD_t scriptFd; /*!< Scriptlet stdout/stderr. */ rpm_tid_t tid; /*!< Transaction id. */ @@ -69,6 +82,10 @@ struct rpmts_s { rpmPlugins plugins; /*!< Transaction plugins */ int nrefs; /*!< Reference count. */ + + rpmtriggers trigs2run; /*!< Transaction file triggers */ + + int min_writes; /*!< macro minimize_writes used */ }; #ifdef __cplusplus @@ -86,6 +103,11 @@ rpmstrPool rpmtsPool(rpmts ts); RPM_GNUC_INTERNAL tsMembers rpmtsMembers(rpmts ts); +/* Return rpmdb iterator with removals optionally pruned out */ +RPM_GNUC_INTERNAL +rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, + const char * key, int prune); + RPM_GNUC_INTERNAL rpmal rpmtsCreateAl(rpmts ts, rpmElementTypes types); @@ -94,29 +116,11 @@ RPM_GNUC_INTERNAL int rpmtsSolve(rpmts ts, rpmds key); RPM_GNUC_INTERNAL -rpmlock rpmtsAcquireLock(rpmts ts); +rpmRC rpmtsSetupTransactionPlugins(rpmts ts); -/** \ingroup rpmts - * Get the selabel handle from the transaction set - * @param ts transaction set - * @return rpm selabel handle, or NULL if it hasn't been initialized yet - */ -struct selabel_handle * rpmtsSELabelHandle(rpmts ts); - -/** \ingroup rpmts - * Initialize selabel - * @param ts transaction set - * @param open_status if the func should open selinux status or just check it - * @return RPMRC_OK on success, RPMRC_FAIL otherwise - */ -rpmRC rpmtsSELabelInit(rpmts ts, int open_status); - -/** \ingroup rpmts - * Clean up selabel - * @param ts transaction set - * @param close_status whether we should close selinux status - */ -void rpmtsSELabelFini(rpmts ts, int close_status); +RPM_GNUC_INTERNAL +rpmRC runScript(rpmts ts, rpmte te, Header h, ARGV_const_t prefixes, + rpmScript script, int arg1, int arg2); #ifdef __cplusplus } diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h index 91ea94907..1948d183b 100644 --- a/lib/rpmtypes.h +++ b/lib/rpmtypes.h @@ -64,10 +64,12 @@ typedef struct rpmts_s * rpmts; typedef struct rpmte_s * rpmte; typedef struct rpmds_s * rpmds; typedef struct rpmfi_s * rpmfi; +typedef struct rpmfiles_s * rpmfiles; typedef struct rpmdb_s * rpmdb; typedef struct rpmdbMatchIterator_s * rpmdbMatchIterator; typedef struct rpmtsi_s * rpmtsi; typedef struct rpmps_s * rpmps; +typedef struct rpmtxn_s * rpmtxn; typedef struct rpmdbIndexIterator_s * rpmdbIndexIterator; typedef const void * fnpyKey; @@ -80,6 +82,7 @@ typedef struct rpmKeyring_s * rpmKeyring; typedef uint32_t rpmsid; typedef struct rpmstrPool_s * rpmstrPool; +typedef struct rpmPlugin_s * rpmPlugin; typedef struct rpmPlugins_s * rpmPlugins; typedef struct rpmgi_s * rpmgi; diff --git a/lib/rpmug.c b/lib/rpmug.c index 53a5a6e4d..119d8e585 100644 --- a/lib/rpmug.c +++ b/lib/rpmug.c @@ -1,7 +1,9 @@ #include "system.h" +#include <pthread.h> #include <pwd.h> #include <grp.h> +#include <netdb.h> #include <rpm/rpmlog.h> #include <rpm/rpmstring.h> @@ -9,32 +11,6 @@ #include "lib/rpmug.h" #include "debug.h" -#define HASHTYPE strCache -#define HTKEYTYPE const char * -#include "lib/rpmhash.H" -#include "lib/rpmhash.C" -#undef HASHTYPE -#undef HTKEYTYPE - -static strCache strStash = NULL; - -const char * rpmugStashStr(const char *str) -{ - const char *ret = NULL; - if (str) { - if (strStash == NULL) { - strStash = strCacheCreate(64, rstrhash, strcmp, - (strCacheFreeKey)rfree); - } - - if (!strCacheGetEntry(strStash, str, &ret)) { - strCacheAddEntry(strStash, xstrdup(str)); - (void) strCacheGetEntry(strStash, str, &ret); - } - } - return ret; -} - /* * These really ought to use hash tables. I just made the * guess that most files would be owned by root or the same person/group @@ -55,7 +31,7 @@ int rpmugUid(const char * thisUname, uid_t * uid) if (!thisUname) { lastUnameLen = 0; return -1; - } else if (rstreq(thisUname, "root")) { + } else if (rstreq(thisUname, UID_0_USER)) { *uid = 0; return 0; } @@ -98,7 +74,7 @@ int rpmugGid(const char * thisGname, gid_t * gid) if (thisGname == NULL) { lastGnameLen = 0; return -1; - } else if (rstreq(thisGname, "root")) { + } else if (rstreq(thisGname, GID_0_GROUP)) { *gid = 0; return 0; } @@ -140,7 +116,7 @@ const char * rpmugUname(uid_t uid) lastUid = (uid_t) -1; return NULL; } else if (uid == (uid_t) 0) { - return "root"; + return UID_0_USER; } else if (uid == lastUid) { return lastUname; } else { @@ -171,7 +147,7 @@ const char * rpmugGname(gid_t gid) lastGid = (gid_t) -1; return NULL; } else if (gid == (gid_t) 0) { - return "root"; + return GID_0_GROUP; } else if (gid == lastGid) { return lastGname; } else { @@ -192,11 +168,27 @@ const char * rpmugGname(gid_t gid) } } +static void loadLibs(void) +{ + (void) getpwnam(UID_0_USER); + endpwent(); + (void) getgrnam(GID_0_GROUP); + endgrent(); + (void) gethostbyname("localhost"); +} + +int rpmugInit(void) +{ + static pthread_once_t libsLoaded = PTHREAD_ONCE_INIT; + + pthread_once(&libsLoaded, loadLibs); + return 0; +} + void rpmugFree(void) { rpmugUid(NULL, NULL); rpmugGid(NULL, NULL); rpmugUname(-1); rpmugGname(-1); - strStash = strCacheFree(strStash); } diff --git a/lib/rpmug.h b/lib/rpmug.h index 35e5d631d..22d64eb88 100644 --- a/lib/rpmug.h +++ b/lib/rpmug.h @@ -3,8 +3,6 @@ #include <sys/types.h> -const char * rpmugStashStr(const char *str); - int rpmugUid(const char * name, uid_t * uid); int rpmugGid(const char * name, gid_t * gid); @@ -13,6 +11,8 @@ const char * rpmugUname(uid_t uid); const char * rpmugGname(gid_t gid); +int rpmugInit(void); + void rpmugFree(void); #endif /* _RPMUG_H */ diff --git a/lib/rpmvercmp.c b/lib/rpmvercmp.c index de3beeab4..b3d08faa4 100644 --- a/lib/rpmvercmp.c +++ b/lib/rpmvercmp.c @@ -36,7 +36,7 @@ int rpmvercmp(const char * a, const char * b) while (*one && !risalnum(*one) && *one != '~') one++; while (*two && !risalnum(*two) && *two != '~') two++; - /* handle the tilde separator, it sorts before everthing else */ + /* handle the tilde separator, it sorts before everything else */ if (*one == '~' || *two == '~') { if (*one != '~') return 1; if (*two != '~') return -1; diff --git a/lib/rpmvf.h b/lib/rpmvf.h index 51690b857..4cb463c2f 100644 --- a/lib/rpmvf.h +++ b/lib/rpmvf.h @@ -3,9 +3,11 @@ /** \ingroup rpmvf * \file lib/rpmvf.h - * @todo Add a more complete API... + * + * \brief Verify a package. The constants that enable/disable some sanity checks (mainly used at post (un)install) */ #include <rpm/rpmtypes.h> +#include <rpm/rpmutil.h> #ifdef __cplusplus extern "C" { @@ -85,13 +87,14 @@ typedef rpmFlags rpmVerifyFlags; /** \ingroup rpmvf * Verify file attributes (including digest). - * @todo gnorpm and python bindings prevent this from being static. + * @deprecated use rpmfiVerify() / rpmfilesVerify() instead * @param ts transaction set * @param fi file info (with linked header and current file index) * @retval *res bit(s) returned to indicate failure * @param omitMask bit(s) to disable verify checks * @return 0 on success (or not installed), 1 on error */ +RPM_GNUC_DEPRECATED int rpmVerifyFile(const rpmts ts, rpmfi fi, rpmVerifyAttrs * res, rpmVerifyAttrs omitMask); diff --git a/lib/rpmvs.c b/lib/rpmvs.c new file mode 100644 index 000000000..f77bfb02d --- /dev/null +++ b/lib/rpmvs.c @@ -0,0 +1,456 @@ +#include "system.h" + +#include <rpm/rpmkeyring.h> +#include "lib/rpmvs.h" +#include "rpmio/digest.h" + +#include "debug.h" + +struct rpmvs_s { + struct rpmsinfo_s *sigs; + rpmRC *rcs; + char **results; + int nsigs; + int nalloced; +}; + +struct vfytag_s { + rpmTagVal tag; + rpmTagType tagtype; + rpm_count_t tagcount; + rpm_count_t tagsize; +}; + +static const struct vfytag_s rpmvfytags[] = { + { RPMSIGTAG_SIZE, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_PGP, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_MD5, RPM_BIN_TYPE, 0, 16, }, + { RPMSIGTAG_GPG, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_PGP5, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_PAYLOADSIZE, RPM_INT32_TYPE, 1, 4, }, + { RPMSIGTAG_RESERVEDSPACE, RPM_BIN_TYPE, 0, 0, }, + { RPMTAG_DSAHEADER, RPM_BIN_TYPE, 0, 0, }, + { RPMTAG_RSAHEADER, RPM_BIN_TYPE, 0, 0, }, + { RPMTAG_SHA1HEADER, RPM_STRING_TYPE, 1, 41, }, + { RPMSIGTAG_LONGSIZE, RPM_INT64_TYPE, 1, 8, }, + { RPMSIGTAG_LONGARCHIVESIZE, RPM_INT64_TYPE, 1, 8, }, + { RPMTAG_SHA256HEADER, RPM_STRING_TYPE, 1, 65, }, + { RPMTAG_PAYLOADDIGEST, RPM_STRING_ARRAY_TYPE, 0, 0, }, + { 0 } /* sentinel */ +}; + +struct vfyinfo_s { + rpmTagVal tag; + struct rpmsinfo_s vi; +}; + +static const struct vfyinfo_s rpmvfyitems[] = { + { RPMSIGTAG_SIZE, + { RPMSIG_OTHER_TYPE, 0, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_PGP, + { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSA, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_MD5, + { RPMSIG_DIGEST_TYPE, RPMVSF_NOMD5, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), PGPHASHALGO_MD5, }, }, + { RPMSIGTAG_GPG, + { RPMSIG_SIGNATURE_TYPE, RPMVSF_NODSA, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_PGP5, + { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSA, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_PAYLOADSIZE, + { RPMSIG_OTHER_TYPE, 0, + (RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_RESERVEDSPACE, + { RPMSIG_OTHER_TYPE, 0, + 0, 0, }, }, + { RPMTAG_DSAHEADER, + { RPMSIG_SIGNATURE_TYPE, RPMVSF_NODSAHEADER, + (RPMSIG_HEADER), 0, }, }, + { RPMTAG_RSAHEADER, + { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSAHEADER, + (RPMSIG_HEADER), 0, }, }, + { RPMTAG_SHA1HEADER, + { RPMSIG_DIGEST_TYPE, RPMVSF_NOSHA1HEADER, + (RPMSIG_HEADER), PGPHASHALGO_SHA1, }, }, + { RPMSIGTAG_LONGSIZE, + { RPMSIG_OTHER_TYPE, 0, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMSIGTAG_LONGARCHIVESIZE, + { RPMSIG_OTHER_TYPE, 0, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, }, + { RPMTAG_SHA256HEADER, + { RPMSIG_DIGEST_TYPE, RPMVSF_NOSHA256HEADER, + (RPMSIG_HEADER), PGPHASHALGO_SHA256, }, }, + { RPMTAG_PAYLOADDIGEST, + { RPMSIG_DIGEST_TYPE, RPMVSF_NOPAYLOAD, + (RPMSIG_PAYLOAD), PGPHASHALGO_SHA256, }, }, + { 0 } /* sentinel */ +}; + +static const char *rangeName(int range); +static const char * rpmSigString(rpmRC res); +static rpmRC rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo, + DIGEST_CTX ctx, char ** result); + +static int sinfoLookup(rpmTagVal tag) +{ + const struct vfyinfo_s *start = &rpmvfyitems[0]; + int ix = -1; + for (const struct vfyinfo_s *si = start; si->tag; si++) { + if (tag == si->tag) { + ix = si - start; + break; + } + } + return ix; +} + +static int validHex(const char *str, size_t slen) +{ + int valid = 0; /* Assume invalid */ + const char *b; + + /* Our hex data is always even sized and at least sha-1 long */ + if (slen % 2 || slen < 40) + goto exit; + + for (b = str ; *b != '\0'; b++) { + if (strchr("0123456789abcdefABCDEF", *b) == NULL) + goto exit; + } + valid = 1; + +exit: + return valid; +} + +static rpmRC rpmsinfoInit(rpmtd td, const char *origin, + struct rpmsinfo_s *sinfo, char **msg) +{ + rpmRC rc = RPMRC_FAIL; + const void *data = NULL; + const struct vfytag_s *tinfo; + const struct vfyinfo_s *vinfo; + rpm_count_t dlen = 0; + int ix; + + if ((ix = sinfoLookup(td->tag)) == -1) { + /* anything unknown just falls through for now */ + rc = RPMRC_OK; + goto exit; + } + vinfo = &rpmvfyitems[ix]; + tinfo = &rpmvfytags[ix]; + assert(tinfo->tag == vinfo->tag); + + *sinfo = rpmvfyitems[ix].vi; /* struct assignment */ + + if (tinfo->tagtype && tinfo->tagtype != td->type) { + rasprintf(msg, _("%s tag %u: invalid type %u"), + origin, td->tag, td->type); + goto exit; + } + + if (tinfo->tagcount && tinfo->tagcount != td->count) { + rasprintf(msg, _("%s: tag %u: invalid count %u"), + origin, td->tag, td->count); + goto exit; + } + + switch (td->type) { + case RPM_STRING_TYPE: + case RPM_STRING_ARRAY_TYPE: + data = rpmtdGetString(td); + if (data) + dlen = strlen(data); + break; + case RPM_BIN_TYPE: + data = td->data; + dlen = td->count; + break; + } + + /* MD5 has data length of 16, everything else is (much) larger */ + if (sinfo->hashalgo && (data == NULL || dlen < 16)) { + rasprintf(msg, _("%s tag %u: invalid data %p (%u)"), + origin, td->tag, data, dlen); + goto exit; + } + + if (td->type == RPM_STRING_TYPE && td->size == 0) + td->size = dlen + 1; + + if (tinfo->tagsize && (td->flags & RPMTD_IMMUTABLE) && + tinfo->tagsize != td->size) { + rasprintf(msg, _("%s tag %u: invalid size %u"), + origin, td->tag, td->size); + goto exit; + } + + if (sinfo->type == RPMSIG_SIGNATURE_TYPE) { + if (pgpPrtParams(data, dlen, PGPTAG_SIGNATURE, &sinfo->sig)) { + rasprintf(msg, _("%s tag %u: invalid OpenPGP signature"), + origin, td->tag); + goto exit; + } + sinfo->hashalgo = pgpDigParamsAlgo(sinfo->sig, PGPVAL_HASHALGO); + sinfo->keyid = pgpGrab(sinfo->sig->signid+4, 4); + } else if (sinfo->type == RPMSIG_DIGEST_TYPE) { + if (td->type == RPM_BIN_TYPE) { + sinfo->dig = pgpHexStr(data, dlen); + } else { + if (!validHex(data, dlen)) { + rasprintf(msg, _("%s: tag %u: invalid hex"), origin, td->tag); + goto exit; + } + sinfo->dig = xstrdup(data); + } + } + + if (sinfo->hashalgo) + sinfo->id = (td->tag << 16) | rpmtdGetIndex(td); + + rc = RPMRC_OK; + +exit: + return rc; +} + +static void rpmsinfoFini(struct rpmsinfo_s *sinfo) +{ + if (sinfo) { + if (sinfo->type == RPMSIG_SIGNATURE_TYPE) + pgpDigParamsFree(sinfo->sig); + else if (sinfo->type == RPMSIG_DIGEST_TYPE) + free(sinfo->dig); + free(sinfo->descr); + memset(sinfo, 0, sizeof(*sinfo)); + } +} + +static int rpmsinfoDisabled(const struct rpmsinfo_s *sinfo, rpmVSFlags vsflags) +{ + if (!(sinfo->type & RPMSIG_VERIFIABLE_TYPE)) + return 1; + if (vsflags & sinfo->disabler) + return 1; + if ((vsflags & RPMVSF_NEEDPAYLOAD) && (sinfo->range & RPMSIG_PAYLOAD)) + return 1; + return 0; +} + +static void rpmvsReserve(struct rpmvs_s *vs, int n) +{ + if (vs->nsigs + n >= vs->nalloced) { + vs->nalloced = (vs->nsigs * 2) + n; + vs->rcs = xrealloc(vs->rcs, vs->nalloced * sizeof(*vs->rcs)); + vs->results = xrealloc(vs->results, vs->nalloced * sizeof(*vs->results)); + vs->sigs = xrealloc(vs->sigs, vs->nalloced * sizeof(*vs->sigs)); + } +} + +const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo) +{ + if (sinfo->descr == NULL) { + char *t; + switch (sinfo->type) { + case RPMSIG_DIGEST_TYPE: + rasprintf(&sinfo->descr, _("%s%s %s"), + rangeName(sinfo->range), + pgpValString(PGPVAL_HASHALGO, sinfo->hashalgo), + _("digest")); + break; + case RPMSIG_SIGNATURE_TYPE: + t = sinfo->sig ? pgpIdentItem(sinfo->sig) : NULL; + rasprintf(&sinfo->descr, _("%s%s"), + rangeName(sinfo->range), t ? t : _("signature")); + free(t); + break; + } + } + return sinfo->descr; +} + +char *rpmsinfoMsg(struct rpmsinfo_s *sinfo, rpmRC rc, const char *emsg) +{ + char *msg = NULL; + if (emsg) { + rasprintf(&msg, "%s: %s (%s)", + rpmsinfoDescr(sinfo), rpmSigString(rc), emsg); + } else { + rasprintf(&msg, "%s: %s", rpmsinfoDescr(sinfo), rpmSigString(rc)); + } + return msg; +} + +void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob, rpmTagVal tag) +{ + struct rpmtd_s td; + rpmRC rc = hdrblobGet(blob, tag, &td); + + if (rc == RPMRC_OK) { + const char *o = (blob->il > blob->ril) ? _("header") : _("package"); + int ix; + + rpmvsReserve(sis, rpmtdCount(&td)); + + while ((ix = rpmtdNext(&td)) >= 0) { + sis->results[sis->nsigs] = NULL; + sis->rcs[sis->nsigs] = rpmsinfoInit(&td, o, + &sis->sigs[sis->nsigs], + &sis->results[sis->nsigs]); + sis->nsigs++; + } + rpmtdFreeData(&td); + } +} + +struct rpmvs_s *rpmvsCreate(hdrblob blob, rpmVSFlags vsflags) +{ + struct rpmvs_s *sis = xcalloc(1, sizeof(*sis)); + + rpmvsReserve(sis, 2); /* XXX bump this up later */ + + for (const struct vfyinfo_s *si = &rpmvfyitems[0]; si->tag; si++) { + if (rpmsinfoDisabled(&si->vi, vsflags)) + continue; + rpmvsAppend(sis, blob, si->tag); + } + return sis; +} + +struct rpmvs_s *rpmvsFree(struct rpmvs_s *sis) +{ + if (sis) { + free(sis->rcs); + for (int i = 0; i < sis->nsigs; i++) { + rpmsinfoFini(&sis->sigs[i]); + free(sis->results[i]); + } + free(sis->sigs); + free(sis->results); + free(sis); + } + return NULL; +} + +void rpmvsInitDigests(struct rpmvs_s *sis, int range, rpmDigestBundle bundle) +{ + for (int i = 0; i < sis->nsigs; i++) { + struct rpmsinfo_s *sinfo = &sis->sigs[i]; + if (sinfo->range & range) { + if (sis->rcs[i] == RPMRC_OK) + rpmDigestBundleAddID(bundle, sinfo->hashalgo, sinfo->id, 0); + } + } +} + +int rpmvsVerifyItems(rpmPlugins plugins, struct rpmvs_s *sis, int range, rpmDigestBundle bundle, + rpmKeyring keyring, rpmsinfoCb cb, void *cbdata) +{ + int failed = 0; + + for (int i = 0; i < sis->nsigs; i++) { + struct rpmsinfo_s *sinfo = &sis->sigs[i]; + + if (sinfo->range == range) { + if (sis->rcs[i] == RPMRC_OK) { + DIGEST_CTX ctx = rpmDigestBundleDupCtx(bundle, sinfo->id); + sis->results[i] = _free(sis->results[i]); + sis->rcs[i] = rpmVerifySignature(keyring, sinfo, ctx, &sis->results[i]); + /* Run verify hook for all plugins */ + sis->rcs[i] = rpmpluginsCallVerify(plugins, keyring, sinfo->id, sinfo->sig, ctx, sis->rcs[i]); + rpmDigestFinal(ctx, NULL, NULL, 0); + rpmDigestBundleFinal(bundle, sinfo->id, NULL, NULL, 0); + } + + if (cb) + sis->rcs[i] = cb(sinfo, sis->rcs[i], sis->results[i], cbdata); + + if (sis->rcs[i] != RPMRC_OK) + failed++; + } + } + + return failed; +} + +static const char * rpmSigString(rpmRC res) +{ + const char * str; + switch (res) { + case RPMRC_OK: str = "OK"; break; + case RPMRC_FAIL: str = "BAD"; break; + case RPMRC_NOKEY: str = "NOKEY"; break; + case RPMRC_NOTTRUSTED: str = "NOTTRUSTED"; break; + default: + case RPMRC_NOTFOUND: str = "UNKNOWN"; break; + } + return str; +} + +static const char *rangeName(int range) +{ + switch (range) { + case RPMSIG_HEADER: return _("Header "); + case RPMSIG_PAYLOAD: return _("Payload "); + } + /* trad. output for (RPMSIG_HEADER|RPMSIG_PAYLOAD) range is "" */ + return ""; +} + +static rpmRC verifyDigest(struct rpmsinfo_s *sinfo, DIGEST_CTX digctx, + char **msg) +{ + rpmRC res = RPMRC_FAIL; /* assume failure */ + char * dig = NULL; + size_t diglen = 0; + DIGEST_CTX ctx = rpmDigestDup(digctx); + + if (rpmDigestFinal(ctx, (void **)&dig, &diglen, 1) || diglen == 0) + goto exit; + + if (strcasecmp(sinfo->dig, dig) == 0) { + res = RPMRC_OK; + } else { + rasprintf(msg, "Expected %s != %s", sinfo->dig, dig); + } + +exit: + free(dig); + return res; +} + +/** + * Verify DSA/RSA signature. + * @param keyring pubkey keyring + * @param sinfo OpenPGP signature parameters + * @param hashctx digest context + * @retval msg verbose success/failure text + * @return RPMRC_OK on success + */ +static rpmRC +verifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo, + DIGEST_CTX hashctx, char **msg) +{ + rpmRC res = rpmKeyringVerifySig(keyring, sinfo->sig, hashctx); + + return res; +} + +static rpmRC +rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo, + DIGEST_CTX ctx, char ** result) +{ + rpmRC res = RPMRC_FAIL; + + if (sinfo->type == RPMSIG_DIGEST_TYPE) + res = verifyDigest(sinfo, ctx, result); + else if (sinfo->type == RPMSIG_SIGNATURE_TYPE) + res = verifySignature(keyring, sinfo, ctx, result); + + return res; +} diff --git a/lib/rpmvs.h b/lib/rpmvs.h new file mode 100644 index 000000000..f96f20219 --- /dev/null +++ b/lib/rpmvs.h @@ -0,0 +1,75 @@ +#ifndef _RPMVS_H +#define _RPMVS_H + +#include <rpm/rpmtypes.h> +#include <rpm/rpmts.h> /* for rpmVSFlags */ +#include "lib/header_internal.h" + +enum { + RPMSIG_UNKNOWN_TYPE = 0, + RPMSIG_DIGEST_TYPE = (1 << 0), + RPMSIG_SIGNATURE_TYPE = (1 << 1), + RPMSIG_OTHER_TYPE = (1 << 2), +}; + +#define RPMSIG_VERIFIABLE_TYPE (RPMSIG_DIGEST_TYPE|RPMSIG_SIGNATURE_TYPE) + +/* siginfo range bits */ +enum { + RPMSIG_HEADER = (1 << 0), + RPMSIG_PAYLOAD = (1 << 1), +}; + +struct rpmsinfo_s { + /* static data */ + int type; + int disabler; + int range; + /* parsed data */ + int hashalgo; + int id; + unsigned int keyid; + union { + pgpDigParams sig; + char *dig; + }; + char *descr; +}; + +typedef rpmRC (*rpmsinfoCb)(struct rpmsinfo_s *sinfo, rpmRC sigres, const char *result, void *cbdata); + +#ifdef __cplusplus +extern "C" { +#endif + +RPM_GNUC_INTERNAL +const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo); + +RPM_GNUC_INTERNAL +char *rpmsinfoMsg(struct rpmsinfo_s *sinfo, rpmRC rc, const char *emsg); + +RPM_GNUC_INTERNAL +struct rpmvs_s *rpmvsCreate(hdrblob blob, rpmVSFlags vsflags); + +RPM_GNUC_INTERNAL +struct rpmvs_s *rpmvsFree(struct rpmvs_s *sis); + +RPM_GNUC_INTERNAL +void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob, rpmTagVal tag); + +RPM_GNUC_INTERNAL +void rpmvsInitDigests(struct rpmvs_s *sis, int range, rpmDigestBundle bundle); + +RPM_GNUC_INTERNAL +int rpmvsVerifyItems(rpmPlugins plugins, struct rpmvs_s *sis, int range, rpmDigestBundle bundle, + rpmKeyring keyring, rpmsinfoCb cb, void *cbdata); + +RPM_GNUC_INTERNAL +rpmRC rpmpkgRead(rpmPlugins plugins, rpmKeyring keyring, rpmVSFlags flags, FD_t fd, + rpmsinfoCb cb, void *cbdata, Header *hdrp); + +#ifdef __cplusplus +} +#endif + +#endif /* _RPMVS_H */ diff --git a/lib/signature.c b/lib/signature.c index 1c5327072..6f04962e8 100644 --- a/lib/signature.c +++ b/lib/signature.c @@ -11,238 +11,74 @@ #include <rpm/rpmstring.h> #include <rpm/rpmfileutil.h> #include <rpm/rpmlog.h> -#include <rpm/rpmkeyring.h> +#include <rpm/rpmmacro.h> #include "lib/rpmlead.h" -#include "lib/signature.h" #include "lib/header_internal.h" +#include "lib/signature.h" #include "debug.h" -/* Dumb wrapper around headerPut() for signature header */ -static int sighdrPut(Header h, rpmTagVal tag, rpmTagType type, - rpm_data_t p, rpm_count_t c) -{ - struct rpmtd_s sigtd; - rpmtdReset(&sigtd); - sigtd.tag = tag; - sigtd.type = type; - sigtd.data = p; - sigtd.count = c; - return headerPut(h, &sigtd, HEADERPUT_DEFAULT); -} - /** - * Print package size. - * @todo rpmio: use fdSize rather than fstat(2) to get file size. + * Print package size (debug purposes only) * @param fd package file handle - * @param siglen signature header size - * @param pad signature padding - * @param datalen length of header+payload - * @return rpmRC return code + * @param sigh signature header */ -static inline rpmRC printSize(FD_t fd, size_t siglen, size_t pad, rpm_loff_t datalen) +static void printSize(FD_t fd, Header sigh) { struct stat st; int fdno = Fileno(fd); + size_t siglen = headerSizeof(sigh, HEADER_MAGIC_YES); + size_t pad = (8 - (siglen % 8)) % 8; /* 8-byte pad */ + struct rpmtd_s sizetag; + rpm_loff_t datalen = 0; - if (fstat(fdno, &st) < 0) - return RPMRC_FAIL; + /* Print package component sizes. */ + if (headerGet(sigh, RPMSIGTAG_LONGSIZE, &sizetag, HEADERGET_DEFAULT)) { + rpm_loff_t *tsize = rpmtdGetUint64(&sizetag); + datalen = (tsize) ? *tsize : 0; + } else if (headerGet(sigh, RPMSIGTAG_SIZE, &sizetag, HEADERGET_DEFAULT)) { + rpm_off_t *tsize = rpmtdGetUint32(&sizetag); + datalen = (tsize) ? *tsize : 0; + } + rpmtdFreeData(&sizetag); rpmlog(RPMLOG_DEBUG, "Expected size: %12" PRIu64 \ " = lead(%d)+sigs(%zd)+pad(%zd)+data(%" PRIu64 ")\n", RPMLEAD_SIZE+siglen+pad+datalen, RPMLEAD_SIZE, siglen, pad, datalen); - rpmlog(RPMLOG_DEBUG, - " Actual size: %12" PRIu64 "\n", (rpm_loff_t) st.st_size); - return RPMRC_OK; + if (fstat(fdno, &st) == 0) { + rpmlog(RPMLOG_DEBUG, + " Actual size: %12" PRIu64 "\n", (rpm_loff_t) st.st_size); + } } -rpmRC rpmReadSignature(FD_t fd, Header * sighp, sigType sig_type, char ** msg) +rpmRC rpmReadSignature(FD_t fd, Header * sighp, char ** msg) { char *buf = NULL; - int32_t block[4]; - int32_t il; - int32_t dl; - int32_t * ei = NULL; - entryInfo pe; - unsigned int nb, uc; - int32_t ril = 0; - struct indexEntry_s entry; - struct entryInfo_s info; - unsigned char * dataStart; - unsigned char * dataEnd = NULL; + struct hdrblob_s blob; Header sigh = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ - int xx; - int i; if (sighp) *sighp = NULL; - if (sig_type != RPMSIGTYPE_HEADERSIG) - goto exit; - - memset(block, 0, sizeof(block)); - if ((xx = Freadall(fd, block, sizeof(block))) != sizeof(block)) { - rasprintf(&buf, _("sigh size(%d): BAD, read returned %d\n"), - (int)sizeof(block), xx); - goto exit; - } - if (memcmp(block, rpm_header_magic, sizeof(rpm_header_magic))) { - rasprintf(&buf, _("sigh magic: BAD\n")); + if (hdrblobRead(fd, 1, 1, RPMTAG_HEADERSIGNATURES, &blob, &buf) != RPMRC_OK) goto exit; - } - il = ntohl(block[2]); - if (il < 0 || il > 32) { - rasprintf(&buf, - _("sigh tags: BAD, no. of tags(%d) out of range\n"), il); - goto exit; - } - dl = ntohl(block[3]); - if (dl < 0 || dl > 8192) { - rasprintf(&buf, - _("sigh data: BAD, no. of bytes(%d) out of range\n"), dl); - goto exit; - } - - memset(&entry, 0, sizeof(entry)); - memset(&info, 0, sizeof(info)); - - nb = (il * sizeof(struct entryInfo_s)) + dl; - uc = sizeof(il) + sizeof(dl) + nb; - ei = xmalloc(uc); - ei[0] = block[2]; - ei[1] = block[3]; - pe = (entryInfo) &ei[2]; - dataStart = (unsigned char *) (pe + il); - if ((xx = Freadall(fd, pe, nb)) != nb) { - rasprintf(&buf, - _("sigh blob(%d): BAD, read returned %d\n"), (int)nb, xx); - goto exit; - } - /* Check (and convert) the 1st tag element. */ - xx = headerVerifyInfo(1, dl, pe, &entry.info, 0); - if (xx != -1) { - rasprintf(&buf, _("tag[%d]: BAD, tag %d type %d offset %d count %d\n"), - 0, entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - - /* Is there an immutable header region tag? */ - if (entry.info.tag == RPMTAG_HEADERSIGNATURES) { - /* Is the region tag sane? */ - if (!(entry.info.type == REGION_TAG_TYPE && - entry.info.count == REGION_TAG_COUNT)) { - rasprintf(&buf, - _("region tag: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - - /* Is the trailer within the data area? */ - if (entry.info.offset + REGION_TAG_COUNT > dl) { - rasprintf(&buf, - _("region offset: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - - /* Is there an immutable header region tag trailer? */ - dataEnd = dataStart + entry.info.offset; - (void) memcpy(&info, dataEnd, REGION_TAG_COUNT); - /* XXX Really old packages have HEADER_IMAGE, not HEADER_SIGNATURES. */ - if (info.tag == htonl(RPMTAG_HEADERIMAGE)) { - rpmTagVal stag = htonl(RPMTAG_HEADERSIGNATURES); - info.tag = stag; - memcpy(dataEnd, &stag, sizeof(stag)); - } - dataEnd += REGION_TAG_COUNT; - - xx = headerVerifyInfo(1, il * sizeof(*pe), &info, &entry.info, 1); - if (xx != -1 || - !((entry.info.tag == RPMTAG_HEADERSIGNATURES || entry.info.tag == RPMTAG_HEADERIMAGE) - && entry.info.type == REGION_TAG_TYPE - && entry.info.count == REGION_TAG_COUNT)) - { - rasprintf(&buf, - _("region trailer: BAD, tag %d type %d offset %d count %d\n"), - entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - memset(&info, 0, sizeof(info)); - - /* Is the no. of tags in the region less than the total no. of tags? */ - ril = entry.info.offset/sizeof(*pe); - if ((entry.info.offset % sizeof(*pe)) || ril > il) { - rasprintf(&buf, _("region size: BAD, ril(%d) > il(%d)\n"), ril, il); - goto exit; - } - } - - /* Sanity check signature tags */ - memset(&info, 0, sizeof(info)); - for (i = 1; i < il; i++) { - xx = headerVerifyInfo(1, dl, pe+i, &entry.info, 0); - if (xx != -1) { - rasprintf(&buf, - _("sigh tag[%d]: BAD, tag %d type %d offset %d count %d\n"), - i, entry.info.tag, entry.info.type, - entry.info.offset, entry.info.count); - goto exit; - } - } - /* OK, blob looks sane, load the header. */ - sigh = headerImport(ei, uc, 0); - if (sigh == NULL) { - rasprintf(&buf, _("sigh load: BAD\n")); + if (hdrblobImport(&blob, 0, &sigh, &buf) != RPMRC_OK) goto exit; - } - { size_t sigSize = headerSizeof(sigh, HEADER_MAGIC_YES); - size_t pad = (8 - (sigSize % 8)) % 8; /* 8-byte pad */ - ssize_t trc; - struct rpmtd_s sizetag; - rpm_loff_t archSize = 0; - - /* Position at beginning of header. */ - if (pad && (trc = Freadall(fd, block, pad)) != pad) { - rasprintf(&buf, - _("sigh pad(%zd): BAD, read %zd bytes\n"), pad, trc); - goto exit; - } - - /* Print package component sizes. */ - if (headerGet(sigh, RPMSIGTAG_LONGSIZE, &sizetag, HEADERGET_DEFAULT)) { - rpm_loff_t *tsize = rpmtdGetUint64(&sizetag); - archSize = (tsize) ? *tsize : 0; - } else if (headerGet(sigh, RPMSIGTAG_SIZE, &sizetag, HEADERGET_DEFAULT)) { - rpm_off_t *tsize = rpmtdGetUint32(&sizetag); - archSize = (tsize) ? *tsize : 0; - } - rpmtdFreeData(&sizetag); - rc = printSize(fd, sigSize, pad, archSize); - if (rc != RPMRC_OK) { - rasprintf(&buf, - _("sigh sigSize(%zd): BAD, fstat(2) failed\n"), sigSize); - goto exit; - } - } - ei = NULL; /* XXX will be freed with header */ + printSize(fd, sigh); + rc = RPMRC_OK; exit: if (sighp && sigh && rc == RPMRC_OK) *sighp = headerLink(sigh); headerFree(sigh); - free(ei); if (msg != NULL) { *msg = buf; @@ -255,7 +91,7 @@ exit: int rpmWriteSignature(FD_t fd, Header sigh) { - static uint8_t buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static const uint8_t zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int sigSize, pad; int rc; @@ -266,276 +102,128 @@ int rpmWriteSignature(FD_t fd, Header sigh) sigSize = headerSizeof(sigh, HEADER_MAGIC_YES); pad = (8 - (sigSize % 8)) % 8; if (pad) { - if (Fwrite(buf, sizeof(buf[0]), pad, fd) != pad) + if (Fwrite(zeros, sizeof(zeros[0]), pad, fd) != pad) rc = 1; } rpmlog(RPMLOG_DEBUG, "Signature: size(%d)+pad(%d)\n", sigSize, pad); return rc; } -Header rpmNewSignature(void) -{ - Header sigh = headerNew(); - return sigh; -} - -Header rpmFreeSignature(Header sigh) -{ - return headerFree(sigh); -} - -static int makeHDRDigest(Header sigh, const char * file, rpmTagVal sigTag) -{ - Header h = NULL; - FD_t fd = NULL; - char * SHA1 = NULL; - int ret = -1; /* assume failure. */ - - switch (sigTag) { - case RPMSIGTAG_SHA1: - fd = Fopen(file, "r.fdio"); - if (fd == NULL || Ferror(fd)) - goto exit; - h = headerRead(fd, HEADER_MAGIC_YES); - if (h == NULL) - goto exit; - - if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) { - DIGEST_CTX ctx; - struct rpmtd_s utd; - - if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT) - || utd.data == NULL) - { - rpmlog(RPMLOG_ERR, - _("Immutable header region could not be read. " - "Corrupted package?\n")); - goto exit; - } - ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE); - (void) rpmDigestUpdate(ctx, rpm_header_magic, sizeof(rpm_header_magic)); - (void) rpmDigestUpdate(ctx, utd.data, utd.count); - (void) rpmDigestFinal(ctx, (void **)&SHA1, NULL, 1); - rpmtdFreeData(&utd); - } else { - rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages\n")); - goto exit; - } - - if (SHA1 == NULL) - goto exit; - if (!sighdrPut(sigh, RPMSIGTAG_SHA1, RPM_STRING_TYPE, SHA1, 1)) - goto exit; - ret = 0; - break; - default: - break; - } - -exit: - free(SHA1); - headerFree(h); - if (fd != NULL) (void) Fclose(fd); - return ret; -} - -int rpmGenDigest(Header sigh, const char * file, rpmTagVal sigTag) +rpmRC rpmGenerateSignature(char *SHA256, char *SHA1, uint8_t *MD5, + rpm_loff_t size, rpm_loff_t payloadSize, FD_t fd) { - struct stat st; - uint8_t * pkt = NULL; - size_t pktlen; - int ret = -1; /* assume failure. */ - - switch (sigTag) { - case RPMSIGTAG_SIZE: { - rpm_off_t size; - if (stat(file, &st) != 0) - break; - size = st.st_size; - if (!sighdrPut(sigh, sigTag, RPM_INT32_TYPE, &size, 1)) - break; - ret = 0; - } break; - case RPMSIGTAG_LONGSIZE: { - rpm_loff_t size; - if (stat(file, &st) != 0) - break; - size = st.st_size; - if (!sighdrPut(sigh, sigTag, RPM_INT64_TYPE, &size, 1)) - break; - ret = 0; - } break; - case RPMSIGTAG_MD5: - pktlen = 16; - pkt = xcalloc(pktlen, sizeof(*pkt)); - if (rpmDoDigest(PGPHASHALGO_MD5, file, 0, pkt, NULL) - || !sighdrPut(sigh, sigTag, RPM_BIN_TYPE, pkt, pktlen)) - break; - ret = 0; - break; - case RPMSIGTAG_SHA1: - ret = makeHDRDigest(sigh, file, sigTag); - break; - default: - break; - } - free(pkt); - - return ret; -} - -static const char * rpmSigString(rpmRC res) -{ - const char * str; - switch (res) { - case RPMRC_OK: str = "OK"; break; - case RPMRC_FAIL: str = "BAD"; break; - case RPMRC_NOKEY: str = "NOKEY"; break; - case RPMRC_NOTTRUSTED: str = "NOTRUSTED"; break; - default: - case RPMRC_NOTFOUND: str = "UNKNOWN"; break; - } - return str; -} - -static rpmRC -verifyMD5Digest(rpmtd sigtd, DIGEST_CTX md5ctx, char **msg) -{ - rpmRC res = RPMRC_FAIL; /* assume failure */ - uint8_t * md5sum = NULL; - size_t md5len = 0; - char *md5; - const char *title = _("MD5 digest:"); - *msg = NULL; - DIGEST_CTX ctx = rpmDigestDup(md5ctx); - - if (ctx == NULL) { - rasprintf(msg, "%s %s\n", title, rpmSigString(res)); + Header sig = headerNew(); + struct rpmtd_s td; + rpmRC rc = RPMRC_OK; + char *reservedSpace; + int spaceSize = 32; /* always reserve a bit of space */ + int gpgSize = rpmExpandNumeric("%{__gpg_reserved_space}"); + rpm_off_t size32 = size; + rpm_off_t payloadSize32 = payloadSize; + + /* Prepare signature */ + if (SHA256) { + rpmtdReset(&td); + td.tag = RPMSIGTAG_SHA256; + td.count = 1; + td.type = RPM_STRING_TYPE; + td.data = SHA256; + headerPut(sig, &td, HEADERPUT_DEFAULT); + } + + if (SHA1) { + rpmtdReset(&td); + td.tag = RPMSIGTAG_SHA1; + td.count = 1; + td.type = RPM_STRING_TYPE; + td.data = SHA1; + headerPut(sig, &td, HEADERPUT_DEFAULT); + } + + if (MD5) { + rpmtdReset(&td); + td.tag = RPMSIGTAG_MD5; + td.count = 16; + td.type = RPM_BIN_TYPE; + td.data = MD5; + headerPut(sig, &td, HEADERPUT_DEFAULT); + } + + rpmtdReset(&td); + td.count = 1; + td.type = RPM_INT32_TYPE; + + td.tag = RPMSIGTAG_PAYLOADSIZE; + td.data = &payloadSize32; + headerPut(sig, &td, HEADERPUT_DEFAULT); + + td.tag = RPMSIGTAG_SIZE; + td.data = &size32; + headerPut(sig, &td, HEADERPUT_DEFAULT); + + if (size >= UINT32_MAX || payloadSize >= UINT32_MAX) { + /* + * Put the 64bit size variants into the header, but + * modify spaceSize so that the resulting header has + * the same size. Note that this only works if all tags + * with a lower number than RPMSIGTAG_RESERVEDSPACE are + * already added and no tag with a higher number is + * added yet. + */ + rpm_loff_t p = payloadSize; + rpm_loff_t s = size; + int newsigSize, oldsigSize; + + oldsigSize = headerSizeof(sig, HEADER_MAGIC_YES); + + headerDel(sig, RPMSIGTAG_PAYLOADSIZE); + headerDel(sig, RPMSIGTAG_SIZE); + + td.type = RPM_INT64_TYPE; + + td.tag = RPMSIGTAG_LONGARCHIVESIZE; + td.data = &p; + headerPut(sig, &td, HEADERPUT_DEFAULT); + + td.tag = RPMSIGTAG_LONGSIZE; + td.data = &s; + headerPut(sig, &td, HEADERPUT_DEFAULT); + + newsigSize = headerSizeof(sig, HEADER_MAGIC_YES); + spaceSize -= newsigSize - oldsigSize; + } + + if (gpgSize > 0) + spaceSize += gpgSize; + + if (spaceSize > 0) { + reservedSpace = xcalloc(spaceSize, sizeof(char)); + rpmtdReset(&td); + td.tag = RPMSIGTAG_RESERVEDSPACE; + td.count = spaceSize; + td.type = RPM_BIN_TYPE; + td.data = reservedSpace; + headerPut(sig, &td, HEADERPUT_DEFAULT); + free(reservedSpace); + } + + /* Reallocate the signature into one contiguous region. */ + sig = headerReload(sig, RPMTAG_HEADERSIGNATURES); + if (sig == NULL) { /* XXX can't happen */ + rpmlog(RPMLOG_ERR, _("Unable to reload signature header.\n")); + rc = RPMRC_FAIL; goto exit; } - (void) rpmDigestFinal(ctx, (void **)&md5sum, &md5len, 0); - - md5 = pgpHexStr(md5sum, md5len); - if (md5len != sigtd->count || memcmp(md5sum, sigtd->data, md5len)) { - char *hex = rpmtdFormat(sigtd, RPMTD_FORMAT_STRING, NULL); - rasprintf(msg, "%s %s Expected(%s) != (%s)\n", title, - rpmSigString(res), hex, md5); - free(hex); - } else { - res = RPMRC_OK; - rasprintf(msg, "%s %s (%s)\n", title, rpmSigString(res), md5); - } - free(md5); - -exit: - md5sum = _free(md5sum); - return res; -} - -/** - * Verify header immutable region SHA1 digest. - * @retval msg verbose success/failure text - * @param sha1ctx - * @return RPMRC_OK on success - */ -static rpmRC -verifySHA1Digest(rpmtd sigtd, DIGEST_CTX sha1ctx, char **msg) -{ - rpmRC res = RPMRC_FAIL; /* assume failure */ - char * SHA1 = NULL; - const char *title = _("Header SHA1 digest:"); - const char *sig = sigtd->data; - *msg = NULL; - DIGEST_CTX ctx = rpmDigestDup(sha1ctx); - - if (ctx == NULL) { - rasprintf(msg, "%s %s\n", title, rpmSigString(res)); + /* Write the signature section into the package. */ + if (rpmWriteSignature(fd, sig)) { + rc = RPMRC_FAIL; goto exit; } - (void) rpmDigestFinal(ctx, (void **)&SHA1, NULL, 1); - - if (SHA1 == NULL || !rstreq(SHA1, sig)) { - rasprintf(msg, "%s %s Expected(%s) != (%s)\n", title, - rpmSigString(res), sig, SHA1 ? SHA1 : "(nil)"); - } else { - res = RPMRC_OK; - rasprintf(msg, "%s %s (%s)\n", title, rpmSigString(res), SHA1); - } - exit: - SHA1 = _free(SHA1); - return res; -} - -/** - * Verify DSA/RSA signature. - * @param keyring pubkey keyring - * @param sig OpenPGP signature parameters - * @param hashctx digest context - * @param isHdr header-only signature? - * @retval msg verbose success/failure text - * @return RPMRC_OK on success - */ -static rpmRC -verifySignature(rpmKeyring keyring, pgpDigParams sig, DIGEST_CTX hashctx, - int isHdr, char **msg) -{ - - rpmRC res = rpmKeyringVerifySig(keyring, sig, hashctx); - - char *sigid = pgpIdentItem(sig); - rasprintf(msg, "%s%s: %s\n", isHdr ? _("Header ") : "", sigid, - rpmSigString(res)); - free(sigid); - return res; + headerFree(sig); + return rc; } -rpmRC -rpmVerifySignature(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, - DIGEST_CTX ctx, char ** result) -{ - rpmRC res = RPMRC_NOTFOUND; - char *msg = NULL; - int hdrsig = 0; - if (sigtd->data == NULL || sigtd->count <= 0 || ctx == NULL) - goto exit; - - switch (sigtd->tag) { - case RPMSIGTAG_MD5: - res = verifyMD5Digest(sigtd, ctx, &msg); - break; - case RPMSIGTAG_SHA1: - res = verifySHA1Digest(sigtd, ctx, &msg); - break; - case RPMSIGTAG_RSA: - case RPMSIGTAG_DSA: - hdrsig = 1; - /* fallthrough */ - case RPMSIGTAG_PGP5: /* XXX legacy */ - case RPMSIGTAG_PGP: - case RPMSIGTAG_GPG: - if (sig != NULL) - res = verifySignature(keyring, sig, ctx, hdrsig, &msg); - break; - default: - break; - } - -exit: - if (res == RPMRC_NOTFOUND) { - rasprintf(&msg, - _("Verify signature: BAD PARAMETERS (%d %p %d %p %p)\n"), - sigtd->tag, sigtd->data, sigtd->count, ctx, sig); - res = RPMRC_FAIL; - } - - if (result) { - *result = msg; - } else { - free(msg); - } - return res; -} diff --git a/lib/signature.h b/lib/signature.h index 18bbdc709..85dccd1d3 100644 --- a/lib/signature.h +++ b/lib/signature.h @@ -5,36 +5,21 @@ * \file lib/signature.h * Generate and verify signatures. */ - -#include <rpm/header.h> - -/** \ingroup signature - * Signature types stored in rpm lead. - */ -typedef enum sigType_e { - RPMSIGTYPE_HEADERSIG= 5 /*!< Header style signature */ -} sigType; +#include <rpm/rpmtypes.h> #ifdef __cplusplus extern "C" { #endif /** \ingroup signature - * Return new, empty (signature) header instance. - * @return signature header - */ -Header rpmNewSignature(void); - -/** \ingroup signature * Read (and verify header+payload size) signature header. * If an old-style signature is found, we emulate a new style one. * @param fd file handle * @retval sighp address of (signature) header (or NULL) - * @param sig_type type of signature header to read (from lead) * @retval msg failure msg * @return rpmRC return code */ -rpmRC rpmReadSignature(FD_t fd, Header *sighp, sigType sig_type, char ** msg); +rpmRC rpmReadSignature(FD_t fd, Header *sighp, char ** msg); /** \ingroup signature * Write signature header. @@ -45,38 +30,16 @@ rpmRC rpmReadSignature(FD_t fd, Header *sighp, sigType sig_type, char ** msg); int rpmWriteSignature(FD_t fd, Header h); /** \ingroup signature - * Generate digest(s) from a header+payload file, save in signature header. - * @param sigh signature header - * @param file header+payload file name - * @param sigTag type of digest(s) to add - * @return 0 on success, -1 on failure + * Generate signature and write to file + * @param SHA256 SHA256 digest + * @param SHA1 SHA1 digest + * @param MD5 MD5 digest + * @param size size of header + * @param payloadSize size of archive + * @param fd output file */ -int rpmGenDigest(Header sigh, const char * file, rpmTagVal sigTag); - -/** \ingroup signature - * Verify a signature from a package. - * - * @param keyring keyring handle - * @param sigtd signature tag data container - * @param sig signature/pubkey parameters - * @retval result detailed text result of signature verification - * (malloc'd) - * @return result of signature verification - */ -rpmRC rpmVerifySignature(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, - DIGEST_CTX ctx, char ** result); - -/** \ingroup signature - * Destroy signature header from package. - * @param h signature header - * @return NULL always - */ -Header rpmFreeSignature(Header h); - -/* Dumb wrapper around pgpPrtParams() to log some error messages on failure */ -RPM_GNUC_INTERNAL -int parsePGPSig(rpmtd sigtd, const char *type, const char *fn, - pgpDigParams *sig); +rpmRC rpmGenerateSignature(char *SHA256, char *SHA1, uint8_t *MD5, + rpm_loff_t size, rpm_loff_t payloadSize, FD_t fd); #ifdef __cplusplus } diff --git a/lib/tagexts.c b/lib/tagexts.c index 95c5e074d..00b500c26 100644 --- a/lib/tagexts.c +++ b/lib/tagexts.c @@ -301,28 +301,65 @@ static char * strtolocale(char *str) return (char *)cc; } +typedef enum tMode_e { + NORMALTRIGGER = 0, + FILETRIGGER = 1, + TRANSFILETRIGGER = 2, +} tMode; + /** * Retrieve trigger info. + * @param mode type of trigger (see tMode_e) * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ -static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags) +static int triggercondsTagFor(tMode mode, Header h, rpmtd td, + headerGetFlags hgflags) { uint32_t * indices; int i, j; char ** conds; struct rpmtd_s nametd, indextd, flagtd, versiontd, scripttd; int hgeflags = HEADERGET_MINMEM; + rpmTagVal triggername, triggerindex, triggerflags; + rpmTagVal triggerversion, triggerscripts; + + switch (mode) { + case NORMALTRIGGER: + triggername = RPMTAG_TRIGGERNAME; + triggerindex = RPMTAG_TRIGGERINDEX; + triggerflags = RPMTAG_TRIGGERFLAGS; + triggerversion = RPMTAG_TRIGGERVERSION; + triggerscripts = RPMTAG_TRIGGERSCRIPTS; + break; + case FILETRIGGER: + triggername = RPMTAG_FILETRIGGERNAME; + triggerindex = RPMTAG_FILETRIGGERINDEX; + triggerflags = RPMTAG_FILETRIGGERFLAGS; + triggerversion = RPMTAG_FILETRIGGERVERSION; + triggerscripts = RPMTAG_FILETRIGGERSCRIPTS; + break; + case TRANSFILETRIGGER: + triggername = RPMTAG_TRANSFILETRIGGERNAME; + triggerindex = RPMTAG_TRANSFILETRIGGERINDEX; + triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS; + triggerversion = RPMTAG_TRANSFILETRIGGERVERSION; + triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS; + break; + default: + return 0; + } - if (!headerGet(h, RPMTAG_TRIGGERNAME, &nametd, hgeflags)) { + if (!headerGet(h, triggername, &nametd, hgeflags)) { return 0; } - headerGet(h, RPMTAG_TRIGGERINDEX, &indextd, hgeflags); - headerGet(h, RPMTAG_TRIGGERFLAGS, &flagtd, hgeflags); - headerGet(h, RPMTAG_TRIGGERVERSION, &versiontd, hgeflags); - headerGet(h, RPMTAG_TRIGGERSCRIPTS, &scripttd, hgeflags); + headerGet(h, triggerindex, &indextd, hgeflags); + headerGet(h, triggerflags, &flagtd, hgeflags); + headerGet(h, triggerversion, &versiontd, hgeflags); + headerGet(h, triggerscripts, &scripttd, hgeflags); td->type = RPM_STRING_ARRAY_TYPE; td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED; @@ -371,24 +408,63 @@ static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags) return 1; } +static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggercondsTagFor(NORMALTRIGGER, h, td, hgflags); +} + +static int filetriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggercondsTagFor(FILETRIGGER, h, td, hgflags); +} + +static int transfiletriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggercondsTagFor(TRANSFILETRIGGER, h, td, hgflags); +} + /** * Retrieve trigger type info. + * @param mode type of trigger (see tMode_e) * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ -static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags) +static int triggertypeTagFor(tMode mode, Header h, rpmtd td, + headerGetFlags hgflags) { int i; char ** conds; struct rpmtd_s indices, flags, scripts; + rpmTagVal triggerindex, triggerflags, triggerscripts; + + switch (mode) { + case NORMALTRIGGER: + triggerindex = RPMTAG_TRIGGERINDEX; + triggerflags = RPMTAG_TRIGGERFLAGS; + triggerscripts = RPMTAG_TRIGGERSCRIPTS; + break; + case FILETRIGGER: + triggerindex = RPMTAG_FILETRIGGERINDEX; + triggerflags = RPMTAG_FILETRIGGERFLAGS; + triggerscripts = RPMTAG_FILETRIGGERSCRIPTS; + break; + case TRANSFILETRIGGER: + triggerindex = RPMTAG_TRANSFILETRIGGERINDEX; + triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS; + triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS; + break; + default: + return 0; + } - if (!headerGet(h, RPMTAG_TRIGGERINDEX, &indices, HEADERGET_MINMEM)) { + if (!headerGet(h, triggerindex, &indices, HEADERGET_MINMEM)) { return 0; } - headerGet(h, RPMTAG_TRIGGERFLAGS, &flags, HEADERGET_MINMEM); - headerGet(h, RPMTAG_TRIGGERSCRIPTS, &scripts, HEADERGET_MINMEM); + headerGet(h, triggerflags, &flags, HEADERGET_MINMEM); + headerGet(h, triggerscripts, &scripts, HEADERGET_MINMEM); td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED; td->count = rpmtdCount(&scripts); @@ -424,20 +500,38 @@ static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags) return 1; } +static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggertypeTagFor(NORMALTRIGGER, h, td, hgflags); +} + +static int filetriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggertypeTagFor(FILETRIGGER, h, td, hgflags); +} + +static int transfiletriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return triggertypeTagFor(TRANSFILETRIGGER, h, td, hgflags); +} + /** * Retrieve installed file paths. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int instfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags) { return fnTag(h, RPMTAG_BASENAMES, 1, td); } + /** * Retrieve file paths. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int filenamesTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -449,6 +543,7 @@ static int filenamesTag(Header h, rpmtd td, headerGetFlags hgflags) * Retrieve original file paths (wrt relocation). * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int origfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -499,6 +594,7 @@ static char *makeFClass(rpmfi fi) * Retrieve/generate file classes. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -529,6 +625,7 @@ static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags) * Retrieve file provides. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int fileprovideTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -540,6 +637,7 @@ static int fileprovideTag(Header h, rpmtd td, headerGetFlags hgflags) * Retrieve file requires. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int filerequireTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -561,6 +659,7 @@ static const char * const _macro_i18ndomains = "%{?_i18ndomains}"; * @param h header * @param tag tag * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int i18nTag(Header h, rpmTag tag, rpmtd td, headerGetFlags hgflags) @@ -656,6 +755,7 @@ static int localeTag(Header h, rpmTag tag, rpmtd td) * Retrieve summary text. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int summaryTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -667,6 +767,7 @@ static int summaryTag(Header h, rpmtd td, headerGetFlags hgflags) * Retrieve description text. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int descriptionTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -688,6 +789,7 @@ static int changelogtextTag(Header h, rpmtd td) * Retrieve group text. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int groupTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -734,6 +836,7 @@ static int get64(Header h, rpmtd td, rpmTag newtag, rpmTag oldtag) * Retrieve file sizes as 64bit regardless of how they're stored. * @param h header * @retval td tag data container + * @param hgflags header get flags * @return 1 on success */ static int longfilesizesTag(Header h, rpmtd td, headerGetFlags hgflags) @@ -800,7 +903,7 @@ typedef rpmFlags nevraFlags; static int getNEVRA(Header h, rpmtd td, nevraFlags flags) { const char *val = NULL; - char *res = NULL; + char *res = xstrdup(""); if ((flags & NEVRA_NAME)) { val = headerGetString(h, RPMTAG_NAME); @@ -907,11 +1010,63 @@ static int depnevrsTag(Header h, rpmtd td, headerGetFlags hgflags, return (ndeps > 0); } +#define RPMSENSE_STRONG (1 << 27) + +static int depnevrsTagFiltered(Header h, rpmtd td, headerGetFlags hgflags, + rpmTagVal tag, int strong) +{ + rpmds ds = rpmdsNew(h, tag, 0); + int ndeps = rpmdsCount(ds); + + if (ndeps > 0) { + char **deps = xmalloc(sizeof(*deps) * ndeps); + ndeps = 0; + while (rpmdsNext(ds) >= 0) { + if ((rpmdsFlags(ds) & RPMSENSE_STRONG) == (strong ? RPMSENSE_STRONG : 0)) + deps[ndeps++] = rpmdsNewDNEVR(NULL, ds); + } + if (ndeps) { + td->data = deps; + td->type = RPM_STRING_ARRAY_TYPE; + td->count = ndeps; + td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED); + } else { + _free(deps); + } + } + rpmdsFree(ds); + return (ndeps > 0); +} + static int requirenevrsTag(Header h, rpmtd td, headerGetFlags hgflags) { return depnevrsTag(h, td, hgflags, RPMTAG_REQUIRENAME); } +static int recommendnevrsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return depnevrsTag(h, td, hgflags, RPMTAG_RECOMMENDNAME) || + depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 1); +} + +static int suggestnevrsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return depnevrsTag(h, td, hgflags, RPMTAG_SUGGESTNAME) || + depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 0); +} + +static int supplementnevrsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return depnevrsTag(h, td, hgflags, RPMTAG_SUPPLEMENTNAME) || + depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 1); +} + +static int enhancenevrsTag(Header h, rpmtd td, headerGetFlags hgflags) +{ + return depnevrsTag(h, td, hgflags, RPMTAG_ENHANCENAME) || + depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 0); +} + static int providenevrsTag(Header h, rpmtd td, headerGetFlags hgflags) { return depnevrsTag(h, td, hgflags, RPMTAG_PROVIDENAME); @@ -958,7 +1113,11 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { { RPMTAG_FILEPROVIDE, fileprovideTag }, { RPMTAG_FILEREQUIRE, filerequireTag }, { RPMTAG_TRIGGERCONDS, triggercondsTag }, + { RPMTAG_FILETRIGGERCONDS, filetriggercondsTag }, + { RPMTAG_TRANSFILETRIGGERCONDS, transfiletriggercondsTag }, { RPMTAG_TRIGGERTYPE, triggertypeTag }, + { RPMTAG_FILETRIGGERTYPE, filetriggertypeTag }, + { RPMTAG_TRANSFILETRIGGERTYPE, transfiletriggertypeTag }, { RPMTAG_LONGFILESIZES, longfilesizesTag }, { RPMTAG_LONGARCHIVESIZE, longarchivesizeTag }, { RPMTAG_LONGSIZE, longsizeTag }, @@ -976,6 +1135,10 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { { RPMTAG_EPOCHNUM, epochnumTag }, { RPMTAG_INSTFILENAMES, instfilenamesTag }, { RPMTAG_REQUIRENEVRS, requirenevrsTag }, + { RPMTAG_RECOMMENDNEVRS, recommendnevrsTag}, + { RPMTAG_SUGGESTNEVRS, suggestnevrsTag}, + { RPMTAG_SUPPLEMENTNEVRS, supplementnevrsTag}, + { RPMTAG_ENHANCENEVRS, enhancenevrsTag}, { RPMTAG_PROVIDENEVRS, providenevrsTag }, { RPMTAG_OBSOLETENEVRS, obsoletenevrsTag }, { RPMTAG_CONFLICTNEVRS, conflictnevrsTag }, diff --git a/lib/tagname.c b/lib/tagname.c index 0c2696803..68b252991 100644 --- a/lib/tagname.c +++ b/lib/tagname.c @@ -4,6 +4,8 @@ #include "system.h" +#include <pthread.h> + #include <rpm/header.h> #include <rpm/rpmstring.h> #include "debug.h" @@ -23,26 +25,11 @@ struct headerTagTableEntry_s { #include "lib/tagtbl.C" -static const int rpmTagTableSize = sizeof(rpmTagTable) / sizeof(rpmTagTable[0]) - 1; +#define TABLESIZE (sizeof(rpmTagTable) / sizeof(rpmTagTable[0]) - 1) +static const int rpmTagTableSize = TABLESIZE; -/** - */ -typedef struct headerTagIndices_s * headerTagIndices; - -struct headerTagIndices_s { - int (*loadIndex) (headerTagTableEntry ** ipp, int * np, - int (*cmp) (const void * avp, const void * bvp)); - /*!< load sorted tag index. */ - headerTagTableEntry * byName; /*!< header tags sorted by name. */ - int byNameSize; /*!< no. of entries. */ - int (*byNameCmp) (const void * avp, const void * bvp); /*!< compare entries by name. */ - rpmTagVal (*tagValue) (const char * name); /* return value from name. */ - headerTagTableEntry * byValue; /*!< header tags sorted by value. */ - int byValueSize; /*!< no. of entries. */ - int (*byValueCmp) (const void * avp, const void * bvp); /*!< compare entries by value. */ - const char * (*tagName) (rpmTagVal value); /* Return name from value. */ - rpmTagType (*tagType) (rpmTagVal value); /* Return type from value. */ -}; +static headerTagTableEntry tagsByName[TABLESIZE]; /*!< tags sorted by name. */ +static headerTagTableEntry tagsByValue[TABLESIZE]; /*!< tags sorted by value. */ /** * Compare tag table entries by name. @@ -74,56 +61,76 @@ static int tagCmpValue(const void * avp, const void * bvp) return ret; } -/** - * Load/sort a tag index. - * @retval *ipp tag index - * @retval *np no. of tags - * @param cmp sort compare routine - * @return 0 always - */ -static int tagLoadIndex(headerTagTableEntry ** ipp, int * np, - int (*cmp) (const void * avp, const void * bvp)) +static pthread_once_t tagsLoaded = PTHREAD_ONCE_INIT; + +/* Initialize tag by-value and by-name lookup tables */ +static void loadTags(void) { - headerTagTableEntry tte, *ip; - int n = 0; - - ip = xcalloc(rpmTagTableSize, sizeof(*ip)); - n = 0; - for (tte = (headerTagTableEntry)rpmTagTable; tte->name != NULL; tte++) { - ip[n] = tte; - n++; + for (int i = 0; i < rpmTagTableSize; i++) { + tagsByValue[i] = &rpmTagTable[i]; + tagsByName[i] = &rpmTagTable[i]; } -assert(n == rpmTagTableSize); - if (n > 1) - qsort(ip, n, sizeof(*ip), cmp); - *ipp = ip; - *np = n; - return 0; + qsort(tagsByValue, rpmTagTableSize, sizeof(*tagsByValue), tagCmpValue); + qsort(tagsByName, rpmTagTableSize, sizeof(*tagsByName), tagCmpName); } +static headerTagTableEntry entryByTag(rpmTagVal tag) +{ + headerTagTableEntry entry = NULL; + int i, comparison; + int l = 0; + int u = rpmTagTableSize; -/* forward refs */ -static const char * _tagName(rpmTagVal tag); -static rpmTagType _tagType(rpmTagVal tag); -static rpmTagVal _tagValue(const char * tagstr); + while (l < u) { + i = (l + u) / 2; + comparison = (tag - tagsByValue[i]->val); -static struct headerTagIndices_s _rpmTags = { - tagLoadIndex, - NULL, 0, tagCmpName, _tagValue, - NULL, 0, tagCmpValue, _tagName, _tagType, -}; + if (comparison < 0) { + u = i; + } else if (comparison > 0) { + l = i + 1; + } else { + /* Make sure that the bsearch retrieve is stable. */ + while (i > 0 && tag == tagsByValue[i-1]->val) { + i--; + } + entry = tagsByValue[i]; + break; + } + } + return entry; +} + +static headerTagTableEntry entryByName(const char *tag) +{ + headerTagTableEntry entry = NULL; + int i, comparison; + int l = 0; + int u = rpmTagTableSize; -static headerTagIndices const rpmTags = &_rpmTags; + while (l < u) { + i = (l + u) / 2; + comparison = rstrcasecmp(tag, tagsByName[i]->shortname); -static const char * _tagName(rpmTagVal tag) + if (comparison < 0) { + u = i; + } else if (comparison > 0) { + l = i + 1; + } else { + entry = tagsByName[i]; + break; + } + } + return entry; +} + +const char * rpmTagGetName(rpmTagVal tag) { const char *name = "(unknown)"; const struct headerTagTableEntry_s *t; - int comparison, i, l, u; - if (_rpmTags.byValue == NULL) - tagLoadIndex(&_rpmTags.byValue, &_rpmTags.byValueSize, tagCmpValue); + pthread_once(&tagsLoaded, loadTags); switch (tag) { case RPMDBI_PACKAGES: @@ -138,119 +145,54 @@ static const char * _tagName(rpmTagVal tag) break; default: - if (_rpmTags.byValue == NULL) - break; - l = 0; - u = _rpmTags.byValueSize; - while (l < u) { - i = (l + u) / 2; - t = _rpmTags.byValue[i]; - - comparison = (tag - t->val); - - if (comparison < 0) - u = i; - else if (comparison > 0) - l = i + 1; - else { - /* Make sure that the bsearch retrieve is stable. */ - while (i > 0 && tag == _rpmTags.byValue[i-1]->val) { - i--; - } - t = _rpmTags.byValue[i]; - if (t->shortname != NULL) - name = t->shortname; - break; - } - } + t = entryByTag(tag); + if (t && t->shortname) + name = t->shortname; break; } return name; } -static rpmTagType _tagType(rpmTagVal tag) +rpmTagType rpmTagGetType(rpmTagVal tag) { const struct headerTagTableEntry_s *t; - int comparison, i, l, u; - - if (_rpmTags.byValue == NULL) - tagLoadIndex(&_rpmTags.byValue, &_rpmTags.byValueSize, tagCmpValue); - if (_rpmTags.byValue) { - l = 0; - u = _rpmTags.byValueSize; - while (l < u) { - i = (l + u) / 2; - t = _rpmTags.byValue[i]; - - comparison = (tag - t->val); - - if (comparison < 0) - u = i; - else if (comparison > 0) - l = i + 1; - else { - /* Make sure that the bsearch retrieve is stable. */ - while (i > 0 && t->val == _rpmTags.byValue[i-1]->val) { - i--; - } - t = _rpmTags.byValue[i]; - /* XXX this is dumb */ - return (rpmTagType)(t->type | t->retype); - } - } + rpmTagType tagtype = RPM_NULL_TYPE; + + pthread_once(&tagsLoaded, loadTags); + + t = entryByTag(tag); + if (t) { + /* XXX this is dumb */ + tagtype = (rpmTagType)(t->type | t->retype); } - return RPM_NULL_TYPE; + return tagtype; } -static rpmTagVal _tagValue(const char * tagstr) +rpmTagVal rpmTagGetValue(const char * tagstr) { const struct headerTagTableEntry_s *t; - int comparison, i, l, u; + rpmTagType tagval = RPMTAG_NOT_FOUND; + + pthread_once(&tagsLoaded, loadTags); if (!rstrcasecmp(tagstr, "Packages")) return RPMDBI_PACKAGES; - if (_rpmTags.byName == NULL) - tagLoadIndex(&_rpmTags.byName, &_rpmTags.byNameSize, tagCmpName); - if (_rpmTags.byName == NULL) - return RPMTAG_NOT_FOUND; - - l = 0; - u = _rpmTags.byNameSize; - while (l < u) { - i = (l + u) / 2; - t = _rpmTags.byName[i]; + t = entryByName(tagstr); + if (t) + tagval = t->val; - comparison = rstrcasecmp(tagstr, t->shortname); - - if (comparison < 0) - u = i; - else if (comparison > 0) - l = i + 1; - else - return t->val; - } - return RPMTAG_NOT_FOUND; -} - -const char * rpmTagGetName(rpmTagVal tag) -{ - return ((*rpmTags->tagName)(tag)); -} - -rpmTagType rpmTagGetType(rpmTagVal tag) -{ - return ((*rpmTags->tagType)(tag)); + return tagval; } rpmTagType rpmTagGetTagType(rpmTagVal tag) { - return (rpmTagType)((*rpmTags->tagType)(tag) & RPM_MASK_TYPE); + return (rpmTagGetType(tag) & RPM_MASK_TYPE); } rpmTagReturnType rpmTagGetReturnType(rpmTagVal tag) { - return ((*rpmTags->tagType)(tag) & RPM_MASK_RETURN_TYPE); + return (rpmTagGetType(tag) & RPM_MASK_RETURN_TYPE); } rpmTagClass rpmTagTypeGetClass(rpmTagType type) @@ -285,30 +227,25 @@ rpmTagClass rpmTagGetClass(rpmTagVal tag) return rpmTagTypeGetClass(rpmTagGetTagType(tag)); } -rpmTagVal rpmTagGetValue(const char * tagstr) -{ - return ((*rpmTags->tagValue)(tagstr)); -} - int rpmTagGetNames(rpmtd tagnames, int fullname) { const char **names; const char *name; - if (_rpmTags.byName == NULL) - tagLoadIndex(&_rpmTags.byName, &_rpmTags.byNameSize, tagCmpName); - if (tagnames == NULL ||_rpmTags.byName == NULL) + pthread_once(&tagsLoaded, loadTags); + + if (tagnames == NULL || tagsByName == NULL) return 0; rpmtdReset(tagnames); - tagnames->count = _rpmTags.byNameSize; + tagnames->count = rpmTagTableSize; tagnames->data = names = xmalloc(tagnames->count * sizeof(*names)); tagnames->type = RPM_STRING_ARRAY_TYPE; tagnames->flags = RPMTD_ALLOCED | RPMTD_IMMUTABLE; for (int i = 0; i < tagnames->count; i++) { - name = fullname ? _rpmTags.byName[i]->name : - _rpmTags.byName[i]->shortname; + name = fullname ? tagsByName[i]->name : + tagsByName[i]->shortname; names[i] = name; } return tagnames->count; diff --git a/lib/transaction.c b/lib/transaction.c index b91953cf8..6af1d1662 100644 --- a/lib/transaction.c +++ b/lib/transaction.c @@ -4,6 +4,9 @@ #include "system.h" +#include <inttypes.h> +#include <libgen.h> + #include <rpm/rpmlib.h> /* rpmMachineScore, rpmReadPackageFile */ #include <rpm/rpmmacro.h> /* XXX for rpmExpand */ #include <rpm/rpmlog.h> @@ -11,6 +14,7 @@ #include <rpm/rpmds.h> #include <rpm/rpmfileutil.h> #include <rpm/rpmstring.h> +#include <rpm/rpmsq.h> #include "lib/fprint.h" #include "lib/misc.h" @@ -21,6 +25,7 @@ #include "lib/rpmte_internal.h" /* only internal apis */ #include "lib/rpmts_internal.h" #include "rpmio/rpmhook.h" +#include "lib/rpmtriggers.h" #include "lib/rpmplugins.h" @@ -63,6 +68,45 @@ struct diskspaceInfo_s { #define adj_fs_blocks(_nb) (((_nb) * 21) / 20) #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block)) +static char *getMntPoint(const char *dirName, dev_t dev) +{ + char mntPoint[PATH_MAX]; + char *resolved_path = realpath(dirName, mntPoint); + char *end = NULL; + struct stat sb; + char *res = NULL; + + if (!resolved_path) { + strncpy(mntPoint, dirName, PATH_MAX); + mntPoint[PATH_MAX-1] = '\0'; + } + + while (end != mntPoint) { + end = strrchr(mntPoint, '/'); + if (end == mntPoint) { /* reached "/" */ + stat("/", &sb); + if (dev != sb.st_dev) { + res = xstrdup(mntPoint); + } else { + res = xstrdup("/"); + } + break; + } else if (end) { + *end = '\0'; + } else { /* dirName doesn't start with / - should not happen */ + res = xstrdup(dirName); + break; + } + stat(mntPoint, &sb); + if (dev != sb.st_dev) { + *end = '/'; + res = xstrdup(mntPoint); + break; + } + } + return res; +} + static int rpmtsInitDSI(const rpmts ts) { if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE) @@ -77,8 +121,6 @@ static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev, { rpmDiskSpaceInfo dsi; struct stat sb; - char * resolved_path; - char mntPoint[PATH_MAX]; int rc; #if STATFS_IN_SYS_STATVFS @@ -119,11 +161,7 @@ static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev, dsi->bneeded = 0; dsi->ineeded = 0; #ifdef STATFS_HAS_F_BAVAIL -# ifdef ST_RDONLY dsi->bavail = (sfb.f_flag & ST_RDONLY) ? 0 : sfb.f_bavail; -# else - dsi->bavail = sfb.f_bavail; -# endif #else /* FIXME: the statfs struct doesn't have a member to tell how many blocks are * available for non-superusers. f_blocks - f_bfree is probably too big, but @@ -137,34 +175,16 @@ static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev, ? sfb.f_ffree : -1; /* Find mount point belonging to this device number */ - resolved_path = realpath(dirName, mntPoint); - if (!resolved_path) { - strncpy(mntPoint, dirName, PATH_MAX); - mntPoint[PATH_MAX-1] = '\0'; - } - char * end = NULL; - while (end != mntPoint) { - end = strrchr(mntPoint, '/'); - if (end == mntPoint) { /* reached "/" */ - stat("/", &sb); - if (dsi->dev != sb.st_dev) { - dsi->mntPoint = xstrdup(mntPoint); - } else { - dsi->mntPoint = xstrdup("/"); - } - break; - } else if (end) { - *end = '\0'; - } else { /* dirName doesn't start with / - should not happen */ - dsi->mntPoint = xstrdup(dirName); - break; - } - stat(mntPoint, &sb); - if (dsi->dev != sb.st_dev) { - *end = '/'; - dsi->mntPoint = xstrdup(mntPoint); - break; - } + dsi->mntPoint = getMntPoint(dirName, dsi->dev); + + /* normalize block size to 4096 bytes if it is too big. */ + if (dsi->bsize > 4096) { + uint64_t old_size = dsi->bavail * dsi->bsize; + rpmlog(RPMLOG_DEBUG, + "dubious blocksize % " PRId64 " on %s, normalizing to 4096\n", + dsi->bsize, dsi->mntPoint); + dsi->bsize = 4096; /* Assume 4k block size */ + dsi->bavail = old_size / dsi->bsize; } rpmlog(RPMLOG_DEBUG, @@ -291,17 +311,21 @@ static uint64_t countFiles(rpmts ts) uint64_t fc = 0; rpmtsi pi = rpmtsiInit(ts); rpmte p; - while ((p = rpmtsiNext(pi, 0)) != NULL) - fc += rpmfiFC(rpmteFI(p)); + rpmfiles files; + while ((p = rpmtsiNext(pi, 0)) != NULL) { + files = rpmteFiles(p); + fc += rpmfilesFC(files); + rpmfilesFree(files); + } rpmtsiFree(pi); return fc; } -static int handleRemovalConflict(rpmfi fi, int fx, rpmfi ofi, int ofx) +static int handleRemovalConflict(rpmfiles fi, int fx, rpmfiles ofi, int ofx) { int rConflicts = 0; /* Removed files don't conflict, normally */ - rpmFileTypes ft = rpmfiWhatis(rpmfiFModeIndex(fi, fx)); - rpmFileTypes oft = rpmfiWhatis(rpmfiFModeIndex(ofi, ofx)); + rpmFileTypes ft = rpmfiWhatis(rpmfilesFMode(fi, fx)); + rpmFileTypes oft = rpmfiWhatis(rpmfilesFMode(ofi, ofx)); struct stat sb; char *fn = NULL; @@ -312,7 +336,7 @@ static int handleRemovalConflict(rpmfi fi, int fx, rpmfi ofi, int ofx) } else if (oft == LINK) { /* We can't correctly handle directory symlink changing to directory */ if (ft == XDIR) { - fn = rpmfiFNIndex(fi, fx); + fn = rpmfilesFN(fi, fx); if (stat(fn, &sb) == 0 && S_ISDIR(sb.st_mode)) rConflicts = 1; } @@ -324,7 +348,7 @@ static int handleRemovalConflict(rpmfi fi, int fx, rpmfi ofi, int ofx) */ if (rConflicts) { if (fn == NULL) - fn = rpmfiFNIndex(fi, fx); + fn = rpmfilesFN(fi, fx); if (lstat(fn, &sb) || rpmfiWhatis(sb.st_mode) == ft) rConflicts = 0; } @@ -342,15 +366,15 @@ static int handleRemovalConflict(rpmfi fi, int fx, rpmfi ofi, int ofx) * unnecessary with careful packaging. */ static int handleColorConflict(rpmts ts, - rpmfs fs, rpmfi fi, int fx, - rpmfs ofs, rpmfi ofi, int ofx) + rpmfs fs, rpmfiles fi, int fx, + rpmfs ofs, rpmfiles ofi, int ofx) { int rConflicts = 1; rpm_color_t tscolor = rpmtsColor(ts); if (tscolor != 0) { - rpm_color_t fcolor = rpmfiFColorIndex(fi, fx) & tscolor; - rpm_color_t ofcolor = rpmfiFColorIndex(ofi, ofx) & tscolor; + rpm_color_t fcolor = rpmfilesFColor(fi, fx) & tscolor; + rpm_color_t ofcolor = rpmfilesFColor(ofi, ofx) & tscolor; if (fcolor != 0 && ofcolor != 0 && fcolor != ofcolor) { rpm_color_t prefcolor = rpmtsPrefColor(ts); @@ -377,22 +401,24 @@ static int handleColorConflict(rpmts ts, * @param ts transaction set * @param p current transaction element * @param fi file info set - * @param shared shared file info - * @param sharedCount no. of shared elements - * @param reportConflicts + * @param fx file index + * @param otherHeader header containing the matching file + * @param otherFi matching file info set + * @param ofx matching file index + * @param beingRemoved file being removed (installed otherwise) */ /* XXX only ts->{probs,rpmdb} modified */ -static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi, int fx, - Header otherHeader, rpmfi otherFi, int ofx, +static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfiles fi, int fx, + Header otherHeader, rpmfiles otherFi, int ofx, int beingRemoved) { rpmfs fs = rpmteGetFileStates(p); - int isCfgFile = ((rpmfiFFlagsIndex(otherFi, ofx) | rpmfiFFlagsIndex(fi, fx)) & RPMFILE_CONFIG); + int isCfgFile = ((rpmfilesFFlags(otherFi, ofx) | rpmfilesFFlags(fi, fx)) & RPMFILE_CONFIG); if (XFA_SKIPPING(rpmfsGetAction(fs, fx))) return; - if (rpmfiCompareIndex(otherFi, ofx, fi, fx)) { + if (rpmfilesCompare(otherFi, ofx, fi, fx)) { int rConflicts = 1; char rState = RPMFILE_STATE_REPLACED; @@ -421,20 +447,22 @@ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi, int fx, rState = RPMFILE_STATE_WRONGCOLOR; } + if (rConflicts) { - char *path = rpmfiFNIndex(fi, fx); + char *path = rpmfilesFN(fi, fx); /* Call file conflict hook for all plugins */ rpmpluginsCallFileConflict(ts->plugins, ts, path, otherHeader, otherFi, rConflicts); } - + /* Somebody used The Force, lets shut up... */ if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) { rConflicts = 0; } + if (rConflicts) { char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA); - char *fn = rpmfiFNIndex(fi, fx); + char *fn = rpmfilesFN(fi, fx); rpmteAddProblem(p, RPMPROB_FILE_CONFLICT, altNEVR, fn, headerGetInstance(otherHeader)); free(fn); @@ -449,34 +477,42 @@ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi, int fx, } } - /* Determine config file dispostion, skipping missing files (if any). */ + /* Determine config file disposition, skipping missing files (if any). */ if (isCfgFile) { int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1); rpmFileAction action; - action = rpmfiDecideFateIndex(otherFi, ofx, fi, fx, skipMissing); + action = rpmfilesDecideFate(otherFi, ofx, fi, fx, skipMissing); rpmfsSetAction(fs, fx, action); } - rpmfiSetFReplacedSizeIndex(fi, fx, rpmfiFSizeIndex(otherFi, ofx)); + + /* Skip already existing files - if 'minimize_writes' is set. */ + if ((!isCfgFile) && (rpmfsGetAction(fs, fx) == FA_UNKNOWN) && ts->min_writes) { + if (rpmfileContentsEqual(otherFi, ofx, fi, fx)) { + rpmfsSetAction(fs, fx, FA_TOUCH); + } + } + + rpmfilesSetFReplacedSize(fi, fx, rpmfilesFSize(otherFi, ofx)); } /** * Update disk space needs on each partition for this package's files. */ /* XXX only ts->{probs,di} modified */ -static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi fi) +static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfiles fi) { rpm_loff_t fixupSize = 0; int i, j; rpmfs fs = rpmteGetFileStates(p); rpmfs otherFs; - rpm_count_t fc = rpmfiFC(fi); + rpm_count_t fc = rpmfilesFC(fi); int reportConflicts = !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES); - fingerPrint * fpList = rpmfiFps(fi); + fingerPrint * fpList = rpmfilesFps(fi); for (i = 0; i < fc; i++) { struct fingerPrint_s * fiFps; int otherPkgNum, otherFileNum; - rpmfi otherFi; + rpmfiles otherFi; rpmte otherTe; rpmfileAttrs FFlags; struct rpmffi_s * recs; @@ -485,7 +521,7 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi if (XFA_SKIPPING(rpmfsGetAction(fs, i))) continue; - FFlags = rpmfiFFlagsIndex(fi, i); + FFlags = rpmfilesFFlags(fi, i); fixupSize = 0; @@ -513,7 +549,7 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi * that were just installed. * If both this and the other package are being removed, then each * file removal from preceding packages needs to be skipped so that - * the file removal occurs only on the last occurence of an overlapped + * the file removal occurs only on the last occurrence of an overlapped * file in the transaction set. * */ @@ -535,7 +571,6 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) { otherTe = recs[otherPkgNum].p; - otherFi = rpmteFI(otherTe); otherFileNum = recs[otherPkgNum].fileno; otherFs = rpmteGetFileStates(otherTe); @@ -544,8 +579,10 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi continue; /* XXX Happens iff fingerprint for incomplete package install. */ - if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN) + if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN) { + otherFi = rpmteFiles(otherTe); break; + } } switch (rpmteType(p)) { @@ -555,7 +592,7 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi rpmFileAction action; if (rpmfsGetAction(fs, i) != FA_UNKNOWN) break; - if (rpmfiConfigConflictIndex(fi, i)) { + if (rpmfilesConfigConflict(fi, i)) { /* Here is a non-overlapped pre-existing config file. */ action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_BACKUP; @@ -568,7 +605,7 @@ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfi assert(otherFi != NULL); /* Mark added overlapped non-identical files as a conflict. */ - if (rpmfiCompareIndex(otherFi, otherFileNum, fi, i)) { + if (rpmfilesCompare(otherFi, otherFileNum, fi, i)) { int rConflicts; /* If enabled, resolve colored conflicts to preferred type */ @@ -576,7 +613,7 @@ assert(otherFi != NULL); otherFs, otherFi, otherFileNum); if (rConflicts && reportConflicts) { - char *fn = rpmfiFNIndex(fi, i); + char *fn = rpmfilesFN(fi, i); rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT, rpmteNEVRA(otherTe), fn, 0); free(fn); @@ -587,10 +624,13 @@ assert(otherFi != NULL); if (oaction != FA_UNKNOWN && !XFA_SKIPPING(oaction)) { rpmfileAttrs oflags; /* ...but ghosts aren't really created so... */ - oflags = rpmfiFFlagsIndex(otherFi, otherFileNum); + oflags = rpmfilesFFlags(otherFi, otherFileNum); if (!(oflags & RPMFILE_GHOST)) { rpmfsSetAction(fs, i, FA_SKIP); } + /* if the other file is color skipped then skip this file too */ + } else if (oaction == FA_SKIPCOLOR) { + rpmfsSetAction(fs, i, FA_SKIPCOLOR); } } @@ -599,9 +639,9 @@ assert(otherFi != NULL); break; /* Try to get the disk accounting correct even if a conflict. */ - fixupSize = rpmfiFSizeIndex(otherFi, otherFileNum); + fixupSize = rpmfilesFSize(otherFi, otherFileNum); - if (rpmfiConfigConflictIndex(fi, i)) { + if (rpmfilesConfigConflict(fi, i)) { /* Here is an overlapped pre-existing config file. */ rpmFileAction action; action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP; @@ -627,12 +667,14 @@ assert(otherFi != NULL); } if (XFA_SKIPPING(rpmfsGetAction(fs, i))) break; - if (rpmfiFStateIndex(fi, i) != RPMFILE_STATE_NORMAL) + if (rpmfilesFState(fi, i) != RPMFILE_STATE_NORMAL) { + rpmfsSetAction(fs, i, FA_SKIP); break; + } /* Pre-existing modified config files need to be saved. */ - if (rpmfiConfigConflictIndex(fi, i)) { - rpmfsSetAction(fs, i, FA_BACKUP); + if (rpmfilesConfigConflict(fi, i)) { + rpmfsSetAction(fs, i, FA_SAVE); break; } @@ -640,10 +682,11 @@ assert(otherFi != NULL); rpmfsSetAction(fs, i, FA_ERASE); break; } + rpmfilesFree(otherFi); /* Update disk space info for a file. */ rpmtsUpdateDSI(ts, fpEntryDev(fpc, fiFps), fpEntryDir(fpc, fiFps), - rpmfiFSizeIndex(fi, i), rpmfiFReplacedSizeIndex(fi, i), + rpmfilesFSize(fi, i), rpmfilesFReplacedSize(fi, i), fixupSize, rpmfsGetAction(fs, i)); } @@ -654,7 +697,6 @@ assert(otherFi != NULL); * @param tspool transaction string pool * @param p current transaction element * @param h installed header - * @param ps problem set */ static void ensureOlder(rpmstrPool tspool, const rpmte p, const Header h) { @@ -677,9 +719,9 @@ static void ensureOlder(rpmstrPool tspool, const rpmte p, const Header h) * netshardpath and though should be excluded. * @param ts transaction set * @param fi file info set - * @returns pointer to matching path or NULL + * @returns 1 if path is net shared path, otherwise 0 */ -static char ** matchNetsharedpath(const rpmts ts, rpmfi fi) +static int matchNetsharedpath(const rpmts ts, rpmfi fi) { char ** nsp; const char * dn, * bn; @@ -717,29 +759,25 @@ static char ** matchNetsharedpath(const rpmts ts, rpmfi fi) break; } - return nsp; + return (nsp != NULL && *nsp != NULL); } -static void skipEraseFiles(const rpmts ts, rpmte p) +static void skipEraseFiles(const rpmts ts, rpmfiles files, rpmfs fs) { - rpmfi fi = rpmteFI(p); - rpmfs fs = rpmteGetFileStates(p); int i; - char ** nsp; /* * Skip net shared paths. * Net shared paths are not relative to the current root (though * they do need to take package relocations into account). */ if (ts->netsharedPaths) { - fi = rpmfiInit(fi, 0); + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD); while ((i = rpmfiNext(fi)) >= 0) { - nsp = matchNetsharedpath(ts, fi); - if (nsp && *nsp) { + if (matchNetsharedpath(ts, fi)) rpmfsSetAction(fs, i, FA_SKIPNETSHARED); - } } + rpmfiFree(fi); } } @@ -747,9 +785,10 @@ static void skipEraseFiles(const rpmts ts, rpmte p) /** * Skip any files that do not match install policies. * @param ts transaction set - * @param fi file info set + * @param files file info set + * @param fs file states */ -static void skipInstallFiles(const rpmts ts, rpmte p) +static void skipInstallFiles(const rpmts ts, rpmfiles files, rpmfs fs) { rpm_color_t tscolor = rpmtsColor(ts); rpm_color_t FColor; @@ -759,8 +798,7 @@ static void skipInstallFiles(const rpmts ts, rpmte p) char * dff; int dc; int i, j, ix; - rpmfi fi = rpmteFI(p); - rpmfs fs = rpmteGetFileStates(p); + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD); if (!noDocs) noDocs = rpmExpandNumeric("%{_excludedocs}"); @@ -772,13 +810,13 @@ static void skipInstallFiles(const rpmts ts, rpmte p) fi = rpmfiInit(fi, 0); while ((i = rpmfiNext(fi)) >= 0) { - char ** nsp; const char *flangs; ix = rpmfiDX(fi); drc[ix]++; /* Don't bother with skipped files */ + /* XXX FIXME: --excludepath on %license should not be permitted */ if (XFA_SKIPPING(rpmfsGetAction(fs, i))) { drc[ix]--; dff[ix] = 1; continue; @@ -798,8 +836,7 @@ static void skipInstallFiles(const rpmts ts, rpmte p) * they do need to take package relocations into account). */ if (ts->netsharedPaths) { - nsp = matchNetsharedpath(ts, fi); - if (nsp && *nsp) { + if (matchNetsharedpath(ts, fi)) { drc[ix]--; dff[ix] = 1; rpmfsSetAction(fs, i, FA_SKIPNETSHARED); continue; @@ -807,6 +844,14 @@ static void skipInstallFiles(const rpmts ts, rpmte p) } /* + * In general, excluding license files is not permitted. In case + * of SKIPNETSHARED and SKIPCOLOR the file is expected to be + * there via other means however so that is ok. + */ + if (rpmfiFFlags(fi) & RPMFILE_LICENSE) + continue; + + /* * Skip i18n language specific files. */ flangs = (ts->installLangs != NULL) ? rpmfiFLangs(fi) : NULL; @@ -851,7 +896,8 @@ static void skipInstallFiles(const rpmts ts, rpmte p) } /* Skip (now empty) directories that had skipped files. */ - for (j = 0; j < dc; j++) { + /* Iterate over dirs in reversed order to solve subdirs at first */ + for (j = dc - 1; j >= 0; j--) { const char * dn, * bn; size_t dnlen, bnlen; @@ -859,7 +905,7 @@ static void skipInstallFiles(const rpmts ts, rpmte p) if (!dff[j]) continue; /* dir was not emptied here. */ /* Find parent directory and basename. */ - dn = rpmfiDNIndex(fi, j); dnlen = strlen(dn) - 1; + dn = rpmfilesDN(files, j); dnlen = strlen(dn) - 1; bn = dn + dnlen; bnlen = 0; while (bn > dn && bn[-1] != '/') { bnlen++; @@ -892,12 +938,18 @@ static void skipInstallFiles(const rpmts ts, rpmte p) continue; rpmlog(RPMLOG_DEBUG, "excluding directory %s\n", dn); rpmfsSetAction(fs, i, FA_SKIPNSTATE); + ix = rpmfiDX(fi); + /* Decrease count of files for parent directory */ + drc[ix]--; + /* Mark directory because something was removed from them */ + dff[ix] = 1; break; } } free(drc); free(dff); + rpmfiFree(fi); } #undef HASHTYPE @@ -932,6 +984,7 @@ rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount) tsMembers tsmem = rpmtsMembers(ts); rpmstrPool tspool = rpmtsPool(ts); rpmtsi pi; rpmte p; + rpmfiles files; rpmfi fi; rpmdbMatchIterator mi; int oc = 0; @@ -945,12 +998,13 @@ rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount) pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { - (void) rpmdbCheckSignals(); + (void) rpmsqPoll(); rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_PROGRESS, oc++, tsmem->orderCount); /* Gather all installed headers with matching basename's. */ - fi = rpmfiInit(rpmteFI(p), 0); + files = rpmteFiles(p); + fi = rpmfilesIter(files, RPMFI_ITER_FWD); while (rpmfiNext(fi) >= 0) { size_t keylen; @@ -965,7 +1019,9 @@ rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount) keylen++; /* XXX "/" fixup. */ rpmdbExtendIterator(mi, baseName, keylen); rpmStringSetAddEntry(baseNames, baseNameId); - } + } + rpmfiFree(fi); + rpmfilesFree(files); } rpmtsiFree(pi); rpmStringSetFree(baseNames); @@ -986,7 +1042,7 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) { tsMembers tsmem = rpmtsMembers(ts); rpmte p; - rpmfi fi; + rpmfiles fi; rpmfs fs; int j; unsigned int fileNum; @@ -1015,15 +1071,15 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) fingerPrint *fpp = NULL; unsigned int installedPkg; int beingRemoved = 0; - rpmfi otherFi = NULL; + rpmfiles otherFi = NULL; rpmte *removedPkg = NULL; /* Is this package being removed? */ installedPkg = rpmdbGetIteratorOffset(mi); - if (removedHashGetEntry(tsmem->removedPackages, installedPkg, + if (packageHashGetEntry(tsmem->removedPackages, installedPkg, &removedPkg, NULL, NULL)) { beingRemoved = 1; - otherFi = rpmfiLink(rpmteFI(removedPkg[0])); + otherFi = rpmteFiles(removedPkg[0]); } h = headerLink(h); @@ -1057,7 +1113,7 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) fpLookup(fpc, dirName, baseName, &fpp); fpIx = 0; } else { - fpp = rpmfiFps(otherFi); + fpp = rpmfilesFps(otherFi); fpIx = fileNum; } @@ -1066,7 +1122,7 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) for (j = 0; j < numRecs; j++) { p = recs[j].p; - fi = rpmteFI(p); + fi = rpmteFiles(p); fs = rpmteGetFileStates(p); /* Determine the fate of each file. */ @@ -1074,7 +1130,7 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) case TR_ADDED: if (!otherFi) { /* XXX What to do if this fails? */ - otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER); + otherFi = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER); } handleInstInstalledFile(ts, p, fi, recs[j].fileno, h, otherFi, fileNum, beingRemoved); @@ -1086,13 +1142,14 @@ void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc) } break; } + rpmfilesFree(fi); } newheader = rpmdbNextIterator(mi); } while (newheader==h); - otherFi = rpmfiFree(otherFi); + otherFi = rpmfilesFree(otherFi); if (!beingRemoved) { rpmtdFreeData(&ostates); rpmtdFreeData(&bnames); @@ -1146,7 +1203,7 @@ static rpmps checkProblems(rpmts ts) if (!(probFilter & RPMPROB_FILTER_REPLACEPKG)) { Header h; rpmdbMatchIterator mi; - mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0); + mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, rpmteN(p), 1); rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(p)); rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(p)); rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(p)); @@ -1180,92 +1237,16 @@ static int runTransScripts(rpmts ts, pkgGoal goal) int rc = 0; rpmte p; rpmtsi pi = rpmtsiInit(ts); - while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) { - rc += rpmteProcess(p, goal); - } - rpmtsiFree(pi); - return rc; -} - -static int rpmtsSetupCollections(rpmts ts) -{ - /* seenCollectionsPost and TEs are basically a key-value pair. each item in - * seenCollectionsPost is a collection that has been seen from any package, - * and the associated index in the TEs is the last transaction element - * where that collection was seen. */ - ARGV_t seenCollectionsPost = NULL; - rpmte *TEs = NULL; - int numSeenPost = 0; - - /* seenCollectionsPre is a list of collections that have been seen from - * only removed packages */ - ARGV_t seenCollectionsPre = NULL; - int numSeenPre = 0; - - ARGV_const_t collname; - int installing = 1; - int i; - - rpmte p; - rpmtsi pi = rpmtsiInit(ts); - while ((p = rpmtsiNext(pi, 0)) != NULL) { - /* detect when we switch from installing to removing packages, and - * update the lastInCollectionAdd lists */ - if (installing && rpmteType(p) == TR_REMOVED) { - installing = 0; - for (i = 0; i < numSeenPost; i++) { - rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]); - } - } - - rpmteSetupCollectionPlugins(p); - - for (collname = rpmteCollections(p); collname && *collname; collname++) { - /* figure out if we've seen this collection in post before */ - for (i = 0; i < numSeenPost && strcmp(*collname, seenCollectionsPost[i]); i++) { - } - if (i < numSeenPost) { - /* we've seen the collection, update the index */ - TEs[i] = p; - } else { - /* haven't seen the collection yet, add it */ - argvAdd(&seenCollectionsPost, *collname); - TEs = xrealloc(TEs, sizeof(*TEs) * (numSeenPost + 1)); - TEs[numSeenPost] = p; - numSeenPost++; - } + rpmElementTypes types = TR_ADDED; + int i = 0; + if (goal == PKG_TRANSFILETRIGGERUN) + types = TR_REMOVED; - /* figure out if we've seen this collection in pre remove before */ - if (installing == 0) { - for (i = 0; i < numSeenPre && strcmp(*collname, seenCollectionsPre[i]); i++) { - } - if (i >= numSeenPre) { - /* haven't seen this collection, add it */ - rpmteAddToFirstInCollectionRemove(p, *collname); - argvAdd(&seenCollectionsPre, *collname); - numSeenPre++; - } - } - } + while ((p = rpmtsiNext(pi, types)) != NULL) { + rc += rpmteProcess(p, goal, i++); } rpmtsiFree(pi); - - /* we've looked at all the rpmte's, update the lastInCollectionAny lists */ - for (i = 0; i < numSeenPost; i++) { - rpmteAddToLastInCollectionAny(TEs[i], seenCollectionsPost[i]); - if (installing == 1) { - /* lastInCollectionAdd is only updated above if packages were - * removed. if nothing is removed in the transaction, we need to - * update that list here */ - rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]); - } - } - - argvFree(seenCollectionsPost); - argvFree(seenCollectionsPre); - _free(TEs); - - return 0; + return rc; } static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet) @@ -1279,21 +1260,7 @@ static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet) (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers)); if (rpmtsFlags(ts) & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_TEST)) - (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers | RPMTRANS_FLAG_NOCOLLECTIONS)); - - /* if SELinux isn't enabled or it is a test run, don't bother... */ - if (!is_selinux_enabled() || (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) { - rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS)); - } - - if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS) { - rpmlog(RPMLOG_DEBUG, "Selinux disabled.\n"); - } else { - if (rpmtsSELabelInit(ts, 1)) { - rpmlog(RPMLOG_WARNING, "Failed to open SELinux handle.\n"); - rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS)); - } - } + (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers)); /* * Make sure the database is open RDWR for package install/erase. @@ -1315,9 +1282,6 @@ static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet) static int rpmtsFinish(rpmts ts) { - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) { - rpmtsSELabelFini(ts, 1); - } return rpmChrootSet(NULL); } @@ -1326,7 +1290,6 @@ static int rpmtsPrepare(rpmts ts) tsMembers tsmem = rpmtsMembers(ts); rpmtsi pi; rpmte p; - rpmfi fi; int rc = 0; uint64_t fileCount = countFiles(ts); const char *dbhome = NULL; @@ -1336,16 +1299,21 @@ static int rpmtsPrepare(rpmts ts) rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount); - /* Skip netshared paths, not our i18n files, and excluded docs */ + /* Reset actions, set skip for netshared paths and excluded files */ pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { - if (rpmfiFC(rpmteFI(p)) == 0) - continue; - if (rpmteType(p) == TR_ADDED) { - skipInstallFiles(ts, p); - } else { - skipEraseFiles(ts, p); + rpmfiles files = rpmteFiles(p); + if (rpmfilesFC(files) > 0) { + rpmfs fs = rpmteGetFileStates(p); + /* Ensure clean state, this could get called more than once. */ + rpmfsResetActions(fs); + if (rpmteType(p) == TR_ADDED) { + skipInstallFiles(ts, files, fs); + } else { + skipEraseFiles(ts, files, fs); + } } + rpmfilesFree(files); } rpmtsiFree(pi); @@ -1368,13 +1336,14 @@ static int rpmtsPrepare(rpmts ts) pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { - if ((fi = rpmteFI(p)) == NULL) + rpmfiles files = rpmteFiles(p);; + if (files == NULL) continue; /* XXX can't happen */ (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0); /* check files in ts against each other and update disk space needs on each partition for this package. */ - handleOverlappedFiles(ts, fpc, p, fi); + handleOverlappedFiles(ts, fpc, p, files); /* Check added package has sufficient space on each partition used. */ if (rpmteType(p) == TR_ADDED) { @@ -1391,6 +1360,7 @@ static int rpmtsPrepare(rpmts ts) rpmtsCheckDSIProblems(ts, p); } (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0); + rpmfilesFree(files); } rpmtsiFree(pi); rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, tsmem->orderCount); @@ -1403,7 +1373,7 @@ static int rpmtsPrepare(rpmts ts) if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))) { pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { - rpmteSetFI(p, NULL); + rpmteCleanFiles(p); } rpmtsiFree(pi); } @@ -1421,6 +1391,7 @@ static int rpmtsProcess(rpmts ts) { rpmtsi pi; rpmte p; int rc = 0; + int i = 0; pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { @@ -1429,49 +1400,114 @@ static int rpmtsProcess(rpmts ts) rpmlog(RPMLOG_DEBUG, "========== +++ %s %s-%s 0x%x\n", rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p)); - if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) { - rpmtsSELabelInit(ts, 0); - } - - failed = rpmteProcess(p, rpmteType(p)); + failed = rpmteProcess(p, rpmteType(p), i++); if (failed) { rpmlog(RPMLOG_ERR, "%s: %s %s\n", rpmteNEVRA(p), rpmteTypeString(p), failed > 1 ? _("skipped") : _("failed")); rc++; } - (void) rpmdbSync(rpmtsGetRdb(ts)); } rpmtsiFree(pi); return rc; } -static rpmRC rpmtsSetupTransactionPlugins(rpmts ts) +rpmRC rpmtsSetupTransactionPlugins(rpmts ts) { rpmRC rc = RPMRC_OK; - char *plugins = NULL, *plugin = NULL; - const char *delims = ","; - - plugins = rpmExpand("%{?__transaction_plugins}", NULL); - if (!plugins || rstreq(plugins, "")) { - goto exit; - } + ARGV_t files = NULL; + int nfiles = 0; + char *dsoPath = NULL; - plugin = strtok(plugins, delims); - while(plugin != NULL) { - rpmlog(RPMLOG_DEBUG, "plugin is %s\n", plugin); - if (!rpmpluginsPluginAdded(ts->plugins, (const char*)plugin)) { - if (rpmpluginsAddPlugin(ts->plugins, "transaction", - (const char*)plugin) == RPMRC_FAIL) { - /* any configured plugin failing to load is a failure */ + /* + * Assume allocated equals initialized. There are some oddball cases + * (verification of non-installed package) where this is not true + * currently but that's not a new issue. + */ + if ((rpmtsFlags(ts) & RPMTRANS_FLAG_NOPLUGINS) || rpmPluginsGetCount(rpmtsPlugins(ts)) != 0) + return RPMRC_OK; + + dsoPath = rpmExpand("%{__plugindir}/*.so", NULL); + if (rpmGlob(dsoPath, &nfiles, &files) == 0) { + rpmPlugins tsplugins = rpmtsPlugins(ts); + for (int i = 0; i < nfiles; i++) { + char *bn = basename(files[i]); + bn[strlen(bn)-strlen(".so")] = '\0'; + if (rpmpluginsAddPlugin(tsplugins, "transaction", bn) == RPMRC_FAIL) + { + /* any configured plugin failing to load is a failure */ // temporally make the loading policy relaxed: no failures + //refer to commit id: 3959da1846227711d97c17495aa4779e653a1b3a //rc = RPMRC_FAIL; } } - plugin = strtok(NULL, delims); + files = argvFree(files); } + free(dsoPath); + + return rc; +} +/** + * Run a scriptlet with args. + * + * Run a script with an interpreter. If the interpreter is not specified, + * /bin/sh will be used. If the interpreter is /bin/sh, then the args from + * the header will be ignored, passing instead arg1 and arg2. + * + * @param ts transaction set + * @param te transaction element + * @param prefixes install prefixes + * @param script scriptlet from header + * @param arg1 no. instances of package installed after scriptlet exec + * (-1 is no arg) + * @param arg2 ditto, but for the target package + * @return 0 on success + */ +rpmRC runScript(rpmts ts, rpmte te, Header h, ARGV_const_t prefixes, + rpmScript script, int arg1, int arg2) +{ + rpmte xte = te; + rpmRC stoprc, rc = RPMRC_OK; + rpmTagVal stag = rpmScriptTag(script); + FD_t sfd = NULL; + int warn_only = (stag != RPMTAG_PREIN && + stag != RPMTAG_PREUN && + stag != RPMTAG_PRETRANS && + stag != RPMTAG_VERIFYSCRIPT); + + /* Fake up a transaction element for triggers from rpmdb */ + if (te == NULL) { + te = rpmteNew(ts, h, TR_REMOVED, NULL, NULL); + rpmteSetHeader(te, h); + } + + sfd = rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_START, stag, 0); + if (sfd == NULL) + sfd = rpmtsScriptFd(ts); + + rpmswEnter(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0); + rc = rpmScriptRun(script, arg1, arg2, sfd, + prefixes, warn_only, rpmtsPlugins(ts)); + rpmswExit(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0); + + /* Map warn-only errors to "notfound" for script stop callback */ + stoprc = (rc != RPMRC_OK && warn_only) ? RPMRC_NOTFOUND : rc; + rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_STOP, stag, stoprc); + + /* + * Notify callback for all errors. "total" abused for warning/error, + * rc only reflects whether the condition prevented install/erase + * (which is only happens with %prein and %preun scriptlets) or not. + */ + if (rc != RPMRC_OK) { + if (warn_only) { + rc = RPMRC_OK; + } + rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_ERROR, stag, rc); + } + + if (te != xte) + rpmteFree(te); -exit: - free(plugins); return rc; } @@ -1479,9 +1515,11 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet) { int rc = -1; /* assume failure */ tsMembers tsmem = rpmtsMembers(ts); - rpmlock lock = NULL; + rpmtxn txn = NULL; rpmps tsprobs = NULL; int TsmPreDone = 0; /* TsmPre hook hasn't been called */ + /* Ignore SIGPIPE for the duration of transaction */ + rpmsqAction_t oact = rpmsqSetAction(SIGPIPE, RPMSQ_IGN); /* Force default 022 umask during transaction for consistent results */ mode_t oldmask = umask(022); @@ -1494,7 +1532,7 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet) /* If we are in test mode, then there's no need for transaction lock. */ if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) { - if (!(lock = rpmtsAcquireLock(ts))) { + if (!(txn = rpmtxnBegin(ts, RPMTXN_WRITE))) { goto exit; } } @@ -1504,23 +1542,19 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet) goto exit; } - if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) { - goto exit; - } - - rpmtsSetupCollections(ts); - /* Check package set for problems */ tsprobs = checkProblems(ts); /* Run pre transaction hook for all plugins */ TsmPreDone = 1; - if (rpmpluginsCallTsmPre(ts->plugins, ts) == RPMRC_FAIL) { + if (rpmpluginsCallTsmPre(rpmtsPlugins(ts), ts) == RPMRC_FAIL) { goto exit; } - /* Run pre-transaction scripts, but only if there are no known - * problems up to this point and not disabled otherwise. */ + /* Run %pretrans scripts, but only if there are no known problems up to + * this point and not disabled otherwise. This is evil as it runs before + * fingerprinting and problem checking and is best avoided. + */ if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRETRANS)) || (rpmpsNumProblems(tsprobs)))) { rpmlog(RPMLOG_DEBUG, "running pre-transaction scripts\n"); @@ -1553,24 +1587,47 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet) if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))) tsmem->pool = rpmstrPoolFree(tsmem->pool); + /* Run %transfiletriggerun scripts unless disabled */ + if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRETRANS| + RPMTRANS_FLAG_NOTRIGGERUN))) { + + runFileTriggers(ts, NULL, RPMSENSE_TRIGGERUN, + RPMSCRIPT_TRANSFILETRIGGER, 0); + runTransScripts(ts, PKG_TRANSFILETRIGGERUN); + } + /* Actually install and remove packages, get final exit code */ rc = rpmtsProcess(ts) ? -1 : 0; - /* Run post-transaction scripts unless disabled */ + /* Run %posttrans scripts unless disabled */ if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS))) { rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n"); runTransScripts(ts, PKG_POSTTRANS); } + /* Run %transfiletriggerpostun scripts unless disabled */ + if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERIN))) { + runFileTriggers(ts, NULL, RPMSENSE_TRIGGERIN, RPMSCRIPT_TRANSFILETRIGGER, 0); + } + if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) { + runPostUnTransFileTrigs(ts); + } + + /* Run %transfiletriggerin scripts unless disabled */ + if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERIN))) { + runTransScripts(ts, PKG_TRANSFILETRIGGERIN); + } exit: /* Run post transaction hook for all plugins */ if (TsmPreDone) /* If TsmPre hook has been called, call the TsmPost hook */ - rpmpluginsCallTsmPost(ts->plugins, ts, rc); + rpmpluginsCallTsmPost(rpmtsPlugins(ts), ts, rc); /* Finish up... */ (void) umask(oldmask); (void) rpmtsFinish(ts); rpmpsFree(tsprobs); - rpmlockFree(lock); + rpmtxnEnd(txn); + /* Restore SIGPIPE *after* unblocking signals in rpmtxnEnd() */ + rpmsqSetAction(SIGPIPE, oact); return rc; } diff --git a/lib/verify.c b/lib/verify.c index 3dcec10f7..5528ba95e 100644 --- a/lib/verify.c +++ b/lib/verify.c @@ -53,25 +53,22 @@ static int cap_compare(cap_t acap, cap_t bcap) } #endif -int rpmVerifyFile(const rpmts ts, const rpmfi fi, - rpmVerifyAttrs * res, rpmVerifyAttrs omitMask) +rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask) { - rpm_mode_t fmode = rpmfiFMode(fi); - rpmfileAttrs fileAttrs = rpmfiFFlags(fi); - rpmVerifyAttrs flags = rpmfiVFlags(fi); - const char * fn = rpmfiFN(fi); + rpm_mode_t fmode = rpmfilesFMode(fi, ix); + rpmfileAttrs fileAttrs = rpmfilesFFlags(fi, ix); + rpmVerifyAttrs flags = rpmfilesVFlags(fi, ix); + const char * fn = rpmfilesFN(fi, ix); struct stat sb; - int rc; - - *res = RPMVERIFY_NONE; + rpmVerifyAttrs vfy = RPMVERIFY_NONE; /* * Check to see if the file was installed - if not pretend all is OK. */ - switch (rpmfiFState(fi)) { + switch (rpmfilesFState(fi, ix)) { case RPMFILE_STATE_NETSHARED: case RPMFILE_STATE_NOTINSTALLED: - return 0; + goto exit; break; case RPMFILE_STATE_REPLACED: /* For replaced files we can only verify if it exists at all */ @@ -92,8 +89,23 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, } if (fn == NULL || lstat(fn, &sb) != 0) { - *res |= RPMVERIFY_LSTATFAIL; - return 1; + vfy |= RPMVERIFY_LSTATFAIL; + goto exit; + } + + /* If we expected a directory but got a symlink to one, follow the link */ + if (S_ISDIR(fmode) && S_ISLNK(sb.st_mode)) { + struct stat dsb; + /* ...if it actually points to a directory */ + if (stat(fn, &dsb) == 0 && S_ISDIR(dsb.st_mode)) { + uid_t fuid; + /* ...and is by a legit user, to match fsmVerify() behavior */ + if (sb.st_uid == 0 || + (rpmugUid(rpmfilesFUser(fi, ix), &fuid) == 0 && + sb.st_uid == fuid)) { + sb = dsb; /* struct assignment */ + } + } } /* Links have no mode, other types have no linkto */ @@ -122,19 +134,19 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, size_t diglen; /* XXX If --nomd5, then prelinked library sizes are not corrected. */ - if ((digest = rpmfiFDigest(fi, &algo, &diglen))) { + if ((digest = rpmfilesFDigest(fi, ix, &algo, &diglen))) { unsigned char fdigest[diglen]; rpm_loff_t fsize; - rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize); - sb.st_size = fsize; - if (rc) { - *res |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST); - } else if (memcmp(fdigest, digest, diglen)) { - *res |= RPMVERIFY_FILEDIGEST; + if (rpmDoDigest(algo, fn, 0, fdigest, &fsize)) { + vfy |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST); + } else { + sb.st_size = fsize; + if (memcmp(fdigest, digest, diglen)) + vfy |= RPMVERIFY_FILEDIGEST; } } else { - *res |= RPMVERIFY_FILEDIGEST; + vfy |= RPMVERIFY_FILEDIGEST; } } @@ -143,18 +155,18 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, int size = 0; if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1) - *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); + vfy |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); else { - const char * flink = rpmfiFLink(fi); + const char * flink = rpmfilesFLink(fi, ix); linkto[size] = '\0'; if (flink == NULL || !rstreq(linkto, flink)) - *res |= RPMVERIFY_LINKTO; + vfy |= RPMVERIFY_LINKTO; } } if (flags & RPMVERIFY_FILESIZE) { - if (sb.st_size != rpmfiFSize(fi)) - *res |= RPMVERIFY_FILESIZE; + if (sb.st_size != rpmfilesFSize(fi, ix)) + vfy |= RPMVERIFY_FILESIZE; } if (flags & RPMVERIFY_MODE) { @@ -176,7 +188,7 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, } if (metamode != filemode) - *res |= RPMVERIFY_MODE; + vfy |= RPMVERIFY_MODE; #if WITH_ACL /* @@ -186,7 +198,7 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS); if (facl) { if (acl_equiv_mode(facl, NULL) == 1) { - *res |= RPMVERIFY_MODE; + vfy |= RPMVERIFY_MODE; } acl_free(facl); } @@ -197,12 +209,12 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode) || S_ISBLK(fmode) != S_ISBLK(sb.st_mode)) { - *res |= RPMVERIFY_RDEV; + vfy |= RPMVERIFY_RDEV; } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) { rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff); - rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff); + rpm_rdev_t frdev = (rpmfilesFRdev(fi, ix) & 0xffff); if (st_rdev != frdev) - *res |= RPMVERIFY_RDEV; + vfy |= RPMVERIFY_RDEV; } } @@ -213,7 +225,7 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, * capabilities at all but suffices for now... */ cap_t cap, fcap; - cap = cap_from_text(rpmfiFCaps(fi)); + cap = cap_from_text(rpmfilesFCaps(fi, ix)); if (!cap) { cap = cap_from_text("="); } @@ -223,32 +235,71 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi, } if (cap_compare(cap, fcap) != 0) - *res |= RPMVERIFY_CAPS; + vfy |= RPMVERIFY_CAPS; cap_free(fcap); cap_free(cap); } #endif - if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) { - *res |= RPMVERIFY_MTIME; + if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfilesFMtime(fi, ix))) { + vfy |= RPMVERIFY_MTIME; } if (flags & RPMVERIFY_USER) { const char * name = rpmugUname(sb.st_uid); - const char * fuser = rpmfiFUser(fi); - if (name == NULL || fuser == NULL || !rstreq(name, fuser)) - *res |= RPMVERIFY_USER; + const char * fuser = rpmfilesFUser(fi, ix); + uid_t uid; + int namematch = 0; + int idmatch = 0; + + if (name && fuser) + namematch = rstreq(name, fuser); + if (fuser && rpmugUid(fuser, &uid) == 0) + idmatch = (uid == sb.st_uid); + + if (namematch != idmatch) { + rpmlog(RPMLOG_WARNING, + _("Duplicate username or UID for user %s\n"), fuser); + } + + if (!(namematch || idmatch)) + vfy |= RPMVERIFY_USER; } if (flags & RPMVERIFY_GROUP) { const char * name = rpmugGname(sb.st_gid); - const char * fgroup = rpmfiFGroup(fi); - if (name == NULL || fgroup == NULL || !rstreq(name, fgroup)) - *res |= RPMVERIFY_GROUP; + const char * fgroup = rpmfilesFGroup(fi, ix); + gid_t gid; + int namematch = 0; + int idmatch = 0; + + if (name && fgroup) + namematch = rstreq(name, fgroup); + if (fgroup && rpmugGid(fgroup, &gid) == 0) + idmatch = (gid == sb.st_gid); + + if (namematch != idmatch) { + rpmlog(RPMLOG_WARNING, + _("Duplicate groupname or GID for group %s\n"), fgroup); + } + + if (!(namematch || idmatch)) + vfy |= RPMVERIFY_GROUP; } - return 0; +exit: + return vfy; +} + +int rpmVerifyFile(const rpmts ts, const rpmfi fi, + rpmVerifyAttrs * res, rpmVerifyAttrs omitMask) +{ + rpmVerifyAttrs vfy = rpmfiVerify(fi, omitMask); + if (res) + *res = vfy; + + return (vfy & RPMVERIFY_LSTATFAIL) ? 1 : 0; } /** @@ -314,7 +365,7 @@ char * rpmVerifyString(uint32_t verifyResult, const char *pad) char * rpmFFlagsString(uint32_t fflags, const char *pad) { char *fmt = NULL; - rasprintf(&fmt, "%s%s%s%s%s%s%s%s", + rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s", (fflags & RPMFILE_DOC) ? "d" : pad, (fflags & RPMFILE_CONFIG) ? "c" : pad, (fflags & RPMFILE_SPECFILE) ? "s" : pad, @@ -322,22 +373,43 @@ char * rpmFFlagsString(uint32_t fflags, const char *pad) (fflags & RPMFILE_NOREPLACE) ? "n" : pad, (fflags & RPMFILE_GHOST) ? "g" : pad, (fflags & RPMFILE_LICENSE) ? "l" : pad, - (fflags & RPMFILE_README) ? "r" : pad); + (fflags & RPMFILE_README) ? "r" : pad, + (fflags & RPMFILE_ARTIFACT) ? "a" : pad); return fmt; } +static const char * stateStr(rpmfileState fstate) +{ + switch (fstate) { + case RPMFILE_STATE_NORMAL: + return NULL; + case RPMFILE_STATE_NOTINSTALLED: + return rpmIsVerbose() ? _("not installed") : NULL; + case RPMFILE_STATE_NETSHARED: + return rpmIsVerbose() ? _("net shared") : NULL; + case RPMFILE_STATE_WRONGCOLOR: + return rpmIsVerbose() ? _("wrong color") : NULL; + case RPMFILE_STATE_REPLACED: + return _("replaced"); + case RPMFILE_STATE_MISSING: + return _("no state"); + } + return _("unknown state"); +} + /** * Check file info from header against what's actually installed. * @param ts transaction set * @param h header to verify * @param omitMask bits to disable verify checks - * @param ghosts should ghosts be verified? + * @param skipAttr skip files with these attrs (eg %ghost) * @return 0 no problems, 1 problems found */ -static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts) +static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, + rpmfileAttrs skipAttrs) { rpmVerifyAttrs verifyResult = 0; - int ec = 0; /* assume no problems */ + rpmVerifyAttrs verifyAll = 0; /* assume no problems */ rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY); if (fi == NULL) @@ -347,17 +419,17 @@ static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts) while (rpmfiNext(fi) >= 0) { rpmfileAttrs fileAttrs = rpmfiFFlags(fi); char *buf = NULL, *attrFormat; + const char *fstate = NULL; char ac; - int rc; - /* If not verifying %ghost, skip ghost files. */ - if ((fileAttrs & RPMFILE_GHOST) && !ghosts) + /* Skip on attributes (eg from --noghost) */ + if (skipAttrs & fileAttrs) continue; - rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask); + verifyResult = rpmfiVerify(fi, omitMask); /* Filter out timestamp differences of shared files */ - if (rc == 0 && (verifyResult & RPMVERIFY_MTIME)) { + if (verifyResult & RPMVERIFY_MTIME) { rpmdbMatchIterator mi; mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0); if (rpmdbGetIteratorCount(mi) > 1) @@ -365,9 +437,13 @@ static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts) rpmdbFreeIterator(mi); } + /* State is only meaningful for installed packages */ + if (headerGetInstance(h)) + fstate = stateStr(rpmfiFState(fi)); + attrFormat = rpmFFlagsString(fileAttrs, ""); ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0]; - if (rc) { + if (verifyResult & RPMVERIFY_LSTATFAIL) { if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) { rasprintf(&buf, _("missing %c %s"), ac, rpmfiFN(fi)); if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 && @@ -377,25 +453,30 @@ static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts) rstrcat(&buf, app); free(app); } - ec = rc; } - } else if (verifyResult || rpmIsVerbose()) { + } else if (verifyResult || fstate || rpmIsVerbose()) { char *verifyFormat = rpmVerifyString(verifyResult, "."); rasprintf(&buf, "%s %c %s", verifyFormat, ac, rpmfiFN(fi)); free(verifyFormat); - - if (verifyResult) ec = 1; } free(attrFormat); if (buf) { + if (fstate) + buf = rstrscat(&buf, " (", fstate, ")", NULL); rpmlog(RPMLOG_NOTICE, "%s\n", buf); buf = _free(buf); } + + /* Filter out missing %ghost/%missingok errors from final result */ + if (fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) + verifyResult &= ~RPMVERIFY_LSTATFAIL; + + verifyAll |= verifyResult; } rpmfiFree(fi); - return ec; + return (verifyAll != 0) ? 1 : 0; } /** @@ -440,7 +521,6 @@ static int verifyDependencies(rpmts ts, Header h) int showVerifyPackage(QVA_t qva, rpmts ts, Header h) { rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS); - int ghosts = (qva->qva_fflags & RPMFILE_GHOST); int ec = 0; int rc; @@ -449,7 +529,7 @@ int showVerifyPackage(QVA_t qva, rpmts ts, Header h) ec = rc; } if (qva->qva_flags & VERIFY_FILES) { - if ((rc = verifyHeader(ts, h, omitMask, ghosts)) != 0) + if ((rc = verifyHeader(ts, h, omitMask, qva->qva_fflags)) != 0) ec = rc; } if (qva->qva_flags & VERIFY_SCRIPT) { |