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