summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Gregory <andrew.gregory.8@gmail.com>2017-04-10 02:42:01 +0200
committerAllan McRae <allan@archlinux.org>2017-04-12 03:47:33 +0200
commit04d211effa8d65020887112ee30c7b3b0fc28ad3 (patch)
tree9939b65851a6fc5bb6a0863d286b8da0d4876e74
parent86f5c74694cb0403eaf11fa4734f307a0df345cb (diff)
downloadpacman-04d211effa8d65020887112ee30c7b3b0fc28ad3.tar.gz
pacman-04d211effa8d65020887112ee30c7b3b0fc28ad3.tar.xz
add --overwrite option to ignore file conflicts
Allows for safer, more fine-grained control for overwriting files than --force's all-or-nothing approach. Implements FS#31549. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> Signed-off-by: Allan McRae <allan@archlinux.org>
-rw-r--r--doc/pacman.8.txt12
-rw-r--r--lib/libalpm/alpm.h5
-rw-r--r--lib/libalpm/conflict.c14
-rw-r--r--lib/libalpm/handle.c21
-rw-r--r--lib/libalpm/handle.h1
-rw-r--r--src/pacman/conf.c2
-rw-r--r--src/pacman/conf.h2
-rw-r--r--src/pacman/pacman.c6
-rw-r--r--test/pacman/tests/TESTS3
-rw-r--r--test/pacman/tests/overwrite-files-match-negated.py13
-rw-r--r--test/pacman/tests/overwrite-files-match.py13
-rw-r--r--test/pacman/tests/overwrite-files-nonmatch.py13
12 files changed, 101 insertions, 4 deletions
diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt
index 7b5a17e3..2586c1fe 100644
--- a/doc/pacman.8.txt
+++ b/doc/pacman.8.txt
@@ -270,6 +270,18 @@ Upgrade Options (apply to '-S' and '-U')[[UO]]
*\--needed*::
Do not reinstall the targets that are already up-to-date.
+*\--overwrite* <glob>::
+ Bypass file conflict checks and overwrite conflicting files. If the
+ package that is about to be installed contains files that are already
+ installed and match 'glob', this option will cause all those files to be
+ overwritten. Using '\--overwrite' will not allow overwriting a directory
+ with a file or installing packages with conflicting files and directories.
+ Multiple patterns can be specified by separating them with a comma. May be
+ specified multiple times. Patterns can be negated, such that files
+ matching them will not be overwritten, by prefixing them with an
+ exclamation mark. Subsequent matches will override previous ones. A leading
+ literal exclamation mark or backslash needs to be escaped.
+
Query Options (apply to '-Q')[[QO]]
-----------------------------------
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 448d42d2..2637a055 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -830,6 +830,11 @@ int alpm_option_add_hookdir(alpm_handle_t *handle, const char *hookdir);
int alpm_option_remove_hookdir(alpm_handle_t *handle, const char *hookdir);
/** @} */
+alpm_list_t *alpm_option_get_overwrite_files(alpm_handle_t *handle);
+int alpm_option_set_overwrite_files(alpm_handle_t *handle, alpm_list_t *globs);
+int alpm_option_add_overwrite_file(alpm_handle_t *handle, const char *glob);
+int alpm_option_remove_overwrite_file(alpm_handle_t *handle, const char *glob);
+
/** Returns the logfile name. */
const char *alpm_option_get_logfile(alpm_handle_t *handle);
/** Sets the logfile name. */
diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c
index b1666dfe..0e37419f 100644
--- a/lib/libalpm/conflict.c
+++ b/lib/libalpm/conflict.c
@@ -385,6 +385,12 @@ static alpm_list_t *alpm_db_find_file_owners(alpm_db_t* db, const char *path)
return owners;
}
+static int _alpm_can_overwrite_file(alpm_handle_t *handle, const char *path)
+{
+ return handle->trans->flags & ALPM_TRANS_FLAG_FORCE
+ || _alpm_fnmatch_patterns(handle->overwrite_files, path) == 0;
+}
+
/**
* @brief Find file conflicts that may occur during the transaction.
*
@@ -448,8 +454,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
/* can skip file-file conflicts when forced *
* checking presence in p2_files detects dir-file or file-dir
* conflicts as the path from p1 is returned */
- if((handle->trans->flags & ALPM_TRANS_FLAG_FORCE) &&
- alpm_filelist_contains(p2_files, filename)) {
+ if(_alpm_can_overwrite_file(handle, filename)
+ && alpm_filelist_contains(p2_files, filename)) {
_alpm_log(handle, ALPM_LOG_DEBUG,
"%s exists in both '%s' and '%s'\n", filename,
p1->name, p2->name);
@@ -654,8 +660,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
}
/* skip file-file conflicts when being forced */
- if((handle->trans->flags & ALPM_TRANS_FLAG_FORCE) &&
- !S_ISDIR(lsbuf.st_mode)) {
+ if(!S_ISDIR(lsbuf.st_mode)
+ && _alpm_can_overwrite_file(handle, filestr)) {
_alpm_log(handle, ALPM_LOG_DEBUG,
"conflict with file on filesystem being forced\n");
resolved_conflict = 1;
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
index f08f614c..b6b27881 100644
--- a/lib/libalpm/handle.c
+++ b/lib/libalpm/handle.c
@@ -283,6 +283,12 @@ alpm_list_t SYMEXPORT *alpm_option_get_ignoregroups(alpm_handle_t *handle)
return handle->ignoregroup;
}
+alpm_list_t SYMEXPORT *alpm_option_get_overwrite_files(alpm_handle_t *handle)
+{
+ CHECK_HANDLE(handle, return NULL);
+ return handle->overwrite_files;
+}
+
alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
{
CHECK_HANDLE(handle, return NULL);
@@ -657,6 +663,21 @@ int SYMEXPORT alpm_option_remove_ignoregroup(alpm_handle_t *handle, const char *
return _alpm_option_strlist_rem(handle, &(handle->ignoregroup), grp);
}
+int SYMEXPORT alpm_option_add_overwrite_file(alpm_handle_t *handle, const char *glob)
+{
+ return _alpm_option_strlist_add(handle, &(handle->overwrite_files), glob);
+}
+
+int SYMEXPORT alpm_option_set_overwrite_files(alpm_handle_t *handle, alpm_list_t *globs)
+{
+ return _alpm_option_strlist_set(handle, &(handle->overwrite_files), globs);
+}
+
+int SYMEXPORT alpm_option_remove_overwrite_file(alpm_handle_t *handle, const char *glob)
+{
+ return _alpm_option_strlist_rem(handle, &(handle->overwrite_files), glob);
+}
+
int SYMEXPORT alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep)
{
alpm_depend_t *depcpy;
diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
index 223eac12..115c3481 100644
--- a/lib/libalpm/handle.h
+++ b/lib/libalpm/handle.h
@@ -84,6 +84,7 @@ struct __alpm_handle_t {
char *gpgdir; /* Directory where GnuPG files are stored */
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
alpm_list_t *hookdirs; /* Paths to hook directories */
+ alpm_list_t *overwrite_files; /* Paths that may be overwritten */
/* package lists */
alpm_list_t *noupgrade; /* List of packages NOT to be upgraded */
diff --git a/src/pacman/conf.c b/src/pacman/conf.c
index 49b21366..261c8213 100644
--- a/src/pacman/conf.c
+++ b/src/pacman/conf.c
@@ -783,6 +783,8 @@ static int setup_libalpm(void)
alpm_option_set_cachedirs(handle, config->cachedirs);
}
+ alpm_option_set_overwrite_files(handle, config->overwrite_files);
+
alpm_option_set_default_siglevel(handle, config->siglevel);
config->localfilesiglevel = merge_siglevel(config->siglevel,
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index bd8cd77a..e67f7c51 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -123,6 +123,7 @@ typedef struct __config_t {
alpm_list_t *assumeinstalled;
alpm_list_t *noupgrade;
alpm_list_t *noextract;
+ alpm_list_t *overwrite_files;
char *xfercommand;
/* our connection to libalpm */
@@ -172,6 +173,7 @@ enum {
OP_GPGDIR,
OP_DBONLY,
OP_FORCE,
+ OP_OVERWRITE_FILES,
OP_COLOR,
OP_DBPATH,
OP_CASCADE,
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index fe96cb3b..e0e7d01d 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -190,6 +190,8 @@ static void usage(int op, const char * const myname)
case PM_OP_SYNC:
case PM_OP_UPGRADE:
addlist(_(" --force force install, overwrite conflicting files\n"));
+ addlist(_(" --overwrite <path>\n"
+ " overwrite conflicting files (can be used more than once)\n"));
addlist(_(" --asdeps install packages as non-explicitly installed\n"));
addlist(_(" --asexplicit install packages as explicitly installed\n"));
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
@@ -708,6 +710,9 @@ static int parsearg_upgrade(int opt)
case OP_FORCE:
config->flags |= ALPM_TRANS_FLAG_FORCE;
break;
+ case OP_OVERWRITE_FILES:
+ parsearg_util_addlist(&(config->overwrite_files));
+ break;
case OP_ASDEPS:
config->flags |= ALPM_TRANS_FLAG_ALLDEPS;
break;
@@ -929,6 +934,7 @@ static int parseargs(int argc, char *argv[])
{"assume-installed", required_argument, 0, OP_ASSUMEINSTALLED},
{"debug", optional_argument, 0, OP_DEBUG},
{"force", no_argument, 0, OP_FORCE},
+ {"overwrite", required_argument, 0, OP_OVERWRITE_FILES},
{"noprogressbar", no_argument, 0, OP_NOPROGRESSBAR},
{"noscriptlet", no_argument, 0, OP_NOSCRIPTLET},
{"ask", required_argument, 0, OP_ASK},
diff --git a/test/pacman/tests/TESTS b/test/pacman/tests/TESTS
index 11d1c38c..eadb63b4 100644
--- a/test/pacman/tests/TESTS
+++ b/test/pacman/tests/TESTS
@@ -80,6 +80,9 @@ TESTS += test/pacman/tests/mode001.py
TESTS += test/pacman/tests/mode002.py
TESTS += test/pacman/tests/mode003.py
TESTS += test/pacman/tests/noupgrade-inverted.py
+TESTS += test/pacman/tests/overwrite-files-match-negated.py
+TESTS += test/pacman/tests/overwrite-files-match.py
+TESTS += test/pacman/tests/overwrite-files-nonmatch.py
TESTS += test/pacman/tests/pacman001.py
TESTS += test/pacman/tests/pacman002.py
TESTS += test/pacman/tests/pacman003.py
diff --git a/test/pacman/tests/overwrite-files-match-negated.py b/test/pacman/tests/overwrite-files-match-negated.py
new file mode 100644
index 00000000..42b1be2d
--- /dev/null
+++ b/test/pacman/tests/overwrite-files-match-negated.py
@@ -0,0 +1,13 @@
+self.description = "Install a package with an existing file matching a negated --overwrite pattern"
+
+p = pmpkg("dummy")
+p.files = ["foobar"]
+self.addpkg(p)
+
+self.filesystem = ["foobar*"]
+
+self.args = "-U --overwrite=foobar --overwrite=!foo* %s" % p.filename()
+
+self.addrule("!PACMAN_RETCODE=0")
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_MODIFIED=foobar")
diff --git a/test/pacman/tests/overwrite-files-match.py b/test/pacman/tests/overwrite-files-match.py
new file mode 100644
index 00000000..004155c3
--- /dev/null
+++ b/test/pacman/tests/overwrite-files-match.py
@@ -0,0 +1,13 @@
+self.description = "Install a package with an existing file matching an --overwrite pattern"
+
+p = pmpkg("dummy")
+p.files = ["foobar"]
+self.addpkg(p)
+
+self.filesystem = ["foobar*"]
+
+self.args = "-U --overwrite=foobar %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("FILE_MODIFIED=foobar")
diff --git a/test/pacman/tests/overwrite-files-nonmatch.py b/test/pacman/tests/overwrite-files-nonmatch.py
new file mode 100644
index 00000000..38932d5f
--- /dev/null
+++ b/test/pacman/tests/overwrite-files-nonmatch.py
@@ -0,0 +1,13 @@
+self.description = "Install a package with an existing file not matching --overwrite patterns"
+
+p = pmpkg("dummy")
+p.files = ["foobar"]
+self.addpkg(p)
+
+self.filesystem = ["foobar*"]
+
+self.args = "-U --overwrite=foo %s" % p.filename()
+
+self.addrule("!PACMAN_RETCODE=0")
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_MODIFIED=foobar")