[pacman-dev] [PATCH] Fix fileconflict004

Xavier Chantry shiningxc at gmail.com
Sun Jul 19 08:12:16 EDT 2009


When one package wants to replace a directory by a file, we check that all
files in that directory were owned by that package.

Additionally pacman can be more verbose when the extraction of the symlink
(or file) fails. The patch to add.c looks more complex than it is, I just
moved and reindented code to handle cases 10 and 11 together.

Signed-off-by: Xavier Chantry <shiningxc at gmail.com>
---
 lib/libalpm/add.c                |   48 +++++++++++++++-----------------
 lib/libalpm/conflict.c           |   56 ++++++++++++++++++++++++++++++++++++++
 pactest/tests/fileconflict004.py |    2 -
 pactest/tests/fileconflict005.py |    1 -
 pactest/tests/fileconflict006.py |   24 ++++++++++++++++
 5 files changed, 102 insertions(+), 29 deletions(-)
 create mode 100644 pactest/tests/fileconflict006.py

diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c
index 4bd52de..ddbcfee 100644
--- a/lib/libalpm/add.c
+++ b/lib/libalpm/add.c
@@ -353,28 +353,30 @@ static int extract_single_file(struct archive *archive,
 	if(_alpm_lstat(filename, &lsbuf) != 0 || stat(filename, &sbuf) != 0) {
 		/* cases 1,2,3: couldn't stat an existing file, skip all backup checks */
 	} else {
-		if(S_ISDIR(lsbuf.st_mode) && S_ISDIR(entrymode)) {
-			/* case 12: existing dir, ignore it */
-			if(lsbuf.st_mode != entrymode) {
-				/* if filesystem perms are different than pkg perms, warn user */
-				int mask = 07777;
-				_alpm_log(PM_LOG_WARNING, _("directory permissions differ on %s\n"
-							"filesystem: %o  package: %o\n"), entryname, lsbuf.st_mode & mask,
-						entrymode & mask);
-				alpm_logaction("warning: directory permissions differ on %s\n"
+		if(S_ISDIR(lsbuf.st_mode)) {
+			if(S_ISDIR(entrymode)) {
+				/* case 12: existing dir, ignore it */
+				if(lsbuf.st_mode != entrymode) {
+					/* if filesystem perms are different than pkg perms, warn user */
+					int mask = 07777;
+					_alpm_log(PM_LOG_WARNING, _("directory permissions differ on %s\n"
+								"filesystem: %o  package: %o\n"), entryname, lsbuf.st_mode & mask,
+							entrymode & mask);
+					alpm_logaction("warning: directory permissions differ on %s\n"
 							"filesystem: %o  package: %o\n", entryname, lsbuf.st_mode & mask,
-						entrymode & mask);
+							entrymode & mask);
+				}
+				_alpm_log(PM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
+						entryname);
+				archive_read_data_skip(archive);
+				return(0);
+			} else {
+				/* case 10/11: trying to overwrite dir with file/symlink, don't allow it */
+				_alpm_log(PM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
+						entryname);
+				archive_read_data_skip(archive);
+				return(1);
 			}
-			_alpm_log(PM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
-					entryname);
-			archive_read_data_skip(archive);
-			return(0);
-		} else if(S_ISDIR(lsbuf.st_mode) && S_ISLNK(entrymode)) {
-			/* case 11: existing dir, symlink in package, ignore it */
-			_alpm_log(PM_LOG_DEBUG, "extract: skipping symlink extraction of %s\n",
-					entryname);
-			archive_read_data_skip(archive);
-			return(0);
 		} else if(S_ISLNK(lsbuf.st_mode) && S_ISDIR(entrymode)) {
 			/* case 9: existing symlink, dir in package */
 			if(S_ISDIR(sbuf.st_mode)) {
@@ -390,12 +392,6 @@ static int extract_single_file(struct archive *archive,
 				archive_read_data_skip(archive);
 				return(1);
 			}
-		} else if(S_ISDIR(lsbuf.st_mode) && S_ISREG(entrymode)) {
-			/* case 10: trying to overwrite dir tree with file, don't allow it */
-			_alpm_log(PM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
-					entryname);
-			archive_read_data_skip(archive);
-			return(1);
 		} else if(S_ISREG(lsbuf.st_mode) && S_ISDIR(entrymode)) {
 			/* case 6: trying to overwrite file with dir */
 			_alpm_log(PM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c
index db1656f..9cc46d2 100644
--- a/lib/libalpm/conflict.c
+++ b/lib/libalpm/conflict.c
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <limits.h>
 #include <sys/stat.h>
+#include <dirent.h>
 
 /* libalpm */
 #include "conflict.h"
@@ -348,6 +349,50 @@ void _alpm_fileconflict_free(pmfileconflict_t *conflict)
 	FREE(conflict);
 }
 
+static int dir_belongsto_pkg(char *dirpath, pmpkg_t *pkg)
+{
+	struct dirent *ent = NULL;
+	struct stat sbuf;
+	char path[PATH_MAX];
+	char abspath[PATH_MAX];
+	DIR *dir;
+
+	snprintf(abspath, PATH_MAX, "%s%s", handle->root, dirpath);
+	dir = opendir(abspath);
+	if(dir == NULL) {
+		return(1);
+	}
+	while((ent = readdir(dir)) != NULL) {
+		const char *name = ent->d_name;
+
+		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+			continue;
+		}
+		snprintf(path, PATH_MAX, "%s/%s", dirpath, name);
+		snprintf(abspath, PATH_MAX, "%s%s", handle->root, path);
+		if(stat(abspath, &sbuf) != 0) {
+			continue;
+		}
+		if(S_ISDIR(sbuf.st_mode)) {
+			if(dir_belongsto_pkg(path, pkg)) {
+				continue;
+			} else {
+				closedir(dir);
+				return(0);
+			}
+		} else {
+			if(alpm_list_find_str(alpm_pkg_get_files(pkg),path)) {
+				continue;
+			} else {
+				closedir(dir);
+				return(0);
+			}
+		}
+	}
+	closedir(dir);
+	return(1);
+}
+
 /* Find file conflicts that may occur during the transaction with two checks:
  * 1: check every target against every target
  * 2: check every target against the filesystem */
@@ -474,6 +519,17 @@ alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans,
 				}
 			}
 
+			if(S_ISDIR(lsbuf.st_mode) && dbpkg) {
+				char *dir = malloc(strlen(filestr) + 2);
+				sprintf(dir, "%s/", filestr);
+				if(alpm_list_find_str(alpm_pkg_get_files(dbpkg),dir)) {
+					_alpm_log(PM_LOG_DEBUG, "check if all files in %s belongs to %s\n",
+							dir, dbpkg->name);
+					resolved_conflict = dir_belongsto_pkg(filestr, dbpkg);
+				}
+				free(dir);
+			}
+
 			if(!resolved_conflict) {
 				_alpm_log(PM_LOG_DEBUG, "file found in conflict: %s\n", path);
 				conflicts = add_fileconflict(conflicts, PM_FILECONFLICT_FILESYSTEM,
diff --git a/pactest/tests/fileconflict004.py b/pactest/tests/fileconflict004.py
index 2396ced..a5347cc 100644
--- a/pactest/tests/fileconflict004.py
+++ b/pactest/tests/fileconflict004.py
@@ -17,5 +17,3 @@
 self.addrule("PKG_EXIST=pkg1")
 self.addrule("PKG_VERSION=pkg1|2.0-1")
 self.addrule("FILE_TYPE=test|link")
-
-self.expectfailure = True
diff --git a/pactest/tests/fileconflict005.py b/pactest/tests/fileconflict005.py
index b9c0fa9..5c554af 100644
--- a/pactest/tests/fileconflict005.py
+++ b/pactest/tests/fileconflict005.py
@@ -20,4 +20,3 @@
 self.addrule("PACMAN_RETCODE=1")
 self.addrule("PKG_EXIST=pkg1")
 self.addrule("PKG_VERSION=pkg1|1.0-1")
-
diff --git a/pactest/tests/fileconflict006.py b/pactest/tests/fileconflict006.py
new file mode 100644
index 0000000..84afff2
--- /dev/null
+++ b/pactest/tests/fileconflict006.py
@@ -0,0 +1,24 @@
+self.description = "dir->symlink change during package upgrade (conflict)"
+
+p1 = pmpkg("pkg1", "1.0-1")
+p1.files = ["test/",
+            "test/file1",
+	    "test/dir/file1",
+	    "test/dir/file2"]
+self.addpkg2db("local", p1)
+
+p2 = pmpkg("pkg2")
+p2.files = ["test/dir/file3"]
+self.addpkg2db("local", p2)
+
+p3 = pmpkg("pkg1", "2.0-1")
+p3.files = ["test2/",
+            "test2/file3",
+            "test -> test2"]
+self.addpkg2db("sync", p3)
+
+self.args = "-S pkg1"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("PKG_VERSION=pkg1|1.0-1")
-- 
1.6.3.3



More information about the pacman-dev mailing list