[RFC PATCH 1/4] ovl: add feature set support

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

 



In order to distinguish the backward compatible and backward
incompatible features in overlay filesystem, we introduce
"compat/ro_compat/incompat" three kinds of feature set and
store them on the upper root dir via xattr:
"trusted.overlay.feature_[compat|ro_compat|incompat]=xxx,xxx"

Mount operation should fail if one unknown incompatible feature is
detected on the upper root dir, and read-write mount operation should
fail if one unknown ro_compat feature is detected on the upper root
dir. At the same time, lower layers may also have some backward
incompatible features when they used to be upper root dir, so do the
same check as upper root dir.

This patch doesn't add any features, so if any feature xattrs is
detected on the underlying root dir, it will be recognized as unknown
feature.

Suggested-by: Amir Goldstein <amir73il@xxxxxxxxx>
Signed-off-by: zhangyi (F) <yi.zhang@xxxxxxxxxx>
---
 fs/overlayfs/overlayfs.h |  17 +++
 fs/overlayfs/ovl_entry.h |  16 +++
 fs/overlayfs/super.c     | 302 ++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 330 insertions(+), 5 deletions(-)

diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 225ff1171147..121e81cc1550 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -28,6 +28,9 @@ enum ovl_path_type {
 #define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
 #define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
 #define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
+#define OVL_XATTR_FEATURE_COMPAT OVL_XATTR_PREFIX "feature_compat"
+#define OVL_XATTR_FEATURE_RO_COMPAT OVL_XATTR_PREFIX "feature_ro_compat"
+#define OVL_XATTR_FEATURE_INCOMPAT OVL_XATTR_PREFIX "feature_incompat"
 
 enum ovl_inode_flag {
 	/* Pure upper dir that may contain non pure upper entries */
@@ -43,6 +46,20 @@ enum ovl_entry_flag {
 	OVL_E_CONNECTED,
 };
 
+/* Features */
+static inline bool ovl_has_unknown_compat_features(struct super_block *sb)
+{
+	return (OVL_FS(sb)->feature.compat_unknown != NULL);
+}
+static inline bool ovl_has_unknown_ro_compat_features(struct super_block *sb)
+{
+	return (OVL_FS(sb)->feature.ro_compat_unknown != NULL);
+}
+static inline bool ovl_has_unknown_incompat_features(struct super_block *sb)
+{
+	return (OVL_FS(sb)->feature.incompat_unknown != NULL);
+}
+
 /*
  * The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
  * where:
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index bfef6edcc111..f8c5e0191a12 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -32,6 +32,15 @@ struct ovl_path {
 	struct dentry *dentry;
 };
 
+struct ovl_feature {
+	char *compat;
+	char *ro_compat;
+	char *incompat;
+	const char *compat_unknown;
+	const char *ro_compat_unknown;
+	const char *incompat_unknown;
+};
+
 /* private information held for overlayfs's superblock */
 struct ovl_fs {
 	struct vfsmount *upper_mnt;
@@ -55,6 +64,8 @@ struct ovl_fs {
 	/* Did we take the inuse lock? */
 	bool upperdir_locked;
 	bool workdir_locked;
+	/* features */
+	struct ovl_feature feature;
 };
 
 /* private information held for every overlayfs dentry */
@@ -94,6 +105,11 @@ static inline struct ovl_inode *OVL_I(struct inode *inode)
 	return container_of(inode, struct ovl_inode, vfs_inode);
 }
 
+static inline struct ovl_fs *OVL_FS(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
 static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi)
 {
 	return READ_ONCE(oi->__upperdentry);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 7c24619ae7fc..10772ae237b4 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -184,6 +184,272 @@ static const struct dentry_operations ovl_reval_dentry_operations = {
 	.d_weak_revalidate = ovl_dentry_weak_revalidate,
 };
 
+static const char *ovl_feature_compat_supp[] = {
+	NULL
+};
+
+static const char *ovl_feature_ro_compat_supp[] = {
+	NULL
+};
+
+static const char *ovl_feature_incompat_supp[] = {
+	NULL
+};
+
+/*
+ * Get overlay feature from the real root dentry.
+ *
+ * @realroot: real underlying root dir dentry
+ * @set: feature set xattr name
+ * Return feature string if success, NULL if no feature and
+ * error number if error.
+ */
+static char *ovl_get_feature(struct dentry *realroot, const char *set)
+{
+	int res;
+	char *buf;
+
+	res = vfs_getxattr(realroot, set, NULL, 0);
+	if (res <= 0) {
+		if (res == -EOPNOTSUPP || res == -ENODATA)
+			res = 0;
+		return ERR_PTR(res);
+	}
+
+	buf = kmalloc(res + 1, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	res = vfs_getxattr(realroot, set, buf, res);
+	if (res <= 0) {
+		kfree(buf);
+		return ERR_PTR(res);
+	}
+
+	buf[res] = '\0';
+	return buf;
+}
+
+/*
+ * Add new feature to (or generate new) features string, set update flag
+ * if new feature added.
+ *
+ * @feature: output feature string
+ * @add: feature want to add
+ * @update: set when new feature add, no change otherwise
+ * Return 0 if success, error number otherwise.
+ */
+static int ovl_update_feature(char **feature, const char *add,
+			      bool *update)
+{
+	char *p = *feature;
+	char *tmp;
+	int size;
+
+	if (p && strstr(p, add))
+		return 0;
+
+	size = strlen(add) + 1;
+	if (p) {
+		size += strlen(p) + 1;
+		tmp = kmalloc(size, GFP_KERNEL);
+		if (!tmp)
+			return -ENOMEM;
+
+		snprintf(tmp, size, "%s,%s", p, add);
+		kfree(p);
+	} else {
+		tmp = kmalloc(size, GFP_KERNEL);
+		if (!tmp)
+			return -ENOMEM;
+
+		strncpy(tmp, add, size);
+	}
+
+	if (update)
+		*update = true;
+	*feature = tmp;
+	return 0;
+}
+
+/*
+ * Parse features and split out unknown features, set update flag
+ * if new feature added.
+ *
+ * @features: input feature string which need to parse
+ * @supp: support feature list
+ * @feature: output feature string
+ * @unknown: unknown feature string
+ * @update: set when new feature add, no change otherwise
+ * Return 0 if success, errno otherwise.
+ */
+static int ovl_parse_feature(char *features, const char *supp[],
+			     char **feature, const char **unknown,
+			     bool *update)
+{
+	const char **fs;
+	char *tmpf = NULL, *tmpu = NULL;
+	char *p;
+	int err = 0;
+
+	if (!features)
+		return 0;
+
+	while ((p = strsep(&features, ",")) != NULL) {
+		/* Add to or generate parsed feature string */
+		err = ovl_update_feature(&tmpf, p, update);
+		if (err)
+			goto err;
+
+		/* Check support or not */
+		for (fs = supp; *fs; fs++) {
+			if (strcmp(p, *fs) == 0)
+				break;
+		}
+
+		if (*fs)
+			continue;
+
+		/* Non-supported feature found, add to unknown */
+		err = ovl_update_feature(&tmpu, p, NULL);
+		if (err)
+			goto err;
+	}
+
+	*feature = tmpf;
+	*unknown = tmpu;
+	return 0;
+err:
+	kfree(tmpf);
+	kfree(tmpu);
+	return err;
+}
+
+/*
+ * Get one set of features from the underlying root dir and parse
+ * them to get the unknown features.
+ *
+ * @root: overlay root dentry
+ * @set: feature set xattr name
+ * @supp: supported feature list
+ * @feature: output features string
+ * @unknown: unknown features string
+ * Return 0 if success, errno otherwise.
+ */
+static int ovl_get_feature_set(struct dentry *root, const char *set,
+			       const char *supp[], char **feature,
+			       const char **unknown)
+{
+	struct dentry *upperroot = ovl_dentry_upper(root);
+	struct ovl_entry *oe = root->d_fsdata;
+	char *buf = NULL;
+	int i;
+	int err;
+
+	WARN_ON(*feature || *unknown);
+
+	/* Get features from the upper root dir */
+	if (upperroot) {
+		buf = ovl_get_feature(upperroot, set);
+		if (IS_ERR(buf))
+			return PTR_ERR(buf);
+		if (!buf)
+			goto get_lower;
+
+		err = ovl_parse_feature(buf, supp, feature,
+					unknown, NULL);
+		kfree(buf);
+		if (err)
+			return err;
+	}
+
+get_lower:
+	/* Get features from each lower root dir beside the bottom layer */
+	for (i = 0; i < oe->numlower-1; i++) {
+		buf = ovl_get_feature(oe->lowerstack[i].dentry, set);
+		if (IS_ERR(buf))
+			return PTR_ERR(buf);
+		if (!buf)
+			continue;
+
+		err = ovl_parse_feature(buf, supp, feature,
+					unknown, NULL);
+		kfree(buf);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * Get all features from the upper and lower root dir and parse them to get
+ * unknown features.
+ *
+ * @root: overlay root dir dentry
+ * @of: overlay feature set want to init
+ * Return 0 if success, errno otherwise.
+ */
+static int ovl_init_features(struct dentry *root, struct ovl_feature *of)
+{
+	int err;
+
+	err = ovl_get_feature_set(root, OVL_XATTR_FEATURE_COMPAT,
+				  ovl_feature_compat_supp, &of->compat,
+				  &of->compat_unknown);
+	if (err)
+		return err;
+
+	err = ovl_get_feature_set(root, OVL_XATTR_FEATURE_RO_COMPAT,
+				  ovl_feature_ro_compat_supp, &of->ro_compat,
+				  &of->ro_compat_unknown);
+	if (err)
+		return err;
+
+	err = ovl_get_feature_set(root, OVL_XATTR_FEATURE_INCOMPAT,
+				  ovl_feature_incompat_supp, &of->incompat,
+				  &of->incompat_unknown);
+	return err;
+}
+
+/*
+ * Check whether this filesystem can be mounted based on
+ * the features present and the read-only/read-write mount requested.
+ * Returns true if this filesystem can be mounted as requested,
+ * false if it cannot be.
+ */
+static bool ovl_check_feature_ok(struct super_block *sb, bool readonly)
+{
+	struct ovl_feature *of = &OVL_FS(sb)->feature;
+
+	if (ovl_has_unknown_incompat_features(sb)) {
+		pr_err("overlayfs: unknown optional incompat features "
+		       "enabled: %s\n", of->incompat_unknown);
+		pr_err("overlayfs: filesystem cannot not be safely mounted "
+		       "by this kernel\n");
+		return false;
+	}
+
+	if (ovl_has_unknown_ro_compat_features(sb)) {
+		pr_err("overlayfs: unknown optional ro_compat features "
+		       "enabled: %s\n", of->ro_compat_unknown);
+
+		if (readonly)
+			return true;
+
+		pr_err("overlayfs: filesystem cannot not be safely mounted "
+		       "read-write by this kernel\n");
+		return false;
+	}
+
+	if (ovl_has_unknown_compat_features(sb)) {
+		pr_warn("overlayfs: unknown optional compat features "
+		        "enabled: %s\n", of->compat_unknown);
+	}
+
+	return true;
+}
+
 static struct kmem_cache *ovl_inode_cachep;
 
 static struct inode *ovl_alloc_inode(struct super_block *sb)
@@ -242,6 +508,13 @@ static void ovl_free_fs(struct ovl_fs *ofs)
 	}
 	kfree(ofs->lower_layers);
 
+	kfree(ofs->feature.compat);
+	kfree(ofs->feature.ro_compat);
+	kfree(ofs->feature.incompat);
+	kfree(ofs->feature.compat_unknown);
+	kfree(ofs->feature.ro_compat_unknown);
+	kfree(ofs->feature.incompat_unknown);
+
 	kfree(ofs->config.lowerdir);
 	kfree(ofs->config.upperdir);
 	kfree(ofs->config.workdir);
@@ -357,7 +630,8 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct ovl_fs *ofs = sb->s_fs_info;
 
-	if (!(*flags & SB_RDONLY) && ovl_force_readonly(ofs))
+	if (!(*flags & SB_RDONLY) &&
+	     (ovl_force_readonly(ofs) || !ovl_check_feature_ok(sb, false)))
 		return -EROFS;
 
 	return 0;
@@ -902,6 +1176,7 @@ static const struct xattr_handler *ovl_xattr_handlers[] = {
 	NULL
 };
 
+
 static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath)
 {
 	struct vfsmount *upper_mnt;
@@ -1284,11 +1559,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
 		err = ovl_get_upper(ofs, &upperpath);
 		if (err)
-			goto out_err;
+			goto out_free_upper;
 
 		err = ovl_get_workdir(ofs, &upperpath);
 		if (err)
-			goto out_err;
+			goto out_free_upper;
 
 		if (!ofs->workdir)
 			sb->s_flags |= SB_RDONLY;
@@ -1300,7 +1575,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 	oe = ovl_get_lowerstack(sb, ofs);
 	err = PTR_ERR(oe);
 	if (IS_ERR(oe))
-		goto out_err;
+		goto out_free_upper;
 
 	/* If the upper fs is nonexistent, we mark overlayfs r/o too */
 	if (!ofs->upper_mnt)
@@ -1365,13 +1640,30 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
 	sb->s_root = root_dentry;
 
+	/*
+	 * Get features from the upper and lower root dir, and check
+	 * them could be mounted properly by this kernel.
+	 */
+	err = ovl_init_features(sb->s_root, &ofs->feature);
+	if (err)
+		goto out_free_root;
+
+	err = -EINVAL;
+	if (!ovl_check_feature_ok(sb, (sb_rdonly(sb))))
+		goto out_free_root;
+
 	return 0;
 
+out_free_root:
+	dput(sb->s_root);
+	sb->s_root = NULL;
+	goto out_err;
 out_free_oe:
 	ovl_entry_stack_free(oe);
 	kfree(oe);
-out_err:
+out_free_upper:
 	path_put(&upperpath);
+out_err:
 	ovl_free_fs(ofs);
 out:
 	return err;
-- 
2.13.6

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