diff options
Diffstat (limited to 'fs/overlayfs/export.c')
| -rw-r--r-- | fs/overlayfs/export.c | 216 | 
1 files changed, 169 insertions, 47 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index bb94ce9da5c8..87bd4148f4fb 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -19,6 +19,142 @@  #include <linux/ratelimit.h>  #include "overlayfs.h" +static int ovl_encode_maybe_copy_up(struct dentry *dentry) +{ +	int err; + +	if (ovl_dentry_upper(dentry)) +		return 0; + +	err = ovl_want_write(dentry); +	if (!err) { +		err = ovl_copy_up(dentry); +		ovl_drop_write(dentry); +	} + +	if (err) { +		pr_warn_ratelimited("overlayfs: failed to copy up on encode (%pd2, err=%i)\n", +				    dentry, err); +	} + +	return err; +} + +/* + * Before encoding a non-upper directory file handle from real layer N, we need + * to check if it will be possible to reconnect an overlay dentry from the real + * lower decoded dentry. This is done by following the overlay ancestry up to a + * "layer N connected" ancestor and verifying that all parents along the way are + * "layer N connectable". If an ancestor that is NOT "layer N connectable" is + * found, we need to copy up an ancestor, which is "layer N connectable", thus + * making that ancestor "layer N connected". For example: + * + * layer 1: /a + * layer 2: /a/b/c + * + * The overlay dentry /a is NOT "layer 2 connectable", because if dir /a is + * copied up and renamed, upper dir /a will be indexed by lower dir /a from + * layer 1. The dir /a from layer 2 will never be indexed, so the algorithm (*) + * in ovl_lookup_real_ancestor() will not be able to lookup a connected overlay + * dentry from the connected lower dentry /a/b/c. + * + * To avoid this problem on decode time, we need to copy up an ancestor of + * /a/b/c, which is "layer 2 connectable", on encode time. That ancestor is + * /a/b. After copy up (and index) of /a/b, it will become "layer 2 connected" + * and when the time comes to decode the file handle from lower dentry /a/b/c, + * ovl_lookup_real_ancestor() will find the indexed ancestor /a/b and decoding + * a connected overlay dentry will be accomplished. + * + * (*) the algorithm in ovl_lookup_real_ancestor() can be improved to lookup an + * entry /a in the lower layers above layer N and find the indexed dir /a from + * layer 1. If that improvement is made, then the check for "layer N connected" + * will need to verify there are no redirects in lower layers above N. In the + * example above, /a will be "layer 2 connectable". However, if layer 2 dir /a + * is a target of a layer 1 redirect, then /a will NOT be "layer 2 connectable": + * + * layer 1: /A (redirect = /a) + * layer 2: /a/b/c + */ + +/* Return the lowest layer for encoding a connectable file handle */ +static int ovl_connectable_layer(struct dentry *dentry) +{ +	struct ovl_entry *oe = OVL_E(dentry); + +	/* We can get overlay root from root of any layer */ +	if (dentry == dentry->d_sb->s_root) +		return oe->numlower; + +	/* +	 * If it's an unindexed merge dir, then it's not connectable with any +	 * lower layer +	 */ +	if (ovl_dentry_upper(dentry) && +	    !ovl_test_flag(OVL_INDEX, d_inode(dentry))) +		return 0; + +	/* We can get upper/overlay path from indexed/lower dentry */ +	return oe->lowerstack[0].layer->idx; +} + +/* + * @dentry is "connected" if all ancestors up to root or a "connected" ancestor + * have the same uppermost lower layer as the origin's layer. We may need to + * copy up a "connectable" ancestor to make it "connected". A "connected" dentry + * cannot become non "connected", so cache positive result in dentry flags. + * + * Return the connected origin layer or < 0 on error. + */ +static int ovl_connect_layer(struct dentry *dentry) +{ +	struct dentry *next, *parent = NULL; +	int origin_layer; +	int err = 0; + +	if (WARN_ON(dentry == dentry->d_sb->s_root) || +	    WARN_ON(!ovl_dentry_lower(dentry))) +		return -EIO; + +	origin_layer = OVL_E(dentry)->lowerstack[0].layer->idx; +	if (ovl_dentry_test_flag(OVL_E_CONNECTED, dentry)) +		return origin_layer; + +	/* Find the topmost origin layer connectable ancestor of @dentry */ +	next = dget(dentry); +	for (;;) { +		parent = dget_parent(next); +		if (WARN_ON(parent == next)) { +			err = -EIO; +			break; +		} + +		/* +		 * If @parent is not origin layer connectable, then copy up +		 * @next which is origin layer connectable and we are done. +		 */ +		if (ovl_connectable_layer(parent) < origin_layer) { +			err = ovl_encode_maybe_copy_up(next); +			break; +		} + +		/* If @parent is connected or indexed we are done */ +		if (ovl_dentry_test_flag(OVL_E_CONNECTED, parent) || +		    ovl_test_flag(OVL_INDEX, d_inode(parent))) +			break; + +		dput(next); +		next = parent; +	} + +	dput(parent); +	dput(next); + +	if (!err) +		ovl_dentry_set_flag(OVL_E_CONNECTED, dentry); + +	return err ?: origin_layer; +} +  /*   * We only need to encode origin if there is a chance that the same object was   * encoded pre copy up and then we need to stay consistent with the same @@ -41,73 +177,59 @@   * L = lower file handle   *   * (*) Connecting an overlay dir from real lower dentry is not always - * possible when there are redirects in lower layers. To mitigate this case, - * we copy up the lower dir first and then encode an upper dir file handle. + * possible when there are redirects in lower layers and non-indexed merge dirs. + * To mitigate those case, we may copy up the lower dir ancestor before encode + * a lower dir file handle. + * + * Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.   */ -static bool ovl_should_encode_origin(struct dentry *dentry) +static int ovl_check_encode_origin(struct dentry *dentry)  {  	struct ovl_fs *ofs = dentry->d_sb->s_fs_info; +	/* Upper file handle for pure upper */  	if (!ovl_dentry_lower(dentry)) -		return false; +		return 0;  	/* -	 * Decoding a merge dir, whose origin's parent is under a redirected -	 * lower dir is not always possible. As a simple aproximation, we do -	 * not encode lower dir file handles when overlay has multiple lower -	 * layers and origin is below the topmost lower layer. +	 * Upper file handle for non-indexed upper.  	 * -	 * TODO: copy up only the parent that is under redirected lower. +	 * Root is never indexed, so if there's an upper layer, encode upper for +	 * root.  	 */ -	if (d_is_dir(dentry) && ofs->upper_mnt && -	    OVL_E(dentry)->lowerstack[0].layer->idx > 1) -		return false; - -	/* Decoding a non-indexed upper from origin is not implemented */  	if (ovl_dentry_upper(dentry) &&  	    !ovl_test_flag(OVL_INDEX, d_inode(dentry))) -		return false; - -	return true; -} - -static int ovl_encode_maybe_copy_up(struct dentry *dentry) -{ -	int err; - -	if (ovl_dentry_upper(dentry))  		return 0; -	err = ovl_want_write(dentry); -	if (err) -		return err; - -	err = ovl_copy_up(dentry); +	/* +	 * Decoding a merge dir, whose origin's ancestor is under a redirected +	 * lower dir or under a non-indexed upper is not always possible. +	 * ovl_connect_layer() will try to make origin's layer "connected" by +	 * copying up a "connectable" ancestor. +	 */ +	if (d_is_dir(dentry) && ofs->upper_mnt) +		return ovl_connect_layer(dentry); -	ovl_drop_write(dentry); -	return err; +	/* Lower file handle for indexed and non-upper dir/non-dir */ +	return 1;  }  static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)  { -	struct dentry *origin = ovl_dentry_lower(dentry);  	struct ovl_fh *fh = NULL; -	int err; +	int err, enc_lower;  	/* -	 * If we should not encode a lower dir file handle, copy up and encode -	 * an upper dir file handle. +	 * Check if we should encode a lower or upper file handle and maybe +	 * copy up an ancestor to make lower file handle connectable.  	 */ -	if (!ovl_should_encode_origin(dentry)) { -		err = ovl_encode_maybe_copy_up(dentry); -		if (err) -			goto fail; - -		origin = NULL; -	} +	err = enc_lower = ovl_check_encode_origin(dentry); +	if (enc_lower < 0) +		goto fail; -	/* Encode an upper or origin file handle */ -	fh = ovl_encode_fh(origin ?: ovl_dentry_upper(dentry), !origin); +	/* Encode an upper or lower file handle */ +	fh = ovl_encode_fh(enc_lower ? ovl_dentry_lower(dentry) : +				       ovl_dentry_upper(dentry), !enc_lower);  	err = PTR_ERR(fh);  	if (IS_ERR(fh))  		goto fail; @@ -355,8 +477,8 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,  		dput(upper);  	} -	if (!this) -		return NULL; +	if (IS_ERR_OR_NULL(this)) +		return this;  	if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) {  		dput(this); @@ -498,7 +620,7 @@ static struct dentry *ovl_lookup_real(struct super_block *sb,  			if (err == -ECHILD) {  				this = ovl_lookup_real_ancestor(sb, real,  								layer); -				err = IS_ERR(this) ? PTR_ERR(this) : 0; +				err = PTR_ERR_OR_ZERO(this);  			}  			if (!err) {  				dput(connected);  |