Skip to content

Commit

Permalink
Reimplement --restore as a transaction element, obsolete --setperms & co
Browse files Browse the repository at this point in the history
--setperms, --setugids and --setcaps were fun demos of alias capabilities
in the nineties, but they can be downright dangerous when used
separately, are blisfully unaware of all state in rpm yet try to
duplicate functionality existing in C, and thus are a constant source
of bugs that are between hard to impossible to fix in the alias space.

Add a new transaction element type for the restore operation, wire
through all the necessary places. In places (like ordering) this is
an overkill but otherwise it seems like a natural thing to be able
to process restore alongside package install/remove. The restore
operation is a cross between install and erase codepath-wise so touches
some funny places, but FA_TOUCH does just the thing, and now all the
regular disablers like --nocontext and --nocaps can be used if
necessary, plugins get to do their work and also timestamps are
restored.

Remove the dangerous shell implementations of things and just make them
aliases to --restore.

Fixes: rpm-software-management#965
  • Loading branch information
pmatilai committed Feb 3, 2022
1 parent 99f8997 commit 2a8d89a
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 68 deletions.
34 changes: 6 additions & 28 deletions docs/man/rpm.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------
Expand Down Expand Up @@ -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
----------------
Expand Down
16 changes: 16 additions & 0 deletions lib/depends.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion lib/fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions lib/order.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/poptI.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ struct poptOption rpmInstallPoptTable[] = {
&rpmIArgs.installInterfaceFlags, (INSTALL_REINSTALL|INSTALL_INSTALL),
N_("reinstall package(s)"),
N_("<packagefile>+") },
{ "restore", '\0', POPT_BIT_SET,
&rpmIArgs.installInterfaceFlags, (INSTALL_RESTORE),
N_("restore package(s)"),
N_("<packagefile>+") },

POPT_TABLEEND
};
35 changes: 35 additions & 0 deletions lib/psm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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:
Expand Down
10 changes: 10 additions & 0 deletions lib/rpmcli.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
40 changes: 38 additions & 2 deletions lib/rpminstall.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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)
{
Expand Down
23 changes: 17 additions & 6 deletions lib/rpmte.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 "???";
}
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/rpmte.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions lib/rpmte_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -27,6 +28,7 @@ enum addOp_e {
RPMTE_INSTALL = 0,
RPMTE_UPGRADE = 1,
RPMTE_REINSTALL = 2,
RPMTE_RESTORE = 3,
};

#ifdef __cplusplus
Expand Down
8 changes: 8 additions & 0 deletions lib/rpmts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions lib/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions python/rpm/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading

0 comments on commit 2a8d89a

Please sign in to comment.