xref: /openbmc/linux/fs/overlayfs/params.c (revision fac59652993f075d57860769c99045b3ca18780d)
1b36a5780SChristian Brauner // SPDX-License-Identifier: GPL-2.0-only
2b36a5780SChristian Brauner 
3b36a5780SChristian Brauner #include <linux/fs.h>
47fb7998bSChristian Brauner #include <linux/module.h>
5b36a5780SChristian Brauner #include <linux/namei.h>
6b36a5780SChristian Brauner #include <linux/fs_context.h>
7b36a5780SChristian Brauner #include <linux/fs_parser.h>
8b36a5780SChristian Brauner #include <linux/posix_acl_xattr.h>
97fb7998bSChristian Brauner #include <linux/seq_file.h>
10b36a5780SChristian Brauner #include <linux/xattr.h>
11b36a5780SChristian Brauner #include "overlayfs.h"
127fb7998bSChristian Brauner #include "params.h"
137fb7998bSChristian Brauner 
147fb7998bSChristian Brauner static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR);
157fb7998bSChristian Brauner module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
167fb7998bSChristian Brauner MODULE_PARM_DESC(redirect_dir,
177fb7998bSChristian Brauner 		 "Default to on or off for the redirect_dir feature");
187fb7998bSChristian Brauner 
197fb7998bSChristian Brauner static bool ovl_redirect_always_follow =
207fb7998bSChristian Brauner 	IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW);
217fb7998bSChristian Brauner module_param_named(redirect_always_follow, ovl_redirect_always_follow,
227fb7998bSChristian Brauner 		   bool, 0644);
237fb7998bSChristian Brauner MODULE_PARM_DESC(redirect_always_follow,
247fb7998bSChristian Brauner 		 "Follow redirects even if redirect_dir feature is turned off");
257fb7998bSChristian Brauner 
267fb7998bSChristian Brauner static bool ovl_xino_auto_def = IS_ENABLED(CONFIG_OVERLAY_FS_XINO_AUTO);
277fb7998bSChristian Brauner module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
287fb7998bSChristian Brauner MODULE_PARM_DESC(xino_auto,
297fb7998bSChristian Brauner 		 "Auto enable xino feature");
307fb7998bSChristian Brauner 
317fb7998bSChristian Brauner static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
327fb7998bSChristian Brauner module_param_named(index, ovl_index_def, bool, 0644);
337fb7998bSChristian Brauner MODULE_PARM_DESC(index,
347fb7998bSChristian Brauner 		 "Default to on or off for the inodes index feature");
357fb7998bSChristian Brauner 
367fb7998bSChristian Brauner static bool ovl_nfs_export_def = IS_ENABLED(CONFIG_OVERLAY_FS_NFS_EXPORT);
377fb7998bSChristian Brauner module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644);
387fb7998bSChristian Brauner MODULE_PARM_DESC(nfs_export,
397fb7998bSChristian Brauner 		 "Default to on or off for the NFS export feature");
407fb7998bSChristian Brauner 
417fb7998bSChristian Brauner static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY);
427fb7998bSChristian Brauner module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
437fb7998bSChristian Brauner MODULE_PARM_DESC(metacopy,
447fb7998bSChristian Brauner 		 "Default to on or off for the metadata only copy up feature");
457fb7998bSChristian Brauner 
460d809752SAmir Goldstein enum ovl_opt {
477fb7998bSChristian Brauner 	Opt_lowerdir,
48cc0918b3SAmir Goldstein 	Opt_lowerdir_add,
49cc0918b3SAmir Goldstein 	Opt_datadir_add,
507fb7998bSChristian Brauner 	Opt_upperdir,
517fb7998bSChristian Brauner 	Opt_workdir,
527fb7998bSChristian Brauner 	Opt_default_permissions,
537fb7998bSChristian Brauner 	Opt_redirect_dir,
547fb7998bSChristian Brauner 	Opt_index,
557fb7998bSChristian Brauner 	Opt_uuid,
567fb7998bSChristian Brauner 	Opt_nfs_export,
577fb7998bSChristian Brauner 	Opt_userxattr,
587fb7998bSChristian Brauner 	Opt_xino,
597fb7998bSChristian Brauner 	Opt_metacopy,
60ae8cba40SAlexander Larsson 	Opt_verity,
617fb7998bSChristian Brauner 	Opt_volatile,
627fb7998bSChristian Brauner };
637fb7998bSChristian Brauner 
647fb7998bSChristian Brauner static const struct constant_table ovl_parameter_bool[] = {
657fb7998bSChristian Brauner 	{ "on",		true  },
667fb7998bSChristian Brauner 	{ "off",	false },
677fb7998bSChristian Brauner 	{}
687fb7998bSChristian Brauner };
697fb7998bSChristian Brauner 
70b0504bfeSAmir Goldstein static const struct constant_table ovl_parameter_uuid[] = {
71b0504bfeSAmir Goldstein 	{ "off",	OVL_UUID_OFF  },
72b0504bfeSAmir Goldstein 	{ "null",	OVL_UUID_NULL },
73cbb44f09SAmir Goldstein 	{ "auto",	OVL_UUID_AUTO },
74b0504bfeSAmir Goldstein 	{ "on",		OVL_UUID_ON   },
75b0504bfeSAmir Goldstein 	{}
76b0504bfeSAmir Goldstein };
77b0504bfeSAmir Goldstein 
ovl_uuid_mode(struct ovl_config * config)78b0504bfeSAmir Goldstein static const char *ovl_uuid_mode(struct ovl_config *config)
79b0504bfeSAmir Goldstein {
80b0504bfeSAmir Goldstein 	return ovl_parameter_uuid[config->uuid].name;
81b0504bfeSAmir Goldstein }
82b0504bfeSAmir Goldstein 
ovl_uuid_def(void)83b0504bfeSAmir Goldstein static int ovl_uuid_def(void)
84b0504bfeSAmir Goldstein {
85cbb44f09SAmir Goldstein 	return OVL_UUID_AUTO;
86b0504bfeSAmir Goldstein }
87b0504bfeSAmir Goldstein 
887fb7998bSChristian Brauner static const struct constant_table ovl_parameter_xino[] = {
897fb7998bSChristian Brauner 	{ "off",	OVL_XINO_OFF  },
907fb7998bSChristian Brauner 	{ "auto",	OVL_XINO_AUTO },
917fb7998bSChristian Brauner 	{ "on",		OVL_XINO_ON   },
927fb7998bSChristian Brauner 	{}
937fb7998bSChristian Brauner };
947fb7998bSChristian Brauner 
ovl_xino_mode(struct ovl_config * config)957fb7998bSChristian Brauner const char *ovl_xino_mode(struct ovl_config *config)
967fb7998bSChristian Brauner {
977fb7998bSChristian Brauner 	return ovl_parameter_xino[config->xino].name;
987fb7998bSChristian Brauner }
997fb7998bSChristian Brauner 
ovl_xino_def(void)1007fb7998bSChristian Brauner static int ovl_xino_def(void)
1017fb7998bSChristian Brauner {
1027fb7998bSChristian Brauner 	return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF;
1037fb7998bSChristian Brauner }
1047fb7998bSChristian Brauner 
1057fb7998bSChristian Brauner const struct constant_table ovl_parameter_redirect_dir[] = {
1067fb7998bSChristian Brauner 	{ "off",	OVL_REDIRECT_OFF      },
1077fb7998bSChristian Brauner 	{ "follow",	OVL_REDIRECT_FOLLOW   },
1087fb7998bSChristian Brauner 	{ "nofollow",	OVL_REDIRECT_NOFOLLOW },
1097fb7998bSChristian Brauner 	{ "on",		OVL_REDIRECT_ON       },
1107fb7998bSChristian Brauner 	{}
1117fb7998bSChristian Brauner };
1127fb7998bSChristian Brauner 
ovl_redirect_mode(struct ovl_config * config)1137fb7998bSChristian Brauner static const char *ovl_redirect_mode(struct ovl_config *config)
1147fb7998bSChristian Brauner {
1157fb7998bSChristian Brauner 	return ovl_parameter_redirect_dir[config->redirect_mode].name;
1167fb7998bSChristian Brauner }
1177fb7998bSChristian Brauner 
ovl_redirect_mode_def(void)1187fb7998bSChristian Brauner static int ovl_redirect_mode_def(void)
1197fb7998bSChristian Brauner {
1207fb7998bSChristian Brauner 	return ovl_redirect_dir_def	  ? OVL_REDIRECT_ON :
1217fb7998bSChristian Brauner 	       ovl_redirect_always_follow ? OVL_REDIRECT_FOLLOW :
1227fb7998bSChristian Brauner 					    OVL_REDIRECT_NOFOLLOW;
1237fb7998bSChristian Brauner }
1247fb7998bSChristian Brauner 
125ae8cba40SAlexander Larsson static const struct constant_table ovl_parameter_verity[] = {
126ae8cba40SAlexander Larsson 	{ "off",	OVL_VERITY_OFF     },
127ae8cba40SAlexander Larsson 	{ "on",		OVL_VERITY_ON      },
128ae8cba40SAlexander Larsson 	{ "require",	OVL_VERITY_REQUIRE },
129ae8cba40SAlexander Larsson 	{}
130ae8cba40SAlexander Larsson };
131ae8cba40SAlexander Larsson 
ovl_verity_mode(struct ovl_config * config)132ae8cba40SAlexander Larsson static const char *ovl_verity_mode(struct ovl_config *config)
133ae8cba40SAlexander Larsson {
134ae8cba40SAlexander Larsson 	return ovl_parameter_verity[config->verity_mode].name;
135ae8cba40SAlexander Larsson }
136ae8cba40SAlexander Larsson 
ovl_verity_mode_def(void)137ae8cba40SAlexander Larsson static int ovl_verity_mode_def(void)
138ae8cba40SAlexander Larsson {
139ae8cba40SAlexander Larsson 	return OVL_VERITY_OFF;
140ae8cba40SAlexander Larsson }
141ae8cba40SAlexander Larsson 
1427fb7998bSChristian Brauner #define fsparam_string_empty(NAME, OPT) \
1437fb7998bSChristian Brauner 	__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
1447fb7998bSChristian Brauner 
145cc0918b3SAmir Goldstein 
1467fb7998bSChristian Brauner const struct fs_parameter_spec ovl_parameter_spec[] = {
1477fb7998bSChristian Brauner 	fsparam_string_empty("lowerdir",    Opt_lowerdir),
148cc0918b3SAmir Goldstein 	fsparam_string("lowerdir+",         Opt_lowerdir_add),
149cc0918b3SAmir Goldstein 	fsparam_string("datadir+",          Opt_datadir_add),
1507fb7998bSChristian Brauner 	fsparam_string("upperdir",          Opt_upperdir),
1517fb7998bSChristian Brauner 	fsparam_string("workdir",           Opt_workdir),
1527fb7998bSChristian Brauner 	fsparam_flag("default_permissions", Opt_default_permissions),
1537fb7998bSChristian Brauner 	fsparam_enum("redirect_dir",        Opt_redirect_dir, ovl_parameter_redirect_dir),
1547fb7998bSChristian Brauner 	fsparam_enum("index",               Opt_index, ovl_parameter_bool),
155b0504bfeSAmir Goldstein 	fsparam_enum("uuid",                Opt_uuid, ovl_parameter_uuid),
1567fb7998bSChristian Brauner 	fsparam_enum("nfs_export",          Opt_nfs_export, ovl_parameter_bool),
1577fb7998bSChristian Brauner 	fsparam_flag("userxattr",           Opt_userxattr),
1587fb7998bSChristian Brauner 	fsparam_enum("xino",                Opt_xino, ovl_parameter_xino),
1597fb7998bSChristian Brauner 	fsparam_enum("metacopy",            Opt_metacopy, ovl_parameter_bool),
160ae8cba40SAlexander Larsson 	fsparam_enum("verity",              Opt_verity, ovl_parameter_verity),
1617fb7998bSChristian Brauner 	fsparam_flag("volatile",            Opt_volatile),
1627fb7998bSChristian Brauner 	{}
1637fb7998bSChristian Brauner };
164b36a5780SChristian Brauner 
ovl_next_opt(char ** s)165c34706acSAmir Goldstein static char *ovl_next_opt(char **s)
166c34706acSAmir Goldstein {
167c34706acSAmir Goldstein 	char *sbegin = *s;
168c34706acSAmir Goldstein 	char *p;
169c34706acSAmir Goldstein 
170c34706acSAmir Goldstein 	if (sbegin == NULL)
171c34706acSAmir Goldstein 		return NULL;
172c34706acSAmir Goldstein 
173c34706acSAmir Goldstein 	for (p = sbegin; *p; p++) {
174c34706acSAmir Goldstein 		if (*p == '\\') {
175c34706acSAmir Goldstein 			p++;
176c34706acSAmir Goldstein 			if (!*p)
177c34706acSAmir Goldstein 				break;
178c34706acSAmir Goldstein 		} else if (*p == ',') {
179c34706acSAmir Goldstein 			*p = '\0';
180c34706acSAmir Goldstein 			*s = p + 1;
181c34706acSAmir Goldstein 			return sbegin;
182c34706acSAmir Goldstein 		}
183c34706acSAmir Goldstein 	}
184c34706acSAmir Goldstein 	*s = NULL;
185c34706acSAmir Goldstein 	return sbegin;
186c34706acSAmir Goldstein }
187c34706acSAmir Goldstein 
ovl_parse_monolithic(struct fs_context * fc,void * data)188c34706acSAmir Goldstein static int ovl_parse_monolithic(struct fs_context *fc, void *data)
189c34706acSAmir Goldstein {
190c34706acSAmir Goldstein 	return vfs_parse_monolithic_sep(fc, data, ovl_next_opt);
191c34706acSAmir Goldstein }
192c34706acSAmir Goldstein 
ovl_parse_param_split_lowerdirs(char * str)193b36a5780SChristian Brauner static ssize_t ovl_parse_param_split_lowerdirs(char *str)
194b36a5780SChristian Brauner {
195b36a5780SChristian Brauner 	ssize_t nr_layers = 1, nr_colons = 0;
196b36a5780SChristian Brauner 	char *s, *d;
197b36a5780SChristian Brauner 
198b36a5780SChristian Brauner 	for (s = d = str;; s++, d++) {
199b36a5780SChristian Brauner 		if (*s == '\\') {
20032db5107SAmir Goldstein 			/* keep esc chars in split lowerdir */
20132db5107SAmir Goldstein 			*d++ = *s++;
202b36a5780SChristian Brauner 		} else if (*s == ':') {
203b36a5780SChristian Brauner 			bool next_colon = (*(s + 1) == ':');
204b36a5780SChristian Brauner 
205b36a5780SChristian Brauner 			nr_colons++;
206b36a5780SChristian Brauner 			if (nr_colons == 2 && next_colon) {
207b36a5780SChristian Brauner 				pr_err("only single ':' or double '::' sequences of unescaped colons in lowerdir mount option allowed.\n");
208b36a5780SChristian Brauner 				return -EINVAL;
209b36a5780SChristian Brauner 			}
210b36a5780SChristian Brauner 			/* count layers, not colons */
211b36a5780SChristian Brauner 			if (!next_colon)
212b36a5780SChristian Brauner 				nr_layers++;
213b36a5780SChristian Brauner 
214b36a5780SChristian Brauner 			*d = '\0';
215b36a5780SChristian Brauner 			continue;
216b36a5780SChristian Brauner 		}
217b36a5780SChristian Brauner 
218b36a5780SChristian Brauner 		*d = *s;
219b36a5780SChristian Brauner 		if (!*s) {
220b36a5780SChristian Brauner 			/* trailing colons */
221b36a5780SChristian Brauner 			if (nr_colons) {
222b36a5780SChristian Brauner 				pr_err("unescaped trailing colons in lowerdir mount option.\n");
223b36a5780SChristian Brauner 				return -EINVAL;
224b36a5780SChristian Brauner 			}
225b36a5780SChristian Brauner 			break;
226b36a5780SChristian Brauner 		}
227b36a5780SChristian Brauner 		nr_colons = 0;
228b36a5780SChristian Brauner 	}
229b36a5780SChristian Brauner 
230b36a5780SChristian Brauner 	return nr_layers;
231b36a5780SChristian Brauner }
232b36a5780SChristian Brauner 
ovl_mount_dir_noesc(const char * name,struct path * path)233b36a5780SChristian Brauner static int ovl_mount_dir_noesc(const char *name, struct path *path)
234b36a5780SChristian Brauner {
235b36a5780SChristian Brauner 	int err = -EINVAL;
236b36a5780SChristian Brauner 
237b36a5780SChristian Brauner 	if (!*name) {
238b36a5780SChristian Brauner 		pr_err("empty lowerdir\n");
239b36a5780SChristian Brauner 		goto out;
240b36a5780SChristian Brauner 	}
241b36a5780SChristian Brauner 	err = kern_path(name, LOOKUP_FOLLOW, path);
242b36a5780SChristian Brauner 	if (err) {
243b36a5780SChristian Brauner 		pr_err("failed to resolve '%s': %i\n", name, err);
244b36a5780SChristian Brauner 		goto out;
245b36a5780SChristian Brauner 	}
246b36a5780SChristian Brauner 	return 0;
247b36a5780SChristian Brauner 
248b36a5780SChristian Brauner out:
249b36a5780SChristian Brauner 	return err;
250b36a5780SChristian Brauner }
251b36a5780SChristian Brauner 
ovl_unescape(char * s)252b36a5780SChristian Brauner static void ovl_unescape(char *s)
253b36a5780SChristian Brauner {
254b36a5780SChristian Brauner 	char *d = s;
255b36a5780SChristian Brauner 
256b36a5780SChristian Brauner 	for (;; s++, d++) {
257b36a5780SChristian Brauner 		if (*s == '\\')
258b36a5780SChristian Brauner 			s++;
259b36a5780SChristian Brauner 		*d = *s;
260b36a5780SChristian Brauner 		if (!*s)
261b36a5780SChristian Brauner 			break;
262b36a5780SChristian Brauner 	}
263b36a5780SChristian Brauner }
264b36a5780SChristian Brauner 
ovl_mount_dir(const char * name,struct path * path)2650d809752SAmir Goldstein static int ovl_mount_dir(const char *name, struct path *path)
266b36a5780SChristian Brauner {
267b36a5780SChristian Brauner 	int err = -ENOMEM;
268b36a5780SChristian Brauner 	char *tmp = kstrdup(name, GFP_KERNEL);
269b36a5780SChristian Brauner 
270b36a5780SChristian Brauner 	if (tmp) {
271b36a5780SChristian Brauner 		ovl_unescape(tmp);
272b36a5780SChristian Brauner 		err = ovl_mount_dir_noesc(tmp, path);
273b36a5780SChristian Brauner 		kfree(tmp);
274b36a5780SChristian Brauner 	}
275b36a5780SChristian Brauner 	return err;
276b36a5780SChristian Brauner }
277b36a5780SChristian Brauner 
ovl_mount_dir_check(struct fs_context * fc,const struct path * path,enum ovl_opt layer,const char * name,bool upper)2780d809752SAmir Goldstein static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path,
2790d809752SAmir Goldstein 			       enum ovl_opt layer, const char *name, bool upper)
280b36a5780SChristian Brauner {
281cc0918b3SAmir Goldstein 	struct ovl_fs_context *ctx = fc->fs_private;
282cc0918b3SAmir Goldstein 
2830d809752SAmir Goldstein 	if (!d_is_dir(path->dentry))
2840d809752SAmir Goldstein 		return invalfc(fc, "%s is not a directory", name);
285b36a5780SChristian Brauner 
286c6f95031SGabriel Krisman Bertazi 	/*
287c6f95031SGabriel Krisman Bertazi 	 * Root dentries of case-insensitive capable filesystems might
288c6f95031SGabriel Krisman Bertazi 	 * not have the dentry operations set, but still be incompatible
289c6f95031SGabriel Krisman Bertazi 	 * with overlayfs.  Check explicitly to prevent post-mount
290c6f95031SGabriel Krisman Bertazi 	 * failures.
291c6f95031SGabriel Krisman Bertazi 	 */
292c6f95031SGabriel Krisman Bertazi 	if (sb_has_encoding(path->mnt->mnt_sb))
293c6f95031SGabriel Krisman Bertazi 		return invalfc(fc, "case-insensitive capable filesystem on %s not supported", name);
294c6f95031SGabriel Krisman Bertazi 
295c6f95031SGabriel Krisman Bertazi 	if (ovl_dentry_weird(path->dentry))
296c6f95031SGabriel Krisman Bertazi 		return invalfc(fc, "filesystem on %s not supported", name);
297cc0918b3SAmir Goldstein 
298b36a5780SChristian Brauner 	/*
299b36a5780SChristian Brauner 	 * Check whether upper path is read-only here to report failures
300b36a5780SChristian Brauner 	 * early. Don't forget to recheck when the superblock is created
301b36a5780SChristian Brauner 	 * as the mount attributes could change.
302b36a5780SChristian Brauner 	 */
3030d809752SAmir Goldstein 	if (upper) {
3040d809752SAmir Goldstein 		if (path->dentry->d_flags & DCACHE_OP_REAL)
3050d809752SAmir Goldstein 			return invalfc(fc, "filesystem on %s not supported as upperdir", name);
3060d809752SAmir Goldstein 		if (__mnt_is_readonly(path->mnt))
3070d809752SAmir Goldstein 			return invalfc(fc, "filesystem on %s is read-only", name);
308cc0918b3SAmir Goldstein 	} else {
309cc0918b3SAmir Goldstein 		if (ctx->lowerdir_all && layer != Opt_lowerdir)
310cc0918b3SAmir Goldstein 			return invalfc(fc, "lowerdir+ and datadir+ cannot follow lowerdir");
311cc0918b3SAmir Goldstein 		if (ctx->nr_data && layer == Opt_lowerdir_add)
312cc0918b3SAmir Goldstein 			return invalfc(fc, "regular lower layers cannot follow data layers");
313cc0918b3SAmir Goldstein 		if (ctx->nr == OVL_MAX_STACK)
314cc0918b3SAmir Goldstein 			return invalfc(fc, "too many lower directories, limit is %d",
315cc0918b3SAmir Goldstein 				       OVL_MAX_STACK);
316b36a5780SChristian Brauner 	}
317b36a5780SChristian Brauner 	return 0;
318b36a5780SChristian Brauner }
319b36a5780SChristian Brauner 
ovl_ctx_realloc_lower(struct fs_context * fc)320cc0918b3SAmir Goldstein static int ovl_ctx_realloc_lower(struct fs_context *fc)
321cc0918b3SAmir Goldstein {
322cc0918b3SAmir Goldstein 	struct ovl_fs_context *ctx = fc->fs_private;
323cc0918b3SAmir Goldstein 	struct ovl_fs_context_layer *l;
324cc0918b3SAmir Goldstein 	size_t nr;
325cc0918b3SAmir Goldstein 
326cc0918b3SAmir Goldstein 	if (ctx->nr < ctx->capacity)
327cc0918b3SAmir Goldstein 		return 0;
328cc0918b3SAmir Goldstein 
329cc0918b3SAmir Goldstein 	nr = min_t(size_t, max(4096 / sizeof(*l), ctx->capacity * 2),
330cc0918b3SAmir Goldstein 		   OVL_MAX_STACK);
331cc0918b3SAmir Goldstein 	l = krealloc_array(ctx->lower, nr, sizeof(*l), GFP_KERNEL_ACCOUNT);
332cc0918b3SAmir Goldstein 	if (!l)
333cc0918b3SAmir Goldstein 		return -ENOMEM;
334cc0918b3SAmir Goldstein 
335cc0918b3SAmir Goldstein 	ctx->lower = l;
336cc0918b3SAmir Goldstein 	ctx->capacity = nr;
337cc0918b3SAmir Goldstein 	return 0;
338cc0918b3SAmir Goldstein }
339cc0918b3SAmir Goldstein 
ovl_add_layer(struct fs_context * fc,enum ovl_opt layer,struct path * path,char ** pname)3400d809752SAmir Goldstein static void ovl_add_layer(struct fs_context *fc, enum ovl_opt layer,
3410d809752SAmir Goldstein 			 struct path *path, char **pname)
3420d809752SAmir Goldstein {
3430d809752SAmir Goldstein 	struct ovl_fs *ofs = fc->s_fs_info;
3440d809752SAmir Goldstein 	struct ovl_config *config = &ofs->config;
3450d809752SAmir Goldstein 	struct ovl_fs_context *ctx = fc->fs_private;
346cc0918b3SAmir Goldstein 	struct ovl_fs_context_layer *l;
3470d809752SAmir Goldstein 
3480d809752SAmir Goldstein 	switch (layer) {
3490d809752SAmir Goldstein 	case Opt_workdir:
3500d809752SAmir Goldstein 		swap(config->workdir, *pname);
3510d809752SAmir Goldstein 		swap(ctx->work, *path);
3520d809752SAmir Goldstein 		break;
3530d809752SAmir Goldstein 	case Opt_upperdir:
3540d809752SAmir Goldstein 		swap(config->upperdir, *pname);
3550d809752SAmir Goldstein 		swap(ctx->upper, *path);
3560d809752SAmir Goldstein 		break;
357cc0918b3SAmir Goldstein 	case Opt_datadir_add:
358cc0918b3SAmir Goldstein 		ctx->nr_data++;
359cc0918b3SAmir Goldstein 		fallthrough;
360dce7cbeaSZhihao Cheng 	case Opt_lowerdir:
361dce7cbeaSZhihao Cheng 		fallthrough;
362cc0918b3SAmir Goldstein 	case Opt_lowerdir_add:
363cc0918b3SAmir Goldstein 		WARN_ON(ctx->nr >= ctx->capacity);
364cc0918b3SAmir Goldstein 		l = &ctx->lower[ctx->nr++];
365cc0918b3SAmir Goldstein 		memset(l, 0, sizeof(*l));
366cc0918b3SAmir Goldstein 		swap(l->name, *pname);
367cc0918b3SAmir Goldstein 		swap(l->path, *path);
368cc0918b3SAmir Goldstein 		break;
3690d809752SAmir Goldstein 	default:
3700d809752SAmir Goldstein 		WARN_ON(1);
3710d809752SAmir Goldstein 	}
3720d809752SAmir Goldstein }
3730d809752SAmir Goldstein 
ovl_parse_layer(struct fs_context * fc,const char * layer_name,enum ovl_opt layer)3740e1c9709SChristian Brauner static int ovl_parse_layer(struct fs_context *fc, const char *layer_name, enum ovl_opt layer)
3750d809752SAmir Goldstein {
3760e1c9709SChristian Brauner 	char *name = kstrdup(layer_name, GFP_KERNEL);
3770d809752SAmir Goldstein 	bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
3780d809752SAmir Goldstein 	struct path path;
3790d809752SAmir Goldstein 	int err;
3800d809752SAmir Goldstein 
3810d809752SAmir Goldstein 	if (!name)
3820d809752SAmir Goldstein 		return -ENOMEM;
3830d809752SAmir Goldstein 
384dce7cbeaSZhihao Cheng 	if (upper || layer == Opt_lowerdir)
3850d809752SAmir Goldstein 		err = ovl_mount_dir(name, &path);
386cc0918b3SAmir Goldstein 	else
387cc0918b3SAmir Goldstein 		err = ovl_mount_dir_noesc(name, &path);
3880d809752SAmir Goldstein 	if (err)
3890d809752SAmir Goldstein 		goto out_free;
3900d809752SAmir Goldstein 
3910d809752SAmir Goldstein 	err = ovl_mount_dir_check(fc, &path, layer, name, upper);
3920d809752SAmir Goldstein 	if (err)
3930d809752SAmir Goldstein 		goto out_put;
3940d809752SAmir Goldstein 
395cc0918b3SAmir Goldstein 	if (!upper) {
396cc0918b3SAmir Goldstein 		err = ovl_ctx_realloc_lower(fc);
397cc0918b3SAmir Goldstein 		if (err)
398cc0918b3SAmir Goldstein 			goto out_put;
399cc0918b3SAmir Goldstein 	}
400cc0918b3SAmir Goldstein 
4010d809752SAmir Goldstein 	/* Store the user provided path string in ctx to show in mountinfo */
4020d809752SAmir Goldstein 	ovl_add_layer(fc, layer, &path, &name);
4030d809752SAmir Goldstein 
4040d809752SAmir Goldstein out_put:
4050d809752SAmir Goldstein 	path_put(&path);
4060d809752SAmir Goldstein out_free:
4070d809752SAmir Goldstein 	kfree(name);
4080d809752SAmir Goldstein 	return err;
4090d809752SAmir Goldstein }
4100d809752SAmir Goldstein 
ovl_reset_lowerdirs(struct ovl_fs_context * ctx)41126532aebSAmir Goldstein static void ovl_reset_lowerdirs(struct ovl_fs_context *ctx)
412b36a5780SChristian Brauner {
41326532aebSAmir Goldstein 	struct ovl_fs_context_layer *l = ctx->lower;
41426532aebSAmir Goldstein 
41526532aebSAmir Goldstein 	// Reset old user provided lowerdir string
41626532aebSAmir Goldstein 	kfree(ctx->lowerdir_all);
41726532aebSAmir Goldstein 	ctx->lowerdir_all = NULL;
41826532aebSAmir Goldstein 
41926532aebSAmir Goldstein 	for (size_t nr = 0; nr < ctx->nr; nr++, l++) {
42026532aebSAmir Goldstein 		path_put(&l->path);
42126532aebSAmir Goldstein 		kfree(l->name);
42226532aebSAmir Goldstein 		l->name = NULL;
423b36a5780SChristian Brauner 	}
424b36a5780SChristian Brauner 	ctx->nr = 0;
425b36a5780SChristian Brauner 	ctx->nr_data = 0;
426b36a5780SChristian Brauner }
427b36a5780SChristian Brauner 
428b36a5780SChristian Brauner /*
429b36a5780SChristian Brauner  * Parse lowerdir= mount option:
430b36a5780SChristian Brauner  *
4311fff0482SAmir Goldstein  * e.g.: lowerdir=/lower1:/lower2:/lower3::/data1::/data2
432b36a5780SChristian Brauner  *     Set "/lower1", "/lower2", and "/lower3" as lower layers and
433b36a5780SChristian Brauner  *     "/data1" and "/data2" as data lower layers. Any existing lower
434b36a5780SChristian Brauner  *     layers are replaced.
435b36a5780SChristian Brauner  */
ovl_parse_param_lowerdir(const char * name,struct fs_context * fc)4367fb7998bSChristian Brauner static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
437b36a5780SChristian Brauner {
438b36a5780SChristian Brauner 	int err;
439b36a5780SChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
4401fff0482SAmir Goldstein 	char *dup = NULL, *iter;
441be9857a4SAmir Goldstein 	ssize_t nr_lower, nr;
4421fff0482SAmir Goldstein 	bool data_layer = false;
443b36a5780SChristian Brauner 
444b36a5780SChristian Brauner 	/*
445b36a5780SChristian Brauner 	 * Ensure we're backwards compatible with mount(2)
446b36a5780SChristian Brauner 	 * by allowing relative paths.
447b36a5780SChristian Brauner 	 */
448b36a5780SChristian Brauner 
449b36a5780SChristian Brauner 	/* drop all existing lower layers */
45026532aebSAmir Goldstein 	ovl_reset_lowerdirs(ctx);
4511fff0482SAmir Goldstein 
4521fff0482SAmir Goldstein 	if (!*name)
453b36a5780SChristian Brauner 		return 0;
454b36a5780SChristian Brauner 
455beae836eSAmir Goldstein 	if (*name == ':') {
4569de10f5bSZhihao Cheng 		pr_err("cannot append lower layer\n");
457b36a5780SChristian Brauner 		return -EINVAL;
458b36a5780SChristian Brauner 	}
459b36a5780SChristian Brauner 
46026532aebSAmir Goldstein 	// Store user provided lowerdir string to show in mount options
46126532aebSAmir Goldstein 	ctx->lowerdir_all = kstrdup(name, GFP_KERNEL);
46226532aebSAmir Goldstein 	if (!ctx->lowerdir_all)
46326532aebSAmir Goldstein 		return -ENOMEM;
46426532aebSAmir Goldstein 
465b36a5780SChristian Brauner 	dup = kstrdup(name, GFP_KERNEL);
466b36a5780SChristian Brauner 	if (!dup)
467b36a5780SChristian Brauner 		return -ENOMEM;
468b36a5780SChristian Brauner 
469b36a5780SChristian Brauner 	err = -EINVAL;
470b36a5780SChristian Brauner 	nr_lower = ovl_parse_param_split_lowerdirs(dup);
471b36a5780SChristian Brauner 	if (nr_lower < 0)
472b36a5780SChristian Brauner 		goto out_err;
473b36a5780SChristian Brauner 
4741fff0482SAmir Goldstein 	if (nr_lower > OVL_MAX_STACK) {
475b36a5780SChristian Brauner 		pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK);
476b36a5780SChristian Brauner 		goto out_err;
477b36a5780SChristian Brauner 	}
478b36a5780SChristian Brauner 
4791fff0482SAmir Goldstein 	iter = dup;
480dce7cbeaSZhihao Cheng 	for (nr = 0; nr < nr_lower; nr++) {
481dce7cbeaSZhihao Cheng 		err = ovl_parse_layer(fc, iter, Opt_lowerdir);
4820d809752SAmir Goldstein 		if (err)
483dce7cbeaSZhihao Cheng 			goto out_err;
484b36a5780SChristian Brauner 
485b36a5780SChristian Brauner 		if (data_layer)
486be9857a4SAmir Goldstein 			ctx->nr_data++;
487b36a5780SChristian Brauner 
488b36a5780SChristian Brauner 		/* Calling strchr() again would overrun. */
489be9857a4SAmir Goldstein 		if (ctx->nr == nr_lower)
490b36a5780SChristian Brauner 			break;
491b36a5780SChristian Brauner 
492b36a5780SChristian Brauner 		err = -EINVAL;
4931fff0482SAmir Goldstein 		iter = strchr(iter, '\0') + 1;
4941fff0482SAmir Goldstein 		if (*iter) {
495b36a5780SChristian Brauner 			/*
496b36a5780SChristian Brauner 			 * This is a regular layer so we require that
497b36a5780SChristian Brauner 			 * there are no data layers.
498b36a5780SChristian Brauner 			 */
499be9857a4SAmir Goldstein 			if (ctx->nr_data > 0) {
5009de10f5bSZhihao Cheng 				pr_err("regular lower layers cannot follow data lower layers\n");
501dce7cbeaSZhihao Cheng 				goto out_err;
502b36a5780SChristian Brauner 			}
503b36a5780SChristian Brauner 
504b36a5780SChristian Brauner 			data_layer = false;
505b36a5780SChristian Brauner 			continue;
506b36a5780SChristian Brauner 		}
507b36a5780SChristian Brauner 
508b36a5780SChristian Brauner 		/* This is a data lower layer. */
509b36a5780SChristian Brauner 		data_layer = true;
5101fff0482SAmir Goldstein 		iter++;
511b36a5780SChristian Brauner 	}
512b36a5780SChristian Brauner 	kfree(dup);
513b36a5780SChristian Brauner 	return 0;
514b36a5780SChristian Brauner 
515b36a5780SChristian Brauner out_err:
516b36a5780SChristian Brauner 	kfree(dup);
517b36a5780SChristian Brauner 
518b36a5780SChristian Brauner 	/* Intentionally don't realloc to a smaller size. */
519b36a5780SChristian Brauner 	return err;
520b36a5780SChristian Brauner }
5217fb7998bSChristian Brauner 
ovl_parse_param(struct fs_context * fc,struct fs_parameter * param)5227fb7998bSChristian Brauner static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
5237fb7998bSChristian Brauner {
5247fb7998bSChristian Brauner 	int err = 0;
5257fb7998bSChristian Brauner 	struct fs_parse_result result;
5267fb7998bSChristian Brauner 	struct ovl_fs *ofs = fc->s_fs_info;
5277fb7998bSChristian Brauner 	struct ovl_config *config = &ofs->config;
5287fb7998bSChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
5297fb7998bSChristian Brauner 	int opt;
5307fb7998bSChristian Brauner 
5317fb7998bSChristian Brauner 	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
5327fb7998bSChristian Brauner 		/*
5337fb7998bSChristian Brauner 		 * On remount overlayfs has always ignored all mount
5347fb7998bSChristian Brauner 		 * options no matter if malformed or not so for
5357fb7998bSChristian Brauner 		 * backwards compatibility we do the same here.
5367fb7998bSChristian Brauner 		 */
5377fb7998bSChristian Brauner 		if (fc->oldapi)
5387fb7998bSChristian Brauner 			return 0;
5397fb7998bSChristian Brauner 
5407fb7998bSChristian Brauner 		/*
5417fb7998bSChristian Brauner 		 * Give us the freedom to allow changing mount options
5427fb7998bSChristian Brauner 		 * with the new mount api in the future. So instead of
5437fb7998bSChristian Brauner 		 * silently ignoring everything we report a proper
5447fb7998bSChristian Brauner 		 * error. This is only visible for users of the new
5457fb7998bSChristian Brauner 		 * mount api.
5467fb7998bSChristian Brauner 		 */
5477fb7998bSChristian Brauner 		return invalfc(fc, "No changes allowed in reconfigure");
5487fb7998bSChristian Brauner 	}
5497fb7998bSChristian Brauner 
5507fb7998bSChristian Brauner 	opt = fs_parse(fc, ovl_parameter_spec, param, &result);
5517fb7998bSChristian Brauner 	if (opt < 0)
5527fb7998bSChristian Brauner 		return opt;
5537fb7998bSChristian Brauner 
5547fb7998bSChristian Brauner 	switch (opt) {
5557fb7998bSChristian Brauner 	case Opt_lowerdir:
5567fb7998bSChristian Brauner 		err = ovl_parse_param_lowerdir(param->string, fc);
5577fb7998bSChristian Brauner 		break;
558cc0918b3SAmir Goldstein 	case Opt_lowerdir_add:
559cc0918b3SAmir Goldstein 	case Opt_datadir_add:
5607fb7998bSChristian Brauner 	case Opt_upperdir:
5617fb7998bSChristian Brauner 	case Opt_workdir:
5620e1c9709SChristian Brauner 		err = ovl_parse_layer(fc, param->string, opt);
5637fb7998bSChristian Brauner 		break;
5647fb7998bSChristian Brauner 	case Opt_default_permissions:
5657fb7998bSChristian Brauner 		config->default_permissions = true;
5667fb7998bSChristian Brauner 		break;
5677fb7998bSChristian Brauner 	case Opt_redirect_dir:
5687fb7998bSChristian Brauner 		config->redirect_mode = result.uint_32;
5697fb7998bSChristian Brauner 		if (config->redirect_mode == OVL_REDIRECT_OFF) {
5707fb7998bSChristian Brauner 			config->redirect_mode = ovl_redirect_always_follow ?
5717fb7998bSChristian Brauner 						OVL_REDIRECT_FOLLOW :
5727fb7998bSChristian Brauner 						OVL_REDIRECT_NOFOLLOW;
5737fb7998bSChristian Brauner 		}
5747fb7998bSChristian Brauner 		ctx->set.redirect = true;
5757fb7998bSChristian Brauner 		break;
5767fb7998bSChristian Brauner 	case Opt_index:
5777fb7998bSChristian Brauner 		config->index = result.uint_32;
5787fb7998bSChristian Brauner 		ctx->set.index = true;
5797fb7998bSChristian Brauner 		break;
5807fb7998bSChristian Brauner 	case Opt_uuid:
5817fb7998bSChristian Brauner 		config->uuid = result.uint_32;
5827fb7998bSChristian Brauner 		break;
5837fb7998bSChristian Brauner 	case Opt_nfs_export:
5847fb7998bSChristian Brauner 		config->nfs_export = result.uint_32;
5857fb7998bSChristian Brauner 		ctx->set.nfs_export = true;
5867fb7998bSChristian Brauner 		break;
5877fb7998bSChristian Brauner 	case Opt_xino:
5887fb7998bSChristian Brauner 		config->xino = result.uint_32;
5897fb7998bSChristian Brauner 		break;
5907fb7998bSChristian Brauner 	case Opt_metacopy:
5917fb7998bSChristian Brauner 		config->metacopy = result.uint_32;
5927fb7998bSChristian Brauner 		ctx->set.metacopy = true;
5937fb7998bSChristian Brauner 		break;
594ae8cba40SAlexander Larsson 	case Opt_verity:
595ae8cba40SAlexander Larsson 		config->verity_mode = result.uint_32;
596ae8cba40SAlexander Larsson 		break;
5977fb7998bSChristian Brauner 	case Opt_volatile:
5987fb7998bSChristian Brauner 		config->ovl_volatile = true;
5997fb7998bSChristian Brauner 		break;
6007fb7998bSChristian Brauner 	case Opt_userxattr:
6017fb7998bSChristian Brauner 		config->userxattr = true;
6027fb7998bSChristian Brauner 		break;
6037fb7998bSChristian Brauner 	default:
6047fb7998bSChristian Brauner 		pr_err("unrecognized mount option \"%s\" or missing value\n",
6057fb7998bSChristian Brauner 		       param->key);
6067fb7998bSChristian Brauner 		return -EINVAL;
6077fb7998bSChristian Brauner 	}
6087fb7998bSChristian Brauner 
6097fb7998bSChristian Brauner 	return err;
6107fb7998bSChristian Brauner }
6117fb7998bSChristian Brauner 
ovl_get_tree(struct fs_context * fc)6127fb7998bSChristian Brauner static int ovl_get_tree(struct fs_context *fc)
6137fb7998bSChristian Brauner {
6147fb7998bSChristian Brauner 	return get_tree_nodev(fc, ovl_fill_super);
6157fb7998bSChristian Brauner }
6167fb7998bSChristian Brauner 
ovl_fs_context_free(struct ovl_fs_context * ctx)6177fb7998bSChristian Brauner static inline void ovl_fs_context_free(struct ovl_fs_context *ctx)
6187fb7998bSChristian Brauner {
61926532aebSAmir Goldstein 	ovl_reset_lowerdirs(ctx);
6207fb7998bSChristian Brauner 	path_put(&ctx->upper);
6217fb7998bSChristian Brauner 	path_put(&ctx->work);
6227fb7998bSChristian Brauner 	kfree(ctx->lower);
6237fb7998bSChristian Brauner 	kfree(ctx);
6247fb7998bSChristian Brauner }
6257fb7998bSChristian Brauner 
ovl_free(struct fs_context * fc)6267fb7998bSChristian Brauner static void ovl_free(struct fs_context *fc)
6277fb7998bSChristian Brauner {
6287fb7998bSChristian Brauner 	struct ovl_fs *ofs = fc->s_fs_info;
6297fb7998bSChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
6307fb7998bSChristian Brauner 
6317fb7998bSChristian Brauner 	/*
6327fb7998bSChristian Brauner 	 * ofs is stored in the fs_context when it is initialized.
6337fb7998bSChristian Brauner 	 * ofs is transferred to the superblock on a successful mount,
6347fb7998bSChristian Brauner 	 * but if an error occurs before the transfer we have to free
6357fb7998bSChristian Brauner 	 * it here.
6367fb7998bSChristian Brauner 	 */
6377fb7998bSChristian Brauner 	if (ofs)
6387fb7998bSChristian Brauner 		ovl_free_fs(ofs);
6397fb7998bSChristian Brauner 
6407fb7998bSChristian Brauner 	if (ctx)
6417fb7998bSChristian Brauner 		ovl_fs_context_free(ctx);
6427fb7998bSChristian Brauner }
6437fb7998bSChristian Brauner 
ovl_reconfigure(struct fs_context * fc)6447fb7998bSChristian Brauner static int ovl_reconfigure(struct fs_context *fc)
6457fb7998bSChristian Brauner {
6467fb7998bSChristian Brauner 	struct super_block *sb = fc->root->d_sb;
647f01d0889SAndrea Righi 	struct ovl_fs *ofs = OVL_FS(sb);
6487fb7998bSChristian Brauner 	struct super_block *upper_sb;
6497fb7998bSChristian Brauner 	int ret = 0;
6507fb7998bSChristian Brauner 
6517fb7998bSChristian Brauner 	if (!(fc->sb_flags & SB_RDONLY) && ovl_force_readonly(ofs))
6527fb7998bSChristian Brauner 		return -EROFS;
6537fb7998bSChristian Brauner 
6547fb7998bSChristian Brauner 	if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb)) {
6557fb7998bSChristian Brauner 		upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
6567fb7998bSChristian Brauner 		if (ovl_should_sync(ofs)) {
6577fb7998bSChristian Brauner 			down_read(&upper_sb->s_umount);
6587fb7998bSChristian Brauner 			ret = sync_filesystem(upper_sb);
6597fb7998bSChristian Brauner 			up_read(&upper_sb->s_umount);
6607fb7998bSChristian Brauner 		}
6617fb7998bSChristian Brauner 	}
6627fb7998bSChristian Brauner 
6637fb7998bSChristian Brauner 	return ret;
6647fb7998bSChristian Brauner }
6657fb7998bSChristian Brauner 
6667fb7998bSChristian Brauner static const struct fs_context_operations ovl_context_ops = {
667c34706acSAmir Goldstein 	.parse_monolithic = ovl_parse_monolithic,
6687fb7998bSChristian Brauner 	.parse_param = ovl_parse_param,
6697fb7998bSChristian Brauner 	.get_tree    = ovl_get_tree,
6707fb7998bSChristian Brauner 	.reconfigure = ovl_reconfigure,
6717fb7998bSChristian Brauner 	.free        = ovl_free,
6727fb7998bSChristian Brauner };
6737fb7998bSChristian Brauner 
6747fb7998bSChristian Brauner /*
6757fb7998bSChristian Brauner  * This is called during fsopen() and will record the user namespace of
6767fb7998bSChristian Brauner  * the caller in fc->user_ns since we've raised FS_USERNS_MOUNT. We'll
6777fb7998bSChristian Brauner  * need it when we actually create the superblock to verify that the
6787fb7998bSChristian Brauner  * process creating the superblock is in the same user namespace as
6797fb7998bSChristian Brauner  * process that called fsopen().
6807fb7998bSChristian Brauner  */
ovl_init_fs_context(struct fs_context * fc)6817fb7998bSChristian Brauner int ovl_init_fs_context(struct fs_context *fc)
6827fb7998bSChristian Brauner {
6837fb7998bSChristian Brauner 	struct ovl_fs_context *ctx;
6847fb7998bSChristian Brauner 	struct ovl_fs *ofs;
6857fb7998bSChristian Brauner 
6867fb7998bSChristian Brauner 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
6877fb7998bSChristian Brauner 	if (!ctx)
6887fb7998bSChristian Brauner 		return -ENOMEM;
6897fb7998bSChristian Brauner 
6907fb7998bSChristian Brauner 	/*
6917fb7998bSChristian Brauner 	 * By default we allocate for three lower layers. It's likely
6927fb7998bSChristian Brauner 	 * that it'll cover most users.
6937fb7998bSChristian Brauner 	 */
6947fb7998bSChristian Brauner 	ctx->lower = kmalloc_array(3, sizeof(*ctx->lower), GFP_KERNEL_ACCOUNT);
6957fb7998bSChristian Brauner 	if (!ctx->lower)
6967fb7998bSChristian Brauner 		goto out_err;
6977fb7998bSChristian Brauner 	ctx->capacity = 3;
6987fb7998bSChristian Brauner 
6997fb7998bSChristian Brauner 	ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
7007fb7998bSChristian Brauner 	if (!ofs)
7017fb7998bSChristian Brauner 		goto out_err;
7027fb7998bSChristian Brauner 
7037fb7998bSChristian Brauner 	ofs->config.redirect_mode	= ovl_redirect_mode_def();
7047fb7998bSChristian Brauner 	ofs->config.index		= ovl_index_def;
705b0504bfeSAmir Goldstein 	ofs->config.uuid		= ovl_uuid_def();
7067fb7998bSChristian Brauner 	ofs->config.nfs_export		= ovl_nfs_export_def;
7077fb7998bSChristian Brauner 	ofs->config.xino		= ovl_xino_def();
7087fb7998bSChristian Brauner 	ofs->config.metacopy		= ovl_metacopy_def;
7097fb7998bSChristian Brauner 
7107fb7998bSChristian Brauner 	fc->s_fs_info		= ofs;
7117fb7998bSChristian Brauner 	fc->fs_private		= ctx;
7127fb7998bSChristian Brauner 	fc->ops			= &ovl_context_ops;
7137fb7998bSChristian Brauner 	return 0;
7147fb7998bSChristian Brauner 
7157fb7998bSChristian Brauner out_err:
7167fb7998bSChristian Brauner 	ovl_fs_context_free(ctx);
7177fb7998bSChristian Brauner 	return -ENOMEM;
7187fb7998bSChristian Brauner 
7197fb7998bSChristian Brauner }
7207fb7998bSChristian Brauner 
ovl_free_fs(struct ovl_fs * ofs)7217fb7998bSChristian Brauner void ovl_free_fs(struct ovl_fs *ofs)
7227fb7998bSChristian Brauner {
7237fb7998bSChristian Brauner 	struct vfsmount **mounts;
7247fb7998bSChristian Brauner 	unsigned i;
7257fb7998bSChristian Brauner 
7267fb7998bSChristian Brauner 	iput(ofs->workbasedir_trap);
7277fb7998bSChristian Brauner 	iput(ofs->indexdir_trap);
7287fb7998bSChristian Brauner 	iput(ofs->workdir_trap);
7297fb7998bSChristian Brauner 	dput(ofs->whiteout);
7307fb7998bSChristian Brauner 	dput(ofs->indexdir);
7317fb7998bSChristian Brauner 	dput(ofs->workdir);
7327fb7998bSChristian Brauner 	if (ofs->workdir_locked)
7337fb7998bSChristian Brauner 		ovl_inuse_unlock(ofs->workbasedir);
7347fb7998bSChristian Brauner 	dput(ofs->workbasedir);
7357fb7998bSChristian Brauner 	if (ofs->upperdir_locked)
7367fb7998bSChristian Brauner 		ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root);
7377fb7998bSChristian Brauner 
738a535116dSAmir Goldstein 	/* Reuse ofs->config.lowerdirs as a vfsmount array before freeing it */
739a535116dSAmir Goldstein 	mounts = (struct vfsmount **) ofs->config.lowerdirs;
7407fb7998bSChristian Brauner 	for (i = 0; i < ofs->numlayer; i++) {
7417fb7998bSChristian Brauner 		iput(ofs->layers[i].trap);
742a535116dSAmir Goldstein 		kfree(ofs->config.lowerdirs[i]);
7437fb7998bSChristian Brauner 		mounts[i] = ofs->layers[i].mnt;
7447fb7998bSChristian Brauner 	}
7457fb7998bSChristian Brauner 	kern_unmount_array(mounts, ofs->numlayer);
7467fb7998bSChristian Brauner 	kfree(ofs->layers);
7477fb7998bSChristian Brauner 	for (i = 0; i < ofs->numfs; i++)
7487fb7998bSChristian Brauner 		free_anon_bdev(ofs->fs[i].pseudo_dev);
7497fb7998bSChristian Brauner 	kfree(ofs->fs);
7507fb7998bSChristian Brauner 
751a535116dSAmir Goldstein 	kfree(ofs->config.lowerdirs);
7527fb7998bSChristian Brauner 	kfree(ofs->config.upperdir);
7537fb7998bSChristian Brauner 	kfree(ofs->config.workdir);
7547fb7998bSChristian Brauner 	if (ofs->creator_cred)
7557fb7998bSChristian Brauner 		put_cred(ofs->creator_cred);
7567fb7998bSChristian Brauner 	kfree(ofs);
7577fb7998bSChristian Brauner }
7587fb7998bSChristian Brauner 
ovl_fs_params_verify(const struct ovl_fs_context * ctx,struct ovl_config * config)7597fb7998bSChristian Brauner int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
7607fb7998bSChristian Brauner 			 struct ovl_config *config)
7617fb7998bSChristian Brauner {
7627fb7998bSChristian Brauner 	struct ovl_opt_set set = ctx->set;
7637fb7998bSChristian Brauner 
7647fb7998bSChristian Brauner 	/* Workdir/index are useless in non-upper mount */
7657fb7998bSChristian Brauner 	if (!config->upperdir) {
7667fb7998bSChristian Brauner 		if (config->workdir) {
7677fb7998bSChristian Brauner 			pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
7687fb7998bSChristian Brauner 				config->workdir);
7697fb7998bSChristian Brauner 			kfree(config->workdir);
7707fb7998bSChristian Brauner 			config->workdir = NULL;
7717fb7998bSChristian Brauner 		}
7727fb7998bSChristian Brauner 		if (config->index && set.index) {
7737fb7998bSChristian Brauner 			pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n");
7747fb7998bSChristian Brauner 			set.index = false;
7757fb7998bSChristian Brauner 		}
7767fb7998bSChristian Brauner 		config->index = false;
7777fb7998bSChristian Brauner 	}
7787fb7998bSChristian Brauner 
7797fb7998bSChristian Brauner 	if (!config->upperdir && config->ovl_volatile) {
7807fb7998bSChristian Brauner 		pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
7817fb7998bSChristian Brauner 		config->ovl_volatile = false;
7827fb7998bSChristian Brauner 	}
7837fb7998bSChristian Brauner 
784d9544c1bSAmir Goldstein 	if (!config->upperdir && config->uuid == OVL_UUID_ON) {
785d9544c1bSAmir Goldstein 		pr_info("option \"uuid=on\" requires an upper fs, falling back to uuid=null.\n");
786d9544c1bSAmir Goldstein 		config->uuid = OVL_UUID_NULL;
787d9544c1bSAmir Goldstein 	}
788d9544c1bSAmir Goldstein 
789ae8cba40SAlexander Larsson 	/* Resolve verity -> metacopy dependency */
790ae8cba40SAlexander Larsson 	if (config->verity_mode && !config->metacopy) {
791ae8cba40SAlexander Larsson 		/* Don't allow explicit specified conflicting combinations */
792ae8cba40SAlexander Larsson 		if (set.metacopy) {
793ae8cba40SAlexander Larsson 			pr_err("conflicting options: metacopy=off,verity=%s\n",
794ae8cba40SAlexander Larsson 			       ovl_verity_mode(config));
795ae8cba40SAlexander Larsson 			return -EINVAL;
796ae8cba40SAlexander Larsson 		}
797ae8cba40SAlexander Larsson 		/* Otherwise automatically enable metacopy. */
798ae8cba40SAlexander Larsson 		config->metacopy = true;
799ae8cba40SAlexander Larsson 	}
800ae8cba40SAlexander Larsson 
8017fb7998bSChristian Brauner 	/*
8027fb7998bSChristian Brauner 	 * This is to make the logic below simpler.  It doesn't make any other
8037fb7998bSChristian Brauner 	 * difference, since redirect_dir=on is only used for upper.
8047fb7998bSChristian Brauner 	 */
8057fb7998bSChristian Brauner 	if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
8067fb7998bSChristian Brauner 		config->redirect_mode = OVL_REDIRECT_ON;
8077fb7998bSChristian Brauner 
808ae8cba40SAlexander Larsson 	/* Resolve verity -> metacopy -> redirect_dir dependency */
8097fb7998bSChristian Brauner 	if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
8107fb7998bSChristian Brauner 		if (set.metacopy && set.redirect) {
8117fb7998bSChristian Brauner 			pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
8127fb7998bSChristian Brauner 			       ovl_redirect_mode(config));
8137fb7998bSChristian Brauner 			return -EINVAL;
8147fb7998bSChristian Brauner 		}
815ae8cba40SAlexander Larsson 		if (config->verity_mode && set.redirect) {
816ae8cba40SAlexander Larsson 			pr_err("conflicting options: verity=%s,redirect_dir=%s\n",
817ae8cba40SAlexander Larsson 			       ovl_verity_mode(config), ovl_redirect_mode(config));
818ae8cba40SAlexander Larsson 			return -EINVAL;
819ae8cba40SAlexander Larsson 		}
8207fb7998bSChristian Brauner 		if (set.redirect) {
8217fb7998bSChristian Brauner 			/*
8227fb7998bSChristian Brauner 			 * There was an explicit redirect_dir=... that resulted
8237fb7998bSChristian Brauner 			 * in this conflict.
8247fb7998bSChristian Brauner 			 */
8257fb7998bSChristian Brauner 			pr_info("disabling metacopy due to redirect_dir=%s\n",
8267fb7998bSChristian Brauner 				ovl_redirect_mode(config));
8277fb7998bSChristian Brauner 			config->metacopy = false;
8287fb7998bSChristian Brauner 		} else {
8297fb7998bSChristian Brauner 			/* Automatically enable redirect otherwise. */
8307fb7998bSChristian Brauner 			config->redirect_mode = OVL_REDIRECT_ON;
8317fb7998bSChristian Brauner 		}
8327fb7998bSChristian Brauner 	}
8337fb7998bSChristian Brauner 
8347fb7998bSChristian Brauner 	/* Resolve nfs_export -> index dependency */
8357fb7998bSChristian Brauner 	if (config->nfs_export && !config->index) {
8367fb7998bSChristian Brauner 		if (!config->upperdir &&
8377fb7998bSChristian Brauner 		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
8387fb7998bSChristian Brauner 			pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
8397fb7998bSChristian Brauner 			config->nfs_export = false;
8407fb7998bSChristian Brauner 		} else if (set.nfs_export && set.index) {
8417fb7998bSChristian Brauner 			pr_err("conflicting options: nfs_export=on,index=off\n");
8427fb7998bSChristian Brauner 			return -EINVAL;
8437fb7998bSChristian Brauner 		} else if (set.index) {
8447fb7998bSChristian Brauner 			/*
8457fb7998bSChristian Brauner 			 * There was an explicit index=off that resulted
8467fb7998bSChristian Brauner 			 * in this conflict.
8477fb7998bSChristian Brauner 			 */
8487fb7998bSChristian Brauner 			pr_info("disabling nfs_export due to index=off\n");
8497fb7998bSChristian Brauner 			config->nfs_export = false;
8507fb7998bSChristian Brauner 		} else {
8517fb7998bSChristian Brauner 			/* Automatically enable index otherwise. */
8527fb7998bSChristian Brauner 			config->index = true;
8537fb7998bSChristian Brauner 		}
8547fb7998bSChristian Brauner 	}
8557fb7998bSChristian Brauner 
856ae8cba40SAlexander Larsson 	/* Resolve nfs_export -> !metacopy && !verity dependency */
8577fb7998bSChristian Brauner 	if (config->nfs_export && config->metacopy) {
8587fb7998bSChristian Brauner 		if (set.nfs_export && set.metacopy) {
8597fb7998bSChristian Brauner 			pr_err("conflicting options: nfs_export=on,metacopy=on\n");
8607fb7998bSChristian Brauner 			return -EINVAL;
8617fb7998bSChristian Brauner 		}
8627fb7998bSChristian Brauner 		if (set.metacopy) {
8637fb7998bSChristian Brauner 			/*
8647fb7998bSChristian Brauner 			 * There was an explicit metacopy=on that resulted
8657fb7998bSChristian Brauner 			 * in this conflict.
8667fb7998bSChristian Brauner 			 */
8677fb7998bSChristian Brauner 			pr_info("disabling nfs_export due to metacopy=on\n");
8687fb7998bSChristian Brauner 			config->nfs_export = false;
869ae8cba40SAlexander Larsson 		} else if (config->verity_mode) {
870ae8cba40SAlexander Larsson 			/*
871ae8cba40SAlexander Larsson 			 * There was an explicit verity=.. that resulted
872ae8cba40SAlexander Larsson 			 * in this conflict.
873ae8cba40SAlexander Larsson 			 */
874ae8cba40SAlexander Larsson 			pr_info("disabling nfs_export due to verity=%s\n",
875ae8cba40SAlexander Larsson 				ovl_verity_mode(config));
876ae8cba40SAlexander Larsson 			config->nfs_export = false;
8777fb7998bSChristian Brauner 		} else {
8787fb7998bSChristian Brauner 			/*
8797fb7998bSChristian Brauner 			 * There was an explicit nfs_export=on that resulted
8807fb7998bSChristian Brauner 			 * in this conflict.
8817fb7998bSChristian Brauner 			 */
8827fb7998bSChristian Brauner 			pr_info("disabling metacopy due to nfs_export=on\n");
8837fb7998bSChristian Brauner 			config->metacopy = false;
8847fb7998bSChristian Brauner 		}
8857fb7998bSChristian Brauner 	}
8867fb7998bSChristian Brauner 
8877fb7998bSChristian Brauner 
888ae8cba40SAlexander Larsson 	/* Resolve userxattr -> !redirect && !metacopy && !verity dependency */
8897fb7998bSChristian Brauner 	if (config->userxattr) {
8907fb7998bSChristian Brauner 		if (set.redirect &&
8917fb7998bSChristian Brauner 		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
8927fb7998bSChristian Brauner 			pr_err("conflicting options: userxattr,redirect_dir=%s\n",
8937fb7998bSChristian Brauner 			       ovl_redirect_mode(config));
8947fb7998bSChristian Brauner 			return -EINVAL;
8957fb7998bSChristian Brauner 		}
8967fb7998bSChristian Brauner 		if (config->metacopy && set.metacopy) {
8977fb7998bSChristian Brauner 			pr_err("conflicting options: userxattr,metacopy=on\n");
8987fb7998bSChristian Brauner 			return -EINVAL;
8997fb7998bSChristian Brauner 		}
900ae8cba40SAlexander Larsson 		if (config->verity_mode) {
901ae8cba40SAlexander Larsson 			pr_err("conflicting options: userxattr,verity=%s\n",
902ae8cba40SAlexander Larsson 			       ovl_verity_mode(config));
903ae8cba40SAlexander Larsson 			return -EINVAL;
904ae8cba40SAlexander Larsson 		}
9057fb7998bSChristian Brauner 		/*
9067fb7998bSChristian Brauner 		 * Silently disable default setting of redirect and metacopy.
9077fb7998bSChristian Brauner 		 * This shall be the default in the future as well: these
9087fb7998bSChristian Brauner 		 * options must be explicitly enabled if used together with
9097fb7998bSChristian Brauner 		 * userxattr.
9107fb7998bSChristian Brauner 		 */
9117fb7998bSChristian Brauner 		config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
9127fb7998bSChristian Brauner 		config->metacopy = false;
9137fb7998bSChristian Brauner 	}
9147fb7998bSChristian Brauner 
915*bf47be54SMike Baynton 	/*
916*bf47be54SMike Baynton 	 * Fail if we don't have trusted xattr capability and a feature was
917*bf47be54SMike Baynton 	 * explicitly requested that requires them.
918*bf47be54SMike Baynton 	 */
919*bf47be54SMike Baynton 	if (!config->userxattr && !capable(CAP_SYS_ADMIN)) {
920*bf47be54SMike Baynton 		if (set.redirect &&
921*bf47be54SMike Baynton 		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
922*bf47be54SMike Baynton 			pr_err("redirect_dir requires permission to access trusted xattrs\n");
923*bf47be54SMike Baynton 			return -EPERM;
924*bf47be54SMike Baynton 		}
925*bf47be54SMike Baynton 		if (config->metacopy && set.metacopy) {
926*bf47be54SMike Baynton 			pr_err("metacopy requires permission to access trusted xattrs\n");
927*bf47be54SMike Baynton 			return -EPERM;
928*bf47be54SMike Baynton 		}
929*bf47be54SMike Baynton 		if (config->verity_mode) {
930*bf47be54SMike Baynton 			pr_err("verity requires permission to access trusted xattrs\n");
931*bf47be54SMike Baynton 			return -EPERM;
932*bf47be54SMike Baynton 		}
933*bf47be54SMike Baynton 		if (ctx->nr_data > 0) {
934*bf47be54SMike Baynton 			pr_err("lower data-only dirs require permission to access trusted xattrs\n");
935*bf47be54SMike Baynton 			return -EPERM;
936*bf47be54SMike Baynton 		}
937*bf47be54SMike Baynton 		/*
938*bf47be54SMike Baynton 		 * Other xattr-dependent features should be disabled without
939*bf47be54SMike Baynton 		 * great disturbance to the user in ovl_make_workdir().
940*bf47be54SMike Baynton 		 */
941*bf47be54SMike Baynton 	}
942*bf47be54SMike Baynton 
943*bf47be54SMike Baynton 	if (ctx->nr_data > 0 && !config->metacopy) {
944*bf47be54SMike Baynton 		pr_err("lower data-only dirs require metacopy support.\n");
945*bf47be54SMike Baynton 		return -EINVAL;
946*bf47be54SMike Baynton 	}
947*bf47be54SMike Baynton 
9487fb7998bSChristian Brauner 	return 0;
9497fb7998bSChristian Brauner }
9507fb7998bSChristian Brauner 
9517fb7998bSChristian Brauner /**
9527fb7998bSChristian Brauner  * ovl_show_options
9537fb7998bSChristian Brauner  * @m: the seq_file handle
9547fb7998bSChristian Brauner  * @dentry: The dentry to query
9557fb7998bSChristian Brauner  *
9567fb7998bSChristian Brauner  * Prints the mount options for a given superblock.
9577fb7998bSChristian Brauner  * Returns zero; does not fail.
9587fb7998bSChristian Brauner  */
ovl_show_options(struct seq_file * m,struct dentry * dentry)9597fb7998bSChristian Brauner int ovl_show_options(struct seq_file *m, struct dentry *dentry)
9607fb7998bSChristian Brauner {
9617fb7998bSChristian Brauner 	struct super_block *sb = dentry->d_sb;
962f01d0889SAndrea Righi 	struct ovl_fs *ofs = OVL_FS(sb);
963cc0918b3SAmir Goldstein 	size_t nr, nr_merged_lower, nr_lower = 0;
96426532aebSAmir Goldstein 	char **lowerdirs = ofs->config.lowerdirs;
9657fb7998bSChristian Brauner 
96632db5107SAmir Goldstein 	/*
96726532aebSAmir Goldstein 	 * lowerdirs[0] holds the colon separated list that user provided
96826532aebSAmir Goldstein 	 * with lowerdir mount option.
969cc0918b3SAmir Goldstein 	 * lowerdirs[1..numlayer] hold the lowerdir paths that were added
970cc0918b3SAmir Goldstein 	 * using the lowerdir+ and datadir+ mount options.
971cc0918b3SAmir Goldstein 	 * For now, we do not allow mixing the legacy lowerdir mount option
972cc0918b3SAmir Goldstein 	 * with the new lowerdir+ and datadir+ mount options.
97332db5107SAmir Goldstein 	 */
974cc0918b3SAmir Goldstein 	if (lowerdirs[0]) {
97526532aebSAmir Goldstein 		seq_show_option(m, "lowerdir", lowerdirs[0]);
976cc0918b3SAmir Goldstein 	} else {
977cc0918b3SAmir Goldstein 		nr_lower = ofs->numlayer;
978cc0918b3SAmir Goldstein 		nr_merged_lower = nr_lower - ofs->numdatalayer;
979cc0918b3SAmir Goldstein 	}
980cc0918b3SAmir Goldstein 	for (nr = 1; nr < nr_lower; nr++) {
981cc0918b3SAmir Goldstein 		if (nr < nr_merged_lower)
982cc0918b3SAmir Goldstein 			seq_show_option(m, "lowerdir+", lowerdirs[nr]);
983cc0918b3SAmir Goldstein 		else
984cc0918b3SAmir Goldstein 			seq_show_option(m, "datadir+", lowerdirs[nr]);
985cc0918b3SAmir Goldstein 	}
9867fb7998bSChristian Brauner 	if (ofs->config.upperdir) {
9877fb7998bSChristian Brauner 		seq_show_option(m, "upperdir", ofs->config.upperdir);
9887fb7998bSChristian Brauner 		seq_show_option(m, "workdir", ofs->config.workdir);
9897fb7998bSChristian Brauner 	}
9907fb7998bSChristian Brauner 	if (ofs->config.default_permissions)
9917fb7998bSChristian Brauner 		seq_puts(m, ",default_permissions");
9927fb7998bSChristian Brauner 	if (ofs->config.redirect_mode != ovl_redirect_mode_def())
9937fb7998bSChristian Brauner 		seq_printf(m, ",redirect_dir=%s",
9947fb7998bSChristian Brauner 			   ovl_redirect_mode(&ofs->config));
9957fb7998bSChristian Brauner 	if (ofs->config.index != ovl_index_def)
9967fb7998bSChristian Brauner 		seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
997b0504bfeSAmir Goldstein 	if (ofs->config.uuid != ovl_uuid_def())
998b0504bfeSAmir Goldstein 		seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
9997fb7998bSChristian Brauner 	if (ofs->config.nfs_export != ovl_nfs_export_def)
10007fb7998bSChristian Brauner 		seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
10017fb7998bSChristian Brauner 						"on" : "off");
10027fb7998bSChristian Brauner 	if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
10037fb7998bSChristian Brauner 		seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
10047fb7998bSChristian Brauner 	if (ofs->config.metacopy != ovl_metacopy_def)
10057fb7998bSChristian Brauner 		seq_printf(m, ",metacopy=%s",
10067fb7998bSChristian Brauner 			   ofs->config.metacopy ? "on" : "off");
10077fb7998bSChristian Brauner 	if (ofs->config.ovl_volatile)
10087fb7998bSChristian Brauner 		seq_puts(m, ",volatile");
10097fb7998bSChristian Brauner 	if (ofs->config.userxattr)
10107fb7998bSChristian Brauner 		seq_puts(m, ",userxattr");
1011ae8cba40SAlexander Larsson 	if (ofs->config.verity_mode != ovl_verity_mode_def())
1012ae8cba40SAlexander Larsson 		seq_printf(m, ",verity=%s",
1013ae8cba40SAlexander Larsson 			   ovl_verity_mode(&ofs->config));
10147fb7998bSChristian Brauner 	return 0;
10157fb7998bSChristian Brauner }
1016