pkgadd: apply rejection rules against old files when upgrading

This commit is contained in:
John McQuah 2024-09-21 22:55:00 +00:00
parent e2a57cd1a4
commit 95944344b4
4 changed files with 69 additions and 11 deletions

View File

@ -99,17 +99,19 @@ void pkgadd::run(int argc, char** argv)
}
}
set<string> keep_list;
set<string> keep_new;
if (o_upgrade) {
keep_list = make_keep_list(package.second.files, config_rules);
db_rm_pkg(package.first, keep_list);
keep_new = make_keep_list(package.second.files, config_rules);
set<string> files_old = packages[package.first].files;
set<string> keep_old = make_keep_list(files_old, config_rules);
db_rm_pkg(package.first, keep_old, keep_new);
}
db_add_pkg(package.first, package.second);
db_commit();
try {
pkg_install(o_package, keep_list, non_install_files, installed);
pkg_install(o_package, keep_new, non_install_files, installed);
} catch (runtime_error&) {
if (!installed) {
db_rm_pkg(package.first);

View File

@ -22,6 +22,7 @@
#include "pkgutil.h"
#include <iostream>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <algorithm>
@ -199,7 +200,12 @@ void pkgutil::db_rm_pkg(const string& name)
}
}
void pkgutil::db_rm_pkg(const string& name, const set<string>& keep_list)
/* Three-argument db_rm_pkg (to address FS#1074)
* arg1: name of the package being upgraded
* arg2: keep list from the old version of the package
* arg3: keep list from the new version of the package */
void pkgutil::db_rm_pkg(const string& name, const set<string>& keep_old,
const set<string>& keep_new)
{
set<string> files = packages[name].files;
packages.erase(name);
@ -210,9 +216,33 @@ void pkgutil::db_rm_pkg(const string& name, const set<string>& keep_list)
cerr << endl;
#endif
// Don't delete files found in the keep list
for (set<string>::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i)
files.erase(*i);
// Files common to both old and new packages, matching an "UPGRADE NO"
// rule, can be left in place (won't be clobbered later by pkg_install).
// Files that only exist in the old package will be stashed in reject_dir.
const string reject_dir = trim_filename(root + string("/") + string(PKG_REJECTED));
for (set<string>::const_iterator i = keep_old.begin(); i != keep_old.end(); ++i) {
// Exempt directories, which might be shared among other ports,
// but if db_rm_files empties them out, allow them to be deleted.
if ((*i)[i->length()-1] == '/') {
continue;
} else {
files.erase(*i);
}
if ( keep_new.find(*i) == keep_new.end() ) {
const string filename = root + *i;
const string savname = trim_filename(reject_dir + filename);
char* savpath = strdup(const_cast<char*>( savname.c_str() ));
const string savdir = dirname(savpath);
std::filesystem::create_directories(savdir);
if ( file_exists(filename) &&
rename(filename.c_str(),savname.c_str()) == -1 ) {
const char* msg = strerror(errno);
cerr << utilname << ": could not rename " << filename
<< ": " << msg << endl;
}
}
}
#ifndef NDEBUG
cerr << "Removing package phase 2 (files that is in the keep list excluded):" << endl;

View File

@ -65,7 +65,7 @@ protected:
void db_add_pkg(const string& name, const pkginfo_t& info);
bool db_find_pkg(const string& name);
void db_rm_pkg(const string& name);
void db_rm_pkg(const string& name, const set<string>& keep_list);
void db_rm_pkg(const string& name, const set<string>& keep_old, const set<string>& keep_new);
void db_rm_files(set<string> files, const set<string>& keep_list);
set<string> db_find_conflicts(const string& name, const pkginfo_t& info);

View File

@ -155,6 +155,32 @@ diff_menu() {
: > "$TMPFILE"
}
relic_menu() {
while true; do
info "$(basename "$1") has been disowned by the package that installed it."
file "$1"
while true; do
info_n "[R]estore [M]ove to other location [D]elete? "
read -n1 CMD
echo
case "$CMD" in
m|M) info_n "New (absolute) path?"
read DEST
mv "$1" "$DEST" || { info "unable to write to $DEST"; break 1; }
break 2
;;
r|R) mv "$1" "${1##$REJECTED_DIR}" || { info "unable to restore ${1##$REJECTED_DIR}"; break 1; }
break 2
;;
d|D) rm -f "$1"
break 2
;;
esac
done
done
}
file_menu() {
while true; do
info "$1"
@ -253,9 +279,9 @@ main() {
for REJECTED_FILE in $(find $REJECTED_DIR ! -type d); do
INSTALLED_FILE="$REJMERGE_ROOT${REJECTED_FILE##$REJECTED_DIR}"
# Remove rejected file if there is no installed version
# If there is no copy on sysroot, the file is probably stale
if [ ! -e "$INSTALLED_FILE" ]; then
rm -f "$REJECTED_FILE"
relic_menu "$REJECTED_FILE"
continue
fi