summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPanu Matilainen <pmatilai@redhat.com>2020-08-27 10:31:07 +0300
committerPanu Matilainen <pmatilai@redhat.com>2020-12-10 13:28:07 +0200
commitba0f1bc146a0495171837b074255f2eb79670022 (patch)
treeb41e534476b677fc9cd50ea13cd70eb05221de4c
parent24c10a49078e679d7ccf3f1d4fe656d4e5f2c1f0 (diff)
downloadrpm-ba0f1bc146a0495171837b074255f2eb79670022.tar.gz
Upgrade FA_TOUCH to FA_CREATE if the file went away (RhBug:1872141)
When %_minimize_writes is enabled, we determine unchanged files during fingerprinting and only update their metadata (FA_TOUCH) instead of always recreating from scratch (FA_CREATE) during install. However package scriptlets (and administrators) can and will do arbitrary stuff in the meanwhile, such as rm -f their own files in %pre, hoping to get a fresh copy of contents no matter what. Or something. Now, if the file was determined to not need changing by rpm, this will just fail with chown & friends trying to touch non-existent file. One can consider this a case of package shooting itself in the foot, but when a package update fails or succeeds depending on %_minimize_writes this to me suggests the feature is at fault as much as the package. Do fsmVerify() on all files to be FA_TOUCH'ed to detect files whose type changed or were removed since fingerprinting. This still doesn't ensure correctness if something tampers with the contents in the meanwhile, (for that we'd need to run the file through the whole machinery again, checksumming and all) but covers the most glaring cases. (cherry picked from commit 886c24cfc6c0fec90d8db1406a0e32c0e09e92c9)
-rw-r--r--lib/fsm.c15
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/data/SPECS/suicidal.spec19
-rw-r--r--tests/rpmi.at39
4 files changed, 70 insertions, 4 deletions
diff --git a/lib/fsm.c b/lib/fsm.c
index 5fb507993..08980d49b 100644
--- a/lib/fsm.c
+++ b/lib/fsm.c
@@ -902,10 +902,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
if (!skip) {
int setmeta = 1;
- /* When touching we don't need any of this... */
- if (action == FA_TOUCH)
- goto touch;
-
/* Directories replacing something need early backup */
if (!suffix) {
rc = fsmBackup(fi, action);
@@ -917,6 +913,17 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
rc = RPMERR_ENOENT;
}
+ /* See if the file was removed while our attention was elsewhere */
+ if (rc == RPMERR_ENOENT && action == FA_TOUCH) {
+ rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", fpath);
+ action = FA_CREATE;
+ fsmDebug(fpath, action, &sb);
+ }
+
+ /* When touching we don't need any of this... */
+ if (action == FA_TOUCH)
+ goto touch;
+
if (S_ISREG(sb.st_mode)) {
if (rc == RPMERR_ENOENT) {
rc = fsmMkfile(fi, fpath, files, psm, nodigest,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f7f63a91a..f742a9e1d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -73,6 +73,7 @@ EXTRA_DIST += data/SPECS/scriptfail.spec
EXTRA_DIST += data/SPECS/scriptfile.spec
EXTRA_DIST += data/SPECS/selfconflict.spec
EXTRA_DIST += data/SPECS/shebang.spec
+EXTRA_DIST += data/SPECS/suicidal.spec
EXTRA_DIST += data/SPECS/replacetest.spec
EXTRA_DIST += data/SPECS/triggers.spec
EXTRA_DIST += data/SPECS/filetriggers.spec
diff --git a/tests/data/SPECS/suicidal.spec b/tests/data/SPECS/suicidal.spec
new file mode 100644
index 000000000..77d17d8c9
--- /dev/null
+++ b/tests/data/SPECS/suicidal.spec
@@ -0,0 +1,19 @@
+Name: suicidal
+Version: 1
+Release: %{rel}
+License: GPL
+Group: Testing
+Summary: Testing suicidal package behavior
+BuildArch: noarch
+
+%description
+
+%build
+mkdir -p %{buildroot}/opt
+echo shoot > %{buildroot}/opt/foot
+
+%pre -p <lua>
+os.remove('/opt/foot')
+
+%files
+/opt/foot
diff --git a/tests/rpmi.at b/tests/rpmi.at
index a55a50116..42dc52ba3 100644
--- a/tests/rpmi.at
+++ b/tests/rpmi.at
@@ -683,3 +683,42 @@ test -e ${RPMTEST}/opt/vattrtest/a || exit 1
[],
[])
AT_CLEANUP
+
+AT_SETUP([rpm -U <suicidal>])
+AT_KEYWORDS([install])
+RPMDB_INIT
+
+for r in 1 2; do
+ runroot rpmbuild -bb --quiet \
+ --define "rel ${r}" \
+ /data/SPECS/suicidal.spec
+done
+
+AT_CHECK([
+RPMDB_INIT
+
+for r in 1 2; do
+ runroot rpm -U \
+ --define "_minimize_writes 0" \
+ /build/RPMS/noarch/suicidal-1-${r}.noarch.rpm
+done
+runroot rpm -V --nouser --nogroup suicidal
+],
+[0],
+[],
+[])
+
+AT_CHECK([
+RPMDB_INIT
+
+for r in 1 2; do
+ runroot rpm -U \
+ --define "_minimize_writes 1" \
+ /build/RPMS/noarch/suicidal-1-${r}.noarch.rpm
+done
+runroot rpm -V --nouser --nogroup suicidal
+],
+[0],
+[],
+[])
+AT_CLEANUP