Skip to content

Commit af5f239

Browse files
committed
ovl: store enum redirect_mode in config instead of a string
Do all the logic to set the mode during mount options parsing and do not keep the option string around. Use a constant_table to translate from enum redirect mode to string in preperation for new mount api option parsing. The mount option "off" is translated to either "follow" or "nofollow", depending on the "redirect_always_follow" build/module config, so in effect, there are only three possible redirect modes. This results in a minor change to the string that is displayed in show_options() - when redirect_dir is enabled by default and the user mounts with the option "redirect_dir=off", instead of displaying the mode "redirect_dir=off" in show_options(), the displayed mode will be either "redirect_dir=follow" or "redirect_dir=nofollow", depending on the value of "redirect_always_follow" build/module config. The displayed mode reflects the effective mode, so mounting overlayfs again with the dispalyed redirect_dir option will result with the same effective and displayed mode. Signed-off-by: Amir Goldstein <amir73il@gmail.com>
1 parent dcb399d commit af5f239

7 files changed

Lines changed: 107 additions & 89 deletions

File tree

Documentation/filesystems/overlayfs.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,11 @@ Mount options:
231231
Redirects are enabled.
232232
- "redirect_dir=follow":
233233
Redirects are not created, but followed.
234-
- "redirect_dir=off":
235-
Redirects are not created and only followed if "redirect_always_follow"
236-
feature is enabled in the kernel/module config.
237234
- "redirect_dir=nofollow":
238-
Redirects are not created and not followed (equivalent to "redirect_dir=off"
239-
if "redirect_always_follow" feature is not enabled).
235+
Redirects are not created and not followed.
236+
- "redirect_dir=off":
237+
If "redirect_always_follow" is enabled in the kernel/module config,
238+
this "off" traslates to "follow", otherwise it translates to "nofollow".
240239

241240
When the NFS export feature is enabled, every copied up directory is
242241
indexed by the file handle of the lower inode and a file handle of the

fs/overlayfs/dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ static bool ovl_type_merge_or_lower(struct dentry *dentry)
952952

953953
static bool ovl_can_move(struct dentry *dentry)
954954
{
955-
return ovl_redirect_dir(dentry->d_sb) ||
955+
return ovl_redirect_dir(OVL_FS(dentry->d_sb)) ||
956956
!d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
957957
}
958958

fs/overlayfs/namei.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
961961
.is_dir = false,
962962
.opaque = false,
963963
.stop = false,
964-
.last = ofs->config.redirect_follow ? false : !ovl_numlower(poe),
964+
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
965965
.redirect = NULL,
966966
.metacopy = false,
967967
};
@@ -1022,7 +1022,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10221022
for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
10231023
struct ovl_path lower = ovl_lowerstack(poe)[i];
10241024

1025-
if (!ofs->config.redirect_follow)
1025+
if (!ovl_redirect_follow(ofs))
10261026
d.last = i == ovl_numlower(poe) - 1;
10271027
else if (d.is_dir || !ofs->numdatalayer)
10281028
d.last = lower.layer->idx == ovl_numlower(roe);
@@ -1102,7 +1102,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
11021102
* this attack vector when not necessary.
11031103
*/
11041104
err = -EPERM;
1105-
if (d.redirect && !ofs->config.redirect_follow) {
1105+
if (d.redirect && !ovl_redirect_follow(ofs)) {
11061106
pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n",
11071107
dentry);
11081108
goto out_put;

fs/overlayfs/overlayfs.h

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ enum ovl_entry_flag {
5757
OVL_E_CONNECTED,
5858
};
5959

60+
enum {
61+
OVL_REDIRECT_OFF, /* "off" mode is never used. In effect */
62+
OVL_REDIRECT_FOLLOW, /* ...it translates to either "follow" */
63+
OVL_REDIRECT_NOFOLLOW, /* ...or "nofollow". */
64+
OVL_REDIRECT_ON,
65+
};
66+
6067
enum {
6168
OVL_XINO_OFF,
6269
OVL_XINO_AUTO,
@@ -352,17 +359,6 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
352359
return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC));
353360
}
354361

355-
static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs)
356-
{
357-
/*
358-
* To avoid regressions in existing setups with overlay lower offline
359-
* changes, we allow lower changes only if none of the new features
360-
* are used.
361-
*/
362-
return (!ofs->config.index && !ofs->config.metacopy &&
363-
!ofs->config.redirect_dir && ofs->config.xino != OVL_XINO_ON);
364-
}
365-
366362

367363
/* util.c */
368364
int ovl_want_write(struct dentry *dentry);
@@ -421,7 +417,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags);
421417
bool ovl_dentry_needs_data_copy_up_locked(struct dentry *dentry, int flags);
422418
bool ovl_has_upperdata(struct inode *inode);
423419
void ovl_set_upperdata(struct inode *inode);
424-
bool ovl_redirect_dir(struct super_block *sb);
425420
const char *ovl_dentry_get_redirect(struct dentry *dentry);
426421
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
427422
void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
@@ -489,6 +484,16 @@ static inline bool ovl_is_impuredir(struct super_block *sb,
489484
return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE);
490485
}
491486

487+
static inline bool ovl_redirect_follow(struct ovl_fs *ofs)
488+
{
489+
return ofs->config.redirect_mode != OVL_REDIRECT_NOFOLLOW;
490+
}
491+
492+
static inline bool ovl_redirect_dir(struct ovl_fs *ofs)
493+
{
494+
return ofs->config.redirect_mode == OVL_REDIRECT_ON;
495+
}
496+
492497
/*
493498
* With xino=auto, we do best effort to keep all inodes on same st_dev and
494499
* d_ino consistent with st_ino.
@@ -499,6 +504,16 @@ static inline bool ovl_xino_warn(struct ovl_fs *ofs)
499504
return ofs->config.xino == OVL_XINO_ON;
500505
}
501506

507+
/*
508+
* To avoid regressions in existing setups with overlay lower offline changes,
509+
* we allow lower changes only if none of the new features are used.
510+
*/
511+
static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs)
512+
{
513+
return (!ofs->config.index && !ofs->config.metacopy &&
514+
!ovl_redirect_dir(ofs) && !ovl_xino_warn(ofs));
515+
}
516+
502517
/* All layers on same fs? */
503518
static inline bool ovl_same_fs(struct ovl_fs *ofs)
504519
{

fs/overlayfs/ovl_entry.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ struct ovl_config {
1010
char *upperdir;
1111
char *workdir;
1212
bool default_permissions;
13-
bool redirect_dir;
14-
bool redirect_follow;
15-
const char *redirect_mode;
13+
int redirect_mode;
1614
bool index;
1715
bool uuid;
1816
bool nfs_export;

fs/overlayfs/super.c

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ static void ovl_free_fs(struct ovl_fs *ofs)
244244
kfree(ofs->config.lowerdir);
245245
kfree(ofs->config.upperdir);
246246
kfree(ofs->config.workdir);
247-
kfree(ofs->config.redirect_mode);
248247
if (ofs->creator_cred)
249248
put_cred(ofs->creator_cred);
250249
kfree(ofs);
@@ -330,9 +329,24 @@ static bool ovl_force_readonly(struct ovl_fs *ofs)
330329
return (!ovl_upper_mnt(ofs) || !ofs->workdir);
331330
}
332331

333-
static const char *ovl_redirect_mode_def(void)
332+
static const struct constant_table ovl_parameter_redirect_dir[] = {
333+
{ "off", OVL_REDIRECT_OFF },
334+
{ "follow", OVL_REDIRECT_FOLLOW },
335+
{ "nofollow", OVL_REDIRECT_NOFOLLOW },
336+
{ "on", OVL_REDIRECT_ON },
337+
{}
338+
};
339+
340+
static const char *ovl_redirect_mode(struct ovl_config *config)
334341
{
335-
return ovl_redirect_dir_def ? "on" : "off";
342+
return ovl_parameter_redirect_dir[config->redirect_mode].name;
343+
}
344+
345+
static int ovl_redirect_mode_def(void)
346+
{
347+
return ovl_redirect_dir_def ? OVL_REDIRECT_ON :
348+
ovl_redirect_always_follow ? OVL_REDIRECT_FOLLOW :
349+
OVL_REDIRECT_NOFOLLOW;
336350
}
337351

338352
static const struct constant_table ovl_parameter_xino[] = {
@@ -372,8 +386,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
372386
}
373387
if (ofs->config.default_permissions)
374388
seq_puts(m, ",default_permissions");
375-
if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0)
376-
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
389+
if (ofs->config.redirect_mode != ovl_redirect_mode_def())
390+
seq_printf(m, ",redirect_dir=%s",
391+
ovl_redirect_mode(&ofs->config));
377392
if (ofs->config.index != ovl_index_def)
378393
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
379394
if (!ofs->config.uuid)
@@ -431,7 +446,10 @@ enum {
431446
OPT_UPPERDIR,
432447
OPT_WORKDIR,
433448
OPT_DEFAULT_PERMISSIONS,
434-
OPT_REDIRECT_DIR,
449+
OPT_REDIRECT_DIR_ON,
450+
OPT_REDIRECT_DIR_OFF,
451+
OPT_REDIRECT_DIR_FOLLOW,
452+
OPT_REDIRECT_DIR_NOFOLLOW,
435453
OPT_INDEX_ON,
436454
OPT_INDEX_OFF,
437455
OPT_UUID_ON,
@@ -453,7 +471,10 @@ static const match_table_t ovl_tokens = {
453471
{OPT_UPPERDIR, "upperdir=%s"},
454472
{OPT_WORKDIR, "workdir=%s"},
455473
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
456-
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
474+
{OPT_REDIRECT_DIR_ON, "redirect_dir=on"},
475+
{OPT_REDIRECT_DIR_OFF, "redirect_dir=off"},
476+
{OPT_REDIRECT_DIR_FOLLOW, "redirect_dir=follow"},
477+
{OPT_REDIRECT_DIR_NOFOLLOW, "redirect_dir=nofollow"},
457478
{OPT_INDEX_ON, "index=on"},
458479
{OPT_INDEX_OFF, "index=off"},
459480
{OPT_USERXATTR, "userxattr"},
@@ -493,40 +514,12 @@ static char *ovl_next_opt(char **s)
493514
return sbegin;
494515
}
495516

496-
static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode)
497-
{
498-
if (strcmp(mode, "on") == 0) {
499-
config->redirect_dir = true;
500-
/*
501-
* Does not make sense to have redirect creation without
502-
* redirect following.
503-
*/
504-
config->redirect_follow = true;
505-
} else if (strcmp(mode, "follow") == 0) {
506-
config->redirect_follow = true;
507-
} else if (strcmp(mode, "off") == 0) {
508-
if (ovl_redirect_always_follow)
509-
config->redirect_follow = true;
510-
} else if (strcmp(mode, "nofollow") != 0) {
511-
pr_err("bad mount option \"redirect_dir=%s\"\n",
512-
mode);
513-
return -EINVAL;
514-
}
515-
516-
return 0;
517-
}
518-
519517
static int ovl_parse_opt(char *opt, struct ovl_config *config)
520518
{
521519
char *p;
522-
int err;
523520
bool metacopy_opt = false, redirect_opt = false;
524521
bool nfs_export_opt = false, index_opt = false;
525522

526-
config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
527-
if (!config->redirect_mode)
528-
return -ENOMEM;
529-
530523
while ((p = ovl_next_opt(&opt)) != NULL) {
531524
int token;
532525
substring_t args[MAX_OPT_ARGS];
@@ -561,11 +554,25 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
561554
config->default_permissions = true;
562555
break;
563556

564-
case OPT_REDIRECT_DIR:
565-
kfree(config->redirect_mode);
566-
config->redirect_mode = match_strdup(&args[0]);
567-
if (!config->redirect_mode)
568-
return -ENOMEM;
557+
case OPT_REDIRECT_DIR_ON:
558+
config->redirect_mode = OVL_REDIRECT_ON;
559+
redirect_opt = true;
560+
break;
561+
562+
case OPT_REDIRECT_DIR_OFF:
563+
config->redirect_mode = ovl_redirect_always_follow ?
564+
OVL_REDIRECT_FOLLOW :
565+
OVL_REDIRECT_NOFOLLOW;
566+
redirect_opt = true;
567+
break;
568+
569+
case OPT_REDIRECT_DIR_FOLLOW:
570+
config->redirect_mode = OVL_REDIRECT_FOLLOW;
571+
redirect_opt = true;
572+
break;
573+
574+
case OPT_REDIRECT_DIR_NOFOLLOW:
575+
config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
569576
redirect_opt = true;
570577
break;
571578

@@ -654,22 +661,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
654661
config->ovl_volatile = false;
655662
}
656663

657-
err = ovl_parse_redirect_mode(config, config->redirect_mode);
658-
if (err)
659-
return err;
660-
661664
/*
662665
* This is to make the logic below simpler. It doesn't make any other
663-
* difference, since config->redirect_dir is only used for upper.
666+
* difference, since redirect_dir=on is only used for upper.
664667
*/
665-
if (!config->upperdir && config->redirect_follow)
666-
config->redirect_dir = true;
668+
if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
669+
config->redirect_mode = OVL_REDIRECT_ON;
667670

668671
/* Resolve metacopy -> redirect_dir dependency */
669-
if (config->metacopy && !config->redirect_dir) {
672+
if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
670673
if (metacopy_opt && redirect_opt) {
671674
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
672-
config->redirect_mode);
675+
ovl_redirect_mode(config));
673676
return -EINVAL;
674677
}
675678
if (redirect_opt) {
@@ -678,17 +681,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
678681
* in this conflict.
679682
*/
680683
pr_info("disabling metacopy due to redirect_dir=%s\n",
681-
config->redirect_mode);
684+
ovl_redirect_mode(config));
682685
config->metacopy = false;
683686
} else {
684687
/* Automatically enable redirect otherwise. */
685-
config->redirect_follow = config->redirect_dir = true;
688+
config->redirect_mode = OVL_REDIRECT_ON;
686689
}
687690
}
688691

689692
/* Resolve nfs_export -> index dependency */
690693
if (config->nfs_export && !config->index) {
691-
if (!config->upperdir && config->redirect_follow) {
694+
if (!config->upperdir &&
695+
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
692696
pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
693697
config->nfs_export = false;
694698
} else if (nfs_export_opt && index_opt) {
@@ -733,9 +737,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
733737

734738
/* Resolve userxattr -> !redirect && !metacopy dependency */
735739
if (config->userxattr) {
736-
if (config->redirect_follow && redirect_opt) {
740+
if (redirect_opt &&
741+
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
737742
pr_err("conflicting options: userxattr,redirect_dir=%s\n",
738-
config->redirect_mode);
743+
ovl_redirect_mode(config));
739744
return -EINVAL;
740745
}
741746
if (config->metacopy && metacopy_opt) {
@@ -748,7 +753,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
748753
* options must be explicitly enabled if used together with
749754
* userxattr.
750755
*/
751-
config->redirect_dir = config->redirect_follow = false;
756+
config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
752757
config->metacopy = false;
753758
}
754759

@@ -1332,10 +1337,17 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
13321337
if (err) {
13331338
pr_warn("failed to set xattr on upper\n");
13341339
ofs->noxattr = true;
1335-
if (ofs->config.index || ofs->config.metacopy) {
1336-
ofs->config.index = false;
1340+
if (ovl_redirect_follow(ofs)) {
1341+
ofs->config.redirect_mode = OVL_REDIRECT_NOFOLLOW;
1342+
pr_warn("...falling back to redirect_dir=nofollow.\n");
1343+
}
1344+
if (ofs->config.metacopy) {
13371345
ofs->config.metacopy = false;
1338-
pr_warn("...falling back to index=off,metacopy=off.\n");
1346+
pr_warn("...falling back to metacopy=off.\n");
1347+
}
1348+
if (ofs->config.index) {
1349+
ofs->config.index = false;
1350+
pr_warn("...falling back to index=off.\n");
13391351
}
13401352
/*
13411353
* xattr support is required for persistent st_ino.
@@ -1963,6 +1975,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
19631975
if (!cred)
19641976
goto out_err;
19651977

1978+
ofs->config.redirect_mode = ovl_redirect_mode_def();
19661979
ofs->config.index = ovl_index_def;
19671980
ofs->config.uuid = true;
19681981
ofs->config.nfs_export = ovl_nfs_export_def;

fs/overlayfs/util.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -506,13 +506,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags)
506506
return !ovl_has_upperdata(d_inode(dentry));
507507
}
508508

509-
bool ovl_redirect_dir(struct super_block *sb)
510-
{
511-
struct ovl_fs *ofs = sb->s_fs_info;
512-
513-
return ofs->config.redirect_dir && !ofs->noxattr;
514-
}
515-
516509
const char *ovl_dentry_get_redirect(struct dentry *dentry)
517510
{
518511
return OVL_I(d_inode(dentry))->redirect;

0 commit comments

Comments
 (0)