[pacman-dev] [PATCH 4/8] conflict.c: use real path for filesystem checks

Andrew Gregory andrew.gregory.8 at gmail.com
Fri Apr 26 20:00:25 EDT 2013


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 at 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



More information about the pacman-dev mailing list