ovl: copy up of disconnected dentries

With NFS export, some operations on decoded file handles (e.g. open,
link, setattr, xattr_set) may call copy up with a disconnected non-dir.
In this case, we will copy up lower inode to index dir without
linking it to upper dir.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Amir Goldstein 2017-10-15 18:00:20 +03:00 committed by Miklos Szeredi
parent 829c28be9b
commit aa3ff3c152
3 changed files with 48 additions and 19 deletions

View File

@ -450,7 +450,10 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
}
}
inode_unlock(udir);
ovl_set_nlink_upper(c->dentry);
if (err)
return err;
err = ovl_set_nlink_upper(c->dentry);
return err;
}
@ -655,6 +658,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
err = ovl_get_index_name(c->lowerpath.dentry, &c->destname);
if (err)
return err;
} else if (WARN_ON(!c->parent)) {
/* Disconnected dentry must be copied up to index dir */
return -EIO;
} else {
/*
* Mark parent "impure" because it may now contain non-pure
@ -677,12 +683,17 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
}
}
if (!err && c->indexed)
if (err)
goto out;
if (c->indexed)
ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
if (to_index) {
kfree(c->destname.name);
} else if (!err) {
/* Initialize nlink for copy up of disconnected dentry */
err = ovl_set_nlink_upper(c->dentry);
} else {
struct inode *udir = d_inode(c->destdir);
/* Restore timestamps on parent (best effort) */
@ -693,6 +704,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
ovl_dentry_set_upper_alias(c->dentry);
}
out:
if (to_index)
kfree(c->destname.name);
return err;
}
@ -717,14 +731,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err)
return err;
if (parent) {
ovl_path_upper(parent, &parentpath);
ctx.destdir = parentpath.dentry;
ctx.destname = dentry->d_name;
err = vfs_getattr(&parentpath, &ctx.pstat,
STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT);
STATX_ATIME | STATX_MTIME,
AT_STATX_SYNC_AS_STAT);
if (err)
return err;
}
/* maybe truncate regular file. this has no effect on dirs */
if (flags & O_TRUNC)
@ -745,7 +762,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
} else {
if (!ovl_dentry_upper(dentry))
err = ovl_do_copy_up(&ctx);
if (!err && !ovl_dentry_has_upper_alias(dentry))
if (!err && parent && !ovl_dentry_has_upper_alias(dentry))
err = ovl_link_up(&ctx);
ovl_copy_up_end(dentry);
}
@ -758,10 +775,19 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
{
int err = 0;
const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED);
/*
* With NFS export, copy up can get called for a disconnected non-dir.
* In this case, we will copy up lower inode to index dir without
* linking it to upper dir.
*/
if (WARN_ON(disconnected && d_is_dir(dentry)))
return -EIO;
while (!err) {
struct dentry *next;
struct dentry *parent;
struct dentry *parent = NULL;
/*
* Check if copy-up has happened as well as for upper alias (in
@ -777,12 +803,12 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
* with rename.
*/
if (ovl_dentry_upper(dentry) &&
ovl_dentry_has_upper_alias(dentry))
(ovl_dentry_has_upper_alias(dentry) || disconnected))
break;
next = dget(dentry);
/* find the topmost dentry not yet copied up */
for (;;) {
for (; !disconnected;) {
parent = dget_parent(next);
if (ovl_dentry_upper(parent))

View File

@ -351,8 +351,10 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
{
/* Copy up of disconnected dentry does not set upper alias */
if (ovl_dentry_upper(dentry) &&
ovl_dentry_has_upper_alias(dentry))
(ovl_dentry_has_upper_alias(dentry) ||
(dentry->d_flags & DCACHE_DISCONNECTED)))
return false;
if (special_file(d_inode(dentry)->i_mode))

View File

@ -229,9 +229,10 @@ void ovl_dentry_set_opaque(struct dentry *dentry)
}
/*
* For hard links it's possible for ovl_dentry_upper() to return positive, while
* there's no actual upper alias for the inode. Copy up code needs to know
* about the existence of the upper alias, so it can't use ovl_dentry_upper().
* For hard links and decoded file handles, it's possible for ovl_dentry_upper()
* to return positive, while there's no actual upper alias for the inode.
* Copy up code needs to know about the existence of the upper alias, so it
* can't use ovl_dentry_upper().
*/
bool ovl_dentry_has_upper_alias(struct dentry *dentry)
{