From 7bda5ede0576ee90c222759d6fb3d3d3c986c70a Mon Sep 17 00:00:00 2001 From: Erez_Zadok Date: Sun, 18 Nov 2007 14:08:27 -0500 Subject: [PATCH] Unionfs: imported fixes from korg branch Export release_open_intent. SElinux: xattr fixes (CAP_FOWNER). unionfs_xattr_kfree inline function. alloc_lower_nd -> init_lower_nd. free_lower_nd -> release_lower_nd. unionfs_purge_extras -> unionfs_postcopyup_release. unionfs_inherit_mnt -> unionfs_postcopyup_setmnt. minor code/copyright cleanups. MS_SILENT remount fix. simplify unionfs_mntget/put. Signed-off-by: Erez Zadok --- fs/namei.c | 1 + fs/unionfs/copyup.c | 58 +++++++++++++++------------- fs/unionfs/inode.c | 16 ++++---- fs/unionfs/lookup.c | 42 ++++++++------------ fs/unionfs/main.c | 15 ++++---- fs/unionfs/rename.c | 4 +- fs/unionfs/sioq.c | 4 -- fs/unionfs/sioq.h | 4 -- fs/unionfs/union.h | 94 +++++++++++++++------------------------------ fs/unionfs/unlink.c | 2 +- 10 files changed, 99 insertions(+), 141 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 73e2e66581..784ef51e83 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -389,6 +389,7 @@ void release_open_intent(struct nameidata *nd) else fput(nd->intent.open.file); } +EXPORT_SYMBOL(release_open_intent); static inline struct dentry * do_revalidate(struct dentry *dentry, struct nameidata *nd) diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c index 2d67e1d523..1b296a72ea 100644 --- a/fs/unionfs/copyup.c +++ b/fs/unionfs/copyup.c @@ -84,21 +84,27 @@ static int copyup_xattrs(struct dentry *old_lower_dentry, /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); + /* + * Selinux depends on "security.*" xattrs, so to maintain + * the security of copied-up files, if Selinux is active, + * then we must copy these xattrs as well. So we need to + * temporarily get FOWNER privileges. + */ + if (err == -EPERM && !capable(CAP_FOWNER)) { + cap_raise(current->cap_effective, CAP_FOWNER); + err = vfs_setxattr(new_lower_dentry, name_list, + attr_value, size, 0); + cap_lower(current->cap_effective, CAP_FOWNER); + } if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: - if (name_list_orig) - kfree(name_list_orig); - if (attr_value) - kfree(attr_value); - /* - * Ignore if xattr isn't supported. Also ignore EPERM because that - * requires CAP_SYS_ADMIN for security.* xattrs, but copyup happens - * as normal users. - */ - if (err == -ENOTSUPP || err == -EOPNOTSUPP || err == -EPERM) + unionfs_xattr_kfree(name_list_orig); + unionfs_xattr_kfree(attr_value); + /* Ignore if xattr isn't supported */ + if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; } @@ -189,19 +195,18 @@ static int __copyup_ndentry(struct dentry *old_lower_dentry, run_sioq(__unionfs_mknod, &args); err = args.err; } else if (S_ISREG(old_mode)) { - struct nameidata *nd = alloc_lower_nd(LOOKUP_CREATE); - if (!nd) { - err = -ENOMEM; + struct nameidata nd; + err = init_lower_nd(&nd, LOOKUP_CREATE); + if (err < 0) goto out; - } - args.create.nd = nd; + args.create.nd = &nd; args.create.parent = new_lower_parent_dentry->d_inode; args.create.dentry = new_lower_dentry; args.create.mode = old_mode; run_sioq(__unionfs_create, &args); err = args.err; - free_lower_nd(nd, err); + release_lower_nd(&nd, err); } else { printk(KERN_ERR "unionfs: unknown inode type %d\n", old_mode); @@ -516,7 +521,7 @@ out_free: if (err) goto out; if (!S_ISDIR(dentry->d_inode->i_mode)) { - unionfs_purge_extras(dentry); + unionfs_postcopyup_release(dentry); if (!unionfs_lower_inode(dentry->d_inode)) { /* * If we got here, then we copied up to an @@ -529,7 +534,7 @@ out_free: inode); } } - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); /* sync inode times from copied-up inode to our inode */ unionfs_copy_attr_times(dentry->d_inode); unionfs_check_inode(dir); @@ -851,8 +856,11 @@ out: return lower_dentry; } -/* set lower mnt of dentry+parents to the first parent node that has an mnt */ -void unionfs_inherit_mnt(struct dentry *dentry) +/* + * Post-copyup helper to ensure we have valid mnts: set lower mnt of + * dentry+parents to the first parent node that has an mnt. + */ +void unionfs_postcopyup_setmnt(struct dentry *dentry) { struct dentry *parent, *hasone; int bindex = dbstart(dentry); @@ -861,9 +869,8 @@ void unionfs_inherit_mnt(struct dentry *dentry) return; hasone = dentry->d_parent; /* this loop should stop at root dentry */ - while (!unionfs_lower_mnt_idx(hasone, bindex)) { + while (!unionfs_lower_mnt_idx(hasone, bindex)) hasone = hasone->d_parent; - } parent = dentry; while (!unionfs_lower_mnt_idx(parent, bindex)) { unionfs_set_lower_mnt_idx(parent, bindex, @@ -873,11 +880,10 @@ void unionfs_inherit_mnt(struct dentry *dentry) } /* - * Regular files should have only one lower object(s). On copyup, we may - * have leftover objects from previous branches. So purge all such extra - * objects and keep only the most recent, leftmost, copied-up one. + * Post-copyup helper to release all non-directory source objects of a + * copied-up file. Regular files should have only one lower object. */ -void unionfs_purge_extras(struct dentry *dentry) +void unionfs_postcopyup_release(struct dentry *dentry) { int bindex; diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c index fdbd06cc9f..262e955672 100644 --- a/fs/unionfs/inode.c +++ b/fs/unionfs/inode.c @@ -64,7 +64,7 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry, err = PTR_ERR(lower_dentry); goto out; } - unionfs_inherit_mnt(dentry->d_parent); + unionfs_postcopyup_setmnt(dentry->d_parent); } if (lower_dentry->d_inode && UNIONFS_D(dentry)->odf.whiteout) { @@ -109,7 +109,7 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry, out: if (!err) - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); unionfs_unlock_dentry(dentry); unionfs_read_unlock(dentry->d_sb); @@ -297,7 +297,7 @@ out: d_drop(new_dentry); if (!err) - unionfs_inherit_mnt(new_dentry); + unionfs_postcopyup_setmnt(new_dentry); unionfs_unlock_dentry(new_dentry); unionfs_unlock_dentry(old_dentry); @@ -351,7 +351,7 @@ static int unionfs_symlink(struct inode *dir, struct dentry *dentry, "for bindex = %d\n", bstart); goto out; } - unionfs_inherit_mnt(dentry->d_parent); + unionfs_postcopyup_setmnt(dentry->d_parent); } if (lower_dentry->d_inode && UNIONFS_D(dentry)->odf.whiteout) { @@ -397,7 +397,7 @@ static int unionfs_symlink(struct inode *dir, struct dentry *dentry, out: if (!err) - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); unionfs_unlock_dentry(dentry); unionfs_check_inode(dir); @@ -442,7 +442,7 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode) err = PTR_ERR(lower_dentry); goto out; } - unionfs_inherit_mnt(dentry->d_parent); + unionfs_postcopyup_setmnt(dentry->d_parent); } if (lower_dentry->d_inode && UNIONFS_D(dentry)->odf.whiteout) { @@ -547,7 +547,7 @@ static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, bstart, PTR_ERR(lower_dentry)); goto out; } - unionfs_inherit_mnt(dentry->d_parent); + unionfs_postcopyup_setmnt(dentry->d_parent); } if (lower_dentry->d_inode && UNIONFS_D(dentry)->odf.whiteout) { @@ -596,7 +596,7 @@ out: d_drop(dentry); if (!err) - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); unionfs_unlock_dentry(dentry); unionfs_check_inode(dir); diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c index 1f1598d550..c171a94a6f 100644 --- a/fs/unionfs/lookup.c +++ b/fs/unionfs/lookup.c @@ -513,16 +513,15 @@ void update_bstart(struct dentry *dentry) /* - * Allocate and fill in a nameidata structure (the intent part) we can pass - * to a lower file system. Returns NULL on error (only -ENOMEM possible), - * or a valid allocated nameidata structure. Inside that structure, this - * function may also return an allocated struct file (for open intents). - * The caller, when done with this nd, must kfree both the intent file and - * the entire nd. + * Initialize a nameidata structure (the intent part) we can pass to a lower + * file system. Returns 0 on success or -error (only -ENOMEM possible). + * Inside that nd structure, this function may also return an allocated + * struct file (for open intents). The caller, when done with this nd, must + * kfree the intent file (using release_lower_nd). */ -struct nameidata *alloc_lower_nd(unsigned int flags) +int init_lower_nd(struct nameidata *nd, unsigned int flags) { - struct nameidata *nd; + int err = 0; #ifdef ALLOC_LOWER_ND_FILE /* * XXX: one day we may need to have the lower return an open file @@ -532,10 +531,6 @@ struct nameidata *alloc_lower_nd(unsigned int flags) struct file *file; #endif /* ALLOC_LOWER_ND_FILE */ - nd = kzalloc(sizeof(struct nameidata), GFP_KERNEL); - if (!nd) - goto out; - switch (flags) { case LOOKUP_CREATE: nd->flags = LOOKUP_CREATE; @@ -543,9 +538,8 @@ struct nameidata *alloc_lower_nd(unsigned int flags) #ifdef ALLOC_LOWER_ND_FILE file = kzalloc(sizeof(struct file), GFP_KERNEL); if (!file) { - kfree(nd); - nd = NULL; - goto out; + err = -ENOMEM; + break; /* exit switch statement and thus return */ } nd->intent.open.file = file; #endif /* ALLOC_LOWER_ND_FILE */ @@ -558,17 +552,15 @@ struct nameidata *alloc_lower_nd(unsigned int flags) BUG(); break; } -out: - return nd; + + return err; } -void free_lower_nd(struct nameidata *nd, int err) +void release_lower_nd(struct nameidata *nd, int err) { - if (nd->intent.open.file) { - if (!err) - fput(nd->intent.open.file); /* XXX: open file not needed? */ - kfree(nd->intent.open.file); - } - kfree(nd); + if (nd->intent.open.file) + return; + if (!err) + release_open_intent(nd); + kfree(nd->intent.open.file); } - diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c index a26525b0e7..d273583383 100644 --- a/fs/unionfs/main.c +++ b/fs/unionfs/main.c @@ -165,14 +165,15 @@ skip: spliced = d_splice_alias(inode, dentry); if (IS_ERR(spliced)) err = PTR_ERR(spliced); - - /* - * d_splice can return a dentry if it was disconnected and - * had to be moved. We must make sure that the private data - * of the new dentry is correct and that the inode info was - * filled properly. Finally we must return this new dentry. - */ else if (spliced && spliced != dentry) { + /* + * d_splice can return a dentry if it was + * disconnected and had to be moved. We must ensure + * that the private data of the new dentry is + * correct and that the inode info was filled + * properly. Finally we must return this new + * dentry. + */ spliced->d_op = &unionfs_dops; spliced->d_fsdata = dentry->d_fsdata; dentry->d_fsdata = NULL; diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c index e75b5d98fc..3af9a7a82d 100644 --- a/fs/unionfs/rename.c +++ b/fs/unionfs/rename.c @@ -228,7 +228,7 @@ static int do_unionfs_rename(struct inode *old_dir, if (err) goto revert; } - free_lower_nd(nd, local_err); + release_lower_nd(&nd, local_err); } else if (err && old_bstart == 0) { if (bindex == 0) @@ -429,7 +429,7 @@ out: if (S_ISDIR(old_dentry->d_inode->i_mode)) atomic_dec(&UNIONFS_D(old_dentry)->generation); else - unionfs_purge_extras(old_dentry); + unionfs_postcopyup_release(old_dentry); /* * If the dentry already existed before the rename and is diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c index 529a71f9d3..1574461b65 100644 --- a/fs/unionfs/sioq.c +++ b/fs/unionfs/sioq.c @@ -3,11 +3,7 @@ * 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 * diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h index a3167a7c1f..50e1a3d2ea 100644 --- a/fs/unionfs/sioq.h +++ b/fs/unionfs/sioq.h @@ -3,11 +3,7 @@ * 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 * diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h index cf34f679b0..a5c96b9988 100644 --- a/fs/unionfs/union.h +++ b/fs/unionfs/union.h @@ -266,8 +266,8 @@ static inline void unionfs_double_lock_dentry(struct dentry *d1, extern int new_dentry_private_data(struct dentry *dentry); extern void free_dentry_private_data(struct dentry *dentry); extern void update_bstart(struct dentry *dentry); -extern struct nameidata *alloc_lower_nd(unsigned int flags); -extern void free_lower_nd(struct nameidata *nd, int err); +extern int init_lower_nd(struct nameidata *nd, unsigned int flags); +extern void release_lower_nd(struct nameidata *nd, int err); /* * EXTERNALS: @@ -289,9 +289,9 @@ extern int copyup_named_file(struct inode *dir, struct file *file, extern int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, int new_bindex, const char *name, int namelen, struct file **copyup_file, loff_t len); -/* helper functions for post-copyup cleanup */ -extern void unionfs_inherit_mnt(struct dentry *dentry); -extern void unionfs_purge_extras(struct dentry *dentry); +/* helper functions for post-copyup actions */ +extern void unionfs_postcopyup_setmnt(struct dentry *dentry); +extern void unionfs_postcopyup_release(struct dentry *dentry); /* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */ extern int check_empty(struct dentry *dentry, @@ -346,7 +346,7 @@ extern struct dentry *unionfs_interpose(struct dentry *this_dentry, #ifdef CONFIG_UNION_FS_XATTR /* Extended attribute functions. */ extern void *unionfs_xattr_alloc(size_t size, size_t limit); - +static inline void unionfs_xattr_kfree(const void *p) {kfree((p));} extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size); extern int unionfs_removexattr(struct dentry *dentry, const char *name); @@ -452,32 +452,18 @@ static inline struct vfsmount *unionfs_mntget(struct dentry *dentry, { struct vfsmount *mnt; - if (!dentry) { - if (bindex < 0) - return NULL; - if (!dentry && bindex >= 0) { -#ifdef UNIONFS_DEBUG - printk(KERN_DEBUG - "unionfs_mntget: dentry=%p bindex=%d\n", - dentry, bindex); -#endif /* UNIONFS_DEBUG */ - return NULL; - } - } + BUG_ON(!dentry); + BUG_ON(bindex < 0); + mnt = unionfs_lower_mnt_idx(dentry, bindex); - if (!mnt) { - if (bindex < 0) - return NULL; - if (!mnt && bindex >= 0) { + if (mnt) + mnt = mntget(mnt); #ifdef UNIONFS_DEBUG - printk(KERN_DEBUG - "unionfs_mntget: mnt=%p bindex=%d\n", - mnt, bindex); + else + printk(KERN_DEBUG "unionfs_mntget: mnt=%p bindex=%d\n", + mnt, bindex); #endif /* UNIONFS_DEBUG */ - return NULL; - } - } - mnt = mntget(mnt); + return mnt; } @@ -485,42 +471,22 @@ static inline void unionfs_mntput(struct dentry *dentry, int bindex) { struct vfsmount *mnt; - if (!dentry) { - if (bindex < 0) - return; - if (!dentry && bindex >= 0) { -#ifdef UNIONFS_DEBUG - printk(KERN_DEBUG - "unionfs_mntput: dentry=%p bindex=%d\n", - dentry, bindex); -#endif /* UNIONFS_DEBUG */ - return; - } - } + if (!dentry && bindex < 0) + return; + BUG_ON(!dentry || bindex < 0); + mnt = unionfs_lower_mnt_idx(dentry, bindex); - if (!mnt) { - if (bindex < 0) - return; - if (!mnt && bindex >= 0) { -#ifdef UNIONFS_DEBUG - /* - * Directories can have NULL lower objects in - * between start/end, but NOT if at the start/end - * range. We cannot verify that this dentry is a - * type=DIR, because it may already be a negative - * dentry. But if dbstart is greater than dbend, we - * know that this couldn't have been a regular file: - * it had to have been a directory. - */ - if (!(bindex > dbstart(dentry) && - bindex < dbend(dentry))) - printk(KERN_WARNING - "unionfs_mntput: mnt=%p bindex=%d\n", - mnt, bindex); -#endif /* UNIONFS_DEBUG */ - return; - } - } +#ifdef CONFIG_UNION_FS_DEBUG + /* + * Directories can have NULL lower objects in between start/end, but + * NOT if at the start/end range. We cannot verify that this dentry + * is a type=DIR, because it may already be a negative dentry. But + * if dbstart is greater than dbend, we know that this couldn't have + * been a regular file: it had to have been a directory. + */ + if (!mnt && !(bindex > dbstart(dentry) && bindex < dbend(dentry))) + pr_debug("unionfs: mntput: mnt=%p bindex=%d\n", mnt, bindex); +#endif /* CONFIG_UNION_FS_DEBUG */ mntput(mnt); } diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c index 9b67066c7b..303880d32a 100644 --- a/fs/unionfs/unlink.c +++ b/fs/unionfs/unlink.c @@ -126,7 +126,7 @@ int unionfs_unlink(struct inode *dir, struct dentry *dentry) } if (!err) { if (!S_ISDIR(dentry->d_inode->i_mode)) - unionfs_purge_extras(dentry); + unionfs_postcopyup_release(dentry); d_drop(dentry); /* * if unlink/whiteout succeeded, parent dir mtime has -- 2.43.0