lkml.org 
[lkml]   [2017]   [May]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 13/14] Support legacy filesystems
    From
    Date
    Support legacy filesystems by creating a set of legacy sb_config operations
    that builds up a list of mount options and then invokes fs_type->mount()
    within the sb_config mount operation.

    All filesystems can then be accessed using sb_config and the
    fs_type->mount() call is _only_ used from within legacy_mount(). This
    allows some simplification to take place in the core mount code.
    ---

    fs/namespace.c | 37 ++-----------
    fs/sb_config.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
    2 files changed, 161 insertions(+), 36 deletions(-)

    diff --git a/fs/namespace.c b/fs/namespace.c
    index 49d630a4fbd4..6d809f5705cd 100644
    --- a/fs/namespace.c
    +++ b/fs/namespace.c
    @@ -2583,7 +2583,6 @@ static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
    int mnt_flags, const char *name, void *data)
    {
    struct sb_config *sc;
    - struct vfsmount *mnt;
    int err;

    if (!fstype)
    @@ -2599,41 +2598,17 @@ static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
    if (!sc->device)
    goto err_sc;

    - if (sc->ops) {
    - err = parse_monolithic_mount_data(sc, data);
    - if (err < 0)
    - goto err_sc;
    -
    - err = do_new_mount_sc(sc, mountpoint, mnt_flags);
    - if (err)
    - goto err_sc;
    -
    - } else {
    - mnt = vfs_kern_mount(sc->fs_type, flags, name, data);
    - if (!IS_ERR(mnt) && (sc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
    - !mnt->mnt_sb->s_subtype)
    - mnt = fs_set_subtype(mnt, fstype);
    -
    - if (IS_ERR(mnt)) {
    - err = PTR_ERR(mnt);
    - goto err_sc;
    - }
    -
    - err = -EPERM;
    - if (mount_too_revealing(mnt, &mnt_flags))
    - goto err_mnt;
    + err = parse_monolithic_mount_data(sc, data);
    + if (err < 0)
    + goto err_sc;

    - err = do_add_mount(real_mount(mnt), mountpoint, mnt_flags,
    - sc->mnt_ns);
    - if (err)
    - goto err_mnt;
    - }
    + err = do_new_mount_sc(sc, mountpoint, mnt_flags);
    + if (err)
    + goto err_sc;

    put_sb_config(sc);
    return 0;

    -err_mnt:
    - mntput(mnt);
    err_sc:
    if (sc->error_msg)
    pr_info("Mount failed: %s\n", sc->error_msg);
    diff --git a/fs/sb_config.c b/fs/sb_config.c
    index 2c6789d1d71b..4429ac35161c 100644
    --- a/fs/sb_config.c
    +++ b/fs/sb_config.c
    @@ -25,6 +25,15 @@
    #include <net/net_namespace.h>
    #include "mount.h"

    +struct legacy_sb_config {
    + struct sb_config sc;
    + char *legacy_data; /* Data page for legacy filesystems */
    + char *secdata;
    + unsigned int data_usage;
    +};
    +
    +static const struct sb_config_operations legacy_sb_config_ops;
    +
    static const match_table_t common_set_mount_options = {
    { MS_DIRSYNC, "dirsync" },
    { MS_I_VERSION, "iversion" },
    @@ -186,13 +195,15 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
    enum sb_config_purpose purpose)
    {
    struct sb_config *sc;
    + size_t sc_size = fs_type->sb_config_size;
    int ret;

    - BUG_ON(fs_type->init_sb_config &&
    - fs_type->sb_config_size < sizeof(*sc));
    + BUG_ON(fs_type->init_sb_config && sc_size < sizeof(*sc));
    +
    + if (!fs_type->init_sb_config)
    + sc_size = sizeof(struct legacy_sb_config);

    - sc = kzalloc(max_t(size_t, fs_type->sb_config_size, sizeof(*sc)),
    - GFP_KERNEL);
    + sc = kzalloc(sc_size, GFP_KERNEL);
    if (!sc)
    return ERR_PTR(-ENOMEM);

    @@ -211,9 +222,11 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
    ret = sc->fs_type->init_sb_config(sc, src_sb);
    if (ret < 0)
    goto err_sc;
    + } else {
    + sc->ops = &legacy_sb_config_ops;
    }

    - /* Do the security check last because ->fsopen may change the
    + /* Do the security check last because ->init_sb_config may change the
    * namespace subscriptions.
    */
    ret = security_sb_config_alloc(sc, src_sb);
    @@ -275,11 +288,16 @@ struct sb_config *vfs_sb_reconfig(struct vfsmount *mnt,
    struct sb_config *vfs_dup_sb_config(struct sb_config *src_sc)
    {
    struct sb_config *sc;
    + size_t sc_size;
    int ret;

    if (!src_sc->ops->dup)
    return ERR_PTR(-ENOTSUPP);

    + sc_size = src_sc->fs_type->sb_config_size;
    + if (!src_sc->fs_type->init_sb_config)
    + sc_size = sizeof(struct legacy_sb_config);
    +
    sc = kmemdup(src_sc, src_sc->fs_type->sb_config_size, GFP_KERNEL);
    if (!sc)
    return ERR_PTR(-ENOMEM);
    @@ -333,3 +351,135 @@ void put_sb_config(struct sb_config *sc)
    kfree(sc);
    }
    EXPORT_SYMBOL(put_sb_config);
    +
    +/*
    + * Free the config for a filesystem that doesn't support sb_config.
    + */
    +static void legacy_sb_config_free(struct sb_config *sc)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    +
    + free_secdata(cfg->secdata);
    + kfree(cfg->legacy_data);
    +}
    +
    +/*
    + * Duplicate a legacy config.
    + */
    +static int legacy_sb_config_dup(struct sb_config *sc, struct sb_config *src_sc)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    + struct legacy_sb_config *src_cfg = container_of(src_sc, struct legacy_sb_config, sc);
    +
    + cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
    + if (!cfg->legacy_data)
    + return -ENOMEM;
    + memcpy(cfg->legacy_data, src_cfg->legacy_data, sizeof(PAGE_SIZE));
    + return 0;
    +}
    +
    +/*
    + * Add an option to a legacy config. We build up a comma-separated list of
    + * options.
    + */
    +static int legacy_parse_option(struct sb_config *sc, char *p)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    + unsigned int usage = cfg->data_usage;
    + size_t len = strlen(p);
    +
    + if (len > PAGE_SIZE - 2 - usage)
    + return sb_cfg_inval(sc, "VFS: Insufficient data buffer space");
    + if (memchr(p, ',', len) != NULL)
    + return sb_cfg_inval(sc, "VFS: Options cannot contain commas");
    + if (!cfg->legacy_data) {
    + cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
    + if (!cfg->legacy_data)
    + return -ENOMEM;
    + }
    +
    + cfg->legacy_data[usage++] = ',';
    + memcpy(cfg->legacy_data + usage, p, len);
    + usage += len;
    + cfg->legacy_data[usage] = '\0';
    + cfg->data_usage = usage;
    + return 0;
    +}
    +
    +/*
    + * Add monolithic mount data.
    + */
    +static int legacy_monolithic_mount_data(struct sb_config *sc, void *data)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    +
    + if (cfg->data_usage != 0)
    + return sb_cfg_inval(sc, "VFS: Can't mix monolithic and individual options");
    + if (!data)
    + return 0;
    + if (!cfg->legacy_data) {
    + cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
    + if (!cfg->legacy_data)
    + return -ENOMEM;
    + }
    +
    + memcpy(cfg->legacy_data, data, PAGE_SIZE);
    + cfg->data_usage = PAGE_SIZE;
    + return 0;
    +}
    +
    +/*
    + * Use the legacy mount validation step to strip out and process security
    + * config options.
    + */
    +static int legacy_validate(struct sb_config *sc)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    +
    + if (!cfg->legacy_data || cfg->sc.fs_type->fs_flags & FS_BINARY_MOUNTDATA)
    + return 0;
    +
    + cfg->secdata = alloc_secdata();
    + if (!cfg->secdata)
    + return -ENOMEM;
    +
    + return security_sb_copy_data(cfg->legacy_data, cfg->secdata);
    +}
    +
    +/*
    + * Perform a legacy mount.
    + */
    +static struct dentry *legacy_mount(struct sb_config *sc)
    +{
    + struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
    + struct super_block *sb;
    + struct dentry *root;
    + int ret;
    +
    + root = cfg->sc.fs_type->mount(cfg->sc.fs_type, cfg->sc.ms_flags,
    + cfg->sc.device, cfg->legacy_data);
    + if (IS_ERR(root))
    + return ERR_CAST(root);
    +
    + sb = root->d_sb;
    + BUG_ON(!sb);
    + ret = security_sb_kern_mount(sb, cfg->sc.ms_flags, cfg->secdata);
    + if (ret < 0)
    + goto err_sb;
    +
    + return root;
    +
    +err_sb:
    + dput(root);
    + deactivate_locked_super(sb);
    + return ERR_PTR(ret);
    +}
    +
    +static const struct sb_config_operations legacy_sb_config_ops = {
    + .free = legacy_sb_config_free,
    + .dup = legacy_sb_config_dup,
    + .parse_option = legacy_parse_option,
    + .monolithic_mount_data = legacy_monolithic_mount_data,
    + .validate = legacy_validate,
    + .mount = legacy_mount,
    +};
    \
     
     \ /
      Last update: 2017-05-10 21:22    [W:4.194 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site