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 <al...@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


Reply via email to