Skip to content

Commit

Permalink
ovl: copy up of disconnected dentries
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Amir Goldstein authored and Miklos Szeredi committed Jan 24, 2018
1 parent 829c28b commit aa3ff3c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 19 deletions.
56 changes: 41 additions & 15 deletions fs/overlayfs/copy_up.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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) */
Expand All @@ -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;
}

Expand All @@ -717,14 +731,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err)
return err;

ovl_path_upper(parent, &parentpath);
ctx.destdir = parentpath.dentry;
ctx.destname = dentry->d_name;
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);
if (err)
return err;
err = vfs_getattr(&parentpath, &ctx.pstat,
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)
Expand All @@ -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);
}
Expand All @@ -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
Expand All @@ -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))
Expand Down
4 changes: 3 additions & 1 deletion fs/overlayfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
7 changes: 4 additions & 3 deletions fs/overlayfs/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit aa3ff3c

Please sign in to comment.