Commit aa3ff3c1 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

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: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 829c28be
...@@ -450,7 +450,10 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -450,7 +450,10 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
} }
} }
inode_unlock(udir); inode_unlock(udir);
ovl_set_nlink_upper(c->dentry); if (err)
return err;
err = ovl_set_nlink_upper(c->dentry);
return err; return err;
} }
...@@ -655,6 +658,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -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); err = ovl_get_index_name(c->lowerpath.dentry, &c->destname);
if (err) if (err)
return err; return err;
} else if (WARN_ON(!c->parent)) {
/* Disconnected dentry must be copied up to index dir */
return -EIO;
} else { } else {
/* /*
* Mark parent "impure" because it may now contain non-pure * 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) ...@@ -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)); ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
if (to_index) { if (to_index) {
kfree(c->destname.name); /* Initialize nlink for copy up of disconnected dentry */
} else if (!err) { err = ovl_set_nlink_upper(c->dentry);
} else {
struct inode *udir = d_inode(c->destdir); struct inode *udir = d_inode(c->destdir);
/* Restore timestamps on parent (best effort) */ /* Restore timestamps on parent (best effort) */
...@@ -693,6 +704,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -693,6 +704,9 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
} }
out:
if (to_index)
kfree(c->destname.name);
return err; return err;
} }
...@@ -717,14 +731,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -717,14 +731,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err) if (err)
return err; return err;
ovl_path_upper(parent, &parentpath); if (parent) {
ctx.destdir = parentpath.dentry; ovl_path_upper(parent, &parentpath);
ctx.destname = dentry->d_name; ctx.destdir = parentpath.dentry;
ctx.destname = dentry->d_name;
err = vfs_getattr(&parentpath, &ctx.pstat, err = vfs_getattr(&parentpath, &ctx.pstat,
STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT); STATX_ATIME | STATX_MTIME,
if (err) AT_STATX_SYNC_AS_STAT);
return err; if (err)
return err;
}
/* maybe truncate regular file. this has no effect on dirs */ /* maybe truncate regular file. this has no effect on dirs */
if (flags & O_TRUNC) if (flags & O_TRUNC)
...@@ -745,7 +762,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -745,7 +762,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
} else { } else {
if (!ovl_dentry_upper(dentry)) if (!ovl_dentry_upper(dentry))
err = ovl_do_copy_up(&ctx); 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); err = ovl_link_up(&ctx);
ovl_copy_up_end(dentry); ovl_copy_up_end(dentry);
} }
...@@ -758,10 +775,19 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags) ...@@ -758,10 +775,19 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
{ {
int err = 0; int err = 0;
const struct cred *old_cred = ovl_override_creds(dentry->d_sb); 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) { while (!err) {
struct dentry *next; struct dentry *next;
struct dentry *parent; struct dentry *parent = NULL;
/* /*
* Check if copy-up has happened as well as for upper alias (in * 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) ...@@ -777,12 +803,12 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
* with rename. * with rename.
*/ */
if (ovl_dentry_upper(dentry) && if (ovl_dentry_upper(dentry) &&
ovl_dentry_has_upper_alias(dentry)) (ovl_dentry_has_upper_alias(dentry) || disconnected))
break; break;
next = dget(dentry); next = dget(dentry);
/* find the topmost dentry not yet copied up */ /* find the topmost dentry not yet copied up */
for (;;) { for (; !disconnected;) {
parent = dget_parent(next); parent = dget_parent(next);
if (ovl_dentry_upper(parent)) if (ovl_dentry_upper(parent))
......
...@@ -351,8 +351,10 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type) ...@@ -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) 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) && if (ovl_dentry_upper(dentry) &&
ovl_dentry_has_upper_alias(dentry)) (ovl_dentry_has_upper_alias(dentry) ||
(dentry->d_flags & DCACHE_DISCONNECTED)))
return false; return false;
if (special_file(d_inode(dentry)->i_mode)) if (special_file(d_inode(dentry)->i_mode))
......
...@@ -229,9 +229,10 @@ void ovl_dentry_set_opaque(struct dentry *dentry) ...@@ -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 * For hard links and decoded file handles, it's possible for ovl_dentry_upper()
* there's no actual upper alias for the inode. Copy up code needs to know * to return positive, while there's no actual upper alias for the inode.
* about the existence of the upper alias, so it can't use ovl_dentry_upper(). * 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) bool ovl_dentry_has_upper_alias(struct dentry *dentry)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment