[PATCH v3 5/6] ovl: Handle escaped xwhiteouts across layers

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

 



We use the "overlay.whiteouts" to mark any directory in a lower dir that may
contain "overlay.whiteout" files (xwhiteouts). This works even if other layers
overlap that directory in a mount.

For example, take these files in three layers:

 layer2/dir/ - overlay.whiteouts
 layer2/dir/file - overlay.whiteout
 layer1/dir/
 layer1/dir/file

An overlayfs mount with -o lowerdir=layer2:layer1 would have a whiteout in
layer2.

However, suppose you wanted to put this inside an overlayfs layer (say
"layerA"). I.e. you want to escape the whiteouts above so that when the new
layer is mounted using overlayfs the mount shows the above content. The natural
approach is to just take each layer and escape it:

 layerA/layer2/dir/ - overlay.overlay.whiteouts
 layerA/layer2/dir/file - overlay.overlay.whiteout
 layerA/layer1/dir/
 layerA/layer1/dir/file

This initially seems to work, however if there is another lowerdir (say
"layerB") that overlaps the xwhiteouts dir, then this runs into problem:

 layerB/layer2/dir/ - **NO overlay.overlay.whiteouts **
 layerA/layer2/dir/ - overlay.overlay.whiteouts
 layerA/layer2/dir/file - overlay.overlay.whiteout
 layerA/layer1/dir/
 layerA/layer1/dir/file

If you mount this with -o lowerdir=layerB:layerA, then in the final mount,
there will be no overlay.whiteouts xattrs on the "layer2/dir" merged
directory, because the topmost lower dir xattrs win.

We would like this to work as is, to avoid having layer escaping depend on other
layers. So, to fix this up we special case the reading of escaped
"overlay.whiteouts" xattrs by looking in all layers for the first hit.

Signed-off-by: Alexander Larsson <alexl@xxxxxxxxxx>
---
 fs/overlayfs/xattrs.c | 46 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index 27b31f812eb1..9e5f50ba333d 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -92,6 +92,25 @@ static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char
 	return res;
 }
 
+static int ovl_xattr_get_first(struct dentry *dentry, struct inode *inode, const char *name,
+			       void *value, size_t size)
+{
+	const struct cred *old_cred;
+	struct path realpath;
+	int idx, next;
+	int res = -ENODATA;
+
+	old_cred = ovl_override_creds(dentry->d_sb);
+	for (idx = 0; idx != -1; idx = next) {
+		next = ovl_path_next(idx, dentry, &realpath);
+		res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
+		if (res != -ENODATA && res != -EOPNOTSUPP)
+			break;
+	}
+	revert_creds(old_cred);
+	return res;
+}
+
 static bool ovl_can_list(struct super_block *sb, const char *s)
 {
 	/* Never list non-escaped private (.overlay) */
@@ -176,6 +195,18 @@ static char *ovl_xattr_escape_name(const char *prefix, const char *name)
 	return escaped;
 }
 
+
+static int str_ends_with(const char *s, const char *sub)
+{
+	int slen = strlen(s);
+	int sublen = strlen(sub);
+
+	if (sublen > slen)
+		return 0;
+
+	return !memcmp(s + slen - sublen, sub, sublen);
+}
+
 static int ovl_own_xattr_get(const struct xattr_handler *handler,
 			     struct dentry *dentry, struct inode *inode,
 			     const char *name, void *buffer, size_t size)
@@ -187,7 +218,20 @@ static int ovl_own_xattr_get(const struct xattr_handler *handler,
 	if (IS_ERR(escaped))
 		return PTR_ERR(escaped);
 
-	r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
+	/*
+	 * Escaped "overlay.whiteouts" directories need to be combined across layers.
+	 * For example, if a lower layer contains an escaped "overlay.whiteout"
+	 * its parent directory will be marked with an escaped "overlay.whiteouts".
+	 * The merged directory will contain a (now non-escaped) whiteout, and its
+	 * parent should therefore be marked too. However, if a layer above the marked
+	 * one has covers the same directory but without whiteouts the covering directory
+	 * would not be marged, and thus the merged directory would not be marked.
+	 */
+	if (d_is_dir(dentry) &&
+	    str_ends_with(escaped, "overlay.whiteouts"))
+		r = ovl_xattr_get_first(dentry, inode, escaped, buffer, size);
+	else
+		r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
 
 	kfree(escaped);
 
-- 
2.41.0




[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