Save backup files with extension .pacsave.n

Teach pacman to save backup files with extension .pacsave.n, where n is a
positive integer. The current backup file shall be saved as <name>.pacsave,
while existing .pacsave.n files will be renamed to <name>.pacsave.n+1

Example:
1. You have subversion installed in your local repo. /etc/conf.d/svnserve
   is a file to be backed up. It contains local modifications

2. You remove subversion from your repo. /etc/conf.d/svnserve is backed up as
   /etc/conf.d/svnserve.pacsave

2. You install subversion again

3. You edit /etc/conf.d/svnserve

4. You remove subversion. The existing /etc/conf.d/svnserve.pacsave is renamed
   to /etc/conf.d/svnserve.pacsave.1 and /etc/conf.d/svnserve is backed up as
   /etc/conf.d/svnserve.pacsave

Signed-off-by: Pang Yan Han <pangyanhan@gmail.com>

Rebased from original email and adjusted for util-common usage.
Signed-off-by: Florian Pritz <bluewind@xinu.at>

Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
Pang Yan Han 2012-08-01 17:34:37 +02:00 committed by Allan McRae
parent 27d9c25ee2
commit b6320de4be
2 changed files with 96 additions and 0 deletions

View file

@ -26,8 +26,11 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <regex.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
/* libalpm */
#include "remove.h"
@ -299,6 +302,78 @@ static int can_remove_file(alpm_handle_t *handle, const alpm_file_t *file,
return 1;
}
static void shift_pacsave(alpm_handle_t *handle, const char *file)
{
DIR *dir = NULL;
struct dirent *ent;
struct stat st;
regex_t reg;
const char *basename;
char *dirname;
char oldfile[PATH_MAX];
char newfile[PATH_MAX];
char regstr[PATH_MAX];
unsigned long log_max = 0;
size_t basename_len;
dirname = mdirname(file);
if(!dirname) {
return;
}
basename = mbasename(file);
basename_len = strlen(basename);
snprintf(regstr, PATH_MAX, "^%s\\.pacsave\\.([[:digit:]]+)$", basename);
if(regcomp(&reg, regstr, REG_EXTENDED | REG_NEWLINE) != 0) {
goto cleanup;
}
dir = opendir(dirname);
if(dir == NULL) {
_alpm_log(handle, ALPM_LOG_ERROR, _("could not open directory: %s: %s\n"),
dirname, strerror(errno));
goto cleanup;
}
while((ent = readdir(dir)) != NULL) {
if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
}
if(regexec(&reg, ent->d_name, 0, 0, 0) == 0) {
unsigned long cur_log;
cur_log = strtoul(ent->d_name + basename_len + strlen(".pacsave."), NULL, 10);
if(cur_log > log_max) {
log_max = cur_log;
}
}
}
/* Shift pacsaves */
unsigned long i;
for(i = log_max + 1; i > 1; i--) {
snprintf(oldfile, PATH_MAX, "%s.pacsave.%lu", file, i-1);
snprintf(newfile, PATH_MAX, "%s.pacsave.%lu", file, i);
rename(oldfile, newfile);
}
snprintf(oldfile, PATH_MAX, "%s.pacsave", file);
if(stat(oldfile, &st) == 0) {
snprintf(newfile, PATH_MAX, "%s.1", oldfile);
rename(oldfile, newfile);
}
regfree(&reg);
cleanup:
free(dirname);
closedir(dir);
}
/**
* @brief Unlink a package file, backing it up if necessary.
*
@ -399,6 +474,7 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *oldpkg,
char *newpath;
size_t len = strlen(file) + 8 + 1;
MALLOC(newpath, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
shift_pacsave(handle, file);
snprintf(newpath, len, "%s.pacsave", file);
if(rename(file, newpath)) {
_alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),

View file

@ -0,0 +1,20 @@
self.description = "Remove a package with a modified file marked for backup and has existing pacsaves"
self.filesystem = ["etc/dummy.conf.pacsave",
"etc/dummy.conf.pacsave.1",
"etc/dummy.conf.pacsave.2"]
p1 = pmpkg("dummy")
p1.files = ["etc/dummy.conf*"]
p1.backup = ["etc/dummy.conf"]
self.addpkg2db("local", p1)
self.args = "-R %s" % p1.name
self.addrule("PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=dummy")
self.addrule("!FILE_EXIST=etc/dummy.conf")
self.addrule("FILE_PACSAVE=etc/dummy.conf")
self.addrule("FILE_EXIST=etc/dummy.conf.pacsave.1")
self.addrule("FILE_EXIST=etc/dummy.conf.pacsave.2")
self.addrule("FILE_EXIST=etc/dummy.conf.pacsave.3")