[PATCH 2/3] ovl: set origin xattr for merge dir on lookup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For old existing merge dirs whose "origin" xattr was not set at copy up
time, we amend the situation on lookup.

If no origin fh is stored in upper of a merge dir, store fh of upper most
lower dir or 'null' fh if lower does not support file handles. We do this
so we can filter out whiteouts in case lower dir is removed offline and
then upper dir becomes a pure upper in a future mount.

Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
---
 fs/overlayfs/namei.c     | 61 ++++++++++++++++++++++++++++++++++++++++--------
 fs/overlayfs/overlayfs.h |  2 +-
 fs/overlayfs/super.c     |  4 ++--
 3 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 322c6214114f..65606a0a124c 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -87,6 +87,8 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
 	return 1;
 }
 
+static struct ovl_fh ovl_null_fh;
+
 static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 {
 	int res;
@@ -100,7 +102,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 	}
 	/* Zero size value means "copied up but origin unknown" */
 	if (res == 0)
-		return NULL;
+		goto null_fh;
 
 	fh  = kzalloc(res, GFP_KERNEL);
 	if (!fh)
@@ -118,12 +120,12 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 
 	/* Treat larger version and unknown flags as "origin unknown" */
 	if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
-		goto out;
+		goto null_fh;
 
 	/* Treat endianness mismatch as "origin unknown" */
 	if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
 	    (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
-		goto out;
+		goto null_fh;
 
 	return fh;
 
@@ -131,6 +133,10 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 	kfree(fh);
 	return NULL;
 
+null_fh:
+	kfree(fh);
+	return &ovl_null_fh;
+
 fail:
 	pr_warn_ratelimited("overlayfs: failed to get origin (%i)\n", res);
 	goto out;
@@ -149,6 +155,9 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
 	if (IS_ERR_OR_NULL(fh))
 		return (struct dentry *)fh;
 
+	if (fh == &ovl_null_fh)
+		return NULL;
+
 	/*
 	 * Make sure that the stored uuid matches the uuid of the lower
 	 * layer where file handle will be decoded.
@@ -333,6 +342,10 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
 	if (IS_ERR(ofh))
 		return PTR_ERR(ofh);
 
+	/* NULL fh (no lower fh support) and stored 'null' fh is a match */
+	if (ofh == &ovl_null_fh)
+		return fh ? -ESTALE : 0;
+
 	if (fh->len != ofh->len || memcmp(fh, ofh, fh->len))
 		err = -ESTALE;
 
@@ -345,24 +358,40 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
  *
  * If @set is true and there is no stored file handle, encode and store origin
  * file handle in OVL_XATTR_ORIGIN.
+ * If @strict is true, then the stored file handle must be non 'null'.
+ * If @strict is false, then a stored 'null' file handle and lower with no file
+ * handle support is considered a match.
  *
  * Return 0 on match, -ESTALE on mismatch, < 0 on error.
  */
 int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
-		      bool is_upper, bool set)
+		      bool is_upper, bool set, bool strict)
 {
 	struct inode *inode;
-	struct ovl_fh *fh;
+	const struct ovl_fh *fh = NULL;
 	int err;
 
-	fh = ovl_encode_fh(origin, is_upper);
-	err = PTR_ERR(fh);
-	if (IS_ERR(fh))
+	/*
+	 * When lower layer doesn't support export operations store a 'null' fh,
+	 * so we can use the overlay.origin xattr to distignuish between a copy
+	 * up and a pure upper inode.
+	 */
+	err = -EOPNOTSUPP;
+	if (ovl_can_decode_fh(origin->d_sb)) {
+		fh = ovl_encode_fh(origin, is_upper);
+		err = PTR_ERR(fh);
+		if (IS_ERR(fh))
+			goto fail;
+	} else if (strict) {
 		goto fail;
+	}
 
 	err = ovl_verify_origin_fh(dentry, fh);
-	if (set && err == -ENODATA)
-		err = ovl_do_setxattr(dentry, OVL_XATTR_ORIGIN, fh, fh->len, 0);
+	if (set && err == -ENODATA) {
+		err = ovl_do_setxattr(dentry, OVL_XATTR_ORIGIN, fh,
+				      fh ? fh->len : 0, 0);
+	}
+
 	if (err)
 		goto fail;
 
@@ -662,6 +691,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 		if (!this)
 			continue;
 
+		/*
+		 * If no origin fh is stored in upper of a merge dir, store fh
+		 * of upper most lower dir or 'null' fh if lower does not
+		 * support file handles. We do this so we can filter out
+		 * whiteouts in case lower dir is removed offline and this upper
+		 * becomes a pure upper in a future mount.
+		 */
+		if (this && upperdentry && !ctr) {
+			(void)ovl_verify_origin(upperdentry, this,
+						false, true, false);
+		}
+
 		stack[ctr].dentry = this;
 		stack[ctr].layer = lower.layer;
 		ctr++;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index bee51296db38..df925188394f 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -252,7 +252,7 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
 
 /* namei.c */
 int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
-		      bool is_upper, bool set);
+		      bool is_upper, bool set, bool strict);
 int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
 		     unsigned int numlower);
 int ovl_get_index_name(struct dentry *origin, struct qstr *name);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index af4d3e876398..f8bba4f8b940 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1120,7 +1120,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 		/* Verify lower root is upper root origin */
 		err = ovl_verify_origin(upperpath.dentry,
 					oe->lowerstack[0].dentry,
-					false, true);
+					false, true, true);
 		if (err) {
 			pr_err("overlayfs: failed to verify upper root origin\n");
 			goto out_free_oe;
@@ -1131,7 +1131,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 		if (ufs->indexdir) {
 			/* Verify upper root is index dir origin */
 			err = ovl_verify_origin(ufs->indexdir, upperpath.dentry,
-						true, true);
+						true, true, true);
 			if (err)
 				pr_err("overlayfs: failed to verify index dir origin\n");
 
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux