summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBernd Schubert <bernd.schubert@fastmail.fm>2010-05-31 21:27:52 +0200
committerBernd Schubert <bernd.schubert@fastmail.fm>2010-05-31 21:27:52 +0200
commit1d9f17c0536cb2891987feee7f4e08a5dabf2579 (patch)
treed2a420dcca28b9d186b0dbd75ab1a6ee3f38606c /src
parent5857e2ae19211ae94a3c026413fe94c292a1f0ed (diff)
downloadunionfs-fuse-1d9f17c0536cb2891987feee7f4e08a5dabf2579.tar.gz
Fix ubuntu bug 587917, Properly check on rmdir() if sub-branches are empty
Diffstat (limited to 'src')
-rw-r--r--src/readdir.c80
-rw-r--r--src/readdir.h1
-rw-r--r--src/rmdir.c3
3 files changed, 84 insertions, 0 deletions
diff --git a/src/readdir.c b/src/readdir.c
index b5eb19c..d5c3d01 100644
--- a/src/readdir.c
+++ b/src/readdir.c
@@ -182,3 +182,83 @@ out:
return rc;
}
+
+/**
+ * check if a directory on all paths is empty
+ * return 0 if empty, 1 if not and negative value on error
+ *
+ * TODO: This shares lots of code with unionfs_readdir(), can we merge
+ * both functions?
+ */
+int dir_not_empty(const char *path) {
+
+ DBG_IN();
+
+ int i = 0;
+ int rc = 0;
+ int not_empty = 0;
+
+ struct hashtable *whiteouts = NULL;
+
+ if (uopt.cow_enabled) whiteouts = create_hashtable(16, string_hash, string_equal);
+
+ bool subdir_hidden = false;
+
+ for (i = 0; i < uopt.nbranches; i++) {
+ if (subdir_hidden) break;
+
+ char p[PATHLEN_MAX];
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) {
+ rc = -ENAMETOOLONG;
+ goto out;
+ }
+
+ // check if branches below this branch are hidden
+ int res = path_hidden(path, i);
+ if (res < 0) {
+ rc = res; // error
+ goto out;
+ }
+
+ if (res > 0) subdir_hidden = true;
+
+ DIR *dp = opendir(p);
+ if (dp == NULL) {
+ if (uopt.cow_enabled) read_whiteouts(path, whiteouts, i);
+ continue;
+ }
+
+ struct dirent *de;
+ while ((de = readdir(dp)) != NULL) {
+
+ // Ignore . and ..
+ if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0))
+ continue;
+
+ // check if we need file hiding
+ if (uopt.cow_enabled) {
+ // file should be hidden from the user
+ if (hashtable_search(whiteouts, de->d_name) != NULL) continue;
+ }
+
+ if (hide_meta_dir(i, p, de) == true) continue;
+
+ // When we arrive here, a valid entry was found
+ not_empty = 1;
+ closedir(dp);
+ goto out;
+ }
+
+ closedir(dp);
+ if (uopt.cow_enabled) read_whiteouts(path, whiteouts, i);
+ }
+
+out:
+ if (uopt.cow_enabled) hashtable_destroy(whiteouts, 1);
+
+ if (rc) return rc;
+
+ return not_empty;
+}
+
+
diff --git a/src/readdir.h b/src/readdir.h
index e52a622..cfbf227 100644
--- a/src/readdir.h
+++ b/src/readdir.h
@@ -4,5 +4,6 @@
#include <fuse.h>
int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi);
+int dir_not_empty(const char *path);
#endif
diff --git a/src/rmdir.c b/src/rmdir.c
index ccaf7e0..e5212c9 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -31,6 +31,7 @@
#include "general.h"
#include "findbranch.h"
#include "string.h"
+#include "readdir.h"
/**
* If the branch that has the directory to be removed is in read-write mode,
@@ -84,6 +85,8 @@ static int rmdir_ro(const char *path, int branch_ro) {
int unionfs_rmdir(const char *path) {
DBG_IN();
+ if (dir_not_empty(path)) return -ENOTEMPTY;
+
int i = find_rorw_branch(path);
if (i == -1) return -errno;