From b9178e43c355e1b60280937737d96a50f7597c1e Mon Sep 17 00:00:00 2001 From: Pavlina Moravcova Varekova Date: Wed, 19 Apr 2017 18:16:18 +0200 Subject: Add %mutable and %noupdate update policies (#152) %mutable - is defined for files and links. It means update until modified. In more details: - if a file/link is the same as in new package then touch it, - if a file/link is the same as in old package then upgrade it as "normal" file/link, - else do nothing. %noupdate - is defined for all file types used internally by rpm. It is for cases, where packager wants just the initial content, never to be touched by rpm again. In more details: - if the file does not exist, then create it, - if the file exists, then do nothing. --- build/files.c | 4 + lib/rpmfi.c | 136 +++++++++++++++++++++ lib/rpmfi_internal.h | 25 ++++ lib/rpmfiles.h | 2 + lib/transaction.c | 15 ++- tests/data/SPECS/updpolicy.spec | 38 ++++++ tests/rpmi.at | 254 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 tests/data/SPECS/updpolicy.spec diff --git a/build/files.c b/build/files.c index f58569e3e..f0bd242ef 100644 --- a/build/files.c +++ b/build/files.c @@ -836,6 +836,10 @@ static VFA_t const virtualAttrs[] = { { "%license", RPMFILE_LICENSE }, { "%pubkey", RPMFILE_PUBKEY }, { "%missingok", RPMFILE_MISSINGOK }, + { "%mutable", RPMFILE_MUTABLE }, + { "%noupdate", RPMFILE_NOUPDATE }, + { "%updatepolicy(mutable)", RPMFILE_MUTABLE }, + { "%updatepolicy(noupdate)", RPMFILE_NOUPDATE }, { NULL, 0 } }; diff --git a/lib/rpmfi.c b/lib/rpmfi.c index 320296a4d..93970ea05 100644 --- a/lib/rpmfi.c +++ b/lib/rpmfi.c @@ -1089,6 +1089,142 @@ exit: return action; } + +rpmFileAction rpmfilesSetMutableAction(rpmfiles ofi, int oix, + rpmfiles nfi, int nix) +{ + char * fn = rpmfilesFN(nfi, nix); + rpmFileTypes diskWhat; + struct stat sb; + int action = FA_UNKNOWN; + + /* The file doesn't exist on the disk. Create it. */ + if (lstat(fn, &sb)) { + action = FA_CREATE; + goto exit; + } + + diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); + + if ((diskWhat == REG) && + (rpmfiWhatis(rpmfilesFMode(ofi, oix)) == REG) && + (rpmfiWhatis(rpmfilesFMode(nfi, nix)) == REG) ) { + char buffer[1024]; + int oalgo, nalgo; + size_t odiglen, ndiglen; + const unsigned char * odigest, * ndigest; + const char * nfuser, * ofuser; + uid_t uid; + const char * nfgroup, * ofgroup; + gid_t gid; + + /* If the file on disk is identical to the one in new pkg, then touch it */ + if (rpmfilesFSize(nfi, nix) == sb.st_size) { + + if (rpmfilesFMode(nfi, nix) == ((rpm_mode_t)sb.st_mode)) { + + nfuser = rpmfilesFUser(nfi, nix); + if ((nfuser && rpmugUid(nfuser, &uid) == 0) && + (uid == sb.st_uid)) { + + nfgroup = rpmfilesFGroup(nfi, nix); + if ((nfgroup && rpmugGid(nfgroup, &gid) == 0) && + (gid == sb.st_gid)) { + + ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen); + if ((!rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL)) && + (ndigest && memcmp(ndigest, buffer, ndiglen) == 0)) { + action = FA_TOUCH; + goto exit; + } + } + } + } + } + + /* If the file on disk is identical to the one in old pkg, then create it */ + if (rpmfilesFSize(ofi, oix) == sb.st_size) { + + if (rpmfilesFMode(ofi, oix) == ((rpm_mode_t)sb.st_mode)) { + + ofuser = rpmfilesFUser(ofi, oix); + if ((ofuser && rpmugUid(ofuser, &uid) == 0) && + (uid == sb.st_uid)) { + + ofgroup = rpmfilesFGroup(ofi, oix); + if ((ofgroup && rpmugGid(ofgroup, &gid) == 0) && + (gid == sb.st_gid)) { + + odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen); + if ((!rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL)) && + (odigest && memcmp(odigest, buffer, odiglen) == 0)) { + action = FA_CREATE; + goto exit; + } + } + } + } + } + + /* file is changed, let it be */ + action = FA_SKIP; + + } else if ((diskWhat == LINK) && + (rpmfiWhatis(rpmfilesFMode(ofi, oix)) == LINK) && + (rpmfiWhatis(rpmfilesFMode(nfi, nix)) == LINK) ) { + + char buffer[1024]; + const char * oFLink, * nFLink; + + /* if the link on disk is identical to the one in new pkg, then touch it */ + nFLink = rpmfilesFLink(nfi, nix); + + ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1); + if (link_len != -1) { + buffer[link_len] = '\0'; + if (nFLink && rstreq(nFLink, buffer)) { + action = FA_TOUCH; + goto exit; + } + } + + /* if the link on disk is identical to the one in old pkg, then create it */ + oFLink = rpmfilesFLink(ofi, oix); + if ((link_len != -1) && (oFLink && rstreq(oFLink, buffer))) { + action = FA_CREATE; + goto exit; + } + + /* link is changed, let it be */ + action = FA_SKIP; + } + +exit: + free(fn); + return action; + } + + +rpmFileAction rpmfilesSetNoupdateAction(rpmfiles ofi, int oix, + rpmfiles nfi, int nix) +{ + char * fn = rpmfilesFN(nfi, nix); + struct stat sb; + int action = FA_UNKNOWN; + + if (lstat(fn, &sb)) { + /* The file/link/ doesn't exist on the disk. Create it. */ + action = FA_CREATE; + } else { + /* The file exists, let it be. */ + action = FA_SKIP; + } + + free(fn); + return action; + } + + int rpmfilesConfigConflict(rpmfiles fi, int ix) { char * fn = NULL; diff --git a/lib/rpmfi_internal.h b/lib/rpmfi_internal.h index cb3284c0f..804333f2d 100644 --- a/lib/rpmfi_internal.h +++ b/lib/rpmfi_internal.h @@ -73,6 +73,31 @@ rpmFileAction rpmfilesDecideFate(rpmfiles ofi, int oix, rpmfiles nfi, int nix, int skipMissing); + +/** \ingroup rpmfi + * Return action which should be done with the %mutable file + * @param new file info set + * @param new file index + * @param old file info set + * @param old file index + * @return action name + */ +RPM_GNUC_INTERNAL +rpmFileAction rpmfilesSetMutableAction(rpmfiles ofi, int oix, + rpmfiles nfi, int nix); + +/** \ingroup rpmfi + * Return action which should be done with the %noupdate file + * @param new file info set + * @param new file index + * @param old file info set + * @param old file index + * @return action name + */ +RPM_GNUC_INTERNAL +rpmFileAction rpmfilesSetNoupdateAction(rpmfiles ofi, int oix, + rpmfiles nfi, int nix); + RPM_GNUC_INTERNAL int rpmfilesConfigConflict(rpmfiles fi, int ix); diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h index 4dba88fc4..8e92d0c4f 100644 --- a/lib/rpmfiles.h +++ b/lib/rpmfiles.h @@ -60,6 +60,8 @@ enum rpmfileAttrs_e { RPMFILE_README = (1 << 8), /*!< from %%readme */ /* bits 9-10 unused */ RPMFILE_PUBKEY = (1 << 11), /*!< from %%pubkey */ + RPMFILE_MUTABLE = (1 << 12), /*!< from %%mutable or %updatepolicy(mutable) */ + RPMFILE_NOUPDATE = (1 << 13), /*!< from %%noupdate or %updatepolicy(noupdate) */ }; typedef rpmFlags rpmfileAttrs; diff --git a/lib/transaction.c b/lib/transaction.c index d8cd1e6e9..e7efe65c2 100644 --- a/lib/transaction.c +++ b/lib/transaction.c @@ -414,6 +414,7 @@ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfiles fi, int fx { rpmfs fs = rpmteGetFileStates(p); int isCfgFile = ((rpmfilesFFlags(otherFi, ofx) | rpmfilesFFlags(fi, fx)) & RPMFILE_CONFIG); + rpmFileAction action; if (XFA_SKIPPING(rpmfsGetAction(fs, fx))) return; @@ -471,10 +472,22 @@ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfiles fi, int fx /* Determine config file disposition, skipping missing files (if any). */ if (isCfgFile) { int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1); - rpmFileAction action; action = rpmfilesDecideFate(otherFi, ofx, fi, fx, skipMissing); rpmfsSetAction(fs, fx, action); + + } else { + if (rpmfilesFFlags(fi, fx) & RPMFILE_MUTABLE) { + action = rpmfilesSetMutableAction(otherFi, ofx, fi, fx); + rpmfsSetAction(fs, fx, action); + + } else { + if (rpmfilesFFlags(fi, fx) & RPMFILE_NOUPDATE) { + action = rpmfilesSetNoupdateAction(otherFi, ofx, fi, fx); + rpmfsSetAction(fs, fx, action); + } + } } + rpmfilesSetFReplacedSize(fi, fx, rpmfilesFSize(otherFi, ofx)); } diff --git a/tests/data/SPECS/updpolicy.spec b/tests/data/SPECS/updpolicy.spec new file mode 100644 index 000000000..4310e8c23 --- /dev/null +++ b/tests/data/SPECS/updpolicy.spec @@ -0,0 +1,38 @@ +# avoid depending on rpm configuration +%define _sysconfdir /etc + +%{!?filetype: %global filetype file} + +Name: update-policy-test%{?sub:-%{sub}} +Version: %{ver} +Release: 1 +Summary: Testing update policy + +Group: Testing +License: GPL +BuildArch: noarch + +%description +%{summary} + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir} +case %{filetype} in +file) + echo "%{filedata}" > $RPM_BUILD_ROOT/%{_sysconfdir}/policy.conf + ;; +link) + ln -s "%{filedata}" $RPM_BUILD_ROOT/%{_sysconfdir}/policy.conf + ;; +dir) + mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/policy.conf + ;; +esac + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +%{?fileattr} %{_sysconfdir}/policy.conf diff --git a/tests/rpmi.at b/tests/rpmi.at index 2fd7d5b5d..8bfa0ab4c 100644 --- a/tests/rpmi.at +++ b/tests/rpmi.at @@ -354,3 +354,257 @@ runroot rpm -e testdoc [], []) AT_CLEANUP + + +# ------------------------------ +# Test mutable file +AT_SETUP([mutable file - only works as root!]) +AT_KEYWORDS([mutable policy link]) +AT_CHECK([ +AT_XFAIL_IF([test `whoami` != root ]) +RPMDB_CLEAR +RPMDB_INIT +cf="${RPMTEST}"/etc/policy.conf +rm -rf "${cf}" "${cf}".rpm* +rm -rf "${TOPDIR}" + +for v in 1.0 2.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype file" \ + --define "filedata old_contents" \ + --define "fileattr %mutable" \ + /data/SPECS/updpolicy.spec +done + +for v in 3.0 4.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype file" \ + --define "filedata new_contents" \ + --define "fileattr %mutable" \ + /data/SPECS/updpolicy.spec +done + + +#test update mutable file without changes and with changed contents + +runroot rpm -U /build/RPMS/noarch/update-policy-test-1.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-2.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-3.0-1.noarch.rpm +cat "${cf}" +echo "CHANGE" > "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-4.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -e update-policy-test +test ! -f "${cf}" && echo OK1 + + +#test update mutable file with changed mode +runroot rpm -U /build/RPMS/noarch/update-policy-test-2.0-1.noarch.rpm +cat "${cf}" +chmod a+x "${cf}" +runroot rpm -U /build/RPMS/noarch/update-policy-test-3.0-1.noarch.rpm +cat "${cf}" +runroot rpm -e update-policy-test +test ! -f "${cf}" && echo OK2 +], +[], +[old_contents +old_contents +new_contents +CHANGE +OK1 +old_contents +old_contents +OK2 +], +[]) +AT_CLEANUP + + +# ------------------------------ +# Test mutable link +AT_SETUP([mutable link]) +AT_KEYWORDS([mutable policy link]) +AT_CHECK([ +RPMDB_CLEAR +RPMDB_INIT +cf="${RPMTEST}"/etc/policy.conf +rm -rf "${cf}" "${cf}".rpm* +rm -rf "${TOPDIR}" + +for v in 1.0 2.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype link" \ + --define "filedata old_contents" \ + --define "fileattr %mutable" \ + /data/SPECS/updpolicy.spec +done + +for v in 3.0 4.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype link" \ + --define "filedata new_contents" \ + --define "fileattr %mutable" \ + /data/SPECS/updpolicy.spec +done + + +#test update mutable link without changes and with changed contents + +runroot rpm -U /build/RPMS/noarch/update-policy-test-1.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-2.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-3.0-1.noarch.rpm +readlink "${cf}" +ln -sf "CHANGE" "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-4.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -e update-policy-test +test ! -L "${cf}" && echo OK1 +], +[], +[old_contents +old_contents +new_contents +CHANGE +OK1 +], +[]) +AT_CLEANUP + + + + + +# ------------------------------ +# Test noupdate file +AT_SETUP([noupdate file]) +AT_KEYWORDS([noupdate policy link]) +AT_CHECK([ +RPMDB_CLEAR +RPMDB_INIT +cf="${RPMTEST}"/etc/policy.conf +rm -rf "${cf}" "${cf}".rpm* +rm -rf "${TOPDIR}" + +for v in 1.0 2.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype file" \ + --define "filedata old_contents" \ + --define "fileattr %noupdate" \ + /data/SPECS/updpolicy.spec +done + +for v in 3.0 4.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype file" \ + --define "filedata new_contents" \ + --define "fileattr %noupdate" \ + /data/SPECS/updpolicy.spec +done + + +#test update mutable file without changes and with changed contents + +runroot rpm -U /build/RPMS/noarch/update-policy-test-1.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-2.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-3.0-1.noarch.rpm +cat "${cf}" +echo "CHANGE" > "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-4.0-1.noarch.rpm +cat "${cf}" + +runroot rpm -e update-policy-test +test ! -f "${cf}" && echo OK1 + +], +[], +[old_contents +old_contents +old_contents +CHANGE +OK1 +], +[]) +AT_CLEANUP + + +# ------------------------------ +# Test noupdate link +AT_SETUP([noupdate link]) +AT_KEYWORDS([noupdate policy link]) +AT_CHECK([ +RPMDB_CLEAR +RPMDB_INIT +cf="${RPMTEST}"/etc/policy.conf +rm -rf "${cf}" "${cf}".rpm* +rm -rf "${TOPDIR}" + +for v in 1.0 2.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype link" \ + --define "filedata old_contents" \ + --define "fileattr %noupdate" \ + /data/SPECS/updpolicy.spec +done + +for v in 3.0 4.0; do + runroot rpmbuild --quiet -bb \ + --define "ver ${v}" \ + --define "filetype link" \ + --define "filedata new_contents" \ + --define "fileattr %noupdate" \ + /data/SPECS/updpolicy.spec +done + + +#test update mutable link without changes and with changed contents + +runroot rpm -U /build/RPMS/noarch/update-policy-test-1.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-2.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-3.0-1.noarch.rpm +readlink "${cf}" +ln -sf "CHANGE" "${cf}" + +runroot rpm -U /build/RPMS/noarch/update-policy-test-4.0-1.noarch.rpm +readlink "${cf}" + +runroot rpm -e update-policy-test +test ! -L "${cf}" && echo OK1 +], +[], +[old_contents +old_contents +old_contents +CHANGE +OK1 +], +[]) +AT_CLEANUP -- cgit v1.2.1