overlayfs fixes for 4.18

This contains a fix for the vfs_mkdir() issue discovered by Al, as well as
 other fixes and cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCWxatHQAKCRDh3BK/laaZ
 POg5AP95a/uUOrTJeTsENJwTmyAwHed9a6y4abKtvNErxUm4awD9FmhyYXodzJNq
 9/mheT4kV2XkR/KkxI5sizfT1uPuvgA=
 =+ljQ
 -----END PGP SIGNATURE-----

Merge tag 'ovl-fixes-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "This contains a fix for the vfs_mkdir() issue discovered by Al, as
  well as other fixes and cleanups"

* tag 'ovl-fixes-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: use inode_insert5() to hash a newly created inode
  ovl: Pass argument to ovl_get_inode() in a structure
  vfs: factor out inode_insert5()
  ovl: clean up copy-up error paths
  ovl: return EIO on internal error
  ovl: make ovl_create_real() cope with vfs_mkdir() safely
  ovl: create helper ovl_create_temp()
  ovl: return dentry from ovl_create_real()
  ovl: struct cattr cleanups
  ovl: strip debug argument from ovl_do_ helpers
  ovl: remove WARN_ON() real inode attributes mismatch
  ovl: Kconfig documentation fixes
  ovl: update documentation for unionmount-testsuite
This commit is contained in:
Linus Torvalds 2018-06-07 08:53:50 -07:00
commit 70f2ae1f00
11 changed files with 320 additions and 274 deletions

View File

@ -429,11 +429,12 @@ This verification may cause significant overhead in some cases.
Testsuite
---------
There's testsuite developed by David Howells at:
There's a testsuite originally developed by David Howells and currently
maintained by Amir Goldstein at:
git://git.infradead.org/users/dhowells/unionmount-testsuite.git
https://github.com/amir73il/unionmount-testsuite.git
Run as root:
# cd unionmount-testsuite
# ./run --ov
# ./run --ov --verify

View File

@ -1003,6 +1003,70 @@ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2)
}
EXPORT_SYMBOL(unlock_two_nondirectories);
/**
* inode_insert5 - obtain an inode from a mounted file system
* @inode: pre-allocated inode to use for insert to cache
* @hashval: hash value (usually inode number) to get
* @test: callback used for comparisons between inodes
* @set: callback used to initialize a new struct inode
* @data: opaque data pointer to pass to @test and @set
*
* Search for the inode specified by @hashval and @data in the inode cache,
* and if present it is return it with an increased reference count. This is
* a variant of iget5_locked() for callers that don't want to fail on memory
* allocation of inode.
*
* If the inode is not in cache, insert the pre-allocated inode to cache and
* return it locked, hashed, and with the I_NEW flag set. The file system gets
* to fill it in before unlocking it via unlock_new_inode().
*
* Note both @test and @set are called with the inode_hash_lock held, so can't
* sleep.
*/
struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *), void *data)
{
struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
struct inode *old;
again:
spin_lock(&inode_hash_lock);
old = find_inode(inode->i_sb, head, test, data);
if (unlikely(old)) {
/*
* Uhhuh, somebody else created the same inode under us.
* Use the old inode instead of the preallocated one.
*/
spin_unlock(&inode_hash_lock);
wait_on_inode(old);
if (unlikely(inode_unhashed(old))) {
iput(old);
goto again;
}
return old;
}
if (set && unlikely(set(inode, data))) {
inode = NULL;
goto unlock;
}
/*
* Return the locked inode with I_NEW set, the
* caller is responsible for filling in the contents
*/
spin_lock(&inode->i_lock);
inode->i_state |= I_NEW;
hlist_add_head(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
unlock:
spin_unlock(&inode_hash_lock);
return inode;
}
EXPORT_SYMBOL(inode_insert5);
/**
* iget5_locked - obtain an inode from a mounted file system
* @sb: super block of file system
@ -1027,66 +1091,18 @@ struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *), void *data)
{
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
struct inode *inode;
again:
spin_lock(&inode_hash_lock);
inode = find_inode(sb, head, test, data);
spin_unlock(&inode_hash_lock);
struct inode *inode = ilookup5(sb, hashval, test, data);
if (inode) {
wait_on_inode(inode);
if (unlikely(inode_unhashed(inode))) {
iput(inode);
goto again;
}
return inode;
}
if (!inode) {
struct inode *new = new_inode(sb);
inode = alloc_inode(sb);
if (inode) {
struct inode *old;
spin_lock(&inode_hash_lock);
/* We released the lock, so.. */
old = find_inode(sb, head, test, data);
if (!old) {
if (set(inode, data))
goto set_failed;
spin_lock(&inode->i_lock);
inode->i_state = I_NEW;
hlist_add_head(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
inode_sb_list_add(inode);
spin_unlock(&inode_hash_lock);
/* Return the locked inode with I_NEW set, the
* caller is responsible for filling in the contents
*/
return inode;
}
/*
* Uhhuh, somebody else created the same inode under
* us. Use the old inode instead of the one we just
* allocated.
*/
spin_unlock(&inode_hash_lock);
destroy_inode(inode);
inode = old;
wait_on_inode(inode);
if (unlikely(inode_unhashed(inode))) {
iput(inode);
goto again;
if (new) {
inode = inode_insert5(new, hashval, test, set, data);
if (unlikely(inode != new))
iput(new);
}
}
return inode;
set_failed:
spin_unlock(&inode_hash_lock);
destroy_inode(inode);
return NULL;
}
EXPORT_SYMBOL(iget5_locked);
@ -1427,43 +1443,13 @@ EXPORT_SYMBOL(insert_inode_locked);
int insert_inode_locked4(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *), void *data)
{
struct super_block *sb = inode->i_sb;
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
struct inode *old = inode_insert5(inode, hashval, test, NULL, data);
while (1) {
struct inode *old = NULL;
spin_lock(&inode_hash_lock);
hlist_for_each_entry(old, head, i_hash) {
if (old->i_sb != sb)
continue;
if (!test(old, data))
continue;
spin_lock(&old->i_lock);
if (old->i_state & (I_FREEING|I_WILL_FREE)) {
spin_unlock(&old->i_lock);
continue;
}
break;
}
if (likely(!old)) {
spin_lock(&inode->i_lock);
inode->i_state |= I_NEW;
hlist_add_head(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
return 0;
}
__iget(old);
spin_unlock(&old->i_lock);
spin_unlock(&inode_hash_lock);
wait_on_inode(old);
if (unlikely(!inode_unhashed(old))) {
iput(old);
return -EBUSY;
}
if (old != inode) {
iput(old);
return -EBUSY;
}
return 0;
}
EXPORT_SYMBOL(insert_inode_locked4);

View File

@ -11,7 +11,7 @@ config OVERLAY_FS
For more information see Documentation/filesystems/overlayfs.txt
config OVERLAY_FS_REDIRECT_DIR
bool "Overlayfs: turn on redirect dir feature by default"
bool "Overlayfs: turn on redirect directory feature by default"
depends on OVERLAY_FS
help
If this config option is enabled then overlay filesystems will use
@ -46,7 +46,7 @@ config OVERLAY_FS_INDEX
depends on OVERLAY_FS
help
If this config option is enabled then overlay filesystems will use
the inodes index dir to map lower inodes to upper inodes by default.
the index directory to map lower inodes to upper inodes by default.
In this case it is still possible to turn off index globally with the
"index=off" module option or on a filesystem instance basis with the
"index=off" mount option.
@ -66,7 +66,7 @@ config OVERLAY_FS_NFS_EXPORT
depends on OVERLAY_FS_INDEX
help
If this config option is enabled then overlay filesystems will use
the inodes index dir to decode overlay NFS file handles by default.
the index directory to decode overlay NFS file handles by default.
In this case, it is still possible to turn off NFS export support
globally with the "nfs_export=off" module option or on a filesystem
instance basis with the "nfs_export=off" mount option.

View File

@ -365,17 +365,14 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
if (err)
return err;
temp = ovl_lookup_temp(indexdir);
temp = ovl_create_temp(indexdir, OVL_CATTR(S_IFDIR | 0));
err = PTR_ERR(temp);
if (IS_ERR(temp))
goto temp_err;
err = ovl_do_mkdir(dir, temp, S_IFDIR, true);
if (err)
goto out;
goto free_name;
err = ovl_set_upper_fh(upper, temp);
if (err)
goto out_cleanup;
goto out;
index = lookup_one_len(name.name, indexdir, name.len);
if (IS_ERR(index)) {
@ -384,23 +381,13 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
err = ovl_do_rename(dir, temp, dir, index, 0);
dput(index);
}
if (err)
goto out_cleanup;
out:
if (err)
ovl_cleanup(dir, temp);
dput(temp);
free_name:
kfree(name.name);
return err;
temp_err:
err = PTR_ERR(temp);
temp = NULL;
goto out;
out_cleanup:
ovl_cleanup(dir, temp);
goto out;
}
struct ovl_copy_up_ctx {
@ -439,8 +426,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
c->dentry->d_name.len);
err = PTR_ERR(upper);
if (!IS_ERR(upper)) {
err = ovl_do_link(ovl_dentry_upper(c->dentry), udir, upper,
true);
err = ovl_do_link(ovl_dentry_upper(c->dentry), udir, upper);
dput(upper);
if (!err) {
@ -470,7 +456,7 @@ static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
return PTR_ERR(upper);
if (c->tmpfile)
err = ovl_do_link(temp, udir, upper, true);
err = ovl_do_link(temp, udir, upper);
else
err = ovl_do_rename(d_inode(c->workdir), temp, udir, upper, 0);
@ -481,13 +467,13 @@ static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
return err;
}
static int ovl_get_tmpfile(struct ovl_copy_up_ctx *c, struct dentry **tempp)
static struct dentry *ovl_get_tmpfile(struct ovl_copy_up_ctx *c)
{
int err;
struct dentry *temp;
const struct cred *old_creds = NULL;
struct cred *new_creds = NULL;
struct cattr cattr = {
struct ovl_cattr cattr = {
/* Can't properly set mode on creation because of the umask */
.mode = c->stat.mode & S_IFMT,
.rdev = c->stat.rdev,
@ -495,41 +481,24 @@ static int ovl_get_tmpfile(struct ovl_copy_up_ctx *c, struct dentry **tempp)
};
err = security_inode_copy_up(c->dentry, &new_creds);
temp = ERR_PTR(err);
if (err < 0)
goto out;
if (new_creds)
old_creds = override_creds(new_creds);
if (c->tmpfile) {
if (c->tmpfile)
temp = ovl_do_tmpfile(c->workdir, c->stat.mode);
if (IS_ERR(temp))
goto temp_err;
} else {
temp = ovl_lookup_temp(c->workdir);
if (IS_ERR(temp))
goto temp_err;
err = ovl_create_real(d_inode(c->workdir), temp, &cattr,
NULL, true);
if (err) {
dput(temp);
goto out;
}
}
err = 0;
*tempp = temp;
else
temp = ovl_create_temp(c->workdir, &cattr);
out:
if (new_creds) {
revert_creds(old_creds);
put_cred(new_creds);
}
return err;
temp_err:
err = PTR_ERR(temp);
goto out;
return temp;
}
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
@ -579,21 +548,21 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
struct inode *udir = c->destdir->d_inode;
struct inode *inode;
struct dentry *newdentry = NULL;
struct dentry *temp = NULL;
struct dentry *temp;
int err;
err = ovl_get_tmpfile(c, &temp);
if (err)
goto out;
temp = ovl_get_tmpfile(c);
if (IS_ERR(temp))
return PTR_ERR(temp);
err = ovl_copy_up_inode(c, temp);
if (err)
goto out_cleanup;
goto out;
if (S_ISDIR(c->stat.mode) && c->indexed) {
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
if (err)
goto out_cleanup;
goto out;
}
if (c->tmpfile) {
@ -604,7 +573,7 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
err = ovl_install_temp(c, temp, &newdentry);
}
if (err)
goto out_cleanup;
goto out;
inode = d_inode(c->dentry);
ovl_inode_update(inode, newdentry);
@ -612,13 +581,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
ovl_set_flag(OVL_WHITEOUTS, inode);
out:
if (err && !c->tmpfile)
ovl_cleanup(d_inode(c->workdir), temp);
dput(temp);
return err;
out_cleanup:
if (!c->tmpfile)
ovl_cleanup(d_inode(c->workdir), temp);
goto out;
}
/*

View File

@ -43,7 +43,7 @@ int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
return err;
}
struct dentry *ovl_lookup_temp(struct dentry *workdir)
static struct dentry *ovl_lookup_temp(struct dentry *workdir)
{
struct dentry *temp;
char name[20];
@ -114,36 +114,72 @@ kill_whiteout:
goto out;
}
int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr, struct dentry *hardlink, bool debug)
static int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry,
umode_t mode)
{
int err;
struct dentry *d, *dentry = *newdentry;
err = ovl_do_mkdir(dir, dentry, mode);
if (err)
return err;
if (likely(!d_unhashed(dentry)))
return 0;
/*
* vfs_mkdir() may succeed and leave the dentry passed
* to it unhashed and negative. If that happens, try to
* lookup a new hashed and positive dentry.
*/
d = lookup_one_len(dentry->d_name.name, dentry->d_parent,
dentry->d_name.len);
if (IS_ERR(d)) {
pr_warn("overlayfs: failed lookup after mkdir (%pd2, err=%i).\n",
dentry, err);
return PTR_ERR(d);
}
dput(dentry);
*newdentry = d;
return 0;
}
struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct ovl_cattr *attr)
{
int err;
if (newdentry->d_inode)
return -ESTALE;
if (IS_ERR(newdentry))
return newdentry;
if (hardlink) {
err = ovl_do_link(hardlink, dir, newdentry, debug);
err = -ESTALE;
if (newdentry->d_inode)
goto out;
if (attr->hardlink) {
err = ovl_do_link(attr->hardlink, dir, newdentry);
} else {
switch (attr->mode & S_IFMT) {
case S_IFREG:
err = ovl_do_create(dir, newdentry, attr->mode, debug);
err = ovl_do_create(dir, newdentry, attr->mode);
break;
case S_IFDIR:
err = ovl_do_mkdir(dir, newdentry, attr->mode, debug);
/* mkdir is special... */
err = ovl_mkdir_real(dir, &newdentry, attr->mode);
break;
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
err = ovl_do_mknod(dir, newdentry,
attr->mode, attr->rdev, debug);
err = ovl_do_mknod(dir, newdentry, attr->mode,
attr->rdev);
break;
case S_IFLNK:
err = ovl_do_symlink(dir, newdentry, attr->link, debug);
err = ovl_do_symlink(dir, newdentry, attr->link);
break;
default:
@ -155,9 +191,20 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry,
* Not quite sure if non-instantiated dentry is legal or not.
* VFS doesn't seem to care so check and warn here.
*/
err = -ENOENT;
err = -EIO;
}
return err;
out:
if (err) {
dput(newdentry);
return ERR_PTR(err);
}
return newdentry;
}
struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr)
{
return ovl_create_real(d_inode(workdir), ovl_lookup_temp(workdir),
attr);
}
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
@ -182,24 +229,54 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
}
/* Common operations required to be done after creation of file on upper */
static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
struct dentry *newdentry, bool hardlink)
/*
* Common operations required to be done after creation of file on upper.
* If @hardlink is false, then @inode is a pre-allocated inode, we may or
* may not use to instantiate the new dentry.
*/
static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
struct dentry *newdentry, bool hardlink)
{
struct ovl_inode_params oip = {
.upperdentry = newdentry,
.newinode = inode,
};
ovl_dentry_version_inc(dentry->d_parent, false);
ovl_dentry_set_upper_alias(dentry);
if (!hardlink) {
ovl_inode_update(inode, newdentry);
ovl_copyattr(newdentry->d_inode, inode);
/*
* ovl_obtain_alias() can be called after ovl_create_real()
* and before we get here, so we may get an inode from cache
* with the same real upperdentry that is not the inode we
* pre-allocated. In this case we will use the cached inode
* to instantiate the new dentry.
*
* XXX: if we ever use ovl_obtain_alias() to decode directory
* file handles, need to use ovl_get_inode_locked() and
* d_instantiate_new() here to prevent from creating two
* hashed directory inode aliases.
*/
inode = ovl_get_inode(dentry->d_sb, &oip);
if (WARN_ON(IS_ERR(inode)))
return PTR_ERR(inode);
} else {
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
dput(newdentry);
inc_nlink(inode);
}
d_instantiate(dentry, inode);
if (inode != oip.newinode) {
pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n",
dentry);
}
/* Force lookup of new upper hardlink to find its lower */
if (hardlink)
d_drop(dentry);
return 0;
}
static bool ovl_type_merge(struct dentry *dentry)
@ -213,38 +290,42 @@ static bool ovl_type_origin(struct dentry *dentry)
}
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct cattr *attr, struct dentry *hardlink)
struct ovl_cattr *attr)
{
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *udir = upperdir->d_inode;
struct dentry *newdentry;
int err;
if (!hardlink && !IS_POSIXACL(udir))
if (!attr->hardlink && !IS_POSIXACL(udir))
attr->mode &= ~current_umask();
inode_lock_nested(udir, I_MUTEX_PARENT);
newdentry = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len);
newdentry = ovl_create_real(udir,
lookup_one_len(dentry->d_name.name,
upperdir,
dentry->d_name.len),
attr);
err = PTR_ERR(newdentry);
if (IS_ERR(newdentry))
goto out_unlock;
err = ovl_create_real(udir, newdentry, attr, hardlink, false);
if (err)
goto out_dput;
if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry)) {
/* Setting opaque here is just an optimization, allow to fail */
ovl_set_opaque(dentry, newdentry);
}
ovl_instantiate(dentry, inode, newdentry, !!hardlink);
newdentry = NULL;
out_dput:
dput(newdentry);
err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
if (err)
goto out_cleanup;
out_unlock:
inode_unlock(udir);
return err;
out_cleanup:
ovl_cleanup(udir, newdentry);
dput(newdentry);
goto out_unlock;
}
static struct dentry *ovl_clear_empty(struct dentry *dentry,
@ -280,16 +361,11 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
if (upper->d_parent->d_inode != udir)
goto out_unlock;
opaquedir = ovl_lookup_temp(workdir);
opaquedir = ovl_create_temp(workdir, OVL_CATTR(stat.mode));
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
goto out_unlock;
err = ovl_create_real(wdir, opaquedir,
&(struct cattr){.mode = stat.mode}, NULL, true);
if (err)
goto out_dput;
err = ovl_copy_xattr(upper, opaquedir);
if (err)
goto out_cleanup;
@ -319,7 +395,6 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
out_cleanup:
ovl_cleanup(wdir, opaquedir);
out_dput:
dput(opaquedir);
out_unlock:
unlock_rename(workdir, upperdir);
@ -354,8 +429,7 @@ out_free:
}
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct cattr *cattr,
struct dentry *hardlink)
struct ovl_cattr *cattr)
{
struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode;
@ -365,6 +439,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct dentry *newdentry;
int err;
struct posix_acl *acl, *default_acl;
bool hardlink = !!cattr->hardlink;
if (WARN_ON(!workdir))
return -EROFS;
@ -380,20 +455,16 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err)
goto out;
newdentry = ovl_lookup_temp(workdir);
err = PTR_ERR(newdentry);
if (IS_ERR(newdentry))
goto out_unlock;
upper = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len);
err = PTR_ERR(upper);
if (IS_ERR(upper))
goto out_dput;
goto out_unlock;
err = ovl_create_real(wdir, newdentry, cattr, hardlink, true);
if (err)
goto out_dput2;
newdentry = ovl_create_temp(workdir, cattr);
err = PTR_ERR(newdentry);
if (IS_ERR(newdentry))
goto out_dput;
/*
* mode could have been mutilated due to umask (e.g. sgid directory)
@ -439,12 +510,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err)
goto out_cleanup;
}
ovl_instantiate(dentry, inode, newdentry, !!hardlink);
newdentry = NULL;
out_dput2:
dput(upper);
err = ovl_instantiate(dentry, inode, newdentry, hardlink);
if (err)
goto out_cleanup;
out_dput:
dput(newdentry);
dput(upper);
out_unlock:
unlock_rename(workdir, upperdir);
out:
@ -456,12 +526,12 @@ out:
out_cleanup:
ovl_cleanup(wdir, newdentry);
goto out_dput2;
dput(newdentry);
goto out_dput;
}
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct cattr *attr, struct dentry *hardlink,
bool origin)
struct ovl_cattr *attr, bool origin)
{
int err;
const struct cred *old_cred;
@ -489,7 +559,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
if (override_cred) {
override_cred->fsuid = inode->i_uid;
override_cred->fsgid = inode->i_gid;
if (!hardlink) {
if (!attr->hardlink) {
err = security_dentry_create_files_as(dentry,
attr->mode, &dentry->d_name, old_cred,
override_cred);
@ -502,21 +572,12 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
put_cred(override_cred);
if (!ovl_dentry_is_whiteout(dentry))
err = ovl_create_upper(dentry, inode, attr,
hardlink);
err = ovl_create_upper(dentry, inode, attr);
else
err = ovl_create_over_whiteout(dentry, inode, attr,
hardlink);
err = ovl_create_over_whiteout(dentry, inode, attr);
}
out_revert_creds:
revert_creds(old_cred);
if (!err) {
struct inode *realinode = d_inode(ovl_dentry_upper(dentry));
WARN_ON(inode->i_mode != realinode->i_mode);
WARN_ON(!uid_eq(inode->i_uid, realinode->i_uid));
WARN_ON(!gid_eq(inode->i_gid, realinode->i_gid));
}
return err;
}
@ -525,7 +586,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
{
int err;
struct inode *inode;
struct cattr attr = {
struct ovl_cattr attr = {
.rdev = rdev,
.link = link,
};
@ -534,6 +595,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
if (err)
goto out;
/* Preallocate inode to be used by ovl_get_inode() */
err = -ENOMEM;
inode = ovl_new_inode(dentry->d_sb, mode, rdev);
if (!inode)
@ -542,8 +604,9 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
inode_init_owner(inode, dentry->d_parent->d_inode, mode);
attr.mode = inode->i_mode;
err = ovl_create_or_link(dentry, inode, &attr, NULL, false);
if (err)
err = ovl_create_or_link(dentry, inode, &attr, false);
/* Did we end up using the preallocated inode? */
if (inode != d_inode(dentry))
iput(inode);
out_drop_write:
@ -601,8 +664,9 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
inode = d_inode(old);
ihold(inode);
err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old),
ovl_type_origin(old));
err = ovl_create_or_link(new, inode,
&(struct ovl_cattr) {.hardlink = ovl_dentry_upper(old)},
ovl_type_origin(old));
if (err)
iput(inode);

View File

@ -300,12 +300,18 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
struct dentry *dentry;
struct inode *inode;
struct ovl_entry *oe;
struct ovl_inode_params oip = {
.lowerpath = lowerpath,
.index = index,
.numlower = !!lower
};
/* We get overlay directory dentries with ovl_lookup_real() */
if (d_is_dir(upper ?: lower))
return ERR_PTR(-EIO);
inode = ovl_get_inode(sb, dget(upper), lowerpath, index, !!lower);
oip.upperdentry = dget(upper);
inode = ovl_get_inode(sb, &oip);
if (IS_ERR(inode)) {
dput(upper);
return ERR_CAST(inode);

View File

@ -749,15 +749,26 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
return true;
}
struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
struct ovl_path *lowerpath, struct dentry *index,
unsigned int numlower)
static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode,
struct inode *key)
{
return newinode ? inode_insert5(newinode, (unsigned long) key,
ovl_inode_test, ovl_inode_set, key) :
iget5_locked(sb, (unsigned long) key,
ovl_inode_test, ovl_inode_set, key);
}
struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip)
{
struct dentry *upperdentry = oip->upperdentry;
struct ovl_path *lowerpath = oip->lowerpath;
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode;
struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL;
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
int fsid = bylower ? lowerpath->layer->fsid : 0;
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry,
oip->index);
int fsid = bylower ? oip->lowerpath->layer->fsid : 0;
bool is_dir;
unsigned long ino = 0;
@ -774,8 +785,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
upperdentry);
unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
inode = iget5_locked(sb, (unsigned long) key,
ovl_inode_test, ovl_inode_set, key);
inode = ovl_iget5(sb, oip->newinode, key);
if (!inode)
goto out_nomem;
if (!(inode->i_state & I_NEW)) {
@ -811,12 +821,12 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
if (upperdentry && ovl_is_impuredir(upperdentry))
ovl_set_flag(OVL_IMPURE, inode);
if (index)
if (oip->index)
ovl_set_flag(OVL_INDEX, inode);
/* Check for non-merge dir that may have whiteouts */
if (is_dir) {
if (((upperdentry && lowerdentry) || numlower > 1) ||
if (((upperdentry && lowerdentry) || oip->numlower > 1) ||
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
ovl_set_flag(OVL_WHITEOUTS, inode);
}

View File

@ -1004,8 +1004,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperdentry = dget(index);
if (upperdentry || ctr) {
inode = ovl_get_inode(dentry->d_sb, upperdentry, stack, index,
ctr);
struct ovl_inode_params oip = {
.upperdentry = upperdentry,
.lowerpath = stack,
.index = index,
.numlower = ctr,
};
inode = ovl_get_inode(dentry->d_sb, &oip);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_free_oe;

View File

@ -86,6 +86,7 @@ struct ovl_fh {
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{
int err = vfs_rmdir(dir, dentry);
pr_debug("rmdir(%pd2) = %i\n", dentry, err);
return err;
}
@ -93,56 +94,52 @@ static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry)
{
int err = vfs_unlink(dir, dentry, NULL);
pr_debug("unlink(%pd2) = %i\n", dentry, err);
return err;
}
static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry, bool debug)
struct dentry *new_dentry)
{
int err = vfs_link(old_dentry, dir, new_dentry, NULL);
if (debug) {
pr_debug("link(%pd2, %pd2) = %i\n",
old_dentry, new_dentry, err);
}
pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err);
return err;
}
static inline int ovl_do_create(struct inode *dir, struct dentry *dentry,
umode_t mode, bool debug)
umode_t mode)
{
int err = vfs_create(dir, dentry, mode, true);
if (debug)
pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err);
pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err);
return err;
}
static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry,
umode_t mode, bool debug)
umode_t mode)
{
int err = vfs_mkdir(dir, dentry, mode);
if (debug)
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
return err;
}
static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t dev, bool debug)
umode_t mode, dev_t dev)
{
int err = vfs_mknod(dir, dentry, mode, dev);
if (debug) {
pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n",
dentry, mode, dev, err);
}
pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err);
return err;
}
static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry,
const char *oldname, bool debug)
const char *oldname)
{
int err = vfs_symlink(dir, dentry, oldname);
if (debug)
pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
return err;
}
@ -168,11 +165,8 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
{
int err;
pr_debug("rename(%pd2, %pd2, 0x%x)\n",
olddentry, newdentry, flags);
pr_debug("rename(%pd2, %pd2, 0x%x)\n", olddentry, newdentry, flags);
err = vfs_rename(olddir, olddentry, newdir, newdentry, NULL, flags);
if (err) {
pr_debug("...rename(%pd2, %pd2, ...) = %i\n",
olddentry, newdentry, err);
@ -334,12 +328,18 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name);
struct ovl_inode_params {
struct inode *newinode;
struct dentry *upperdentry;
struct ovl_path *lowerpath;
struct dentry *index;
unsigned int numlower;
};
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
bool is_upper);
struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
struct ovl_path *lowerpath, struct dentry *index,
unsigned int numlower);
struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip);
static inline void ovl_copyattr(struct inode *from, struct inode *to)
{
to->i_uid = from->i_uid;
@ -352,18 +352,21 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
/* dir.c */
extern const struct inode_operations ovl_dir_inode_operations;
struct dentry *ovl_lookup_temp(struct dentry *workdir);
int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
struct dentry *dentry);
struct cattr {
struct ovl_cattr {
dev_t rdev;
umode_t mode;
const char *link;
struct dentry *hardlink;
};
int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr,
struct dentry *hardlink, bool debug);
#define OVL_CATTR(m) (&(struct ovl_cattr) { .mode = (m) })
struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct ovl_cattr *attr);
int ovl_cleanup(struct inode *dir, struct dentry *dentry);
struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
/* copy_up.c */
int ovl_copy_up(struct dentry *dentry);

View File

@ -611,11 +611,10 @@ retry:
goto retry;
}
err = ovl_create_real(dir, work,
&(struct cattr){.mode = S_IFDIR | 0},
NULL, true);
if (err)
goto out_dput;
work = ovl_create_real(dir, work, OVL_CATTR(attr.ia_mode));
err = PTR_ERR(work);
if (IS_ERR(work))
goto out_err;
/*
* Try to remove POSIX ACL xattrs from workdir. We are good if:

View File

@ -2886,6 +2886,10 @@ extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *, void *), void *data);
extern struct inode *ilookup(struct super_block *sb, unsigned long ino);
extern struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *),
void *data);
extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *);
extern struct inode * iget_locked(struct super_block *, unsigned long);
extern struct inode *find_inode_nowait(struct super_block *,