From patchwork Wed May 10 16:20:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 9720189 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id DCB136035D for ; Wed, 10 May 2017 16:21:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CD57027FBC for ; Wed, 10 May 2017 16:21:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C20EB28607; Wed, 10 May 2017 16:21:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3E9CB27FBC for ; Wed, 10 May 2017 16:21:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932151AbdEJQU3 (ORCPT ); Wed, 10 May 2017 12:20:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44018 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932148AbdEJQU0 (ORCPT ); Wed, 10 May 2017 12:20:26 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 05FFC804F1; Wed, 10 May 2017 16:20:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 05FFC804F1 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=dhowells@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 05FFC804F1 Received: from warthog.procyon.org.uk (ovpn-120-67.rdu2.redhat.com [10.10.120.67]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6295A78489; Wed, 10 May 2017 16:20:17 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 13/14] Support legacy filesystems From: David Howells To: mszeredi@redhat.com, viro@zeniv.linux.org.uk, jlayton@redhat.com Cc: dhowells@redhat.com, linux-fsdevel@vger.kernel.org, linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org Date: Wed, 10 May 2017 17:20:16 +0100 Message-ID: <149443321667.2378.3697013521563316132.stgit@warthog.procyon.org.uk> In-Reply-To: <149443309780.2378.6532276992468576087.stgit@warthog.procyon.org.uk> References: <149443309780.2378.6532276992468576087.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Wed, 10 May 2017 16:20:26 +0000 (UTC) Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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(-) -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 #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, +};