diff options
Diffstat (limited to 'fs/overlayfs/super.c')
-rw-r--r-- | fs/overlayfs/super.c | 103 |
1 files changed, 61 insertions, 42 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 704b37311467..2e0fc93c2c06 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -64,6 +64,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe) dput(oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY); +module_param_named(metacopy, ovl_metacopy_def, bool, 0644); +MODULE_PARM_DESC(ovl_metacopy_def, + "Default to on or off for the metadata only copy up feature"); + static void ovl_dentry_release(struct dentry *dentry) { struct ovl_entry *oe = dentry->d_fsdata; @@ -74,31 +79,14 @@ static void ovl_dentry_release(struct dentry *dentry) } } -static int ovl_check_append_only(struct inode *inode, int flag) -{ - /* - * This test was moot in vfs may_open() because overlay inode does - * not have the S_APPEND flag, so re-check on real upper inode - */ - if (IS_APPEND(inode)) { - if ((flag & O_ACCMODE) != O_RDONLY && !(flag & O_APPEND)) - return -EPERM; - if (flag & O_TRUNC) - return -EPERM; - } - - return 0; -} - static struct dentry *ovl_d_real(struct dentry *dentry, - const struct inode *inode, - unsigned int open_flags, unsigned int flags) + const struct inode *inode) { struct dentry *real; - int err; - if (flags & D_REAL_UPPER) - return ovl_dentry_upper(dentry); + /* It's an overlay file */ + if (inode && d_inode(dentry) == inode) + return dentry; if (!d_is_reg(dentry)) { if (!inode || inode == d_inode(dentry)) @@ -106,28 +94,19 @@ static struct dentry *ovl_d_real(struct dentry *dentry, goto bug; } - if (open_flags) { - err = ovl_open_maybe_copy_up(dentry, open_flags); - if (err) - return ERR_PTR(err); - } - real = ovl_dentry_upper(dentry); - if (real && (!inode || inode == d_inode(real))) { - if (!inode) { - err = ovl_check_append_only(d_inode(real), open_flags); - if (err) - return ERR_PTR(err); - } + if (real && (inode == d_inode(real))) + return real; + + if (real && !inode && ovl_has_upperdata(d_inode(dentry))) return real; - } - real = ovl_dentry_lower(dentry); + real = ovl_dentry_lowerdata(dentry); if (!real) goto bug; /* Handle recursion */ - real = d_real(real, inode, open_flags, 0); + real = d_real(real, inode); if (!inode || inode == d_inode(real)) return real; @@ -205,6 +184,7 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->flags = 0; oi->__upperdentry = NULL; oi->lower = NULL; + oi->lowerdata = NULL; mutex_init(&oi->lock); return &oi->vfs_inode; @@ -223,8 +203,11 @@ static void ovl_destroy_inode(struct inode *inode) dput(oi->__upperdentry); iput(oi->lower); + if (S_ISDIR(inode->i_mode)) + ovl_dir_cache_free(inode); + else + iput(oi->lowerdata); kfree(oi->redirect); - ovl_dir_cache_free(inode); mutex_destroy(&oi->lock); call_rcu(&inode->i_rcu, ovl_i_callback); @@ -376,6 +359,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) "on" : "off"); if (ofs->config.xino != ovl_xino_def()) seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]); + if (ofs->config.metacopy != ovl_metacopy_def) + seq_printf(m, ",metacopy=%s", + ofs->config.metacopy ? "on" : "off"); return 0; } @@ -413,6 +399,8 @@ enum { OPT_XINO_ON, OPT_XINO_OFF, OPT_XINO_AUTO, + OPT_METACOPY_ON, + OPT_METACOPY_OFF, OPT_ERR, }; @@ -429,6 +417,8 @@ static const match_table_t ovl_tokens = { {OPT_XINO_ON, "xino=on"}, {OPT_XINO_OFF, "xino=off"}, {OPT_XINO_AUTO, "xino=auto"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} }; @@ -481,6 +471,7 @@ static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode) static int ovl_parse_opt(char *opt, struct ovl_config *config) { char *p; + int err; config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL); if (!config->redirect_mode) @@ -555,6 +546,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->xino = OVL_XINO_AUTO; break; + case OPT_METACOPY_ON: + config->metacopy = true; + break; + + case OPT_METACOPY_OFF: + config->metacopy = false; + break; + default: pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p); return -EINVAL; @@ -569,7 +568,20 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->workdir = NULL; } - return ovl_parse_redirect_mode(config, config->redirect_mode); + err = ovl_parse_redirect_mode(config, config->redirect_mode); + if (err) + return err; + + /* metacopy feature with upper requires redirect_dir=on */ + if (config->upperdir && config->metacopy && !config->redirect_dir) { + pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=on\", falling back to metacopy=off.\n"); + config->metacopy = false; + } else if (config->metacopy && !config->redirect_follow) { + pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n"); + config->metacopy = false; + } + + return 0; } #define OVL_WORKDIR_NAME "work" @@ -1042,7 +1054,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) if (err) { ofs->noxattr = true; ofs->config.index = false; - pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off.\n"); + ofs->config.metacopy = false; + pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off and metacopy=off.\n"); err = 0; } else { vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE); @@ -1064,7 +1077,6 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n"); ofs->config.nfs_export = false; } - out: mnt_drop_write(mnt); return err; @@ -1375,6 +1387,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.index = ovl_index_def; ofs->config.nfs_export = ovl_nfs_export_def; ofs->config.xino = ovl_xino_def(); + ofs->config.metacopy = ovl_metacopy_def; err = ovl_parse_opt((char *) data, &ofs->config); if (err) goto out_err; @@ -1445,6 +1458,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } } + if (ofs->config.metacopy && ofs->config.nfs_export) { + pr_warn("overlayfs: NFS export is not supported with metadata only copy up, falling back to nfs_export=off.\n"); + ofs->config.nfs_export = false; + } + if (ofs->config.nfs_export) sb->s_export_op = &ovl_export_operations; @@ -1455,7 +1473,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &ovl_super_operations; sb->s_xattr = ovl_xattr_handlers; sb->s_fs_info = ofs; - sb->s_flags |= SB_POSIXACL | SB_NOREMOTELOCK; + sb->s_flags |= SB_POSIXACL; err = -ENOMEM; root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); @@ -1474,8 +1492,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) /* Root is always merge -> can have whiteouts */ ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry)); ovl_dentry_set_flag(OVL_E_CONNECTED, root_dentry); + ovl_set_upperdata(d_inode(root_dentry)); ovl_inode_init(d_inode(root_dentry), upperpath.dentry, - ovl_dentry_lower(root_dentry)); + ovl_dentry_lower(root_dentry), NULL); sb->s_root = root_dentry; |