From: Jan Blunck <jblunck@xxxxxxx> do_whiteout() allows removal of a directory when it has whiteouts but is logically empty. XXX - This patch abuses readdir() to check if the union directory is logically empty - that is, all the entries are whiteouts (or "." or ".."). Currently, we have no clean VFS interface to ask the lower file system if a directory is empty. Fixes: - Add ->is_directory_empty() op - Add is_directory_empty flag to dentry (ugly dcache populate) - Ask underlying fs to remove it and look for an error return - (your idea here) Signed-off-by: Jan Blunck <jblunck@xxxxxxx> Signed-off-by: Valerie Aurora <vaurora@xxxxxxxxxx> --- fs/namei.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 90 insertions(+), 0 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 99ca663..0014052 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -32,6 +32,7 @@ #include <linux/fcntl.h> #include <linux/device_cgroup.h> #include <linux/fs_struct.h> +#include <linux/init_task.h> #include <asm/uaccess.h> #include "internal.h" @@ -2645,6 +2646,95 @@ error_unlock: * dentry, and if that is true (possibly after pruning the dcache), * then we drop the dentry now. * + * XXX - We are abusing readdir to check if a union directory is + * logically empty. + */ +static int filldir_is_empty(void *__buf, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + int *is_empty = __buf; + + switch (namelen) { + case 2: + if (name[1] != '.') + break; + case 1: + if (name[0] != '.') + break; + return 0; + } + + if (d_type == DT_WHT) + return 0; + + *is_empty = 0; + return 1; /* no point scanning further */ +} + +static int directory_is_empty(struct path *path) +{ + struct file *file; + int err; + int is_empty = 1; + + BUG_ON(!S_ISDIR(path->dentry->d_inode->i_mode)); + + /* references for the file pointer */ + path_get(path); + + file = dentry_open(path->dentry, path->mnt, O_RDONLY, &init_cred); + if (IS_ERR(file)) + return 0; + + err = vfs_readdir(file, filldir_is_empty, &is_empty); + + fput(file); + return is_empty; +} + +static int do_whiteout(struct nameidata *nd, struct path *path, int isdir) +{ + struct path safe = nd->path; + struct dentry *dentry = path->dentry; + int err; + + path_get(&safe); + + err = may_delete(nd->path.dentry->d_inode, dentry, isdir); + if (err) + goto out; + + err = -ENOTEMPTY; + if (isdir && !directory_is_empty(path)) + goto out; + + if (nd->path.dentry != dentry->d_parent) { + dentry = __lookup_hash(&path->dentry->d_name, nd->path.dentry, + nd); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + + dput(path->dentry); + if (path->mnt != safe.mnt) + mntput(path->mnt); + path->mnt = nd->path.mnt; + path->dentry = dentry; + } + + err = vfs_whiteout(nd->path.dentry->d_inode, dentry, isdir); + +out: + path_put(&safe); + return err; +} + +/* + * We try to drop the dentry early: we should have + * a usage count of 2 if we're the only user of this + * dentry, and if that is true (possibly after pruning + * the dcache), then we drop the dentry now. + * * A low-level filesystem can, if it choses, legally * do a * -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html