[pacman-dev] [PATCH 2/3] [RFC] Resolve file paths during inter-package conflict check

Allan McRae allan at archlinux.org
Tue Jul 17 10:42:42 EDT 2012


File paths are resolved if necessary during inter-package conflict
checks so that packages carrying the same effective file due to
directory symlinks on the filesystem are flagged as conflicting.

Signed-off-by: Allan McRae <allan at archlinux.org>
---

Prepare for some ugly...

 lib/libalpm/conflict.c               | 180 ++++++++++++++++++++++++++++++++++-
 test/pacman/tests/fileconflict001.py |   2 -
 test/pacman/tests/fileconflict016.py |   2 -
 test/pacman/tests/fileconflict017.py |  26 +++++
 4 files changed, 204 insertions(+), 6 deletions(-)
 create mode 100644 test/pacman/tests/fileconflict017.py

diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c
index adb4b37..faee7bd 100644
--- a/lib/libalpm/conflict.c
+++ b/lib/libalpm/conflict.c
@@ -213,6 +213,166 @@ alpm_list_t SYMEXPORT *alpm_checkconflicts(alpm_handle_t *handle,
 	return _alpm_innerconflicts(handle, pkglist);
 }
 
+
+/* Takes a file list and resolves all directory paths
+ * Pre-condition: list must be sorted!
+ * If the filelist has no resolving needed, the original list is returned.
+ * Otherwise the list and will need freed */
+static alpm_filelist_t *resolved_filelist(alpm_handle_t *handle,
+		alpm_filelist_t *filelist)
+{
+	size_t i;
+	struct stat sbuf;
+	char path[PATH_MAX];
+	alpm_filelist_t *resolved;
+	int list_resolved = 1;
+
+	const char *root = handle->root;
+
+	for(i = 0; i < filelist->count; i++) {
+		const char *filename = filelist->files[i].name;
+
+		if(filename[strlen(filename)-1] != '/') {
+			continue;
+		}
+
+		snprintf(path, PATH_MAX, "%s%s", root, filename);
+
+		if(_alpm_lstat(path, &sbuf) != 0) {
+			continue;
+		}
+
+		if(S_ISLNK(sbuf.st_mode)) {
+			list_resolved = 0;
+
+			resolved = malloc(sizeof(alpm_filelist_t));
+			resolved->files = malloc(sizeof(alpm_file_t) * filelist->count);
+			resolved->count = i;
+
+			break;
+		}
+	}
+
+	if(list_resolved == 1) {
+		return filelist;
+	}
+
+	/* generate the resolved list */
+	char *resolved_root, *resolved_path, *causal_dir, *current_dir;
+	char resolved_dir[PATH_MAX];
+	int resolving = 0, resolved_dir_len;
+
+	for(i = 0; i < resolved->count; i++) {
+		alpm_file_t *current_file = filelist->files + i;
+		alpm_file_t *resolved_file = resolved->files + i;
+
+		resolved_file->name = strdup(current_file->name);
+		resolved_file->size = current_file->size;
+		resolved_file->mode = current_file->mode;
+	}
+
+	resolved_root = realpath(root, NULL);
+
+	causal_dir = filelist->files[resolved->count].name;
+	current_dir = strdup(causal_dir);
+	snprintf(path, PATH_MAX, "%s%s", root, causal_dir);
+	resolved_path = realpath(path, NULL);
+	snprintf(resolved_dir, PATH_MAX, "%s/", resolved_path + strlen(resolved_root) + 1);
+	resolved->files[resolved->count].name = strdup(resolved_dir);
+	resolved_dir_len = strlen(resolved_dir);
+	free(resolved_path);
+	resolving = 1;
+
+	for(i = resolved->count + 1; i < filelist->count; i++) {
+		alpm_file_t *current_file = filelist->files + i;
+		alpm_file_t *resolved_file = resolved->files + i;
+		char *filename = current_file->name;
+
+		resolved_file->size = current_file->size;
+		resolved_file->mode = current_file->mode;
+
+		if(filename[strlen(filename) - 1] == '/') {
+			if(strncmp(filename, causal_dir, strlen(causal_dir)) == 0) {
+				char *c = filename + strlen(causal_dir) + 1;
+				// we must resolve path
+				while((c = strchr(c, '/')) != NULL) {
+					*c = '\0';
+					snprintf(path, PATH_MAX, "%s%s", root, filename);
+					if(_alpm_lstat(path, &sbuf) != 0) {
+						*c = '/';
+						*(c + 1) = '\0';
+						snprintf(path, PATH_MAX, "%s%s", root, current_dir);
+						break;
+					}
+					free(current_dir);
+					current_dir = strdup(filename);
+					*c = '/';
+				}
+				resolved_path = realpath(path, NULL);
+				snprintf(resolved_dir, PATH_MAX, "%s/", resolved_path + strlen(resolved_root) + 1);
+				resolved->files[resolved->count].name = strdup(resolved_dir);
+				resolved_dir_len = strlen(resolved_dir);
+				free(resolved_path);
+				resolved_file->name = strdup(filename);
+				continue;
+			} else {
+				// test if path needs resolved
+				snprintf(path, PATH_MAX, "%s%s", root, filename);
+				if(_alpm_lstat(path, &sbuf) != 0 || !S_ISLNK(sbuf.st_mode)) {
+						resolved_file->name = strdup(filename);
+					resolving = 0;
+					if(current_dir) {
+						free(current_dir);
+						current_dir = NULL;
+					}
+					resolved_file->name = strdup(filename);
+					continue;
+				}
+
+				causal_dir = filename;
+				current_dir = strdup(causal_dir);
+				snprintf(path, PATH_MAX, "%s%s", root, causal_dir);
+				resolved_path = realpath(path, NULL);
+				snprintf(resolved_dir, PATH_MAX, "%s/", resolved_path + strlen(resolved_root) + 1);
+				resolved_file->name = strdup(resolved_dir);
+				resolved_dir_len = strlen(resolved_dir);
+				free(resolved_path);
+				resolving = 1;
+				continue;
+			}
+		}
+
+		if(resolving == 0) {
+			resolved_file->name = strdup(filename);
+		} else {
+			size_t len;
+			filename = filename + strlen(current_dir);
+			len = resolved_dir_len + strlen(filename) + 1;
+			resolved_file->name = malloc(len);
+			snprintf(resolved_file->name, len, "%s%s", resolved_dir, filename);
+		}
+	}
+
+	resolved->count = filelist->count;
+
+	if(current_dir) {
+		free(current_dir);
+	}
+
+	_alpm_files_msort(resolved->files, resolved->count);
+	return resolved;
+}
+
+static void free_filelist(alpm_filelist_t *fl)
+{
+	size_t i;
+	for(i = 0; i < fl->count; i++) {
+		free(fl->files[i].name);
+	}
+	free(fl->files);
+	free(fl);
+}
+
 /* Returns the difference of the provided two lists of files.
  * Pre-condition: both lists are sorted!
  * When done, free the list but NOT the contained data.
@@ -481,6 +641,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
 		alpm_pkg_t *dbpkg;
 		size_t filenum;
 
+		alpm_filelist_t *p1_filelist = resolved_filelist(handle, alpm_pkg_get_files(p1));
+
 		int percent = (current * 100) / numtargs;
 		PROGRESS(handle, ALPM_PROGRESS_CONFLICTS_START, "", percent,
 		         numtargs, current);
@@ -490,8 +652,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
 		for(j = i->next; j; j = j->next) {
 			alpm_list_t *common_files;
 			alpm_pkg_t *p2 = j->data;
-			common_files = filelist_intersection(alpm_pkg_get_files(p1),
-					alpm_pkg_get_files(p2));
+			alpm_filelist_t *p2_filelist = resolved_filelist(handle, alpm_pkg_get_files(p2));
+			common_files = filelist_intersection(p1_filelist, p2_filelist);
 
 			if(common_files) {
 				alpm_list_t *k;
@@ -503,11 +665,25 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
 					if(handle->pm_errno == ALPM_ERR_MEMORY) {
 						FREELIST(conflicts);
 						FREELIST(common_files);
+						if(p1_filelist != alpm_pkg_get_files(p1)) {
+							free_filelist(p1_filelist);
+						}
+						if(p2_filelist != alpm_pkg_get_files(p2)) {
+							free_filelist(p2_filelist);
+						}
 						return NULL;
 					}
 				}
 				alpm_list_free(common_files);
 			}
+
+			if(p2_filelist != alpm_pkg_get_files(p2)) {
+				free_filelist(p2_filelist);
+			}
+		}
+
+		if(p1_filelist != alpm_pkg_get_files(p1)) {
+			free_filelist(p1_filelist);
 		}
 
 		/* CHECK 2: check every target against the filesystem */
diff --git a/test/pacman/tests/fileconflict001.py b/test/pacman/tests/fileconflict001.py
index e1371f8..b1ad5e1 100644
--- a/test/pacman/tests/fileconflict001.py
+++ b/test/pacman/tests/fileconflict001.py
@@ -25,5 +25,3 @@
 self.addrule("!PKG_EXIST=pkg2")
 self.addrule("FILE_EXIST=dir/realdir/realfile")
 self.addrule("!FILE_EXIST=dir/realdir/file")
-
-self.expectfailure = True
diff --git a/test/pacman/tests/fileconflict016.py b/test/pacman/tests/fileconflict016.py
index 5625984..c5daf48 100644
--- a/test/pacman/tests/fileconflict016.py
+++ b/test/pacman/tests/fileconflict016.py
@@ -22,5 +22,3 @@
 self.addrule("PACMAN_RETCODE=1")
 self.addrule("!PKG_EXIST=pkg1")
 self.addrule("!PKG_EXIST=pkg2")
-
-self.expectfailure = True
diff --git a/test/pacman/tests/fileconflict017.py b/test/pacman/tests/fileconflict017.py
new file mode 100644
index 0000000..2df7865
--- /dev/null
+++ b/test/pacman/tests/fileconflict017.py
@@ -0,0 +1,26 @@
+self.description = "file conflict with same effective path across packages (directory symlink - deep)"
+
+lp1 = pmpkg("filesystem", "1.0-1")
+lp1.files = ["usr/",
+             "usr/lib/",
+             "lib -> usr/lib/"]
+self.addpkg2db("local", lp1)
+
+p1 = pmpkg("pkg1")
+p1.files = ["lib/",
+            "lib/foo/",
+            "lib/foo/bar"]
+self.addpkg2db("sync", p1)
+
+p2 = pmpkg("pkg2")
+p2.files = ["usr/",
+            "usr/lib/",
+	    "usr/lib/foo/",
+	    "usr/lib/foo/bar"]
+self.addpkg2db("sync", p2)
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
-- 
1.7.11.2



More information about the pacman-dev mailing list