|
@@ -874,13 +874,13 @@ static bool ovl_can_move(struct dentry *dentry)
|
|
|
!d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
|
|
|
}
|
|
|
|
|
|
-static char *ovl_get_redirect(struct dentry *dentry, bool samedir)
|
|
|
+static char *ovl_get_redirect(struct dentry *dentry, bool abs_redirect)
|
|
|
{
|
|
|
char *buf, *ret;
|
|
|
struct dentry *d, *tmp;
|
|
|
int buflen = ovl_redirect_max + 1;
|
|
|
|
|
|
- if (samedir) {
|
|
|
+ if (!abs_redirect) {
|
|
|
ret = kstrndup(dentry->d_name.name, dentry->d_name.len,
|
|
|
GFP_KERNEL);
|
|
|
goto out;
|
|
@@ -934,15 +934,43 @@ out:
|
|
|
return ret ? ret : ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
|
|
|
+static bool ovl_need_absolute_redirect(struct dentry *dentry, bool samedir)
|
|
|
+{
|
|
|
+ struct dentry *lowerdentry;
|
|
|
+
|
|
|
+ if (!samedir)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (d_is_dir(dentry))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For non-dir hardlinked files, we need absolute redirects
|
|
|
+ * in general as two upper hardlinks could be in different
|
|
|
+ * dirs. We could put a relative redirect now and convert
|
|
|
+ * it to absolute redirect later. But when nlink > 1 and
|
|
|
+ * indexing is on, that means relative redirect needs to be
|
|
|
+ * converted to absolute during copy up of another lower
|
|
|
+ * hardllink as well.
|
|
|
+ *
|
|
|
+ * So without optimizing too much, just check if lower is
|
|
|
+ * a hard link or not. If lower is hard link, put absolute
|
|
|
+ * redirect.
|
|
|
+ */
|
|
|
+ lowerdentry = ovl_dentry_lower(dentry);
|
|
|
+ return (d_inode(lowerdentry)->i_nlink > 1);
|
|
|
+}
|
|
|
+
|
|
|
static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
|
|
{
|
|
|
int err;
|
|
|
const char *redirect = ovl_dentry_get_redirect(dentry);
|
|
|
+ bool absolute_redirect = ovl_need_absolute_redirect(dentry, samedir);
|
|
|
|
|
|
- if (redirect && (samedir || redirect[0] == '/'))
|
|
|
+ if (redirect && (!absolute_redirect || redirect[0] == '/'))
|
|
|
return 0;
|
|
|
|
|
|
- redirect = ovl_get_redirect(dentry, samedir);
|
|
|
+ redirect = ovl_get_redirect(dentry, absolute_redirect);
|
|
|
if (IS_ERR(redirect))
|
|
|
return PTR_ERR(redirect);
|
|
|
|
|
@@ -1118,22 +1146,20 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
|
|
goto out_dput;
|
|
|
|
|
|
err = 0;
|
|
|
- if (is_dir) {
|
|
|
- if (ovl_type_merge_or_lower(old))
|
|
|
- err = ovl_set_redirect(old, samedir);
|
|
|
- else if (!old_opaque && ovl_type_merge(new->d_parent))
|
|
|
- err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
|
|
|
- if (err)
|
|
|
- goto out_dput;
|
|
|
- }
|
|
|
- if (!overwrite && new_is_dir) {
|
|
|
- if (ovl_type_merge_or_lower(new))
|
|
|
- err = ovl_set_redirect(new, samedir);
|
|
|
- else if (!new_opaque && ovl_type_merge(old->d_parent))
|
|
|
- err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
|
|
|
- if (err)
|
|
|
- goto out_dput;
|
|
|
- }
|
|
|
+ if (ovl_type_merge_or_lower(old))
|
|
|
+ err = ovl_set_redirect(old, samedir);
|
|
|
+ else if (is_dir && !old_opaque && ovl_type_merge(new->d_parent))
|
|
|
+ err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
|
|
|
+ if (err)
|
|
|
+ goto out_dput;
|
|
|
+
|
|
|
+ if (!overwrite && ovl_type_merge_or_lower(new))
|
|
|
+ err = ovl_set_redirect(new, samedir);
|
|
|
+ else if (!overwrite && new_is_dir && !new_opaque &&
|
|
|
+ ovl_type_merge(old->d_parent))
|
|
|
+ err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
|
|
|
+ if (err)
|
|
|
+ goto out_dput;
|
|
|
|
|
|
err = ovl_do_rename(old_upperdir->d_inode, olddentry,
|
|
|
new_upperdir->d_inode, newdentry, flags);
|