From 188a347ad632d24545c7dee581361d4c7ee9f43e Mon Sep 17 00:00:00 2001 From: Erez_Zadok Date: Sat, 18 Aug 2007 16:02:02 -0400 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 | 8 ++-- fs/unionfs/lookup.c | 42 +++++++++------------ fs/unionfs/main.c | 15 ++++---- fs/unionfs/rename.c | 18 ++++----- fs/unionfs/sioq.c | 4 -- fs/unionfs/sioq.h | 4 -- fs/unionfs/subr.c | 24 +++++------- fs/unionfs/super.c | 9 +++-- fs/unionfs/union.h | 92 ++++++++++++++++----------------------------- fs/unionfs/unlink.c | 2 +- 12 files changed, 119 insertions(+), 158 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a6fdd952274..0e03ed0019f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -399,6 +399,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 f13702d1e15..86b14773d56 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); @@ -510,7 +515,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 @@ -523,7 +528,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); @@ -837,8 +842,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); @@ -847,9 +855,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, @@ -859,11 +866,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 9e0801a3d0b..9adf27258ff 100644 --- a/fs/unionfs/inode.c +++ b/fs/unionfs/inode.c @@ -223,7 +223,7 @@ out: kfree(name); if (!err) - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); unionfs_unlock_dentry(dentry); unionfs_read_unlock(dentry->d_sb); @@ -412,7 +412,7 @@ out: kfree(name); if (!err) - unionfs_inherit_mnt(new_dentry); + unionfs_postcopyup_setmnt(new_dentry); unionfs_unlock_dentry(new_dentry); unionfs_unlock_dentry(old_dentry); @@ -577,7 +577,7 @@ out: kfree(name); if (!err) - unionfs_inherit_mnt(dentry); + unionfs_postcopyup_setmnt(dentry); unionfs_unlock_dentry(dentry); unionfs_check_inode(dir); @@ -856,7 +856,7 @@ out: kfree(name); 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 d8f2793a34b..d36f097c97a 100644 --- a/fs/unionfs/lookup.c +++ b/fs/unionfs/lookup.c @@ -568,16 +568,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 @@ -587,10 +586,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; @@ -598,9 +593,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 */ @@ -613,17 +607,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 c96133b5fe1..58c289700e7 100644 --- a/fs/unionfs/main.c +++ b/fs/unionfs/main.c @@ -143,14 +143,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 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. - */ 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 d6b0215343d..fcf5d7b3570 100644 --- a/fs/unionfs/rename.c +++ b/fs/unionfs/rename.c @@ -256,7 +256,7 @@ static int do_unionfs_rename(struct inode *old_dir, */ if ((old_bstart != old_bend) || (do_copyup != -1)) { struct dentry *lower_parent; - struct nameidata *nd; + struct nameidata nd; if (!wh_old || wh_old->d_inode || bwh_old < 0) { printk(KERN_ERR "unionfs: rename error " "(wh_old=%p/%p bwh_old=%d)\n", wh_old, @@ -264,14 +264,12 @@ static int do_unionfs_rename(struct inode *old_dir, err = -EIO; goto out; } - nd = alloc_lower_nd(LOOKUP_CREATE); - if (!nd) { - err = -ENOMEM; + err = init_lower_nd(&nd, LOOKUP_CREATE); + if (err < 0) goto out; - } lower_parent = lock_parent(wh_old); local_err = vfs_create(lower_parent->d_inode, wh_old, S_IRUGO, - nd); + &nd); unlock_dir(lower_parent); if (!local_err) set_dbopaque(old_dentry, bwh_old); @@ -284,7 +282,7 @@ static int do_unionfs_rename(struct inode *old_dir, "the source in rename!\n"); err = -EIO; } - free_lower_nd(nd, local_err); + release_lower_nd(&nd, local_err); } out: @@ -486,11 +484,11 @@ 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 (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) { - unionfs_purge_extras(new_dentry); - unionfs_inherit_mnt(new_dentry); + unionfs_postcopyup_release(new_dentry); + unionfs_postcopyup_setmnt(new_dentry); if (!unionfs_lower_inode(new_dentry->d_inode)) { /* * If we get here, it means that no copyup diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c index 478041d8031..ce17a8dfd72 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 e1807568c2a..3521ed290c2 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/subr.c b/fs/unionfs/subr.c index 3b7673739da..b7e7904fa85 100644 --- a/fs/unionfs/subr.c +++ b/fs/unionfs/subr.c @@ -29,7 +29,7 @@ int create_whiteout(struct dentry *dentry, int start) struct dentry *lower_dir_dentry; struct dentry *lower_dentry; struct dentry *lower_wh_dentry; - struct nameidata *nd; + struct nameidata nd; char *name = NULL; int err = -EINVAL; @@ -83,20 +83,18 @@ int create_whiteout(struct dentry *dentry, int start) goto out; } - nd = alloc_lower_nd(LOOKUP_CREATE); - if (!nd) { - err = -ENOMEM; + err = init_lower_nd(&nd, LOOKUP_CREATE); + if (err < 0) goto out; - } lower_dir_dentry = lock_parent(lower_wh_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) err = vfs_create(lower_dir_dentry->d_inode, lower_wh_dentry, ~current->fs->umask & S_IRWXUGO, - nd); + &nd); unlock_dir(lower_dir_dentry); dput(lower_wh_dentry); - free_lower_nd(nd, err); + release_lower_nd(&nd, err); if (!err || !IS_COPYUP_ERR(err)) break; @@ -158,7 +156,7 @@ int make_dir_opaque(struct dentry *dentry, int bindex) int err = 0; struct dentry *lower_dentry, *diropq; struct inode *lower_dir; - struct nameidata *nd; + struct nameidata nd; lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); lower_dir = lower_dentry->d_inode; @@ -173,16 +171,14 @@ int make_dir_opaque(struct dentry *dentry, int bindex) goto out; } - nd = alloc_lower_nd(LOOKUP_CREATE); - if (!nd) { - err = -ENOMEM; + err = init_lower_nd(&nd, LOOKUP_CREATE); + if (err < 0) goto out; - } if (!diropq->d_inode) - err = vfs_create(lower_dir, diropq, S_IRUGO, nd); + err = vfs_create(lower_dir, diropq, S_IRUGO, &nd); if (!err) set_dbopaque(dentry, bindex); - free_lower_nd(nd, err); + release_lower_nd(&nd, err); dput(diropq); diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c index 21a554a9244..3d9b7c70984 100644 --- a/fs/unionfs/super.c +++ b/fs/unionfs/super.c @@ -448,11 +448,12 @@ static int unionfs_remount_fs(struct super_block *sb, int *flags, unionfs_write_lock(sb); /* - * The VFS will take care of "ro" and "rw" flags, so anything else - * is an error. So we need to check if any other flags may have - * been passed (none are allowed/supported as of now). + * The VFS will take care of "ro" and "rw" flags, and we can safely + * ignore MS_SILENT, but anything else left over is an error. So we + * need to check if any other flags may have been passed (none are + * allowed/supported as of now). */ - if ((*flags & ~MS_RDONLY) != 0) { + if ((*flags & ~(MS_RDONLY | MS_SILENT)) != 0) { printk(KERN_WARNING "unionfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h index eb881b06286..1b11eabcdb5 100644 --- a/fs/unionfs/union.h +++ b/fs/unionfs/union.h @@ -251,8 +251,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: @@ -283,9 +283,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); extern int remove_whiteouts(struct dentry *dentry, struct dentry *lower_dentry, int bindex); @@ -344,7 +344,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); @@ -460,32 +460,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; } @@ -493,40 +479,28 @@ 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); + BUG_ON(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); + /* + * 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; - } + return; } mntput(mnt); } diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c index 47bebaba6d2..4ea350a63a5 100644 --- a/fs/unionfs/unlink.c +++ b/fs/unionfs/unlink.c @@ -89,7 +89,7 @@ int unionfs_unlink(struct inode *dir, struct dentry *dentry) /* call d_drop so the system "forgets" about us */ 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