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

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

 



On 2017/11/8 5:38, Amir Goldstein Wrote:
> 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;
> +

Is -ESTALE -> -ENODATA better ?

Consider the case: We set 'null' fh in merged upper dir on a 'file handle' not supported
base filesystem, then we copy or tar all underlying directories to another base filesystem which is
'file handle' supported and mount overlayfs again. I think we can update upperdir's fh instead of
return fail.

At the same time, consider the opposite side, all directories move from a base filesystem which 'file
handle' supported to a not supported one. It will trigger NULL pointer crash in ovl_verify_origin_fh()
because fh is NULL but ofh was existed. Following change good ?

-	if (!ofh)
+	if (!ofh || !fh)
		return -ENODATA;

Thanks,
Yi.

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

--
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