In the event of a symlink<->dir transition, the paths for the incoming package need to be resolved to match the filelist for installed packages. Note: this only enables the symlink<->dir transition itself. Any installed packages with invalid file lists containing symlinks are not detected. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- lib/libalpm/conflict.c | 42 ++++++++++++++++------------------- src/common/util-common.c | 43 ++++++++++++++++++++++++++++++++++++ src/common/util-common.h | 1 + test/pacman/tests/fileconflict007.py | 2 -- test/pacman/tests/sync701.py | 2 -- test/pacman/tests/sync702.py | 2 -- 6 files changed, 63 insertions(+), 29 deletions(-) diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c index 518a2c6..08c1cbe 100644 --- a/lib/libalpm/conflict.c +++ b/lib/libalpm/conflict.c @@ -501,7 +501,7 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, /* have we acted on this conflict? */ int resolved_conflict = 0; struct stat lsbuf; - char path[PATH_MAX]; + char path[PATH_MAX], resolved_path[PATH_MAX]; size_t pathlen; pathlen = snprintf(path, PATH_MAX, "%s%s", handle->root, filestr); @@ -513,7 +513,7 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, _alpm_log(handle, ALPM_LOG_DEBUG, "checking possible conflict: %s\n", path); - if(filestr[strlen(filestr) - 1] == '/') { + if(path[pathlen - 1] == '/') { if(S_ISDIR(lsbuf.st_mode)) { _alpm_log(handle, ALPM_LOG_DEBUG, "file is a directory, not a conflict\n"); continue; @@ -524,7 +524,19 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, path[pathlen - 1] = '\0'; } - relative_path = path + rootlen; + if(lrealpath(path, resolved_path) + && strncmp(resolved_path, handle->root, rootlen) == 0) { + relative_path = resolved_path + rootlen; + } else { + relative_path = path + rootlen; + } + + if(!resolved_conflict && dbpkg + && alpm_filelist_contains(alpm_pkg_get_files(dbpkg), relative_path)) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "package contained the resolved realpath\n"); + resolved_conflict = 1; + } /* Check remove list (will we remove the conflicting local file?) */ for(k = rem; k && !resolved_conflict; k = k->next) { @@ -546,12 +558,12 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, alpm_pkg_t *localp2 = _alpm_db_get_pkgfromcache(handle->db_local, p2->name); /* localp2->files will be removed (target conflicts are handled by CHECK 1) */ - if(localp2 && alpm_filelist_contains(alpm_pkg_get_files(localp2), filestr)) { + if(localp2 && alpm_filelist_contains(alpm_pkg_get_files(localp2), relative_path)) { /* skip removal of file, but not add. this will prevent a second * package from removing the file when it was already installed * by its new owner (whether the file is in backup array or not */ handle->trans->skip_remove = - alpm_list_add(handle->trans->skip_remove, strdup(filestr)); + alpm_list_add(handle->trans->skip_remove, strdup(relative_path)); _alpm_log(handle, ALPM_LOG_DEBUG, "file changed packages, adding to remove skiplist\n"); resolved_conflict = 1; @@ -560,8 +572,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, /* check if all files of the dir belong to the installed pkg */ if(!resolved_conflict && S_ISDIR(lsbuf.st_mode) && dbpkg) { - char *dir = malloc(strlen(filestr) + 2); - sprintf(dir, "%s/", filestr); + char *dir = malloc(strlen(relative_path) + 2); + sprintf(dir, "%s/", relative_path); if(alpm_filelist_contains(alpm_pkg_get_files(dbpkg), dir)) { _alpm_log(handle, ALPM_LOG_DEBUG, "checking if all files in %s belong to %s\n", @@ -571,22 +583,6 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, free(dir); } - /* check if a component of the filepath was a link. canonicalize the path - * and look for it in the old package. note that the actual file under - * consideration cannot itself be a link, as it might be unowned- path - * components can be safely checked as all directories are "unowned". */ - if(!resolved_conflict && dbpkg && !S_ISLNK(lsbuf.st_mode)) { - char rpath[PATH_MAX]; - if(realpath(path, rpath)) { - const char *relative_rpath = rpath + rootlen; - if(alpm_filelist_contains(alpm_pkg_get_files(dbpkg), relative_rpath)) { - _alpm_log(handle, ALPM_LOG_DEBUG, - "package contained the resolved realpath\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/src/common/util-common.c b/src/common/util-common.c index 7145e49..c2422e0 100644 --- a/src/common/util-common.c +++ b/src/common/util-common.c @@ -73,6 +73,49 @@ char *mdirname(const char *path) return strdup("."); } +/** Resolve the canonicalized absolute path of a symlink. + * @param path path to resolve + * @param resolved_path destination for the resolved path, will be malloc'd if + * NULL + * @return the resolved path + */ +char *lrealpath(const char *path, char *resolved_path) +{ + const char *bname = mbasename(path); + char *rpath = NULL, *dname = NULL; + int success = 0; + + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { + /* the entire path needs to be resolved */ + return realpath(path, resolved_path); + } + + if(!(dname = mdirname(path))) { + goto cleanup; + } + if(!(rpath = realpath(dname, NULL))) { + goto cleanup; + } + if(!resolved_path) { + if(!(resolved_path = malloc(strlen(rpath) + strlen(bname) + 2))) { + goto cleanup; + } + } + + strcpy(resolved_path, rpath); + if(resolved_path[strlen(resolved_path) - 1] != '/') { + strcat(resolved_path, "/"); + } + strcat(resolved_path, bname); + success = 1; + +cleanup: + free(dname); + free(rpath); + + return (success ? resolved_path : NULL); +} + #ifndef HAVE_STRNDUP /* A quick and dirty implementation derived from glibc */ /** Determines the length of a fixed-size string. diff --git a/src/common/util-common.h b/src/common/util-common.h index 04d4e9d..b3a4df1 100644 --- a/src/common/util-common.h +++ b/src/common/util-common.h @@ -22,6 +22,7 @@ const char *mbasename(const char *path); char *mdirname(const char *path); +char *lrealpath(const char *path, char *resolved_path); #ifndef HAVE_STRNDUP char *strndup(const char *s, size_t n); diff --git a/test/pacman/tests/fileconflict007.py b/test/pacman/tests/fileconflict007.py index b61ddb4..7fe65ed 100644 --- a/test/pacman/tests/fileconflict007.py +++ b/test/pacman/tests/fileconflict007.py @@ -16,5 +16,3 @@ self.addrule("PKG_EXIST=pkg") self.addrule("PKG_VERSION=pkg|1.0-2") self.addrule("FILE_TYPE=dir/symdir/|dir") - -self.expectfailure = True diff --git a/test/pacman/tests/sync701.py b/test/pacman/tests/sync701.py index 912c794..590845f 100644 --- a/test/pacman/tests/sync701.py +++ b/test/pacman/tests/sync701.py @@ -19,5 +19,3 @@ self.addrule("PKG_VERSION=pkg1|1.0-2") self.addrule("PKG_EXIST=pkg2") self.addrule("FILE_TYPE=lib|dir") - -self.expectfailure = True diff --git a/test/pacman/tests/sync702.py b/test/pacman/tests/sync702.py index 8f4c0ad..c3e2320 100644 --- a/test/pacman/tests/sync702.py +++ b/test/pacman/tests/sync702.py @@ -19,5 +19,3 @@ self.addrule("PKG_VERSION=pkg2|1.0-2") self.addrule("PKG_EXIST=pkg1") self.addrule("FILE_TYPE=lib|dir") - -self.expectfailure = True -- 1.8.2.1