diff options
Diffstat (limited to 'fs/overlayfs/super.c')
| -rw-r--r-- | fs/overlayfs/super.c | 52 |
1 files changed, 44 insertions, 8 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 8bc970748b96..e61aab1999d3 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -701,7 +701,7 @@ static int ovl_create_volatile_dirty(struct ovl_fs *ofs) } static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, - const struct path *workpath) + const struct path *workpath, size_t nr_data) { struct vfsmount *mnt = ovl_upper_mnt(ofs); struct dentry *workdir; @@ -765,9 +765,11 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, if (err) { pr_warn("failed to set xattr on upper\n"); ofs->noxattr = true; - if (ovl_redirect_follow(ofs)) { - ofs->config.redirect_mode = OVL_REDIRECT_NOFOLLOW; - pr_warn("...falling back to redirect_dir=nofollow.\n"); + if (ovl_redirect_follow_lower(ofs)) { + ofs->config.redirect_mode = + ovl_redirect_follow_mode(false, nr_data); + pr_warn("...falling back to redirect_dir=%s.\n", + ovl_redirect_mode(&ofs->config)); } if (ofs->config.metacopy) { ofs->config.metacopy = false; @@ -844,7 +846,7 @@ out: static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, const struct path *upperpath, - const struct path *workpath) + const struct path *workpath, size_t nr_data) { int err; @@ -873,7 +875,7 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, if (err) return err; - return ovl_make_workdir(sb, ofs, workpath); + return ovl_make_workdir(sb, ofs, workpath, nr_data); } static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, @@ -1142,6 +1144,29 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, } /* + * Lower file handles identify the lower fs, but not the layer, + * so with different idmaps on the same lower fs, index and + * nfs_export may decode a file handle through a layer with the + * wrong idmap. Following copy up origin without index is only + * used for st_ino, which does not depend on the idmap. + */ + if (i < nr_merged_lower && + (ofs->config.index || ofs->config.nfs_export)) { + const struct mnt_idmap *idmap = mnt_idmap(mnt); + struct ovl_sb *fs = &ofs->fs[fsid]; + + if (!fs->lower_idmap) { + fs->lower_idmap = idmap; + } else if (fs->lower_idmap != idmap) { + pr_err("lower layer '%pd2' conflicts with idmap of another lower layer on the same fs, mount with '-o index=off,nfs_export=off' to override.\n", + l->path.dentry); + iput(trap); + kern_unmount(mnt); + return -EINVAL; + } + } + + /* * Make lower layers R/O. That way fchmod/fchown on lower file * will fail instead of modifying lower fs. */ @@ -1485,7 +1510,8 @@ static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb) } } - err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work); + err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work, + ctx->nr_data); if (err) return err; @@ -1619,13 +1645,23 @@ out_err: return err; } +static void ovl_kill_sb(struct super_block *sb) +{ + struct ovl_fs *ofs = OVL_FS(sb); + + /* Nothing can modify them now, do not hold them across the sync */ + if (ofs) + ovl_inuse_unlock_all(ofs); + kill_anon_super(sb); +} + struct file_system_type ovl_fs_type = { .owner = THIS_MODULE, .name = "overlay", .init_fs_context = ovl_init_fs_context, .parameters = ovl_parameter_spec, .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP, - .kill_sb = kill_anon_super, + .kill_sb = ovl_kill_sb, }; MODULE_ALIAS_FS("overlay"); |
