diff --git a/docs/man/rpm.8.md b/docs/man/rpm.8.md index 7ca4e6e31c..3c8b1dae84 100644 --- a/docs/man/rpm.8.md +++ b/docs/man/rpm.8.md @@ -45,13 +45,7 @@ MISCELLANEOUS: **rpm** **\--showrc** -**rpm** **\--setperms** *PACKAGE\_NAME \...* - -**rpm** **\--setugids** *PACKAGE\_NAME \...* - -**rpm** **\--setcaps** *PACKAGE\_NAME \...* - -**rpm** **\--restore** *PACKAGE\_NAME \...* +**rpm** **\--restore** \[**select-options**\] select-options -------------- @@ -970,30 +964,14 @@ MISCELLANEOUS COMMANDS : shows the values **rpm** will use for all of the options are currently set in *rpmrc* and *macros* configuration file(s). -**rpm** **\--setperms** *PACKAGE\_NAME* - -: sets permissions of files in the given package. Consider using - **\--restore** instead. - -**rpm** **\--setugids** *PACKAGE\_NAME* - -: sets user/group ownership of files in the given package. This - command can change permissions and capabilities of files in that - package. In most cases it is better to use **\--restore** instead. - -**rpm** **\--setcaps** *PACKAGE\_NAME* - -: sets capabilities of files in the given package. Consider using - **\--restore** instead. - -**rpm** **\--restore** *PACKAGE\_NAME* +**rpm** **\--setperms** | **\--setugids** | **\--setcaps** *PACKAGE\_NAME* -: The option restores owner, group, permissions and capabilities of - files in the given package. +: obsolete aliases for **\--restore** -Options **\--setperms**, **\--setugids**, **\--setcaps** and +**rpm** **\--restore** \[**select-options**\] -: **\--restore** are mutually exclusive. +: The option restores file metadata such as timestamp, owner, group, + permissions and capabilities of files in packages. FTP/HTTP OPTIONS ---------------- diff --git a/lib/depends.c b/lib/depends.c index 133a386f15..b690251db6 100644 --- a/lib/depends.c +++ b/lib/depends.c @@ -488,6 +488,22 @@ int rpmtsAddReinstallElement(rpmts ts, Header h, fnpyKey key) return addPackage(ts, h, key, RPMTE_REINSTALL, NULL); } +int rpmtsAddRestoreElement(rpmts ts, Header h) +{ + tsMembers tsmem = rpmtsMembers(ts); + if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) + return 1; + + rpmte p = rpmteNew(ts, h, TR_RESTORED, NULL, NULL, 0); + if (p == NULL) + return 1; + + addElement(tsmem, p, tsmem->orderCount); + rpmtsNotifyChange(ts, RPMTS_EVENT_ADD, p, NULL); + + return 0; +} + int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset) { if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL) diff --git a/lib/fsm.c b/lib/fsm.c index 935a0a5c6f..9118983479 100644 --- a/lib/fsm.c +++ b/lib/fsm.c @@ -924,7 +924,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, if (rc) goto exit; - fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); + if (rpmteType(te) == TR_ADDED) + fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); + else + fi = rpmfilesIter(files, RPMFI_ITER_FWD); if (fi == NULL) { rc = RPMERR_BAD_MAGIC; goto exit; diff --git a/lib/order.c b/lib/order.c index 73c970247e..3ac07e297f 100644 --- a/lib/order.c +++ b/lib/order.c @@ -613,6 +613,14 @@ int rpmtsOrder(rpmts ts) rpmlog(RPMLOG_DEBUG, "========== tsorting packages (order, #predecessors, #succesors, depth)\n"); + /* Restored items first (doesn't matter but is simple) */ + for (int e = 0; e < nelem; e++) { + tsortInfo p = &sortInfo[e]; + if (rpmteType(p->te) == TR_RESTORED) { + newOrder[newOrderCount++] = p->te; + } + } + for (int i = 0; i < 2; i++) { /* Do two separate runs: installs first - then erases */ int oType = !i ? TR_ADDED : TR_REMOVED; diff --git a/lib/poptI.c b/lib/poptI.c index 2d55ae7361..78ea2b92d7 100644 --- a/lib/poptI.c +++ b/lib/poptI.c @@ -269,6 +269,10 @@ struct poptOption rpmInstallPoptTable[] = { &rpmIArgs.installInterfaceFlags, (INSTALL_REINSTALL|INSTALL_INSTALL), N_("reinstall package(s)"), N_("+") }, + { "restore", '\0', POPT_BIT_SET, + &rpmIArgs.installInterfaceFlags, (INSTALL_RESTORE), + N_("restore package(s)"), + N_("+") }, POPT_TABLEEND }; diff --git a/lib/psm.c b/lib/psm.c index df9d039670..b1f6ce1fcb 100644 --- a/lib/psm.c +++ b/lib/psm.c @@ -826,11 +826,43 @@ static rpmRC rpmPackageErase(rpmts ts, rpmpsm psm) return rc; } +static rpmRC rpmPackageRestore(rpmts ts, rpmpsm psm) +{ + rpmRC rc = RPMRC_OK; + + rpmswEnter(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0); + if ((rc = rpmChrootIn()) == 0) { + char *failedFile = NULL; + rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0); + /* make sure first progress call gets made */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0); + + if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) { + if (rpmfilesFC(psm->files) > 0) { + int fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->files, + psm, &failedFile); + rc = (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL; + } + } + + /* XXX make sure progress reaches 100% */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, psm->total); + rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total); + + free(failedFile); + rpmChrootOut(); + } + rpmswExit(rpmtsOp(psm->ts, RPMTS_OP_INSTALL), 0); + + return rc; +} + static const char * pkgGoalString(pkgGoal goal) { switch (goal) { case PKG_INSTALL: return " install"; case PKG_ERASE: return " erase"; + case PKG_RESTORE: return " restore"; case PKG_VERIFY: return " verify"; case PKG_PRETRANS: return " pretrans"; case PKG_POSTTRANS: return "posttrans"; @@ -848,6 +880,9 @@ static rpmRC runGoal(rpmpsm psm, pkgGoal goal) case PKG_ERASE: rc = rpmPackageErase(psm->ts, psm); break; + case PKG_RESTORE: + rc = rpmPackageRestore(psm->ts, psm); + break; case PKG_PRETRANS: case PKG_POSTTRANS: case PKG_VERIFY: diff --git a/lib/rpmcli.h b/lib/rpmcli.h index 906fe99513..ee1321cf6c 100644 --- a/lib/rpmcli.h +++ b/lib/rpmcli.h @@ -305,6 +305,7 @@ enum rpmInstallFlags_e { INSTALL_ERASE = (1 << 8), /*!< from --erase */ INSTALL_ALLMATCHES = (1 << 9), /*!< from --allmatches */ INSTALL_REINSTALL = (1 << 10), /*!< from --reinstall */ + INSTALL_RESTORE = (1 << 11), /*!< from --restore */ }; typedef rpmFlags rpmInstallFlags; @@ -385,6 +386,15 @@ int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv); int rpmErase(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv); +/** \ingroup rpmcli + * Restore file metadata (perms etc) of installed package(s). + * @param ts transaction set + * @param ia control args/bits + * @param argv array of package names (NULL terminated) + * @return 0 on success + */ +int rpmRestore(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv); + /** \ingroup rpmcli */ extern struct rpmInstallArguments_s rpmIArgs; diff --git a/lib/rpminstall.c b/lib/rpminstall.c index 62cf882754..66727ded81 100644 --- a/lib/rpminstall.c +++ b/lib/rpminstall.c @@ -287,8 +287,15 @@ static int rpmcliTransaction(rpmts ts, struct rpmInstallArguments_s * ia) } if (numPackages && !stop) { - rpmlog(RPMLOG_DEBUG, eflags ? "erasing packages\n" : - "installing binary packages\n"); + const char *msg; + if (ia->installInterfaceFlags & INSTALL_RESTORE) + msg = "restoring packages"; + else if (ia->installInterfaceFlags & INSTALL_ERASE) + msg = "erasing packages"; + else + msg = "installing binary packages"; + + rpmlog(RPMLOG_DEBUG, "%s\n", msg); rpmtsClean(ts); rc = rpmtsRun(ts, NULL, ia->probFilter); @@ -738,6 +745,35 @@ int rpmErase(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv) return (numFailed < 0) ? numPackages : numFailed; } +static int handleRestorePackage(QVA_t qva, rpmts ts, Header h) +{ + return rpmtsAddRestoreElement(ts, h); +} + +int rpmRestore(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv) +{ + QVA_t qva = &rpmQVKArgs; + int rc; + rpmVSFlags vsflags = setvsFlags(ia); + rpmVSFlags ovsflags = rpmtsSetVSFlags(ts, vsflags); + + rpmtsSetFlags(ts, ia->transFlags); + setNotifyFlag(ia, ts); + + if (qva->qva_showPackage == NULL) + qva->qva_showPackage = handleRestorePackage; + + rc = rpmcliArgIter(ts, qva, argv); + if (rc == 0) { + rc = rpmcliTransaction(ts, ia); + } + + rpmtsEmpty(ts); + rpmtsSetVSFlags(ts, ovsflags); + + return rc; +} + int rpmInstallSource(rpmts ts, const char * arg, char ** specFilePtr, char ** cookie) { diff --git a/lib/rpmte.c b/lib/rpmte.c index 3663604e7e..81a1f76c3d 100644 --- a/lib/rpmte.c +++ b/lib/rpmte.c @@ -102,10 +102,17 @@ void rpmteCleanDS(rpmte te) static rpmfiles getFiles(rpmte p, Header h) { - /* TR_RPMDB handled as TR_ERASED for now, doesn't really matter */ - rpmfiFlags fiflags; - fiflags = (p->type == TR_ADDED) ? (RPMFI_NOHEADER | RPMFI_FLAGS_INSTALL) : - (RPMFI_NOHEADER | RPMFI_FLAGS_ERASE); + rpmfiFlags fiflags = RPMFI_NOHEADER; + switch (p->type) { + case TR_ADDED: + case TR_RESTORED: + fiflags |= RPMFI_FLAGS_INSTALL; + break; + case TR_REMOVED: + case TR_RPMDB: + fiflags |= RPMFI_FLAGS_ERASE; + break; + } /* relocate stuff in header if necessary */ if (rpmteType(p) == TR_ADDED && rpmfsFC(p->fs) > 0) { @@ -184,7 +191,8 @@ static int addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) /* Relocation needs to know file count before rpmfiNew() */ headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM); - p->fs = rpmfsNew(rpmtdCount(&bnames), (p->type == TR_ADDED)); + p->fs = rpmfsNew(rpmtdCount(&bnames), + (p->type == TR_ADDED || p->type == TR_RESTORED)); rpmtdFreeData(&bnames); p->files = getFiles(p, h); @@ -597,6 +605,7 @@ static int rpmteOpen(rpmte te, int reload_fi) break; case TR_REMOVED: case TR_RPMDB: + case TR_RESTORED: h = rpmteDBHeader(te); break; } @@ -631,6 +640,7 @@ static int rpmteClose(rpmte te, int reset_fi) break; case TR_REMOVED: case TR_RPMDB: + case TR_RESTORED: /* eventually we'll want notifications for erase open too */ break; } @@ -770,6 +780,7 @@ const char * rpmteTypeString(rpmte te) case TR_ADDED: return _("install"); case TR_REMOVED: return _("erase"); case TR_RPMDB: return _("rpmdb"); + case TR_RESTORED: return _("restored"); default: return "???"; } } @@ -797,7 +808,7 @@ int rpmteAddOp(rpmte te) int rpmteProcess(rpmte te, pkgGoal goal, int num) { /* Only install/erase resets pkg file info */ - int scriptstage = (goal != PKG_INSTALL && goal != PKG_ERASE); + int scriptstage = (goal != PKG_INSTALL && goal != PKG_ERASE && goal != PKG_RESTORE); int test = (rpmtsFlags(te->ts) & RPMTRANS_FLAG_TEST); int reset_fi = (scriptstage == 0 && test == 0); int failed = 1; diff --git a/lib/rpmte.h b/lib/rpmte.h index 81acf7a19a..8008cdbbc2 100644 --- a/lib/rpmte.h +++ b/lib/rpmte.h @@ -20,6 +20,7 @@ typedef enum rpmElementType_e { TR_ADDED = (1 << 0), /*!< Package will be installed. */ TR_REMOVED = (1 << 1), /*!< Package will be removed. */ TR_RPMDB = (1 << 2), /*!< Package from the rpmdb. */ + TR_RESTORED = (1 << 3), /*!< Package will be restored. */ } rpmElementType; typedef rpmFlags rpmElementTypes; diff --git a/lib/rpmte_internal.h b/lib/rpmte_internal.h index 8a8b197f33..5ac0bdaa49 100644 --- a/lib/rpmte_internal.h +++ b/lib/rpmte_internal.h @@ -10,6 +10,7 @@ typedef enum pkgGoal_e { /* permit using rpmteType() for install + erase goals */ PKG_INSTALL = TR_ADDED, PKG_ERASE = TR_REMOVED, + PKG_RESTORE = TR_RESTORED, /* permit using scriptname for these for now... */ PKG_VERIFY = RPMTAG_VERIFYSCRIPT, PKG_PRETRANS = RPMTAG_PRETRANS, @@ -27,6 +28,7 @@ enum addOp_e { RPMTE_INSTALL = 0, RPMTE_UPGRADE = 1, RPMTE_REINSTALL = 2, + RPMTE_RESTORE = 3, }; #ifdef __cplusplus diff --git a/lib/rpmts.h b/lib/rpmts.h index 3b00563a2a..db632d24fd 100644 --- a/lib/rpmts.h +++ b/lib/rpmts.h @@ -668,6 +668,14 @@ int rpmtsAddInstallElement(rpmts ts, Header h, */ int rpmtsAddReinstallElement(rpmts ts, Header h, const fnpyKey key); +/** \ingroup rpmts + * Add package to be restored to transaction set. + * @param ts transaction set + * @param h header + * @return 0 on success, 1 on error (not installed) + */ +int rpmtsAddRestoreElement(rpmts ts, Header h); + /** \ingroup rpmts * Add package to be erased to transaction set. * @param ts transaction set diff --git a/lib/transaction.c b/lib/transaction.c index 36c2a7a648..0fb7282671 100644 --- a/lib/transaction.c +++ b/lib/transaction.c @@ -703,6 +703,15 @@ assert(otherFi != NULL); /* Otherwise, we can just erase. */ rpmfsSetAction(fs, i, FA_ERASE); break; + case TR_RESTORED: + if (XFA_SKIPPING(rpmfsGetAction(fs, i))) + break; + if (rpmfilesFState(fi, i) != RPMFILE_STATE_NORMAL) { + rpmfsSetAction(fs, i, FA_SKIP); + break; + } + rpmfsSetAction(fs, i, FA_TOUCH); + break; default: break; } diff --git a/python/rpm/transaction.py b/python/rpm/transaction.py index 72b8ba805f..0bb37d07a7 100644 --- a/python/rpm/transaction.py +++ b/python/rpm/transaction.py @@ -112,6 +112,13 @@ def addErase(self, item): if not TransactionSetCore.addErase(self, h): raise rpm.error("adding erasure to transaction failed") + def addRestore(self, item): + hdrs = self._i2hdrs(item) + + for h in hdrs: + if not TransactionSetCore.addErase(self, h): + raise rpm.error("adding restore to transaction failed") + def run(self, callback, data): rc = TransactionSetCore.run(self, callback, data, self._probFilter) diff --git a/python/rpmts-py.c b/python/rpmts-py.c index bb9fcf74ec..96e43258a9 100644 --- a/python/rpmts-py.c +++ b/python/rpmts-py.c @@ -230,6 +230,17 @@ rpmts_AddErase(rpmtsObject * s, PyObject * args) return PyBool_FromLong(rpmtsAddEraseElement(s->ts, h, -1) == 0); } +static PyObject * +rpmts_AddRestore(rpmtsObject * s, PyObject * args) +{ + Header h; + + if (!PyArg_ParseTuple(args, "O&:AddRestore", hdrFromPyObject, &h)) + return NULL; + + return PyBool_FromLong(rpmtsAddRestoreElement(s->ts, h) == 0); +} + static int rpmts_SolveCallback(rpmts ts, rpmds ds, const void * data) { @@ -740,6 +751,8 @@ static struct PyMethodDef rpmts_methods[] = { " mode : optional argument that specifies if this package should be\n\t\tinstalled ('i'), upgraded ('u')"}, {"addReinstall", (PyCFunction) rpmts_AddReinstall, METH_VARARGS, "ts.addReinstall(hdr, data) -- Adds transaction elements\nrepresenting a reinstall of an already installed package.\n\nSee addInstall for details."}, + {"addRestore", (PyCFunction) rpmts_AddRestore, METH_VARARGS, + "ts.addRestore(hdr) -- Adds transaction elements\nrepresenting a restore of an already installed package."}, {"addErase", (PyCFunction) rpmts_AddErase, METH_VARARGS|METH_KEYWORDS, "addErase(name) -- Add a transaction element representing an erase\nof an installed package.\n\n" " name: the package name to be erased"}, diff --git a/rpm.c b/rpm.c index f651c6c6d2..00285a9032 100644 --- a/rpm.c +++ b/rpm.c @@ -19,6 +19,7 @@ enum modes { MODE_INSTALL = (1 << 1), MODE_ERASE = (1 << 2), + MODE_RESTORE = (1 << 4), #define MODES_IE (MODE_INSTALL | MODE_ERASE) MODE_UNKNOWN = 0 @@ -114,13 +115,16 @@ int main(int argc, char *argv[]) (INSTALL_UPGRADE|INSTALL_FRESHEN| INSTALL_INSTALL|INSTALL_REINSTALL)); int eflags = (ia->installInterfaceFlags & INSTALL_ERASE); + int rflags = (ia->installInterfaceFlags & INSTALL_RESTORE); - if (iflags & eflags) + if (iflags & eflags & rflags) argerror(_("only one major mode may be specified")); else if (iflags) bigMode = MODE_INSTALL; else if (eflags) bigMode = MODE_ERASE; + else if (rflags) + bigMode = MODE_RESTORE; } if (!( bigMode == MODE_INSTALL ) && @@ -267,6 +271,10 @@ int main(int argc, char *argv[]) } break; + case MODE_RESTORE: + ec += rpmRestore(ts, ia, (ARGV_const_t) poptGetArgs(optCon)); + break; + case MODE_QUERY: if (!poptPeekArg(optCon) && !(qva->qva_source == RPMQV_ALL)) argerror(_("no arguments given for query")); diff --git a/rpmpopt.in b/rpmpopt.in index d5a6b140b3..4409a04f6e 100644 --- a/rpmpopt.in +++ b/rpmpopt.in @@ -43,36 +43,10 @@ rpm alias --scripts --qf '\ ' \ --POPTdesc=$"list install/erase scriptlets from package(s)" -rpm alias --setperms -q --qf '[\[ -L %{FILENAMES:shescape} \] || \ - \[ -n %{FILELINKTOS:shescape} \] || \ - ( \[ $((%{FILEFLAGS} & 2#1001000)) != 0 \] && \[ ! -e %{FILENAMES:shescape} \] ) || \ - chmod %7{FILEMODES:octal} %{FILENAMES:shescape}\n]' \ - --pipe "grep -v \(none\) | grep '^. -L ' | sed 's/chmod .../chmod /' | sh" \ - --POPTdesc=$"set permissions of files in a package" - -rpm alias --setugids -q --qf \ - '[ch %{FILEUSERNAME:shescape} %{FILEGROUPNAME:shescape} %{FILENAMES:shescape} %{FILEFLAGS}\n]' \ - --pipe "(echo 'ch() { ( \[ $(($4 & 2#1001000)) != 0 \] && \[ ! -e \"$3\" \] ) || \ - (chown -h -- \"$1\" \"$3\";chgrp -h -- \"$2\" \"$3\";) }'; \ - grep '^ch '|grep -v \(none\))|sh" \ - --POPTdesc=$"set user/group ownership of files in a package" - -rpm alias --setcaps -q --qf \ - "[if \[ -f %{FILENAMES:shescape} -a ! -L %{FILENAMES:shescape} \]; then\n\ -%|FILECAPS?{ if \[ -n %{FILECAPS:shescape} \]; then\n\ - setcap %{FILECAPS:shescape} %{FILENAMES:shescape}\n\ - el}:{ }|if \[ -n \"\$(getcap %{FILENAMES:shescape})\" \]; then\n\ - setcap -r %{FILENAMES:shescape}\n\ - fi\n\ -fi\n]" \ - --pipe "sh" \ - --POPTdesc=$"set capabilities of files in a package" - -rpm alias --restore -q --qf \ - '[ rpm --setugids %{NAME:shescape}; \ - rpm --setperms %{NAME:shescape}; \ - rpm --setcaps %{NAME:shescape}; \n]' --pipe "sh" \ - --POPTdesc=$"restore file/directory permissions" +# obsolete +rpm alias --setperms --restore +rpm alias --setugids --restore +rpm alias --setcaps --restore rpm alias --conflicts --qf \ "[%|VERBOSE?{%{CONFLICTFLAGS:deptype}: }:{}|%{CONFLICTNEVRS}\n]" \ diff --git a/tests/rpmpython.at b/tests/rpmpython.at index 977d423bf2..8289d84c61 100644 --- a/tests/rpmpython.at +++ b/tests/rpmpython.at @@ -286,6 +286,28 @@ package not installed addErase mi] ) +RPMPY_TEST([add restore to transaction],[ +ts = rpm.ts() +for i in ['foo', 1234]: + myprint('addRestore %s' % i) + try: + ts.addRestore(i) + except rpm.error as err: + myprint(err) +myprint('addRestore mi') +mi = ts.dbMatch('name', 'foo') +try: + ts.addRestore(mi) +except rpm.error as err: + myprint(err) +], +[addRestore foo +package not installed +addRestore 1234 +package not installed +addRestore mi] +) + RPMPY_TEST([add bogus package to transaction 1],[ ts = rpm.ts() h = rpm.hdr() diff --git a/tests/rpmverify.at b/tests/rpmverify.at index 6500d51547..08e593f934 100644 --- a/tests/rpmverify.at +++ b/tests/rpmverify.at @@ -611,3 +611,23 @@ runroot rpm -Va --nouser --nogroup --nodeps | grep "/bin/hello" [], []) AT_CLEANUP + +AT_SETUP([rpm --restore]) +AT_KEYWORDS([verify restore]) +AT_CHECK([ +RPMDB_INIT + +runroot rpm -U --nodeps --noscripts --ignorearch --ignoreos \ + /data/RPMS/hello-1.0-1.i386.rpm +runroot_other touch /usr/share/doc/hello-1.0/FAQ +runroot_other chmod a-x /usr/local/bin/hello +runroot rpm -Va --nodeps --nouser --nogroup +runroot rpm --restore hello +runroot rpm -Va --nodeps --nouser --nogroup +], +[0], +[.M....... /usr/local/bin/hello +.......T. d /usr/share/doc/hello-1.0/FAQ +], +[]) +AT_CLEANUP