diff --git a/core/pacman/0001-Add-conflict-for-replacing-owned-empty-directory.patch b/core/pacman/0001-Add-conflict-for-replacing-owned-empty-directory.patch new file mode 100644 index 000000000..30481ebc8 --- /dev/null +++ b/core/pacman/0001-Add-conflict-for-replacing-owned-empty-directory.patch @@ -0,0 +1,151 @@ +From 717fdb8ee0fd23cf72fc7d2832317f513caefa2c Mon Sep 17 00:00:00 2001 +From: Allan McRae +Date: Sun, 8 Jul 2012 21:36:36 +1000 +Subject: [PATCH 1/4] Add conflict for replacing owned empty directory + +When two packages own an empty directory, pacman finds no conflict when +one of those packages wants to replace the directory with a file or a +symlink. When it comes to actually extracting the new file/symlink, +pacman sees the directory is still there (we do not remove empty +directories if they are owned by a package) and refuses to extract. + +Detect this potential conflict early and bail. Note that it is a +_potential_ conflict and not a guaranteed one as the other package owning +the directory could be updated or removed first which would remove +the conflict. However, pacman currently can not sort package installation +order to ensure this, so this conflict requires manual upgrade ordering. + +Signed-off-by: Allan McRae +Signed-off-by: Dan McGee +--- + lib/libalpm/conflict.c | 32 ++++++++++++++++++++++++++------ + test/pacman/tests/fileconflict009.py | 20 ++++++++++++++++++++ + test/pacman/tests/fileconflict010.py | 20 ++++++++++++++++++++ + 3 files changed, 66 insertions(+), 6 deletions(-) + create mode 100644 test/pacman/tests/fileconflict009.py + create mode 100644 test/pacman/tests/fileconflict010.py + +diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c +index 32f6f30..efa1a87 100644 +--- a/lib/libalpm/conflict.c ++++ b/lib/libalpm/conflict.c +@@ -328,15 +328,35 @@ const alpm_file_t *_alpm_filelist_contains(alpm_filelist_t *filelist, + return NULL; + } + +-static int dir_belongsto_pkg(const char *root, const char *dirpath, ++static int dir_belongsto_pkg(alpm_handle_t *handle, const char *dirpath, + alpm_pkg_t *pkg) + { ++ alpm_list_t *i; + struct stat sbuf; + char path[PATH_MAX]; + char abspath[PATH_MAX]; +- struct dirent *ent = NULL; + DIR *dir; ++ struct dirent *ent = NULL; ++ const char *root = handle->root; ++ ++ /* TODO: this is an overly strict check but currently pacman will not ++ * overwrite a directory with a file (case 10/11 in add.c). Adjusting that ++ * is not simple as even if the directory is being unowned by a conflicting ++ * package, pacman does not sort this to ensure all required directory ++ * "removals" happen before installation of file/symlink */ ++ ++ /* check that no other _installed_ package owns the directory */ ++ for(i = _alpm_db_get_pkgcache(handle->db_local); i; i = i->next) { ++ if(pkg == i->data) { ++ continue; ++ } ++ ++ if(_alpm_filelist_contains(alpm_pkg_get_files(i->data), dirpath)) { ++ return 0; ++ } ++ } + ++ /* check all files in directory are owned by the package */ + snprintf(abspath, PATH_MAX, "%s%s", root, dirpath); + dir = opendir(abspath); + if(dir == NULL) { +@@ -349,13 +369,13 @@ static int dir_belongsto_pkg(const char *root, const char *dirpath, + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } +- snprintf(path, PATH_MAX, "%s/%s", dirpath, name); ++ snprintf(path, PATH_MAX, "%s%s", dirpath, name); + snprintf(abspath, PATH_MAX, "%s%s", root, path); + if(stat(abspath, &sbuf) != 0) { + continue; + } + if(S_ISDIR(sbuf.st_mode)) { +- if(dir_belongsto_pkg(root, path, pkg)) { ++ if(dir_belongsto_pkg(handle, path, pkg)) { + continue; + } else { + closedir(dir); +@@ -529,9 +549,9 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle, + sprintf(dir, "%s/", filestr); + if(_alpm_filelist_contains(alpm_pkg_get_files(dbpkg), dir)) { + _alpm_log(handle, ALPM_LOG_DEBUG, +- "check if all files in %s belongs to %s\n", ++ "check if all files in %s belong to %s\n", + dir, dbpkg->name); +- resolved_conflict = dir_belongsto_pkg(handle->root, filestr, dbpkg); ++ resolved_conflict = dir_belongsto_pkg(handle, dir, dbpkg); + } + free(dir); + } +diff --git a/test/pacman/tests/fileconflict009.py b/test/pacman/tests/fileconflict009.py +new file mode 100644 +index 0000000..904af4a +--- /dev/null ++++ b/test/pacman/tests/fileconflict009.py +@@ -0,0 +1,20 @@ ++self.description = "dir->symlink change during package upgrade (directory conflict)" ++ ++lp1 = pmpkg("pkg1") ++lp1.files = ["dir/"] ++self.addpkg2db("local", lp1) ++ ++lp2 = pmpkg("pkg2") ++lp2.files = ["dir/"] ++self.addpkg2db("local", lp2) ++ ++p = pmpkg("pkg1", "1.0-2") ++p.files = ["dir -> /usr/dir"] ++self.addpkg2db("sync", p) ++ ++self.args = "-S pkg1" ++ ++self.addrule("PACMAN_RETCODE=1") ++self.addrule("PKG_VERSION=pkg1|1.0-1") ++self.addrule("PKG_VERSION=pkg2|1.0-1") ++self.addrule("DIR_EXIST=dir/") +diff --git a/test/pacman/tests/fileconflict010.py b/test/pacman/tests/fileconflict010.py +new file mode 100644 +index 0000000..0a3ce83 +--- /dev/null ++++ b/test/pacman/tests/fileconflict010.py +@@ -0,0 +1,20 @@ ++self.description = "dir->file change during package upgrade (directory conflict)" ++ ++lp1 = pmpkg("pkg1") ++lp1.files = ["dir/"] ++self.addpkg2db("local", lp1) ++ ++lp2 = pmpkg("pkg2") ++lp2.files = ["dir/"] ++self.addpkg2db("local", lp2) ++ ++p = pmpkg("pkg1", "1.0-2") ++p.files = ["dir"] ++self.addpkg2db("sync", p) ++ ++self.args = "-S pkg1" ++ ++self.addrule("PACMAN_RETCODE=1") ++self.addrule("PKG_VERSION=pkg1|1.0-1") ++self.addrule("PKG_VERSION=pkg2|1.0-1") ++self.addrule("DIR_EXIST=dir/") +-- +1.7.11.1 diff --git a/core/pacman/0002-Check-empty-subdirectory-ownership.patch b/core/pacman/0002-Check-empty-subdirectory-ownership.patch new file mode 100644 index 000000000..99bf33ca4 --- /dev/null +++ b/core/pacman/0002-Check-empty-subdirectory-ownership.patch @@ -0,0 +1,60 @@ +From 44e9fdd0e848382337edb97d41e7317638a67bac Mon Sep 17 00:00:00 2001 +From: Allan McRae +Date: Sun, 8 Jul 2012 23:58:37 +1000 +Subject: [PATCH 2/4] Check empty subdirectory ownership + +When checking if a package owns a directory, it is important to check +not only that all the files in the directory are part of the package, +but also if the directory is part of a package. This catches empty +subdirectories during conflict checking for directory to file/symlink +replacements. + +Signed-off-by: Allan McRae +Signed-off-by: Dan McGee +--- + lib/libalpm/conflict.c | 5 +++++ + test/pacman/tests/fileconflict012.py | 17 +++++++++++++++++ + 2 files changed, 22 insertions(+) + create mode 100644 test/pacman/tests/fileconflict012.py + +diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c +index efa1a87..d6e5d8c 100644 +--- a/lib/libalpm/conflict.c ++++ b/lib/libalpm/conflict.c +@@ -339,6 +339,11 @@ static int dir_belongsto_pkg(alpm_handle_t *handle, const char *dirpath, + struct dirent *ent = NULL; + const char *root = handle->root; + ++ /* check directory is actually in package - used for subdirectory checks */ ++ if(!_alpm_filelist_contains(alpm_pkg_get_files(pkg), dirpath)) { ++ return 0; ++ } ++ + /* TODO: this is an overly strict check but currently pacman will not + * overwrite a directory with a file (case 10/11 in add.c). Adjusting that + * is not simple as even if the directory is being unowned by a conflicting +diff --git a/test/pacman/tests/fileconflict012.py b/test/pacman/tests/fileconflict012.py +new file mode 100644 +index 0000000..421b739 +--- /dev/null ++++ b/test/pacman/tests/fileconflict012.py +@@ -0,0 +1,17 @@ ++self.description = "dir->file change during package upgrade (filesystem file conflict)" ++ ++lp1 = pmpkg("pkg1") ++lp1.files = ["dir/"] ++self.addpkg2db("local", lp1) ++ ++self.filesystem = ["dir/file"] ++ ++p = pmpkg("pkg1", "1.0-2") ++p.files = ["dir"] ++self.addpkg2db("sync", p) ++ ++self.args = "-S pkg1" ++ ++self.addrule("PACMAN_RETCODE=1") ++self.addrule("PKG_VERSION=pkg1|1.0-1") ++self.addrule("DIR_EXIST=dir/") +-- +1.7.11.1 diff --git a/core/pacman/PKGBUILD b/core/pacman/PKGBUILD index b7e38444e..42d84aac3 100644 --- a/core/pacman/PKGBUILD +++ b/core/pacman/PKGBUILD @@ -12,13 +12,13 @@ noautobuild=1 pkgname=pacman pkgver=4.0.3 -pkgrel=2.1 +pkgrel=3 pkgdesc="A library-based package manager with dependency support" arch=('i686' 'x86_64') url="http://www.archlinux.org/pacman/" license=('GPL') groups=('base') -depends=('bash' 'glibc>=2.15' 'libarchive>=3.0.2' 'curl>=7.19.4' +depends=('bash' 'glibc>=2.16.0' 'libarchive>=3.0.2' 'curl>=7.19.4' 'gpgme' 'pacman-mirrorlist') makedepends=('asciidoc') optdepends=('fakeroot: for makepkg usage as normal user') @@ -26,9 +26,13 @@ backup=(etc/pacman.conf etc/makepkg.conf) install=pacman.install options=(!libtool) source=(ftp://ftp.archlinux.org/other/pacman/$pkgname-$pkgver.tar.gz{,.sig} + 0001-Add-conflict-for-replacing-owned-empty-directory.patch + 0002-Check-empty-subdirectory-ownership.patch pacman.conf makepkg.conf) md5sums=('387965c7125e60e5f0b9ff3b427fe0f9' + '0c1d326d0ca48c9a9819ea8060ea3bba' + '02db451be806335ce189ffadb5cf84b9' '1a70392526c8768470da678b31905a6e' '9a7d914def620cc4dd1a94ed53892175' 'e6d44e71b847d2c98bb38087e33cd76b') @@ -39,6 +43,9 @@ PKGEXT='.pkg.tar.gz' build() { cd $srcdir/$pkgname-$pkgver + patch -p1 -i $srcdir/0001-Add-conflict-for-replacing-owned-empty-directory.patch + patch -p1 -i $srcdir/0002-Check-empty-subdirectory-ownership.patch + ./configure --prefix=/usr --sysconfdir=/etc \ --localstatedir=/var --enable-doc make