summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libalpm/remove.c45
-rw-r--r--lib/libalpm/util.c42
-rw-r--r--lib/libalpm/util.h1
-rw-r--r--test/pacman/pmrule.py8
-rw-r--r--test/pacman/tests/remove070.py24
5 files changed, 106 insertions, 14 deletions
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
index b15dbaa5..c2a8ad2c 100644
--- a/lib/libalpm/remove.c
+++ b/lib/libalpm/remove.c
@@ -232,8 +232,8 @@ static void unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
* see the big comment block in db_find_fileconflicts() for an
* explanation. */
if(alpm_list_find_str(skip_remove, fileobj->name)) {
- _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in skip_remove, skipping removal\n",
- file);
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "%s is in skip_remove, skipping removal\n", file);
return;
}
@@ -247,11 +247,44 @@ static void unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
}
if(S_ISDIR(buf.st_mode)) {
- if(rmdir(file)) {
- /* this is okay, other packages are probably using it (like /usr) */
- _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s\n", file);
+ ssize_t files = _alpm_files_in_directory(handle, file, 0);
+ /* if we have files, no need to remove the directory */
+ if(files > 0) {
+ _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (contains files)\n",
+ file);
+ } else if(files < 0) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "keeping directory %s (could not count files)\n", file);
} else {
- _alpm_log(handle, ALPM_LOG_DEBUG, "removing directory %s\n", file);
+ /* one last check- does any other package own this file? */
+ alpm_list_t *local, *local_pkgs;
+ int found = 0;
+ local_pkgs = _alpm_db_get_pkgcache(handle->db_local);
+ for(local = local_pkgs; local && !found; local = local->next) {
+ alpm_pkg_t *local_pkg = local->data;
+ alpm_list_t *files;
+
+ /* we duplicated the package when we put it in the removal list, so we
+ * so we can't use direct pointer comparison here. */
+ if(_alpm_pkg_cmp(info, local_pkg) == 0) {
+ continue;
+ }
+ files = alpm_pkg_get_files(local_pkg);
+ if(_alpm_filelist_contains(files, fileobj->name)) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "keeping directory %s (owned by %s)\n", file, local_pkg->name);
+ found = 1;
+ }
+ }
+ if(!found) {
+ if(rmdir(file)) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "directory removal of %s failed: %s\n", file, strerror(errno));
+ } else {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "removed directory %s (no remaining owners)\n", file);
+ }
+ }
}
} else {
/* if the file needs backup and has been modified, back it up to .pacsave */
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index cd7f19c9..62313d81 100644
--- a/lib/libalpm/util.c
+++ b/lib/libalpm/util.c
@@ -388,6 +388,48 @@ int _alpm_rmrf(const char *path)
return 0;
}
+/**
+ * Determine if there are files in a directory
+ * @param handle the context handle
+ * @param path the full absolute directory path
+ * @param full_count whether to return an exact count of files
+ * @return a file count if full_count is != 0, else >0 if directory has
+ * contents, 0 if no contents, and -1 on error
+ */
+ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
+ int full_count)
+{
+ ssize_t files = 0;
+ struct dirent *ent;
+ DIR *dir = opendir(path);
+
+ if(!dir) {
+ if(errno == ENOTDIR) {
+ _alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
+ } else {
+ _alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
+ path);
+ }
+ return -1;
+ }
+ while((ent = readdir(dir)) != NULL) {
+ const char *name = ent->d_name;
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+ continue;
+ }
+
+ files++;
+
+ if(!full_count) {
+ break;
+ }
+ }
+
+ closedir(dir);
+ return files;
+}
+
int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args)
{
int ret = 0;
diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h
index 450dac9b..d66ddee9 100644
--- a/lib/libalpm/util.h
+++ b/lib/libalpm/util.h
@@ -100,6 +100,7 @@ int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix,
alpm_list_t *list, int breakfirst);
int _alpm_rmrf(const char *path);
+ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int full_count);
int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args);
int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[]);
int _alpm_ldconfig(alpm_handle_t *handle);
diff --git a/test/pacman/pmrule.py b/test/pacman/pmrule.py
index 0f6ae602..cb7ae88d 100644
--- a/test/pacman/pmrule.py
+++ b/test/pacman/pmrule.py
@@ -146,6 +146,14 @@ class pmrule(object):
else:
print "FILE rule '%s' not found" % case
success = -1
+ elif kind == "DIR":
+ filename = os.path.join(test.root, key)
+ if case == "EXIST":
+ if not os.path.isdir(filename):
+ success = 0
+ else:
+ print "DIR rule '%s' not found" % case
+ success = -1
elif kind == "LINK":
filename = os.path.join(test.root, key)
if case == "EXIST":
diff --git a/test/pacman/tests/remove070.py b/test/pacman/tests/remove070.py
index e0587e17..898e2f50 100644
--- a/test/pacman/tests/remove070.py
+++ b/test/pacman/tests/remove070.py
@@ -1,21 +1,29 @@
-self.description = "Remove a package with an empty directory needed by another package"
+self.description = "Remove a package with various directory overlaps"
+
+self.filesystem = ["lib/alsoonfs/randomfile"]
p1 = pmpkg("pkg1")
-p1.files = ["bin/pkg1", "opt/"]
+p1.files = ["bin/pkg1",
+ "opt/",
+ "lib/onlyinp1/",
+ "lib/alsoonfs/"]
p2 = pmpkg("pkg2")
-p2.files = ["bin/pkg2", "opt/"]
+p2.files = ["bin/pkg2",
+ "opt/",
+ "lib/onlyinp2/"]
for p in p1, p2:
- self.addpkg2db("local", p)
+ self.addpkg2db("local", p)
self.args = "-R %s" % p1.name
self.addrule("PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=pkg1")
self.addrule("PKG_EXIST=pkg2")
-self.addrule("!FILE_EXIST=bin/pkg1")
-self.addrule("FILE_EXIST=bin/pkg2")
-self.addrule("FILE_EXIST=opt/")
-self.expectfailure = True
+self.addrule("DIR_EXIST=bin/")
+self.addrule("DIR_EXIST=opt/")
+self.addrule("!DIR_EXIST=lib/onlyinp1/")
+self.addrule("DIR_EXIST=lib/onlyinp2/")
+self.addrule("DIR_EXIST=lib/alsoonfs/")