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 <ezk@cs.sunysb.edu>
else
fput(nd->intent.open.file);
}
+EXPORT_SYMBOL(release_open_intent);
static inline struct dentry *
do_revalidate(struct dentry *dentry, struct nameidata *nd)
/* 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;
}
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);
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
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);
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);
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,
}
/*
- * 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;
kfree(name);
if (!err)
- unionfs_inherit_mnt(dentry);
+ unionfs_postcopyup_setmnt(dentry);
unionfs_unlock_dentry(dentry);
unionfs_read_unlock(dentry->d_sb);
kfree(name);
if (!err)
- unionfs_inherit_mnt(new_dentry);
+ unionfs_postcopyup_setmnt(new_dentry);
unionfs_unlock_dentry(new_dentry);
unionfs_unlock_dentry(old_dentry);
kfree(name);
if (!err)
- unionfs_inherit_mnt(dentry);
+ unionfs_postcopyup_setmnt(dentry);
unionfs_unlock_dentry(dentry);
unionfs_check_inode(dir);
kfree(name);
if (!err)
- unionfs_inherit_mnt(dentry);
+ unionfs_postcopyup_setmnt(dentry);
unionfs_unlock_dentry(dentry);
unionfs_check_inode(dir);
/*
- * 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
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;
#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 */
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);
}
-
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;
*/
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,
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);
"the source in rename!\n");
err = -EIO;
}
- free_lower_nd(nd, local_err);
+ release_lower_nd(&nd, local_err);
}
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
* 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
*
* 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
*
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;
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;
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;
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);
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;
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:
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);
#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);
{
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;
}
{
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);
}
/* 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