xref: /openbmc/linux/fs/overlayfs/params.c (revision be9857a4)
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;
360cc0918b3SAmir Goldstein 	case Opt_lowerdir_add:
361cc0918b3SAmir Goldstein 		WARN_ON(ctx->nr >= ctx->capacity);
362cc0918b3SAmir Goldstein 		l = &ctx->lower[ctx->nr++];
363cc0918b3SAmir Goldstein 		memset(l, 0, sizeof(*l));
364cc0918b3SAmir Goldstein 		swap(l->name, *pname);
365cc0918b3SAmir Goldstein 		swap(l->path, *path);
366cc0918b3SAmir Goldstein 		break;
3670d809752SAmir Goldstein 	default:
3680d809752SAmir Goldstein 		WARN_ON(1);
3690d809752SAmir Goldstein 	}
3700d809752SAmir Goldstein }
3710d809752SAmir Goldstein 
ovl_parse_layer(struct fs_context * fc,struct fs_parameter * param,enum ovl_opt layer)3720d809752SAmir Goldstein static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
3730d809752SAmir Goldstein 			   enum ovl_opt layer)
3740d809752SAmir Goldstein {
3750d809752SAmir Goldstein 	char *name = kstrdup(param->string, GFP_KERNEL);
3760d809752SAmir Goldstein 	bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
3770d809752SAmir Goldstein 	struct path path;
3780d809752SAmir Goldstein 	int err;
3790d809752SAmir Goldstein 
3800d809752SAmir Goldstein 	if (!name)
3810d809752SAmir Goldstein 		return -ENOMEM;
3820d809752SAmir Goldstein 
383cc0918b3SAmir Goldstein 	if (upper)
3840d809752SAmir Goldstein 		err = ovl_mount_dir(name, &path);
385cc0918b3SAmir Goldstein 	else
386cc0918b3SAmir Goldstein 		err = ovl_mount_dir_noesc(name, &path);
3870d809752SAmir Goldstein 	if (err)
3880d809752SAmir Goldstein 		goto out_free;
3890d809752SAmir Goldstein 
3900d809752SAmir Goldstein 	err = ovl_mount_dir_check(fc, &path, layer, name, upper);
3910d809752SAmir Goldstein 	if (err)
3920d809752SAmir Goldstein 		goto out_put;
3930d809752SAmir Goldstein 
394cc0918b3SAmir Goldstein 	if (!upper) {
395cc0918b3SAmir Goldstein 		err = ovl_ctx_realloc_lower(fc);
396cc0918b3SAmir Goldstein 		if (err)
397cc0918b3SAmir Goldstein 			goto out_put;
398cc0918b3SAmir Goldstein 	}
399cc0918b3SAmir Goldstein 
4000d809752SAmir Goldstein 	/* Store the user provided path string in ctx to show in mountinfo */
4010d809752SAmir Goldstein 	ovl_add_layer(fc, layer, &path, &name);
4020d809752SAmir Goldstein 
4030d809752SAmir Goldstein out_put:
4040d809752SAmir Goldstein 	path_put(&path);
4050d809752SAmir Goldstein out_free:
4060d809752SAmir Goldstein 	kfree(name);
4070d809752SAmir Goldstein 	return err;
4080d809752SAmir Goldstein }
4090d809752SAmir Goldstein 
ovl_reset_lowerdirs(struct ovl_fs_context * ctx)41026532aebSAmir Goldstein static void ovl_reset_lowerdirs(struct ovl_fs_context *ctx)
411b36a5780SChristian Brauner {
41226532aebSAmir Goldstein 	struct ovl_fs_context_layer *l = ctx->lower;
41326532aebSAmir Goldstein 
41426532aebSAmir Goldstein 	// Reset old user provided lowerdir string
41526532aebSAmir Goldstein 	kfree(ctx->lowerdir_all);
41626532aebSAmir Goldstein 	ctx->lowerdir_all = NULL;
41726532aebSAmir Goldstein 
41826532aebSAmir Goldstein 	for (size_t nr = 0; nr < ctx->nr; nr++, l++) {
41926532aebSAmir Goldstein 		path_put(&l->path);
42026532aebSAmir Goldstein 		kfree(l->name);
42126532aebSAmir Goldstein 		l->name = NULL;
422b36a5780SChristian Brauner 	}
423b36a5780SChristian Brauner 	ctx->nr = 0;
424b36a5780SChristian Brauner 	ctx->nr_data = 0;
425b36a5780SChristian Brauner }
426b36a5780SChristian Brauner 
427b36a5780SChristian Brauner /*
428b36a5780SChristian Brauner  * Parse lowerdir= mount option:
429b36a5780SChristian Brauner  *
4301fff0482SAmir Goldstein  * e.g.: lowerdir=/lower1:/lower2:/lower3::/data1::/data2
431b36a5780SChristian Brauner  *     Set "/lower1", "/lower2", and "/lower3" as lower layers and
432b36a5780SChristian Brauner  *     "/data1" and "/data2" as data lower layers. Any existing lower
433b36a5780SChristian Brauner  *     layers are replaced.
434b36a5780SChristian Brauner  */
ovl_parse_param_lowerdir(const char * name,struct fs_context * fc)4357fb7998bSChristian Brauner static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
436b36a5780SChristian Brauner {
437b36a5780SChristian Brauner 	int err;
438b36a5780SChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
439b36a5780SChristian Brauner 	struct ovl_fs_context_layer *l;
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 == ':') {
456beae836eSAmir Goldstein 		pr_err("cannot append lower layer");
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 
479b36a5780SChristian Brauner 	if (nr_lower > ctx->capacity) {
480b36a5780SChristian Brauner 		err = -ENOMEM;
481b36a5780SChristian Brauner 		l = krealloc_array(ctx->lower, nr_lower, sizeof(*ctx->lower),
482b36a5780SChristian Brauner 				   GFP_KERNEL_ACCOUNT);
483b36a5780SChristian Brauner 		if (!l)
484b36a5780SChristian Brauner 			goto out_err;
485b36a5780SChristian Brauner 
486b36a5780SChristian Brauner 		ctx->lower = l;
487b36a5780SChristian Brauner 		ctx->capacity = nr_lower;
488b36a5780SChristian Brauner 	}
489b36a5780SChristian Brauner 
4901fff0482SAmir Goldstein 	iter = dup;
4911fff0482SAmir Goldstein 	l = ctx->lower;
4921fff0482SAmir Goldstein 	for (nr = 0; nr < nr_lower; nr++, l++) {
493be9857a4SAmir Goldstein 		ctx->nr++;
494b36a5780SChristian Brauner 		memset(l, 0, sizeof(*l));
495b36a5780SChristian Brauner 
4960d809752SAmir Goldstein 		err = ovl_mount_dir(iter, &l->path);
4970d809752SAmir Goldstein 		if (err)
4980d809752SAmir Goldstein 			goto out_put;
4990d809752SAmir Goldstein 
5000d809752SAmir Goldstein 		err = ovl_mount_dir_check(fc, &l->path, Opt_lowerdir, iter, false);
501b36a5780SChristian Brauner 		if (err)
502b36a5780SChristian Brauner 			goto out_put;
503b36a5780SChristian Brauner 
504b36a5780SChristian Brauner 		err = -ENOMEM;
5051fff0482SAmir Goldstein 		l->name = kstrdup(iter, GFP_KERNEL_ACCOUNT);
506b36a5780SChristian Brauner 		if (!l->name)
507b36a5780SChristian Brauner 			goto out_put;
508b36a5780SChristian Brauner 
509b36a5780SChristian Brauner 		if (data_layer)
510be9857a4SAmir Goldstein 			ctx->nr_data++;
511b36a5780SChristian Brauner 
512b36a5780SChristian Brauner 		/* Calling strchr() again would overrun. */
513be9857a4SAmir Goldstein 		if (ctx->nr == nr_lower)
514b36a5780SChristian Brauner 			break;
515b36a5780SChristian Brauner 
516b36a5780SChristian Brauner 		err = -EINVAL;
5171fff0482SAmir Goldstein 		iter = strchr(iter, '\0') + 1;
5181fff0482SAmir Goldstein 		if (*iter) {
519b36a5780SChristian Brauner 			/*
520b36a5780SChristian Brauner 			 * This is a regular layer so we require that
521b36a5780SChristian Brauner 			 * there are no data layers.
522b36a5780SChristian Brauner 			 */
523be9857a4SAmir Goldstein 			if (ctx->nr_data > 0) {
524b36a5780SChristian Brauner 				pr_err("regular lower layers cannot follow data lower layers");
525b36a5780SChristian Brauner 				goto out_put;
526b36a5780SChristian Brauner 			}
527b36a5780SChristian Brauner 
528b36a5780SChristian Brauner 			data_layer = false;
529b36a5780SChristian Brauner 			continue;
530b36a5780SChristian Brauner 		}
531b36a5780SChristian Brauner 
532b36a5780SChristian Brauner 		/* This is a data lower layer. */
533b36a5780SChristian Brauner 		data_layer = true;
5341fff0482SAmir Goldstein 		iter++;
535b36a5780SChristian Brauner 	}
536b36a5780SChristian Brauner 	kfree(dup);
537b36a5780SChristian Brauner 	return 0;
538b36a5780SChristian Brauner 
539b36a5780SChristian Brauner out_put:
54026532aebSAmir Goldstein 	ovl_reset_lowerdirs(ctx);
541b36a5780SChristian Brauner 
542b36a5780SChristian Brauner out_err:
543b36a5780SChristian Brauner 	kfree(dup);
544b36a5780SChristian Brauner 
545b36a5780SChristian Brauner 	/* Intentionally don't realloc to a smaller size. */
546b36a5780SChristian Brauner 	return err;
547b36a5780SChristian Brauner }
5487fb7998bSChristian Brauner 
ovl_parse_param(struct fs_context * fc,struct fs_parameter * param)5497fb7998bSChristian Brauner static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
5507fb7998bSChristian Brauner {
5517fb7998bSChristian Brauner 	int err = 0;
5527fb7998bSChristian Brauner 	struct fs_parse_result result;
5537fb7998bSChristian Brauner 	struct ovl_fs *ofs = fc->s_fs_info;
5547fb7998bSChristian Brauner 	struct ovl_config *config = &ofs->config;
5557fb7998bSChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
5567fb7998bSChristian Brauner 	int opt;
5577fb7998bSChristian Brauner 
5587fb7998bSChristian Brauner 	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
5597fb7998bSChristian Brauner 		/*
5607fb7998bSChristian Brauner 		 * On remount overlayfs has always ignored all mount
5617fb7998bSChristian Brauner 		 * options no matter if malformed or not so for
5627fb7998bSChristian Brauner 		 * backwards compatibility we do the same here.
5637fb7998bSChristian Brauner 		 */
5647fb7998bSChristian Brauner 		if (fc->oldapi)
5657fb7998bSChristian Brauner 			return 0;
5667fb7998bSChristian Brauner 
5677fb7998bSChristian Brauner 		/*
5687fb7998bSChristian Brauner 		 * Give us the freedom to allow changing mount options
5697fb7998bSChristian Brauner 		 * with the new mount api in the future. So instead of
5707fb7998bSChristian Brauner 		 * silently ignoring everything we report a proper
5717fb7998bSChristian Brauner 		 * error. This is only visible for users of the new
5727fb7998bSChristian Brauner 		 * mount api.
5737fb7998bSChristian Brauner 		 */
5747fb7998bSChristian Brauner 		return invalfc(fc, "No changes allowed in reconfigure");
5757fb7998bSChristian Brauner 	}
5767fb7998bSChristian Brauner 
5777fb7998bSChristian Brauner 	opt = fs_parse(fc, ovl_parameter_spec, param, &result);
5787fb7998bSChristian Brauner 	if (opt < 0)
5797fb7998bSChristian Brauner 		return opt;
5807fb7998bSChristian Brauner 
5817fb7998bSChristian Brauner 	switch (opt) {
5827fb7998bSChristian Brauner 	case Opt_lowerdir:
5837fb7998bSChristian Brauner 		err = ovl_parse_param_lowerdir(param->string, fc);
5847fb7998bSChristian Brauner 		break;
585cc0918b3SAmir Goldstein 	case Opt_lowerdir_add:
586cc0918b3SAmir Goldstein 	case Opt_datadir_add:
5877fb7998bSChristian Brauner 	case Opt_upperdir:
5887fb7998bSChristian Brauner 	case Opt_workdir:
5890d809752SAmir Goldstein 		err = ovl_parse_layer(fc, param, opt);
5907fb7998bSChristian Brauner 		break;
5917fb7998bSChristian Brauner 	case Opt_default_permissions:
5927fb7998bSChristian Brauner 		config->default_permissions = true;
5937fb7998bSChristian Brauner 		break;
5947fb7998bSChristian Brauner 	case Opt_redirect_dir:
5957fb7998bSChristian Brauner 		config->redirect_mode = result.uint_32;
5967fb7998bSChristian Brauner 		if (config->redirect_mode == OVL_REDIRECT_OFF) {
5977fb7998bSChristian Brauner 			config->redirect_mode = ovl_redirect_always_follow ?
5987fb7998bSChristian Brauner 						OVL_REDIRECT_FOLLOW :
5997fb7998bSChristian Brauner 						OVL_REDIRECT_NOFOLLOW;
6007fb7998bSChristian Brauner 		}
6017fb7998bSChristian Brauner 		ctx->set.redirect = true;
6027fb7998bSChristian Brauner 		break;
6037fb7998bSChristian Brauner 	case Opt_index:
6047fb7998bSChristian Brauner 		config->index = result.uint_32;
6057fb7998bSChristian Brauner 		ctx->set.index = true;
6067fb7998bSChristian Brauner 		break;
6077fb7998bSChristian Brauner 	case Opt_uuid:
6087fb7998bSChristian Brauner 		config->uuid = result.uint_32;
6097fb7998bSChristian Brauner 		break;
6107fb7998bSChristian Brauner 	case Opt_nfs_export:
6117fb7998bSChristian Brauner 		config->nfs_export = result.uint_32;
6127fb7998bSChristian Brauner 		ctx->set.nfs_export = true;
6137fb7998bSChristian Brauner 		break;
6147fb7998bSChristian Brauner 	case Opt_xino:
6157fb7998bSChristian Brauner 		config->xino = result.uint_32;
6167fb7998bSChristian Brauner 		break;
6177fb7998bSChristian Brauner 	case Opt_metacopy:
6187fb7998bSChristian Brauner 		config->metacopy = result.uint_32;
6197fb7998bSChristian Brauner 		ctx->set.metacopy = true;
6207fb7998bSChristian Brauner 		break;
621ae8cba40SAlexander Larsson 	case Opt_verity:
622ae8cba40SAlexander Larsson 		config->verity_mode = result.uint_32;
623ae8cba40SAlexander Larsson 		break;
6247fb7998bSChristian Brauner 	case Opt_volatile:
6257fb7998bSChristian Brauner 		config->ovl_volatile = true;
6267fb7998bSChristian Brauner 		break;
6277fb7998bSChristian Brauner 	case Opt_userxattr:
6287fb7998bSChristian Brauner 		config->userxattr = true;
6297fb7998bSChristian Brauner 		break;
6307fb7998bSChristian Brauner 	default:
6317fb7998bSChristian Brauner 		pr_err("unrecognized mount option \"%s\" or missing value\n",
6327fb7998bSChristian Brauner 		       param->key);
6337fb7998bSChristian Brauner 		return -EINVAL;
6347fb7998bSChristian Brauner 	}
6357fb7998bSChristian Brauner 
6367fb7998bSChristian Brauner 	return err;
6377fb7998bSChristian Brauner }
6387fb7998bSChristian Brauner 
ovl_get_tree(struct fs_context * fc)6397fb7998bSChristian Brauner static int ovl_get_tree(struct fs_context *fc)
6407fb7998bSChristian Brauner {
6417fb7998bSChristian Brauner 	return get_tree_nodev(fc, ovl_fill_super);
6427fb7998bSChristian Brauner }
6437fb7998bSChristian Brauner 
ovl_fs_context_free(struct ovl_fs_context * ctx)6447fb7998bSChristian Brauner static inline void ovl_fs_context_free(struct ovl_fs_context *ctx)
6457fb7998bSChristian Brauner {
64626532aebSAmir Goldstein 	ovl_reset_lowerdirs(ctx);
6477fb7998bSChristian Brauner 	path_put(&ctx->upper);
6487fb7998bSChristian Brauner 	path_put(&ctx->work);
6497fb7998bSChristian Brauner 	kfree(ctx->lower);
6507fb7998bSChristian Brauner 	kfree(ctx);
6517fb7998bSChristian Brauner }
6527fb7998bSChristian Brauner 
ovl_free(struct fs_context * fc)6537fb7998bSChristian Brauner static void ovl_free(struct fs_context *fc)
6547fb7998bSChristian Brauner {
6557fb7998bSChristian Brauner 	struct ovl_fs *ofs = fc->s_fs_info;
6567fb7998bSChristian Brauner 	struct ovl_fs_context *ctx = fc->fs_private;
6577fb7998bSChristian Brauner 
6587fb7998bSChristian Brauner 	/*
6597fb7998bSChristian Brauner 	 * ofs is stored in the fs_context when it is initialized.
6607fb7998bSChristian Brauner 	 * ofs is transferred to the superblock on a successful mount,
6617fb7998bSChristian Brauner 	 * but if an error occurs before the transfer we have to free
6627fb7998bSChristian Brauner 	 * it here.
6637fb7998bSChristian Brauner 	 */
6647fb7998bSChristian Brauner 	if (ofs)
6657fb7998bSChristian Brauner 		ovl_free_fs(ofs);
6667fb7998bSChristian Brauner 
6677fb7998bSChristian Brauner 	if (ctx)
6687fb7998bSChristian Brauner 		ovl_fs_context_free(ctx);
6697fb7998bSChristian Brauner }
6707fb7998bSChristian Brauner 
ovl_reconfigure(struct fs_context * fc)6717fb7998bSChristian Brauner static int ovl_reconfigure(struct fs_context *fc)
6727fb7998bSChristian Brauner {
6737fb7998bSChristian Brauner 	struct super_block *sb = fc->root->d_sb;
674f01d0889SAndrea Righi 	struct ovl_fs *ofs = OVL_FS(sb);
6757fb7998bSChristian Brauner 	struct super_block *upper_sb;
6767fb7998bSChristian Brauner 	int ret = 0;
6777fb7998bSChristian Brauner 
6787fb7998bSChristian Brauner 	if (!(fc->sb_flags & SB_RDONLY) && ovl_force_readonly(ofs))
6797fb7998bSChristian Brauner 		return -EROFS;
6807fb7998bSChristian Brauner 
6817fb7998bSChristian Brauner 	if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb)) {
6827fb7998bSChristian Brauner 		upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
6837fb7998bSChristian Brauner 		if (ovl_should_sync(ofs)) {
6847fb7998bSChristian Brauner 			down_read(&upper_sb->s_umount);
6857fb7998bSChristian Brauner 			ret = sync_filesystem(upper_sb);
6867fb7998bSChristian Brauner 			up_read(&upper_sb->s_umount);
6877fb7998bSChristian Brauner 		}
6887fb7998bSChristian Brauner 	}
6897fb7998bSChristian Brauner 
6907fb7998bSChristian Brauner 	return ret;
6917fb7998bSChristian Brauner }
6927fb7998bSChristian Brauner 
6937fb7998bSChristian Brauner static const struct fs_context_operations ovl_context_ops = {
694c34706acSAmir Goldstein 	.parse_monolithic = ovl_parse_monolithic,
6957fb7998bSChristian Brauner 	.parse_param = ovl_parse_param,
6967fb7998bSChristian Brauner 	.get_tree    = ovl_get_tree,
6977fb7998bSChristian Brauner 	.reconfigure = ovl_reconfigure,
6987fb7998bSChristian Brauner 	.free        = ovl_free,
6997fb7998bSChristian Brauner };
7007fb7998bSChristian Brauner 
7017fb7998bSChristian Brauner /*
7027fb7998bSChristian Brauner  * This is called during fsopen() and will record the user namespace of
7037fb7998bSChristian Brauner  * the caller in fc->user_ns since we've raised FS_USERNS_MOUNT. We'll
7047fb7998bSChristian Brauner  * need it when we actually create the superblock to verify that the
7057fb7998bSChristian Brauner  * process creating the superblock is in the same user namespace as
7067fb7998bSChristian Brauner  * process that called fsopen().
7077fb7998bSChristian Brauner  */
ovl_init_fs_context(struct fs_context * fc)7087fb7998bSChristian Brauner int ovl_init_fs_context(struct fs_context *fc)
7097fb7998bSChristian Brauner {
7107fb7998bSChristian Brauner 	struct ovl_fs_context *ctx;
7117fb7998bSChristian Brauner 	struct ovl_fs *ofs;
7127fb7998bSChristian Brauner 
7137fb7998bSChristian Brauner 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
7147fb7998bSChristian Brauner 	if (!ctx)
7157fb7998bSChristian Brauner 		return -ENOMEM;
7167fb7998bSChristian Brauner 
7177fb7998bSChristian Brauner 	/*
7187fb7998bSChristian Brauner 	 * By default we allocate for three lower layers. It's likely
7197fb7998bSChristian Brauner 	 * that it'll cover most users.
7207fb7998bSChristian Brauner 	 */
7217fb7998bSChristian Brauner 	ctx->lower = kmalloc_array(3, sizeof(*ctx->lower), GFP_KERNEL_ACCOUNT);
7227fb7998bSChristian Brauner 	if (!ctx->lower)
7237fb7998bSChristian Brauner 		goto out_err;
7247fb7998bSChristian Brauner 	ctx->capacity = 3;
7257fb7998bSChristian Brauner 
7267fb7998bSChristian Brauner 	ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
7277fb7998bSChristian Brauner 	if (!ofs)
7287fb7998bSChristian Brauner 		goto out_err;
7297fb7998bSChristian Brauner 
7307fb7998bSChristian Brauner 	ofs->config.redirect_mode	= ovl_redirect_mode_def();
7317fb7998bSChristian Brauner 	ofs->config.index		= ovl_index_def;
732b0504bfeSAmir Goldstein 	ofs->config.uuid		= ovl_uuid_def();
7337fb7998bSChristian Brauner 	ofs->config.nfs_export		= ovl_nfs_export_def;
7347fb7998bSChristian Brauner 	ofs->config.xino		= ovl_xino_def();
7357fb7998bSChristian Brauner 	ofs->config.metacopy		= ovl_metacopy_def;
7367fb7998bSChristian Brauner 
7377fb7998bSChristian Brauner 	fc->s_fs_info		= ofs;
7387fb7998bSChristian Brauner 	fc->fs_private		= ctx;
7397fb7998bSChristian Brauner 	fc->ops			= &ovl_context_ops;
7407fb7998bSChristian Brauner 	return 0;
7417fb7998bSChristian Brauner 
7427fb7998bSChristian Brauner out_err:
7437fb7998bSChristian Brauner 	ovl_fs_context_free(ctx);
7447fb7998bSChristian Brauner 	return -ENOMEM;
7457fb7998bSChristian Brauner 
7467fb7998bSChristian Brauner }
7477fb7998bSChristian Brauner 
ovl_free_fs(struct ovl_fs * ofs)7487fb7998bSChristian Brauner void ovl_free_fs(struct ovl_fs *ofs)
7497fb7998bSChristian Brauner {
7507fb7998bSChristian Brauner 	struct vfsmount **mounts;
7517fb7998bSChristian Brauner 	unsigned i;
7527fb7998bSChristian Brauner 
7537fb7998bSChristian Brauner 	iput(ofs->workbasedir_trap);
7547fb7998bSChristian Brauner 	iput(ofs->indexdir_trap);
7557fb7998bSChristian Brauner 	iput(ofs->workdir_trap);
7567fb7998bSChristian Brauner 	dput(ofs->whiteout);
7577fb7998bSChristian Brauner 	dput(ofs->indexdir);
7587fb7998bSChristian Brauner 	dput(ofs->workdir);
7597fb7998bSChristian Brauner 	if (ofs->workdir_locked)
7607fb7998bSChristian Brauner 		ovl_inuse_unlock(ofs->workbasedir);
7617fb7998bSChristian Brauner 	dput(ofs->workbasedir);
7627fb7998bSChristian Brauner 	if (ofs->upperdir_locked)
7637fb7998bSChristian Brauner 		ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root);
7647fb7998bSChristian Brauner 
765a535116dSAmir Goldstein 	/* Reuse ofs->config.lowerdirs as a vfsmount array before freeing it */
766a535116dSAmir Goldstein 	mounts = (struct vfsmount **) ofs->config.lowerdirs;
7677fb7998bSChristian Brauner 	for (i = 0; i < ofs->numlayer; i++) {
7687fb7998bSChristian Brauner 		iput(ofs->layers[i].trap);
769a535116dSAmir Goldstein 		kfree(ofs->config.lowerdirs[i]);
7707fb7998bSChristian Brauner 		mounts[i] = ofs->layers[i].mnt;
7717fb7998bSChristian Brauner 	}
7727fb7998bSChristian Brauner 	kern_unmount_array(mounts, ofs->numlayer);
7737fb7998bSChristian Brauner 	kfree(ofs->layers);
7747fb7998bSChristian Brauner 	for (i = 0; i < ofs->numfs; i++)
7757fb7998bSChristian Brauner 		free_anon_bdev(ofs->fs[i].pseudo_dev);
7767fb7998bSChristian Brauner 	kfree(ofs->fs);
7777fb7998bSChristian Brauner 
778a535116dSAmir Goldstein 	kfree(ofs->config.lowerdirs);
7797fb7998bSChristian Brauner 	kfree(ofs->config.upperdir);
7807fb7998bSChristian Brauner 	kfree(ofs->config.workdir);
7817fb7998bSChristian Brauner 	if (ofs->creator_cred)
7827fb7998bSChristian Brauner 		put_cred(ofs->creator_cred);
7837fb7998bSChristian Brauner 	kfree(ofs);
7847fb7998bSChristian Brauner }
7857fb7998bSChristian Brauner 
ovl_fs_params_verify(const struct ovl_fs_context * ctx,struct ovl_config * config)7867fb7998bSChristian Brauner int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
7877fb7998bSChristian Brauner 			 struct ovl_config *config)
7887fb7998bSChristian Brauner {
7897fb7998bSChristian Brauner 	struct ovl_opt_set set = ctx->set;
7907fb7998bSChristian Brauner 
7917fb7998bSChristian Brauner 	if (ctx->nr_data > 0 && !config->metacopy) {
7927fb7998bSChristian Brauner 		pr_err("lower data-only dirs require metacopy support.\n");
7937fb7998bSChristian Brauner 		return -EINVAL;
7947fb7998bSChristian Brauner 	}
7957fb7998bSChristian Brauner 
7967fb7998bSChristian Brauner 	/* Workdir/index are useless in non-upper mount */
7977fb7998bSChristian Brauner 	if (!config->upperdir) {
7987fb7998bSChristian Brauner 		if (config->workdir) {
7997fb7998bSChristian Brauner 			pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
8007fb7998bSChristian Brauner 				config->workdir);
8017fb7998bSChristian Brauner 			kfree(config->workdir);
8027fb7998bSChristian Brauner 			config->workdir = NULL;
8037fb7998bSChristian Brauner 		}
8047fb7998bSChristian Brauner 		if (config->index && set.index) {
8057fb7998bSChristian Brauner 			pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n");
8067fb7998bSChristian Brauner 			set.index = false;
8077fb7998bSChristian Brauner 		}
8087fb7998bSChristian Brauner 		config->index = false;
8097fb7998bSChristian Brauner 	}
8107fb7998bSChristian Brauner 
8117fb7998bSChristian Brauner 	if (!config->upperdir && config->ovl_volatile) {
8127fb7998bSChristian Brauner 		pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
8137fb7998bSChristian Brauner 		config->ovl_volatile = false;
8147fb7998bSChristian Brauner 	}
8157fb7998bSChristian Brauner 
816d9544c1bSAmir Goldstein 	if (!config->upperdir && config->uuid == OVL_UUID_ON) {
817d9544c1bSAmir Goldstein 		pr_info("option \"uuid=on\" requires an upper fs, falling back to uuid=null.\n");
818d9544c1bSAmir Goldstein 		config->uuid = OVL_UUID_NULL;
819d9544c1bSAmir Goldstein 	}
820d9544c1bSAmir Goldstein 
821ae8cba40SAlexander Larsson 	/* Resolve verity -> metacopy dependency */
822ae8cba40SAlexander Larsson 	if (config->verity_mode && !config->metacopy) {
823ae8cba40SAlexander Larsson 		/* Don't allow explicit specified conflicting combinations */
824ae8cba40SAlexander Larsson 		if (set.metacopy) {
825ae8cba40SAlexander Larsson 			pr_err("conflicting options: metacopy=off,verity=%s\n",
826ae8cba40SAlexander Larsson 			       ovl_verity_mode(config));
827ae8cba40SAlexander Larsson 			return -EINVAL;
828ae8cba40SAlexander Larsson 		}
829ae8cba40SAlexander Larsson 		/* Otherwise automatically enable metacopy. */
830ae8cba40SAlexander Larsson 		config->metacopy = true;
831ae8cba40SAlexander Larsson 	}
832ae8cba40SAlexander Larsson 
8337fb7998bSChristian Brauner 	/*
8347fb7998bSChristian Brauner 	 * This is to make the logic below simpler.  It doesn't make any other
8357fb7998bSChristian Brauner 	 * difference, since redirect_dir=on is only used for upper.
8367fb7998bSChristian Brauner 	 */
8377fb7998bSChristian Brauner 	if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
8387fb7998bSChristian Brauner 		config->redirect_mode = OVL_REDIRECT_ON;
8397fb7998bSChristian Brauner 
840ae8cba40SAlexander Larsson 	/* Resolve verity -> metacopy -> redirect_dir dependency */
8417fb7998bSChristian Brauner 	if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
8427fb7998bSChristian Brauner 		if (set.metacopy && set.redirect) {
8437fb7998bSChristian Brauner 			pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
8447fb7998bSChristian Brauner 			       ovl_redirect_mode(config));
8457fb7998bSChristian Brauner 			return -EINVAL;
8467fb7998bSChristian Brauner 		}
847ae8cba40SAlexander Larsson 		if (config->verity_mode && set.redirect) {
848ae8cba40SAlexander Larsson 			pr_err("conflicting options: verity=%s,redirect_dir=%s\n",
849ae8cba40SAlexander Larsson 			       ovl_verity_mode(config), ovl_redirect_mode(config));
850ae8cba40SAlexander Larsson 			return -EINVAL;
851ae8cba40SAlexander Larsson 		}
8527fb7998bSChristian Brauner 		if (set.redirect) {
8537fb7998bSChristian Brauner 			/*
8547fb7998bSChristian Brauner 			 * There was an explicit redirect_dir=... that resulted
8557fb7998bSChristian Brauner 			 * in this conflict.
8567fb7998bSChristian Brauner 			 */
8577fb7998bSChristian Brauner 			pr_info("disabling metacopy due to redirect_dir=%s\n",
8587fb7998bSChristian Brauner 				ovl_redirect_mode(config));
8597fb7998bSChristian Brauner 			config->metacopy = false;
8607fb7998bSChristian Brauner 		} else {
8617fb7998bSChristian Brauner 			/* Automatically enable redirect otherwise. */
8627fb7998bSChristian Brauner 			config->redirect_mode = OVL_REDIRECT_ON;
8637fb7998bSChristian Brauner 		}
8647fb7998bSChristian Brauner 	}
8657fb7998bSChristian Brauner 
8667fb7998bSChristian Brauner 	/* Resolve nfs_export -> index dependency */
8677fb7998bSChristian Brauner 	if (config->nfs_export && !config->index) {
8687fb7998bSChristian Brauner 		if (!config->upperdir &&
8697fb7998bSChristian Brauner 		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
8707fb7998bSChristian Brauner 			pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
8717fb7998bSChristian Brauner 			config->nfs_export = false;
8727fb7998bSChristian Brauner 		} else if (set.nfs_export && set.index) {
8737fb7998bSChristian Brauner 			pr_err("conflicting options: nfs_export=on,index=off\n");
8747fb7998bSChristian Brauner 			return -EINVAL;
8757fb7998bSChristian Brauner 		} else if (set.index) {
8767fb7998bSChristian Brauner 			/*
8777fb7998bSChristian Brauner 			 * There was an explicit index=off that resulted
8787fb7998bSChristian Brauner 			 * in this conflict.
8797fb7998bSChristian Brauner 			 */
8807fb7998bSChristian Brauner 			pr_info("disabling nfs_export due to index=off\n");
8817fb7998bSChristian Brauner 			config->nfs_export = false;
8827fb7998bSChristian Brauner 		} else {
8837fb7998bSChristian Brauner 			/* Automatically enable index otherwise. */
8847fb7998bSChristian Brauner 			config->index = true;
8857fb7998bSChristian Brauner 		}
8867fb7998bSChristian Brauner 	}
8877fb7998bSChristian Brauner 
888ae8cba40SAlexander Larsson 	/* Resolve nfs_export -> !metacopy && !verity dependency */
8897fb7998bSChristian Brauner 	if (config->nfs_export && config->metacopy) {
8907fb7998bSChristian Brauner 		if (set.nfs_export && set.metacopy) {
8917fb7998bSChristian Brauner 			pr_err("conflicting options: nfs_export=on,metacopy=on\n");
8927fb7998bSChristian Brauner 			return -EINVAL;
8937fb7998bSChristian Brauner 		}
8947fb7998bSChristian Brauner 		if (set.metacopy) {
8957fb7998bSChristian Brauner 			/*
8967fb7998bSChristian Brauner 			 * There was an explicit metacopy=on that resulted
8977fb7998bSChristian Brauner 			 * in this conflict.
8987fb7998bSChristian Brauner 			 */
8997fb7998bSChristian Brauner 			pr_info("disabling nfs_export due to metacopy=on\n");
9007fb7998bSChristian Brauner 			config->nfs_export = false;
901ae8cba40SAlexander Larsson 		} else if (config->verity_mode) {
902ae8cba40SAlexander Larsson 			/*
903ae8cba40SAlexander Larsson 			 * There was an explicit verity=.. that resulted
904ae8cba40SAlexander Larsson 			 * in this conflict.
905ae8cba40SAlexander Larsson 			 */
906ae8cba40SAlexander Larsson 			pr_info("disabling nfs_export due to verity=%s\n",
907ae8cba40SAlexander Larsson 				ovl_verity_mode(config));
908ae8cba40SAlexander Larsson 			config->nfs_export = false;
9097fb7998bSChristian Brauner 		} else {
9107fb7998bSChristian Brauner 			/*
9117fb7998bSChristian Brauner 			 * There was an explicit nfs_export=on that resulted
9127fb7998bSChristian Brauner 			 * in this conflict.
9137fb7998bSChristian Brauner 			 */
9147fb7998bSChristian Brauner 			pr_info("disabling metacopy due to nfs_export=on\n");
9157fb7998bSChristian Brauner 			config->metacopy = false;
9167fb7998bSChristian Brauner 		}
9177fb7998bSChristian Brauner 	}
9187fb7998bSChristian Brauner 
9197fb7998bSChristian Brauner 
920ae8cba40SAlexander Larsson 	/* Resolve userxattr -> !redirect && !metacopy && !verity dependency */
9217fb7998bSChristian Brauner 	if (config->userxattr) {
9227fb7998bSChristian Brauner 		if (set.redirect &&
9237fb7998bSChristian Brauner 		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
9247fb7998bSChristian Brauner 			pr_err("conflicting options: userxattr,redirect_dir=%s\n",
9257fb7998bSChristian Brauner 			       ovl_redirect_mode(config));
9267fb7998bSChristian Brauner 			return -EINVAL;
9277fb7998bSChristian Brauner 		}
9287fb7998bSChristian Brauner 		if (config->metacopy && set.metacopy) {
9297fb7998bSChristian Brauner 			pr_err("conflicting options: userxattr,metacopy=on\n");
9307fb7998bSChristian Brauner 			return -EINVAL;
9317fb7998bSChristian Brauner 		}
932ae8cba40SAlexander Larsson 		if (config->verity_mode) {
933ae8cba40SAlexander Larsson 			pr_err("conflicting options: userxattr,verity=%s\n",
934ae8cba40SAlexander Larsson 			       ovl_verity_mode(config));
935ae8cba40SAlexander Larsson 			return -EINVAL;
936ae8cba40SAlexander Larsson 		}
9377fb7998bSChristian Brauner 		/*
9387fb7998bSChristian Brauner 		 * Silently disable default setting of redirect and metacopy.
9397fb7998bSChristian Brauner 		 * This shall be the default in the future as well: these
9407fb7998bSChristian Brauner 		 * options must be explicitly enabled if used together with
9417fb7998bSChristian Brauner 		 * userxattr.
9427fb7998bSChristian Brauner 		 */
9437fb7998bSChristian Brauner 		config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
9447fb7998bSChristian Brauner 		config->metacopy = false;
9457fb7998bSChristian Brauner 	}
9467fb7998bSChristian Brauner 
9477fb7998bSChristian Brauner 	return 0;
9487fb7998bSChristian Brauner }
9497fb7998bSChristian Brauner 
9507fb7998bSChristian Brauner /**
9517fb7998bSChristian Brauner  * ovl_show_options
9527fb7998bSChristian Brauner  * @m: the seq_file handle
9537fb7998bSChristian Brauner  * @dentry: The dentry to query
9547fb7998bSChristian Brauner  *
9557fb7998bSChristian Brauner  * Prints the mount options for a given superblock.
9567fb7998bSChristian Brauner  * Returns zero; does not fail.
9577fb7998bSChristian Brauner  */
ovl_show_options(struct seq_file * m,struct dentry * dentry)9587fb7998bSChristian Brauner int ovl_show_options(struct seq_file *m, struct dentry *dentry)
9597fb7998bSChristian Brauner {
9607fb7998bSChristian Brauner 	struct super_block *sb = dentry->d_sb;
961f01d0889SAndrea Righi 	struct ovl_fs *ofs = OVL_FS(sb);
962cc0918b3SAmir Goldstein 	size_t nr, nr_merged_lower, nr_lower = 0;
96326532aebSAmir Goldstein 	char **lowerdirs = ofs->config.lowerdirs;
9647fb7998bSChristian Brauner 
96532db5107SAmir Goldstein 	/*
96626532aebSAmir Goldstein 	 * lowerdirs[0] holds the colon separated list that user provided
96726532aebSAmir Goldstein 	 * with lowerdir mount option.
968cc0918b3SAmir Goldstein 	 * lowerdirs[1..numlayer] hold the lowerdir paths that were added
969cc0918b3SAmir Goldstein 	 * using the lowerdir+ and datadir+ mount options.
970cc0918b3SAmir Goldstein 	 * For now, we do not allow mixing the legacy lowerdir mount option
971cc0918b3SAmir Goldstein 	 * with the new lowerdir+ and datadir+ mount options.
97232db5107SAmir Goldstein 	 */
973cc0918b3SAmir Goldstein 	if (lowerdirs[0]) {
97426532aebSAmir Goldstein 		seq_show_option(m, "lowerdir", lowerdirs[0]);
975cc0918b3SAmir Goldstein 	} else {
976cc0918b3SAmir Goldstein 		nr_lower = ofs->numlayer;
977cc0918b3SAmir Goldstein 		nr_merged_lower = nr_lower - ofs->numdatalayer;
978cc0918b3SAmir Goldstein 	}
979cc0918b3SAmir Goldstein 	for (nr = 1; nr < nr_lower; nr++) {
980cc0918b3SAmir Goldstein 		if (nr < nr_merged_lower)
981cc0918b3SAmir Goldstein 			seq_show_option(m, "lowerdir+", lowerdirs[nr]);
982cc0918b3SAmir Goldstein 		else
983cc0918b3SAmir Goldstein 			seq_show_option(m, "datadir+", lowerdirs[nr]);
984cc0918b3SAmir Goldstein 	}
9857fb7998bSChristian Brauner 	if (ofs->config.upperdir) {
9867fb7998bSChristian Brauner 		seq_show_option(m, "upperdir", ofs->config.upperdir);
9877fb7998bSChristian Brauner 		seq_show_option(m, "workdir", ofs->config.workdir);
9887fb7998bSChristian Brauner 	}
9897fb7998bSChristian Brauner 	if (ofs->config.default_permissions)
9907fb7998bSChristian Brauner 		seq_puts(m, ",default_permissions");
9917fb7998bSChristian Brauner 	if (ofs->config.redirect_mode != ovl_redirect_mode_def())
9927fb7998bSChristian Brauner 		seq_printf(m, ",redirect_dir=%s",
9937fb7998bSChristian Brauner 			   ovl_redirect_mode(&ofs->config));
9947fb7998bSChristian Brauner 	if (ofs->config.index != ovl_index_def)
9957fb7998bSChristian Brauner 		seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
996b0504bfeSAmir Goldstein 	if (ofs->config.uuid != ovl_uuid_def())
997b0504bfeSAmir Goldstein 		seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
9987fb7998bSChristian Brauner 	if (ofs->config.nfs_export != ovl_nfs_export_def)
9997fb7998bSChristian Brauner 		seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
10007fb7998bSChristian Brauner 						"on" : "off");
10017fb7998bSChristian Brauner 	if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
10027fb7998bSChristian Brauner 		seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
10037fb7998bSChristian Brauner 	if (ofs->config.metacopy != ovl_metacopy_def)
10047fb7998bSChristian Brauner 		seq_printf(m, ",metacopy=%s",
10057fb7998bSChristian Brauner 			   ofs->config.metacopy ? "on" : "off");
10067fb7998bSChristian Brauner 	if (ofs->config.ovl_volatile)
10077fb7998bSChristian Brauner 		seq_puts(m, ",volatile");
10087fb7998bSChristian Brauner 	if (ofs->config.userxattr)
10097fb7998bSChristian Brauner 		seq_puts(m, ",userxattr");
1010ae8cba40SAlexander Larsson 	if (ofs->config.verity_mode != ovl_verity_mode_def())
1011ae8cba40SAlexander Larsson 		seq_printf(m, ",verity=%s",
1012ae8cba40SAlexander Larsson 			   ovl_verity_mode(&ofs->config));
10137fb7998bSChristian Brauner 	return 0;
10147fb7998bSChristian Brauner }
1015