Mostly related to whiteouts.

Signed-off-by: Erez Zadok <[EMAIL PROTECTED]>
---
 fs/unionfs/subr.c |  242 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 242 insertions(+), 0 deletions(-)
 create mode 100644 fs/unionfs/subr.c

diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
new file mode 100644
index 0000000..0a0fce9
--- /dev/null
+++ b/fs/unionfs/subr.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005      Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003      Puja Gupta
+ * Copyright (c) 2003      Harikesavan Krishnan
+ * Copyright (c) 2003-2007 Stony Brook University
+ * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/*
+ * Pass an unionfs dentry and an index.  It will try to create a whiteout
+ * for the filename in dentry, and will try in branch 'index'.  On error,
+ * it will proceed to a branch to the left.
+ */
+int create_whiteout(struct dentry *dentry, int start)
+{
+       int bstart, bend, bindex;
+       struct dentry *lower_dir_dentry;
+       struct dentry *lower_dentry;
+       struct dentry *lower_wh_dentry;
+       struct nameidata nd;
+       char *name = NULL;
+       int err = -EINVAL;
+
+       verify_locked(dentry);
+
+       bstart = dbstart(dentry);
+       bend = dbend(dentry);
+
+       /* create dentry's whiteout equivalent */
+       name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
+       if (unlikely(IS_ERR(name))) {
+               err = PTR_ERR(name);
+               goto out;
+       }
+
+       for (bindex = start; bindex >= 0; bindex--) {
+               lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+
+               if (!lower_dentry) {
+                       /*
+                        * if lower dentry is not present, create the
+                        * entire lower dentry directory structure and go
+                        * ahead.  Since we want to just create whiteout, we
+                        * only want the parent dentry, and hence get rid of
+                        * this dentry.
+                        */
+                       lower_dentry = create_parents(dentry->d_inode,
+                                                     dentry,
+                                                     dentry->d_name.name,
+                                                     bindex);
+                       if (!lower_dentry || IS_ERR(lower_dentry)) {
+                               int ret = PTR_ERR(lower_dentry);
+                               if (!IS_COPYUP_ERR(ret))
+                                       printk(KERN_ERR
+                                              "unionfs: create_parents for "
+                                              "whiteout failed: bindex=%d "
+                                              "err=%d\n", bindex, ret);
+                               continue;
+                       }
+               }
+
+               lower_wh_dentry =
+                       lookup_one_len(name, lower_dentry->d_parent,
+                                      dentry->d_name.len + UNIONFS_WHLEN);
+               if (IS_ERR(lower_wh_dentry))
+                       continue;
+
+               /*
+                * The whiteout already exists. This used to be impossible,
+                * but now is possible because of opaqueness.
+                */
+               if (lower_wh_dentry->d_inode) {
+                       dput(lower_wh_dentry);
+                       err = 0;
+                       goto out;
+               }
+
+               err = init_lower_nd(&nd, LOOKUP_CREATE);
+               if (unlikely(err < 0))
+                       goto out;
+               lower_dir_dentry = lock_parent_wh(lower_wh_dentry);
+               err = is_robranch_super(dentry->d_sb, bindex);
+               if (!err)
+                       err = vfs_create(lower_dir_dentry->d_inode,
+                                        lower_wh_dentry,
+                                        ~current->fs->umask & S_IRWXUGO,
+                                        &nd);
+               unlock_dir(lower_dir_dentry);
+               dput(lower_wh_dentry);
+               release_lower_nd(&nd, err);
+
+               if (!err || !IS_COPYUP_ERR(err))
+                       break;
+       }
+
+       /* set dbopaque so that lookup will not proceed after this branch */
+       if (!err)
+               set_dbopaque(dentry, bindex);
+
+out:
+       kfree(name);
+       return err;
+}
+
+/*
+ * This is a helper function for rename, which ends up with hosed over
+ * dentries when it needs to revert.
+ */
+int unionfs_refresh_lower_dentry(struct dentry *dentry, int bindex)
+{
+       struct dentry *lower_dentry;
+       struct dentry *lower_parent;
+       int err = 0;
+
+       verify_locked(dentry);
+
+       unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
+       lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
+       unionfs_unlock_dentry(dentry->d_parent);
+
+       BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode));
+
+       lower_dentry = lookup_one_len(dentry->d_name.name, lower_parent,
+                                     dentry->d_name.len);
+       if (IS_ERR(lower_dentry)) {
+               err = PTR_ERR(lower_dentry);
+               goto out;
+       }
+
+       dput(unionfs_lower_dentry_idx(dentry, bindex));
+       iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
+       unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL);
+
+       if (!lower_dentry->d_inode) {
+               dput(lower_dentry);
+               unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+       } else {
+               unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
+               unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+                                           igrab(lower_dentry->d_inode));
+       }
+
+out:
+       return err;
+}
+
+int make_dir_opaque(struct dentry *dentry, int bindex)
+{
+       int err = 0;
+       struct dentry *lower_dentry, *diropq;
+       struct inode *lower_dir;
+       struct nameidata nd;
+       kernel_cap_t orig_cap;
+
+       /*
+        * Opaque directory whiteout markers are special files (like regular
+        * whiteouts), and should appear to the users as if they don't
+        * exist.  They should be created/deleted regardless of directory
+        * search/create permissions, but only for the duration of this
+        * creation of the .wh.__dir_opaque: file.  Note, this does not
+        * circumvent normal ->permission).
+        */
+       orig_cap = current->cap_effective;
+       cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);
+       cap_raise(current->cap_effective, CAP_DAC_OVERRIDE);
+
+       lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+       lower_dir = lower_dentry->d_inode;
+       BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) ||
+              !S_ISDIR(lower_dir->i_mode));
+
+       mutex_lock(&lower_dir->i_mutex);
+       diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
+                               sizeof(UNIONFS_DIR_OPAQUE) - 1);
+       if (IS_ERR(diropq)) {
+               err = PTR_ERR(diropq);
+               goto out;
+       }
+
+       err = init_lower_nd(&nd, LOOKUP_CREATE);
+       if (unlikely(err < 0))
+               goto out;
+       if (!diropq->d_inode)
+               err = vfs_create(lower_dir, diropq, S_IRUGO, &nd);
+       if (!err)
+               set_dbopaque(dentry, bindex);
+       release_lower_nd(&nd, err);
+
+       dput(diropq);
+
+out:
+       mutex_unlock(&lower_dir->i_mutex);
+       current->cap_effective = orig_cap;
+       return err;
+}
+
+/*
+ * returns the right n_link value based on the inode type
+ */
+int unionfs_get_nlinks(const struct inode *inode)
+{
+       /* don't bother to do all the work since we're unlinked */
+       if (inode->i_nlink == 0)
+               return 0;
+
+       if (!S_ISDIR(inode->i_mode))
+               return unionfs_lower_inode(inode)->i_nlink;
+
+       /*
+        * For directories, we return 1. The only place that could cares
+        * about links is readdir, and there's d_type there so even that
+        * doesn't matter.
+        */
+       return 1;
+}
+
+/* construct whiteout filename */
+char *alloc_whname(const char *name, int len)
+{
+       char *buf;
+
+       buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);
+       if (unlikely(!buf))
+               return ERR_PTR(-ENOMEM);
+
+       strcpy(buf, UNIONFS_WHPFX);
+       strlcat(buf, name, len + UNIONFS_WHLEN + 1);
+
+       return buf;
+}
-- 
1.5.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to