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:
commit
70f2ae1f00
|
@ -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
|
||||
|
|
164
fs/inode.c
164
fs/inode.c
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 *,
|
||||
|
|
Loading…
Reference in New Issue