|
@@ -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;
|
|
|
|
|
|
- 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)
|
|
@@ -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))
|