On Sat, Jul 14, 2012 at 11:21:50PM -0400, Dave Reisner wrote:
This applies to a case such as when /lib is a symlink to /usr/lib. If a package is installed which contains /lib/libfoo.so, pacman will complain if this package is then "fixed" to contain /usr/lib/libfoo.so. Since these have the same effective path and it exists within the same package, ignore the conflict.
Fixes FS#30681.
Signed-off-by: Dave Reisner <dreisner@archlinux.org> ---
So as I discovered last night, this doesn't deal with broken symlinks. For example, if you symlink /bin or /sbin to usr/bin, and then install a package like kmod, you end up with: /usr/bin/modprobe -> ../usr/bin/kmod Which is an orphaned link. The realpath(3) call will fail, and the conflict won't be resolved. Fixing this seems like we need to emulate the behavior of GNU readlink's -m flag, where we simply canonicalize as much as we can (stepping through piece by piece in the path) and then resolving the remainder with heuristics (squeezing successive slashes and handling ../). This is ugly...
Written against maint.
lib/libalpm/conflict.c | 34 ++++++++++++++++++++++++++++++++++ test/pacman/tests/fileconflict013.py | 20 ++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 test/pacman/tests/fileconflict013.py
diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c index d6e5d8c..64dd5b0 100644 --- a/lib/libalpm/conflict.c +++ b/lib/libalpm/conflict.c @@ -314,6 +314,28 @@ void _alpm_fileconflict_free(alpm_fileconflict_t *conflict) FREE(conflict); }
+static const alpm_file_t *_alpm_filelist_contains_resolved(alpm_filelist_t *filelist, + const char *name, const char *root, size_t rootlen) +{ + size_t i; + const alpm_file_t *file; + + if(!filelist) { + return NULL; + } + + for(file = filelist->files, i = 0; i < filelist->count; file++, i++) { + char fullpath[PATH_MAX], abspath[PATH_MAX]; + snprintf(fullpath, sizeof(fullpath), "%s%s", root, file->name); + if(realpath(fullpath, abspath)) { + if(strcmp(&abspath[rootlen], name) == 0) { + return file; + } + } + } + return NULL; +} + const alpm_file_t *_alpm_filelist_contains(alpm_filelist_t *filelist, const char *name) { @@ -579,6 +601,18 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, free(rpath); }
+ /* check if any of the package's own files resolve to the conflict to + * prevent problems with, e.g. /lib/liba.so moving to /usr/lib/liba.so + * when /lib is a symlink to usr/lib */ + if(!resolved_conflict && dbpkg && !S_ISDIR(lsbuf.st_mode)) { + if(_alpm_filelist_contains_resolved(alpm_pkg_get_files(dbpkg), + relative_path, handle->root, rootlen)) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "package contained a realpath resolved to a package file\n"); + resolved_conflict = 1; + } + } + /* is the file unowned and in the backup list of the new package? */ if(!resolved_conflict && _alpm_needbackup(filestr, p1)) { alpm_list_t *local_pkgs = _alpm_db_get_pkgcache(handle->db_local); diff --git a/test/pacman/tests/fileconflict013.py b/test/pacman/tests/fileconflict013.py new file mode 100644 index 0000000..a83923c --- /dev/null +++ b/test/pacman/tests/fileconflict013.py @@ -0,0 +1,20 @@ +self.description = "file->file path change with same effective path (/lib as symlink)" + +lp1 = pmpkg("filesystem", "1.0-1") +lp1.files = ["usr/", + "usr/lib/", + "lib -> usr/lib/"] +self.addpkg2db("local", lp1) + +lp2 = pmpkg("pkg1", "1.0-1") +lp2.files = ["lib/libfoo.so"] +self.addpkg2db("local", lp2) + +sp1 = pmpkg("pkg1", "1.0-2") +sp1.files = ["usr/lib/libfoo.so"] +self.addpkg2db("sync", sp1) + +self.args = "-Su" + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_VERSION=pkg1|1.0-2") -- 1.7.11.2