From patchwork Sat Feb 15 15:36:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383881 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DAB001580 for ; Sat, 15 Feb 2020 15:36:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B2A7B2187F for ; Sat, 15 Feb 2020 15:36:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="fhFcQ6kZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726254AbgBOPg4 (ORCPT ); Sat, 15 Feb 2020 10:36:56 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:52996 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbgBOPg4 (ORCPT ); Sat, 15 Feb 2020 10:36:56 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 965FE8EE302; Sat, 15 Feb 2020 07:36:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781015; bh=QERCmtKjnFN0QPNnxzT6PGdGlU79vFWKJXDvMy/XddQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fhFcQ6kZ7Byzcy0EqiPrfaud839k7K0zMNXxRN+ZBTZ1opmOhvioWl2uPIMbfHuyv 5IXhQhU5ajzUlI2IHKeeA1IY+1x53trv+Gcnvv/1OyIxY7fGYkXNPOL/0eG/v6EzAs iz+PAZ01PX976M8NB2goScWS5WRtx9feJKkdtCcA= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id lJbXDZ6oKHIZ; Sat, 15 Feb 2020 07:36:55 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id C0DA98EE121; Sat, 15 Feb 2020 07:36:54 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 1/6] logger: add a limited buffer logging facility Date: Sat, 15 Feb 2020 10:36:04 -0500 Message-Id: <20200215153609.23797-2-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Can be used by anything that wants to add to a list of messages and then spit them back at a particular time. The current use case for this is the filesystem configuration descriptor, which can accumulate messages about config options which can then be read from the file descriptor later via a .read operation. This code was based on fc_log originally written by David Howells in commit e7582e16a170 "vfs: Implement logging through fs_context" and subsequently extended to include a prefix logger by Al Viro in commit cc3c0b533ab9 "add prefix to fs_context->log". Signed-off-by: James Bottomley --- v3: add prefix logger --- include/linux/logger.h | 45 ++++++++++++ lib/Makefile | 3 +- lib/logger.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 include/linux/logger.h create mode 100644 lib/logger.c diff --git a/include/linux/logger.h b/include/linux/logger.h new file mode 100644 index 000000000000..27c2650a72da --- /dev/null +++ b/include/linux/logger.h @@ -0,0 +1,45 @@ +#ifndef _LINUX_LOGGER_H +#define _LINUX_LOGGER_H + +#include + +#include +#include +#include +#include + +struct logger { + const char *prefix; + refcount_t usage; + struct mutex mutex; + u8 head; /* Insertion index in buffer[] */ + u8 tail; /* Removal index in buffer[] */ + u8 need_free; /* Mask of kfree'able items in buffer[] */ + struct module *owner; /* Owner module for strings that don't then need freeing */ + const char *buffer[8]; +}; + +struct plogger { + const char *prefix; + struct logger *log; +}; + +extern __printf(4, 5) +void logger_log(struct logger *, const char *prefix, char level, + const char *fmt, ...); +extern __printf(4, 0) +void logger_valog(struct logger *, const char *prefix, char level, + const char *fmt, va_list va); +ssize_t logger_read(struct logger *log, char __user *_buf, size_t len); +struct logger *logger_alloc(struct module *owner); +void logger_put(struct logger **logp); +void logger_get(struct logger *log); + +#define logger_err(log, fmt, ...) ({ logger_log(log, NULL, 'e', fmt, ## __VA_ARGS__); }) +#define logger_warn(log, fmt, ...) ({ logger_log(log, NULL, 'w', fmt, ## __VA_ARGS__); }) +#define logger_info(log, fmt, ...) ({ logger_log(log, NULL, 'i', fmt, ## __VA_ARGS__); }) +#define plogger_err(plog, fmt, ...) ({ logger_log((plog)->log, (plog)->prefix, 'e', fmt, ## __VA_ARGS__); }) +#define plogger_warn(log, fmt, ...) ({ logger_log((plog)->log, (plog)->prefix, 'w', fmt, ## __VA_ARGS__); }) +#define plogger_info(log, fmt, ...) ({ logger_log((plog)->log, (plog)->prefix, 'i', fmt, ## __VA_ARGS__); }) + +#endif diff --git a/lib/Makefile b/lib/Makefile index 5d64890d6b6a..3c9b9db35e1a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -31,7 +31,8 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ flex_proportions.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ earlycpio.o seq_buf.o siphash.o dec_and_lock.o \ - nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o + nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o \ + logger.o lib-$(CONFIG_PRINTK) += dump_stack.o lib-$(CONFIG_MMU) += ioremap.o diff --git a/lib/logger.c b/lib/logger.c new file mode 100644 index 000000000000..4442cfd2cdaf --- /dev/null +++ b/lib/logger.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Provide a logging function + * + * code is originally based on fc_log by David Howells and rewritten + * by James Bottomley. + * + * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2019 James.Bottomley@HansenPartnership.com + */ + +#include +#include +#include + +#include + +/** + * logger_valog - Log a message to a logger context + * @log: The logger context + * @fmt: The format of the log entry. + * @va: the variable arguments pointer + * + * Currently @fmt is expected to have a letter prefix of w or e + * followed by a space to signal warning or error. If @log is NULL + * then the message will be logged to the kernel via printk. + * + */ +void logger_valog(struct logger *log, const char *prefix, char level, + const char *fmt, va_list va) +{ + struct va_format vaf = {.fmt = fmt, .va = (va_list *)&va}; + + if (!log) { + switch (level) { + case 'w': + printk(KERN_WARNING "%s%s%pV\n", prefix ? prefix : "", + prefix ? ": " : "", &vaf); + break; + case 'e': + printk(KERN_ERR "%s%s%pV\n", prefix ? prefix : "", + prefix ? ": " : "", &vaf); + break; + default: + printk(KERN_NOTICE "%s%s%pV\n", prefix ? prefix : "", + prefix ? ": " : "", &vaf); + break; + } + } else { + unsigned int logsize = ARRAY_SIZE(log->buffer); + u8 index; + char *q = kasprintf(GFP_KERNEL, "%c %s%s%pV\n", level, + prefix ? prefix : "", + prefix ? ": " : "", &vaf); + + index = log->head & (logsize - 1); + BUILD_BUG_ON(sizeof(log->head) != sizeof(u8) || + sizeof(log->tail) != sizeof(u8)); + if ((u8)(log->head - log->tail) == logsize) { + /* The buffer is full, discard the oldest message */ + if (log->need_free & (1 << index)) + kfree(log->buffer[index]); + log->tail++; + } + + log->buffer[index] = q ? q : "OOM: Can't store error string"; + if (q) + log->need_free |= 1 << index; + else + log->need_free &= ~(1 << index); + log->head++; + } +} +EXPORT_SYMBOL(logger_valog); + +/** + * logger_log - Log a message to a logger context + * @log: The logger context + * @fmt: The format of the log entry. + */ +void logger_log(struct logger *log, const char *prefix, char level, + const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + logger_valog(log, prefix, level, fmt, va); + va_end(va); +} +EXPORT_SYMBOL(logger_log); + +/** + * logger_read - read back a log at an arbitrary position + * @logger: the logger context + * @buf: the buffer to read to (may be user space pointer) + * @len: the length of data to read + * + * Allow the user to read back any error, warning or informational + * messages. This is designed to be wrapped for a file descriptor read. + */ +ssize_t logger_read(struct logger *log, char __user *_buf, size_t len) +{ + unsigned int logsize = ARRAY_SIZE(log->buffer); + ssize_t ret; + const char *p; + bool need_free; + int index, n; + + ret = mutex_lock_interruptible(&log->mutex); + if (ret < 0) + return ret; + + if (log->head == log->tail) { + mutex_unlock(&log->mutex); + return -ENODATA; + } + + index = log->tail & (logsize - 1); + p = log->buffer[index]; + need_free = log->need_free & (1 << index); + log->buffer[index] = NULL; + log->need_free &= ~(1 << index); + log->tail++; + mutex_unlock(&log->mutex); + + ret = -EMSGSIZE; + n = strlen(p); + if (n > len) + goto err_free; + ret = -EFAULT; + if (copy_to_user(_buf, p, n) != 0) + goto err_free; + ret = n; + +err_free: + if (need_free) + kfree(p); + return ret; +} +EXPORT_SYMBOL(logger_read); + +struct logger *logger_alloc(struct module *owner) +{ + struct logger *log = kzalloc(sizeof(*log), GFP_KERNEL); + + if (!log) + return ERR_PTR(-ENOMEM); + + refcount_set(&log->usage, 1); + mutex_init(&log->mutex); + log->owner = owner; + + return log; +} +EXPORT_SYMBOL(logger_alloc); + +/** + * logger_put - decrement refcount and free log if zero + * @logp: pointer to logger context to be freed + * + * if the logger is actually freed, then the @logp will be NULLed. + */ +void logger_put(struct logger **logp) +{ + int i; + struct logger *log; + + if (!logp || !*logp) + return; + + log = *logp; + if (refcount_dec_and_test(&log->usage)) { + *logp = NULL; + for (i = 0; i <= 7; i++) + if (log->need_free & (1 << i)) + kfree(log->buffer[i]); + kfree(log); + } +} +EXPORT_SYMBOL(logger_put); + +/** + * logger_get - get a reference to a logger context + * @log: the logger context + */ +void logger_get(struct logger *log) +{ + if (!log) + return; + refcount_inc(&log->usage); +} From patchwork Sat Feb 15 15:36:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383883 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A1B391580 for ; Sat, 15 Feb 2020 15:37:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 71C4A2187F for ; Sat, 15 Feb 2020 15:37:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="KPs3QwUK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726346AbgBOPhW (ORCPT ); Sat, 15 Feb 2020 10:37:22 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:53036 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbgBOPhW (ORCPT ); Sat, 15 Feb 2020 10:37:22 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 00A8C8EE302; Sat, 15 Feb 2020 07:37:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781042; bh=79DNQJtOLsrjgopN00pLq0OrcdjMW6qBZk2QT7VtPZE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KPs3QwUKaPn1qc9mNJE+QOHktDoaSjwhcnh71y5HrJHvRJ51MAdm2LFOWX11EIzBa MlOX1w2tM+Lm5tyTJfQc8GqclHYHoIXTeU1doO6k/holNsXuv1yKk2bgXlxpwbIVTr mJO91K0NX9tvRQaOCNCpCufTlzNRGOyEwrWpWofg= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ijlHIV3axbNQ; Sat, 15 Feb 2020 07:37:21 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 7101E8EE121; Sat, 15 Feb 2020 07:37:20 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 2/6] configfd: add generic file descriptor based configuration parser Date: Sat, 15 Feb 2020 10:36:05 -0500 Message-Id: <20200215153609.23797-3-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This code is based on the original filesystem context based configuration parser by David Howells, but lifted up so it can apply to anything rather than only filesystem contexts. Signed-off-by: James Bottomley --- v3: use prefix logger --- fs/Makefile | 3 +- fs/configfd.c | 451 ++++++++++++++++++++++++++++++++++++++++++ include/linux/configfd.h | 61 ++++++ include/uapi/linux/configfd.h | 20 ++ 4 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 fs/configfd.c create mode 100644 include/linux/configfd.h create mode 100644 include/uapi/linux/configfd.h diff --git a/fs/Makefile b/fs/Makefile index 505e51166973..2c078355fdf5 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,7 +13,8 @@ obj-y := open.o read_write.o file_table.o super.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o splice.o sync.o utimes.o d_path.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ - fs_types.o fs_context.o fs_parser.o fsopen.o + fs_types.o fs_context.o fs_parser.o fsopen.o \ + configfd.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/configfd.c b/fs/configfd.c new file mode 100644 index 000000000000..50421ddbef11 --- /dev/null +++ b/fs/configfd.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Generic configuration file descriptor handling + * + * Copyright (C) 2019 James.Bottomley@HansenPartnership.com + */ + +#include +#include +#include +#include +#include +#include +#include + + +static struct configfd_type *configfds; +static DEFINE_RWLOCK(configfds_lock); + +static ssize_t configfd_read(struct file *file, + char __user *buf, size_t len, loff_t *pos) +{ + struct configfd_context *cfc = file->private_data; + + return logger_read(cfc->log.log, buf, len); +} + +static void configfd_type_put(const struct configfd_type *cft) +{ + module_put(cft->owner); +} + +static void configfd_context_free(struct configfd_context *cfc) +{ + if (cfc->cft->ops->free) + cfc->cft->ops->free(cfc); + logger_put(&cfc->log.log); + configfd_type_put(cfc->cft); + kfree(cfc); +} + +static int configfd_release(struct inode *inode, struct file *file) +{ + struct configfd_context *cfc = file->private_data; + + if (cfc) { + file->private_data = NULL; + configfd_context_free(cfc); + } + return 0; +} + +const struct file_operations configfd_context_fops = { + .read = configfd_read, + .release = configfd_release, + .llseek = no_llseek, +}; + +static int configfd_create_fd(struct configfd_context *cfc, + unsigned int flags) +{ + int fd; + + fd = anon_inode_getfd("[configfd]", &configfd_context_fops, cfc, + O_RDWR | flags); + if (fd < 0) + configfd_context_free(cfc); + return fd; +} + +static struct configfd_type **configfd_type_find(const char *name) +{ + struct configfd_type **c; + + for (c = &configfds; *c; c = &(*c)->next) { + if (strcmp((*c)->name, name) == 0) + break; + } + + return c; +} + +static struct configfd_type *configfd_type_get(const char *name) +{ + struct configfd_type *cft; + + read_lock(&configfds_lock); + cft = *(configfd_type_find(name)); + if (cft && !try_module_get(cft->owner)) + cft = NULL; + read_unlock(&configfds_lock); + + return cft; +} + +struct configfd_context *configfd_context_alloc(const struct configfd_type *cft, + unsigned int op) +{ + struct configfd_context *cfc; + struct logger *log; + int ret; + + cfc = kzalloc(sizeof(*cfc) + cft->data_size, GFP_KERNEL); + if (!cfc) + return ERR_PTR(-ENOMEM); + + if (cft->data_size) + cfc->data = &cfc[1]; + + cfc->cft = cft; + cfc->op = op; + log = logger_alloc(cft->owner); + if (IS_ERR(log)) { + ret = PTR_ERR(log); + goto out_free; + } + + cfc->log.log = log; + cfc->log.prefix = NULL; + if (cft->ops->alloc) { + ret = cft->ops->alloc(cfc); + if (ret) + goto out_put; + } + + return cfc; + out_put: + logger_put(&cfc->log.log); + out_free: + kfree(cfc); + return ERR_PTR(ret); +} + +int kern_configfd_open(const char *config_name, unsigned int flags, + unsigned int op) +{ + const struct configfd_type *cft; + struct configfd_context *cfc; + + if (flags & ~O_CLOEXEC) + return -EINVAL; + if (op != CONFIGFD_CMD_CREATE && op != CONFIGFD_CMD_RECONFIGURE) + return -EINVAL; + + cft = configfd_type_get(config_name); + if (!cft) + return -ENODEV; + cfc = configfd_context_alloc(cft, op); + if (IS_ERR(cfc)) { + configfd_type_put(cft); + return PTR_ERR(cfc); + } + + return configfd_create_fd(cfc, flags); +} + +long ksys_configfd_open(const char __user *_config_name, unsigned int flags, + unsigned int op) +{ + const char *config_name = strndup_user(_config_name, PAGE_SIZE); + int ret; + + if (IS_ERR(config_name)) + return PTR_ERR(config_name); + ret = kern_configfd_open(config_name, flags, op); + kfree(config_name); + + return ret; +} + +SYSCALL_DEFINE3(configfd_open, + const char __user *, _config_name, + unsigned int, flags, + unsigned int, op) +{ + return ksys_configfd_open(_config_name, flags, op); +} + +int kern_configfd_action(int fd, struct configfd_param *p) +{ + struct fd f = fdget(fd); + struct configfd_context *cfc; + const struct configfd_ops *ops; + int ret = -EINVAL; + /* upper 24 bits are available to consumers */ + u8 our_cmd = p->cmd & 0xff; + + if (!f.file) + return -EBADF; + if (f.file->f_op != &configfd_context_fops) + goto out_f; + cfc = f.file->private_data; + + ops = cfc->cft->ops; + + /* check allowability */ + ret = -EOPNOTSUPP; + switch (our_cmd) { + case CONFIGFD_SET_FLAG: + case CONFIGFD_SET_STRING: + case CONFIGFD_SET_BINARY: + case CONFIGFD_SET_PATH: + case CONFIGFD_SET_PATH_EMPTY: + case CONFIGFD_SET_INT: + if (!ops->set) + goto out_f; + break; + case CONFIGFD_GET_FD: + if (!ops->get) + goto out_f; + break; + case CONFIGFD_CMD_CREATE: + case CONFIGFD_CMD_RECONFIGURE: + if (our_cmd != cfc->op) { + plogger_err(&cfc->log, "Wrong operation, we were opened for %d", cfc->op); + goto out_f; + } + if (!ops->act) + goto out_f; + break; + default: + break; + } + + /* + * Execute + */ + switch (our_cmd) { + case CONFIGFD_SET_STRING: + case CONFIGFD_SET_BINARY: + case CONFIGFD_SET_PATH: + case CONFIGFD_SET_PATH_EMPTY: + case CONFIGFD_SET_FD: + case CONFIGFD_SET_FLAG: + case CONFIGFD_SET_INT: + ret = ops->set(cfc, p); + break; + case CONFIGFD_GET_FD: + ret = ops->get(cfc, p); + if (ret == 0) { + int fd; + + fd = get_unused_fd_flags(p->aux & O_CLOEXEC); + if (fd < 0) { + ret = fd; + break; + } + fd_install(fd, p->file); + p->file = NULL; /* consume the file */ + p->aux = fd; + } + break; + case CONFIGFD_CMD_RECONFIGURE: + case CONFIGFD_CMD_CREATE: + ret = ops->act(cfc, p->cmd); + break; + default: + break; + } +out_f: + fdput(f); + return ret; +} + +long ksys_configfd_action(int fd, unsigned int cmd, const char __user *_key, + void __user *_value, int aux) +{ + struct configfd_param param = { + .cmd = cmd, + }; + u8 our_cmd = cmd & 0xff; + int ret; + + /* check parameters required for action */ + switch (our_cmd) { + case CONFIGFD_SET_FLAG: + if (!_key || _value || aux) + return -EINVAL; + break; + case CONFIGFD_SET_STRING: + case CONFIGFD_GET_FD: + if (!_key || !_value) + return -EINVAL; + break; + case CONFIGFD_SET_BINARY: + if (!_key || !_value || aux <= 0 || aux > 1024 * 1024) + return -EINVAL; + break; + case CONFIGFD_SET_PATH: + case CONFIGFD_SET_PATH_EMPTY: + if (!_key || !_value || (aux != AT_FDCWD && aux < 0)) + return -EINVAL; + break; + case CONFIGFD_SET_FD: + if (!_key || _value || aux < 0) + return -EINVAL; + break; + case CONFIGFD_SET_INT: + if (!_key) + return -EINVAL; + break; + case CONFIGFD_CMD_CREATE: + case CONFIGFD_CMD_RECONFIGURE: + if (_key || _value || aux) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + if (_key) { + param.key = strndup_user(_key, 256); + if (IS_ERR(param.key)) + return PTR_ERR(param.key); + } + + /* now pull the parameters into the kernel */ + switch (our_cmd) { + case CONFIGFD_SET_STRING: + param.string = strndup_user(_value, 256); + if (IS_ERR(param.string)) { + ret = PTR_ERR(param.string); + goto out_key; + } + param.size = strlen(param.string); + break; + case CONFIGFD_SET_BINARY: + param.size = aux; + param.blob = memdup_user_nul(_value, aux); + if (IS_ERR(param.blob)) { + ret = PTR_ERR(param.blob); + goto out_key; + } + break; + case CONFIGFD_SET_PATH: + param.name = getname_flags(_value, 0, NULL); + if (IS_ERR(param.name)) { + ret = PTR_ERR(param.name); + goto out_key; + } + param.aux = aux; + param.size = strlen(param.name->name); + break; + case CONFIGFD_SET_PATH_EMPTY: + param.name = getname_flags(_value, LOOKUP_EMPTY, NULL); + if (IS_ERR(param.name)) { + ret = PTR_ERR(param.name); + goto out_key; + } + param.aux = aux; + param.size = strlen(param.name->name); + break; + case CONFIGFD_SET_FD: + ret = -EBADF; + param.file = fget_raw(aux); + if (!param.file) + goto out_key; + break; + case CONFIGFD_SET_INT: + param.aux = aux; + break; + default: + break; + } + ret = kern_configfd_action(fd, ¶m); + /* clean up unconsumed parameters */ + switch (our_cmd) { + case CONFIGFD_SET_STRING: + case CONFIGFD_SET_BINARY: + kfree(param.string); + break; + case CONFIGFD_SET_PATH: + case CONFIGFD_SET_PATH_EMPTY: + if (param.name) + putname(param.name); + break; + case CONFIGFD_GET_FD: + if (!ret) + ret = put_user(param.aux, (int __user *)_value); + /* FALL THROUGH */ + case CONFIGFD_SET_FD: + if (param.file) + fput(param.file); + break; + default: + break; + } + out_key: + kfree(param.key); + + return ret; +} + + +SYSCALL_DEFINE5(configfd_action, + int, fd, unsigned int, cmd, + const char __user *, _key, + void __user *, _value, + int, aux) +{ + return ksys_configfd_action(fd, cmd, _key, _value, aux); +} + +int configfd_type_register(struct configfd_type *cft) +{ + int ret = 0; + struct configfd_type **c; + + if (WARN(cft->next, + "BUG: registering already registered configfd_type: %s", + cft->name)) + return -EBUSY; + + if (WARN(cft->ops == NULL, + "BUG: configfd_type has no ops set: %s", cft->name)) + return -EINVAL; + + if (WARN(cft->ops->alloc && (!cft->ops->free), + "BUG: if configfd ops alloc is set, free must also be")) + return -EINVAL; + + if (WARN(cft->name == NULL || cft->name[0] == '\0', + "BUG: configfd_type has no name")) + return -EINVAL; + + write_lock(&configfds_lock); + c = configfd_type_find(cft->name); + if (WARN(*c, "BUG: configfd_type name already exists: %s", + cft->name)) + ret = -EBUSY; + else + *c = cft; + write_unlock(&configfds_lock); + + return ret; +} + +void configfd_type_unregister(struct configfd_type *cft) +{ + struct configfd_type **c; + + write_lock(&configfds_lock); + c = configfd_type_find(cft->name); + if (WARN(*c != cft, "BUG: trying to register %s from wrong structure", + cft->name)) + goto out; + *c = cft->next; + cft->next = NULL; + out: + write_unlock(&configfds_lock); +} diff --git a/include/linux/configfd.h b/include/linux/configfd.h new file mode 100644 index 000000000000..7ef31f3da916 --- /dev/null +++ b/include/linux/configfd.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LINUX_CONFIGFD_H +#define _LINUX_CONFIGFD_H + +#include + +#include + +struct configfd_context; + +struct configfd_param { + const char *key; + union { + char *string; + void *blob; + struct filename *name; + struct file *file; + }; + int aux; + unsigned int cmd; + size_t size; +}; + +struct configfd_ops { + int (*alloc)(struct configfd_context *cfc); + void (*free)(const struct configfd_context *cfc); + int (*set)(const struct configfd_context *cfc, + struct configfd_param *p); + int (*get)(const struct configfd_context *cfc, + struct configfd_param *p); + int (*act)(const struct configfd_context *cfc, unsigned int cmd); +}; + +struct configfd_type { + const char *name; + size_t data_size; + struct module *owner; + struct configfd_ops *ops; + struct configfd_type *next; +}; + +struct configfd_context { + const struct configfd_type *cft; + struct plogger log; + void *data; + unsigned int op; +}; + +int configfd_type_register(struct configfd_type *cft); +void configfd_type_unregister(struct configfd_type *cft); + +long ksys_configfd_open(const char __user *config_name, unsigned int flags, + unsigned int op); +long ksys_configfd_action(int fd, unsigned int cmd, const char __user *key, + void __user *value, int aux); +int kern_configfd_action(int fd, struct configfd_param *p); +int kern_configfd_open(const char *name, unsigned int flags, + unsigned int op); + +#endif diff --git a/include/uapi/linux/configfd.h b/include/uapi/linux/configfd.h new file mode 100644 index 000000000000..3e54cfef0182 --- /dev/null +++ b/include/uapi/linux/configfd.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ + +#ifndef _UAPI_LINUX_CONFIGFD_H +#define _UAPI_LINUX_CONFIGFD_H + +enum configfd_cmd { + CONFIGFD_SET_FLAG = 0, + CONFIGFD_SET_STRING = 1, + CONFIGFD_SET_BINARY = 2, + CONFIGFD_SET_PATH = 3, + CONFIGFD_SET_PATH_EMPTY = 4, + CONFIGFD_SET_FD = 5, + CONFIGFD_CMD_CREATE = 6, + CONFIGFD_CMD_RECONFIGURE = 7, + /* gap for 30 other commands */ + CONFIGFD_SET_INT = 38, + CONFIGFD_GET_FD = 39, +}; + +#endif From patchwork Sat Feb 15 15:36:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383885 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E62B6159A for ; Sat, 15 Feb 2020 15:37:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BE7632086A for ; Sat, 15 Feb 2020 15:37:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="ceaIPTZs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726273AbgBOPhz (ORCPT ); Sat, 15 Feb 2020 10:37:55 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:53064 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbgBOPhz (ORCPT ); Sat, 15 Feb 2020 10:37:55 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id C15A48EE302; Sat, 15 Feb 2020 07:37:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781074; bh=g1xQnKvgTT/Bp3YFWXKHmacREUxY5RF57sqz/uo5Hy4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ceaIPTZst+klnMBYIeE2D4KTbAb9VZBgxvTJl7JDpeV4Aii/gB2jU3WA16NVnPt0u ou6svuHnosTDS8/OQxALydBFDU1QR37LzfYqZ+yy3e9+PwAgT/g/SzgMwT6a/+cwGk V0oHAZwF1yUQlEDVjWFtCu6HoRaaJf5/8dApvuRA= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1vLRYk4ZX36h; Sat, 15 Feb 2020 07:37:54 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 87DB78EE121; Sat, 15 Feb 2020 07:37:53 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 3/6] configfd: syscall: wire up configfd syscalls Date: Sat, 15 Feb 2020 10:36:06 -0500 Message-Id: <20200215153609.23797-4-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Signed-off-by: James Bottomley --- v3: Changed numbering to avoid clash with pidfd calls --- arch/alpha/kernel/syscalls/syscall.tbl | 2 ++ arch/arm/tools/syscall.tbl | 2 ++ arch/arm64/include/asm/unistd.h | 2 +- arch/arm64/include/asm/unistd32.h | 4 ++++ arch/ia64/kernel/syscalls/syscall.tbl | 2 ++ arch/m68k/kernel/syscalls/syscall.tbl | 2 ++ arch/microblaze/kernel/syscalls/syscall.tbl | 2 ++ arch/mips/kernel/syscalls/syscall_n32.tbl | 2 ++ arch/mips/kernel/syscalls/syscall_n64.tbl | 2 ++ arch/mips/kernel/syscalls/syscall_o32.tbl | 2 ++ arch/parisc/kernel/syscalls/syscall.tbl | 2 ++ arch/powerpc/kernel/syscalls/syscall.tbl | 2 ++ arch/s390/kernel/syscalls/syscall.tbl | 2 ++ arch/sh/kernel/syscalls/syscall.tbl | 2 ++ arch/sparc/kernel/syscalls/syscall.tbl | 2 ++ arch/x86/entry/syscalls/syscall_32.tbl | 2 ++ arch/x86/entry/syscalls/syscall_64.tbl | 2 ++ arch/xtensa/kernel/syscalls/syscall.tbl | 2 ++ include/linux/syscalls.h | 5 +++++ include/uapi/asm-generic/unistd.h | 9 +++++++-- 20 files changed, 49 insertions(+), 3 deletions(-) diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl index 36d42da7466a..657c9cd71307 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl @@ -477,3 +477,5 @@ # 545 reserved for clone3 547 common openat2 sys_openat2 548 common pidfd_getfd sys_pidfd_getfd +549 common configfd_open configfd_open +550 common configfd_action configfd_action diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index 4d1cf74a2caa..43cbcfe14e38 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -451,3 +451,5 @@ 435 common clone3 sys_clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 1dd22da1c3a9..bc0f923e0e04 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -38,7 +38,7 @@ #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) -#define __NR_compat_syscalls 439 +#define __NR_compat_syscalls 441 #endif #define __ARCH_WANT_SYS_CLONE diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index c1c61635f89c..46881451186e 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -883,6 +883,10 @@ __SYSCALL(__NR_clone3, sys_clone3) __SYSCALL(__NR_openat2, sys_openat2) #define __NR_pidfd_getfd 438 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) +#define __NR_configfd_open 439 +__SYSCALL(__NR_configfd_open, sys_configfd_open) +#define __NR_configfd_action 440 +__SYSCALL(__NR_configfd_action, sys_configfd_action) /* * Please add new compat syscalls above this comment and update diff --git a/arch/ia64/kernel/syscalls/syscall.tbl b/arch/ia64/kernel/syscalls/syscall.tbl index 042911e670b8..e9c143d99b96 100644 --- a/arch/ia64/kernel/syscalls/syscall.tbl +++ b/arch/ia64/kernel/syscalls/syscall.tbl @@ -358,3 +358,5 @@ # 435 reserved for clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl index f4f49fcb76d0..d9b54e720918 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl @@ -437,3 +437,5 @@ 435 common clone3 __sys_clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl index 4c67b11f9c9e..4431f66cb9a6 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl @@ -443,3 +443,5 @@ 435 common clone3 sys_clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index 1f9e8ad636cc..51523527fe88 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -376,3 +376,5 @@ 435 n32 clone3 __sys_clone3 437 n32 openat2 sys_openat2 438 n32 pidfd_getfd sys_pidfd_getfd +439 n32 configfd_open sys_configfd_open +440 n32 configfd_action sys_configfd_action diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index c0b9d802dbf6..eae82af997c2 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -352,3 +352,5 @@ 435 n64 clone3 __sys_clone3 437 n64 openat2 sys_openat2 438 n64 pidfd_getfd sys_pidfd_getfd +439 n64 configfd_open sys_configfd_open +440 n64 configfd_action sys_configfd_action diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index ac586774c980..d813d89521a3 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -425,3 +425,5 @@ 435 o32 clone3 __sys_clone3 437 o32 openat2 sys_openat2 438 o32 pidfd_getfd sys_pidfd_getfd +439 o32 configfd_open sys_configfd_open +440 o32 configfd_action sys_configfd_action diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index 52a15f5cd130..f24f48b7123a 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -435,3 +435,5 @@ 435 common clone3 sys_clone3_wrapper 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl index 35b61bfc1b1a..4586f9c5a76c 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl @@ -519,3 +519,5 @@ 435 nospu clone3 ppc_clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl index bd7bd3581a0f..a047a75ed6e7 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl @@ -440,3 +440,5 @@ 435 common clone3 sys_clone3 sys_clone3 437 common openat2 sys_openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action sys_configfd_action diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl index c7a30fcd135f..435bf5ce7be0 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl @@ -440,3 +440,5 @@ # 435 reserved for clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl index f13615ecdecc..4f73a7d80c4a 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl @@ -483,3 +483,5 @@ # 435 reserved for clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index c17cb77eb150..fc5101e9e6c4 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -442,3 +442,5 @@ 435 i386 clone3 sys_clone3 __ia32_sys_clone3 437 i386 openat2 sys_openat2 __ia32_sys_openat2 438 i386 pidfd_getfd sys_pidfd_getfd __ia32_sys_pidfd_getfd +436 i386 configfd_open sys_configfd_open __ia32_sys_configfd_open +437 i386 configfd_action sys_configfd_action __ia32_sys_configfd_action diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 44d510bc9b78..3dc52c1329dc 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -359,6 +359,8 @@ 435 common clone3 __x64_sys_clone3/ptregs 437 common openat2 __x64_sys_openat2 438 common pidfd_getfd __x64_sys_pidfd_getfd +439 common configfd_open __x64_sys_configfd_open +440 common configfd_action __x64_sys_configfd_action # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl index 85a9ab1bc04d..b023e36c4964 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl @@ -408,3 +408,5 @@ 435 common clone3 sys_clone3 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common configfd_open sys_configfd_open +440 common configfd_action sys_configfd_action diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1815065d52f3..298d56120470 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1003,6 +1003,11 @@ asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, siginfo_t __user *info, unsigned int flags); asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags); +asmlinkage long sys_configfd_open(const char __user *config_name, + unsigned int flags, unsigned int op); +asmlinkage long sys_configfd_action(int fd, unsigned int cmd, + const char __user *key, void __user *value, + int aux); /* * Architecture-specific system calls diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 3a3201e4618e..29c55ef11fb1 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -849,15 +849,20 @@ __SYSCALL(__NR_pidfd_open, sys_pidfd_open) #ifdef __ARCH_WANT_SYS_CLONE3 #define __NR_clone3 435 __SYSCALL(__NR_clone3, sys_clone3) -#endif +#endif #define __NR_openat2 437 __SYSCALL(__NR_openat2, sys_openat2) #define __NR_pidfd_getfd 438 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) +#define __NR_configfd_open 439 +__SYSCALL(__NR_configfd_open, sys_configfd_open) +#define __NR_configfd_action 440 +__SYSCALL(__NR_configfd_action, sys_configfd_action) + #undef __NR_syscalls -#define __NR_syscalls 439 +#define __NR_syscalls 441 /* * 32 bit systems traditionally used different From patchwork Sat Feb 15 15:36:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383887 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0F5C41820 for ; Sat, 15 Feb 2020 15:38:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BC7CA217F4 for ; Sat, 15 Feb 2020 15:38:21 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="CXQ6r7ph" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726346AbgBOPiV (ORCPT ); Sat, 15 Feb 2020 10:38:21 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:53090 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726291AbgBOPiV (ORCPT ); Sat, 15 Feb 2020 10:38:21 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id B79378EE302; Sat, 15 Feb 2020 07:38:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781100; bh=YkoAJehBTGOwfLZ8o9ktyLnPWwrYcxdOVRvOAgl20XA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CXQ6r7phfKk20I3RK3rUYFUNDpnXit1tT7H79m4OYWP+pp5mVpGTv755BCMJBPj0x k09ktrdB9VNLgwp+eatVDDziIxJgZUzD5o6240uvPzX7e+3yzt8komvlw7CQiCLhwZ JH6t7CzB9pgG0r5kI/SrGzxdQm9oDm/VKqPivlxE= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id oc81nOfb5SHa; Sat, 15 Feb 2020 07:38:20 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id B79EF8EE121; Sat, 15 Feb 2020 07:38:19 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 4/6] fs: implement fsconfig via configfd Date: Sat, 15 Feb 2020 10:36:07 -0500 Message-Id: <20200215153609.23797-5-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This changes the internal implementation of fsconfig, while keeping all the external system calls. However, it now becomes possible to all the same operations via configfd instead of fsconfig. For example a filesystem remount read-only can be done: fd = configfd_open("mount", O_CLOEXEC, CONFIGFD_CMD_RECONFIGURE); mntpnt = open("/path/to/mount", O_PATH); configfd_action(fd, CONFIGFD_SET_FD, "pathfd", NULL, mntpnt); configfd_action(fd, CONFIGFD_SET_FLAG, "ro", NULL, 0); configfd_action(fd, CONFIGFD_CMD_RECONFIGURE, NULL, NULL, 0); And mounting a tmpfs filesystem nodev,noexec: fd = configfd_open("tmpfs", O_CLOEXEC, CONFIGFD_CMD_CREATE); configfd_action(fd, CONFIGFD_SET_INT, "mount_attrs", NULL, MOUNT_ATTR_NODEV|MOUNT_ATTR_NOEXEC); configfd_action(fd, CONFIGFD_CMD_CREATE, NULL, NULL, 0); configfd_action(fd, CONFIGFD_GET_FD, "mountfd", &mfd, O_CLOEXEC); mount_move("", mfd, AT_FDCWD, "/mountpoint", MOVE_MOUNT_F_EMPTY_PATH); --- v2: fix a log oops v3: sweep up ceph/rdb p_log Signed-off-by: James Bottomley --- drivers/block/rbd.c | 2 +- fs/ceph/super.c | 4 +- fs/filesystems.c | 8 +- fs/fs_context.c | 97 ++------ fs/fs_parser.c | 24 +- fs/fsopen.c | 529 +++++++++++++++++++++---------------------- fs/internal.h | 4 + fs/namespace.c | 111 ++++----- include/linux/ceph/libceph.h | 5 +- include/linux/fs.h | 2 + include/linux/fs_context.h | 58 +++-- include/linux/fs_parser.h | 9 +- net/ceph/ceph_common.c | 26 +-- 13 files changed, 400 insertions(+), 479 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6343402c09e6..629fc9f1f6cc 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -6348,7 +6348,7 @@ static int rbd_parse_param(struct fs_parameter *param, { struct rbd_options *opt = pctx->opts; struct fs_parse_result result; - struct p_log log = {.prefix = "rbd"}; + struct plogger log = {.prefix = "rbd"}; int token, ret; ret = ceph_parse_param(param, pctx->copts, NULL); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 1d9f083b8a11..052750ae897f 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -246,7 +246,7 @@ static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc) dout("server path '%s'\n", fsopt->server_path); ret = ceph_parse_mon_ips(param->string, dev_name_end - dev_name, - pctx->copts, fc->log.log); + pctx->copts, &fc->cfc->log); if (ret) return ret; @@ -264,7 +264,7 @@ static int ceph_parse_mount_param(struct fs_context *fc, unsigned int mode; int token, ret; - ret = ceph_parse_param(param, pctx->copts, fc->log.log); + ret = ceph_parse_param(param, pctx->copts, &fc->cfc->log); if (ret != -ENOPARAM) return ret; diff --git a/fs/filesystems.c b/fs/filesystems.c index 77bf5f95362d..d68e8a415c01 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -7,6 +7,7 @@ * table of configured filesystems */ +#include #include #include #include @@ -83,10 +84,12 @@ int register_filesystem(struct file_system_type * fs) return -EBUSY; write_lock(&file_systems_lock); p = find_filesystem(fs->name, strlen(fs->name)); - if (*p) + if (*p) { res = -EBUSY; - else + } else { *p = fs; + res = fs_context_register(fs); + } write_unlock(&file_systems_lock); return res; } @@ -115,6 +118,7 @@ int unregister_filesystem(struct file_system_type * fs) if (fs == *tmp) { *tmp = fs->next; fs->next = NULL; + fs_context_unregister(fs); write_unlock(&file_systems_lock); synchronize_rcu(); return 0; diff --git a/fs/fs_context.c b/fs/fs_context.c index fc9f6ef93b55..518e9a010616 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -238,6 +238,13 @@ int generic_parse_monolithic(struct fs_context *fc, void *data) } EXPORT_SYMBOL(generic_parse_monolithic); +void fs_context_set_reconfigure(struct fs_context *fc, struct dentry *reference) +{ + atomic_inc(&reference->d_sb->s_active); + fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); + fc->root = dget(reference); +} + /** * alloc_fs_context - Create a filesystem context. * @fs_type: The filesystem type. @@ -271,7 +278,7 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, fc->fs_type = get_filesystem(fs_type); fc->cred = get_current_cred(); fc->net_ns = get_net(current->nsproxy->net_ns); - fc->log.prefix = fs_type->name; + fc->cfc->log.prefix = fs_type->name; mutex_init(&fc->uapi_mutex); @@ -283,9 +290,8 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); break; case FS_CONTEXT_FOR_RECONFIGURE: - atomic_inc(&reference->d_sb->s_active); - fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); - fc->root = dget(reference); + if (reference) + fs_context_set_reconfigure(fc, reference); break; } @@ -317,7 +323,11 @@ struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, unsigned int sb_flags, unsigned int sb_flags_mask) { - return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags, + struct file_system_type *fs_type = NULL; + + if (dentry) + fs_type = dentry->d_sb->s_type; + return alloc_fs_context(fs_type, dentry, sb_flags, sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE); } EXPORT_SYMBOL(fs_context_for_reconfigure); @@ -365,8 +375,7 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc) get_net(fc->net_ns); get_user_ns(fc->user_ns); get_cred(fc->cred); - if (fc->log.log) - refcount_inc(&fc->log.log->usage); + logger_get(fc->cfc->log.log); /* Can't call put until we've called ->dup */ ret = fc->ops->dup(fc, src_fc); @@ -384,79 +393,6 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc) } EXPORT_SYMBOL(vfs_dup_fs_context); -/** - * logfc - Log a message to a filesystem context - * @fc: The filesystem context to log to. - * @fmt: The format of the buffer. - */ -void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, ...) -{ - va_list va; - struct va_format vaf = {.fmt = fmt, .va = &va}; - - va_start(va, fmt); - if (!log) { - switch (level) { - case 'w': - printk(KERN_WARNING "%s%s%pV\n", prefix ? prefix : "", - prefix ? ": " : "", &vaf); - break; - case 'e': - printk(KERN_ERR "%s%s%pV\n", prefix ? prefix : "", - prefix ? ": " : "", &vaf); - break; - default: - printk(KERN_NOTICE "%s%s%pV\n", prefix ? prefix : "", - prefix ? ": " : "", &vaf); - break; - } - } else { - unsigned int logsize = ARRAY_SIZE(log->buffer); - u8 index; - char *q = kasprintf(GFP_KERNEL, "%c %s%s%pV\n", level, - prefix ? prefix : "", - prefix ? ": " : "", &vaf); - - index = log->head & (logsize - 1); - BUILD_BUG_ON(sizeof(log->head) != sizeof(u8) || - sizeof(log->tail) != sizeof(u8)); - if ((u8)(log->head - log->tail) == logsize) { - /* The buffer is full, discard the oldest message */ - if (log->need_free & (1 << index)) - kfree(log->buffer[index]); - log->tail++; - } - - log->buffer[index] = q ? q : "OOM: Can't store error string"; - if (q) - log->need_free |= 1 << index; - else - log->need_free &= ~(1 << index); - log->head++; - } - va_end(va); -} -EXPORT_SYMBOL(logfc); - -/* - * Free a logging structure. - */ -static void put_fc_log(struct fs_context *fc) -{ - struct fc_log *log = fc->log.log; - int i; - - if (log) { - if (refcount_dec_and_test(&log->usage)) { - fc->log.log = NULL; - for (i = 0; i <= 7; i++) - if (log->need_free & (1 << i)) - kfree(log->buffer[i]); - kfree(log); - } - } -} - /** * put_fs_context - Dispose of a superblock configuration context. * @fc: The context to dispose of. @@ -479,7 +415,6 @@ void put_fs_context(struct fs_context *fc) put_net(fc->net_ns); put_user_ns(fc->user_ns); put_cred(fc->cred); - put_fc_log(fc); put_filesystem(fc->fs_type); kfree(fc->source); kfree(fc); diff --git a/fs/fs_parser.c b/fs/fs_parser.c index 7e6fb43f9541..609f7e1ea506 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -100,7 +100,7 @@ static const struct fs_parameter_spec *fs_lookup_key( * unknown parameters are okay and -EINVAL if there was a conversion issue or * the parameter wasn't recognised and unknowns aren't okay. */ -int __fs_parse(struct p_log *log, +int __fs_parse(struct plogger *log, const struct fs_parameter_spec *desc, struct fs_parameter *param, struct fs_parse_result *result) @@ -189,12 +189,12 @@ int fs_lookup_param(struct fs_context *fc, } EXPORT_SYMBOL(fs_lookup_param); -int fs_param_bad_value(struct p_log *log, struct fs_parameter *param) +int fs_param_bad_value(struct plogger *log, struct fs_parameter *param) { return inval_plog(log, "Bad value for '%s'", param->key); } -int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_bool(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { int b; @@ -208,7 +208,7 @@ int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_bool); -int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_u32(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { int base = (unsigned long)p->data; @@ -219,7 +219,7 @@ int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_u32); -int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_s32(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { if (param->type != fs_value_is_string || @@ -229,7 +229,7 @@ int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_s32); -int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_u64(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { if (param->type != fs_value_is_string || @@ -239,7 +239,7 @@ int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_u64); -int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_enum(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { const struct constant_table *c; @@ -253,7 +253,7 @@ int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_enum); -int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_string(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { if (param->type != fs_value_is_string || !*param->string) @@ -262,7 +262,7 @@ int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_string); -int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_blob(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { if (param->type != fs_value_is_blob) @@ -271,7 +271,7 @@ int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_blob); -int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_fd(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { switch (param->type) { @@ -293,14 +293,14 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_fd); -int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_blockdev(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { return 0; } EXPORT_SYMBOL(fs_param_is_blockdev); -int fs_param_is_path(struct p_log *log, const struct fs_parameter_spec *p, +int fs_param_is_path(struct plogger *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { return 0; diff --git a/fs/fsopen.c b/fs/fsopen.c index 2fa3f241b762..b898623e4682 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -5,6 +5,7 @@ * Written by David Howells (dhowells@redhat.com) */ +#include #include #include #include @@ -18,90 +19,41 @@ #include "internal.h" #include "mount.h" -/* - * Allow the user to read back any error, warning or informational messages. - */ -static ssize_t fscontext_read(struct file *file, - char __user *_buf, size_t len, loff_t *pos) +static void fsopen_cf_free(const struct configfd_context *cfc) { - struct fs_context *fc = file->private_data; - struct fc_log *log = fc->log.log; - unsigned int logsize = ARRAY_SIZE(log->buffer); - ssize_t ret; - char *p; - bool need_free; - int index, n; - - ret = mutex_lock_interruptible(&fc->uapi_mutex); - if (ret < 0) - return ret; + struct fs_context *fc = cfc->data; - if (log->head == log->tail) { - mutex_unlock(&fc->uapi_mutex); - return -ENODATA; - } - - index = log->tail & (logsize - 1); - p = log->buffer[index]; - need_free = log->need_free & (1 << index); - log->buffer[index] = NULL; - log->need_free &= ~(1 << index); - log->tail++; - mutex_unlock(&fc->uapi_mutex); - - ret = -EMSGSIZE; - n = strlen(p); - if (n > len) - goto err_free; - ret = -EFAULT; - if (copy_to_user(_buf, p, n) != 0) - goto err_free; - ret = n; - -err_free: - if (need_free) - kfree(p); - return ret; + put_fs_context(fc); } -static int fscontext_release(struct inode *inode, struct file *file) +static int fsopen_cf_alloc(struct configfd_context *cfc) { - struct fs_context *fc = file->private_data; + struct fs_context *fc; + struct file_system_type *fs_type; - if (fc) { - file->private_data = NULL; - put_fs_context(fc); - } - return 0; -} + if (cfc->op != CONFIGFD_CMD_CREATE) + return -EINVAL; -const struct file_operations fscontext_fops = { - .read = fscontext_read, - .release = fscontext_release, - .llseek = no_llseek, -}; + fs_type = get_fs_type(cfc->cft->name); + if (WARN(!fs_type, "BUG: fs_type %s should exist if configfd type does", + cfc->cft->name)) + return -ENODEV; -/* - * Attach a filesystem context to a file and an fd. - */ -static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags) -{ - int fd; + if (cfc->op == CONFIGFD_CMD_RECONFIGURE) + fc = fs_context_for_reconfigure(NULL, 0, 0); + else + fc = fs_context_for_mount(fs_type, 0); + put_filesystem(fs_type); + if (IS_ERR(fc)) + return PTR_ERR(fc); - fd = anon_inode_getfd("[fscontext]", &fscontext_fops, fc, - O_RDWR | o_flags); - if (fd < 0) - put_fs_context(fc); - return fd; -} + if (cfc->op == CONFIGFD_CMD_RECONFIGURE) + fc->phase = FS_CONTEXT_RECONF_PARAMS; + else + fc->phase = FS_CONTEXT_CREATE_PARAMS; + cfc->data = fc; + fc->cfc = cfc; -static int fscontext_alloc_log(struct fs_context *fc) -{ - fc->log.log = kzalloc(sizeof(*fc->log.log), GFP_KERNEL); - if (!fc->log.log) - return -ENOMEM; - refcount_set(&fc->log.log->usage, 1); - fc->log.log->owner = fc->fs_type->owner; return 0; } @@ -114,42 +66,14 @@ static int fscontext_alloc_log(struct fs_context *fc) */ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags) { - struct file_system_type *fs_type; - struct fs_context *fc; - const char *fs_name; - int ret; - if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN)) return -EPERM; if (flags & ~FSOPEN_CLOEXEC) return -EINVAL; - fs_name = strndup_user(_fs_name, PAGE_SIZE); - if (IS_ERR(fs_name)) - return PTR_ERR(fs_name); - - fs_type = get_fs_type(fs_name); - kfree(fs_name); - if (!fs_type) - return -ENODEV; - - fc = fs_context_for_mount(fs_type, 0); - put_filesystem(fs_type); - if (IS_ERR(fc)) - return PTR_ERR(fc); - - fc->phase = FS_CONTEXT_CREATE_PARAMS; - - ret = fscontext_alloc_log(fc); - if (ret < 0) - goto err_fc; - - return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0); - -err_fc: - put_fs_context(fc); - return ret; + return ksys_configfd_open(_fs_name, flags ? O_CLOEXEC : 0, + CONFIGFD_CMD_CREATE); } /* @@ -157,10 +81,14 @@ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags) */ SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags) { - struct fs_context *fc; - struct path target; + struct path *target; unsigned int lookup_flags; int ret; + int fd; + struct configfd_param cp; + struct open_flags of; + struct filename *name; + struct file *file; if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN)) return -EPERM; @@ -178,34 +106,49 @@ SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags lookup_flags &= ~LOOKUP_AUTOMOUNT; if (flags & FSPICK_EMPTY_PATH) lookup_flags |= LOOKUP_EMPTY; - ret = user_path_at(dfd, path, lookup_flags, &target); - if (ret < 0) - goto err; - ret = -EINVAL; - if (target.mnt->mnt_root != target.dentry) - goto err_path; + of.lookup_flags = lookup_flags; + of.intent = LOOKUP_OPEN; + of.acc_mode = 0; + of.mode = 0; + of.open_flag = O_PATH; - fc = fs_context_for_reconfigure(target.dentry, 0, 0); - if (IS_ERR(fc)) { - ret = PTR_ERR(fc); - goto err_path; - } + name = getname_kernel(path); + if (IS_ERR(name)) + return PTR_ERR(name); - fc->phase = FS_CONTEXT_RECONF_PARAMS; + file = do_filp_open(dfd, name, &of); + putname(name); + if (IS_ERR(file)) + return PTR_ERR(file); - ret = fscontext_alloc_log(fc); - if (ret < 0) - goto err_fc; + target = &file->f_path; + ret = -EINVAL; + if (target->mnt->mnt_root != target->dentry) + goto err_file; - path_put(&target); - return fscontext_create_fd(fc, flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0); + ret = fd = kern_configfd_open("mount", + flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0, + CONFIGFD_CMD_RECONFIGURE); + if (ret < 0) + goto err_file; + cp = (struct configfd_param) { + .key = "pathfd", + .file = file, + .cmd = CONFIGFD_SET_FD, + }; + ret = kern_configfd_action(fd, &cp); + /* file gets NULL'd if successfully installed otherwise we put */ + if (cp.file) + fput(file); + if (ret < 0) + goto err_close; + return fd; -err_fc: - put_fs_context(fc); -err_path: - path_put(&target); -err: + err_close: + ksys_close(fd); + err_file: + fput(file); return ret; } @@ -268,6 +211,161 @@ static int vfs_fsconfig_locked(struct fs_context *fc, int cmd, return ret; } +static int fsopen_cf_mount_set(const struct configfd_context *icfc, + struct configfd_param *p) +{ + struct fs_context *fc; + struct path *path; + /* cheat: we're going to mutate the context so drop the const */ + struct configfd_context *cfc = (struct configfd_context *)icfc; + + if (strcmp(p->key, "pathfd") != 0 || p->cmd != CONFIGFD_SET_FD) { + plogger_err(&cfc->log, "must set pathfd before any other parameter"); + return -EINVAL; + } + if (cfc->op != CONFIGFD_CMD_RECONFIGURE) { + plogger_err(&cfc->log, "may only be opened for reconfigure"); + return -EINVAL; + } + + path = &p->file->f_path; + if (path->mnt->mnt_root != path->dentry) { + plogger_err(&cfc->log, "pathfd must identify a mount point"); + return -EINVAL; + } + fc = fs_context_for_reconfigure(path->dentry, 0, 0); + if (IS_ERR(fc)) + return PTR_ERR(fc); + fc->phase = FS_CONTEXT_RECONF_PARAMS; + /* hacky: reconfigure the cfc so all ops now pass to the + * correct filesystem type */ + cfc->cft = &path->dentry->d_sb->s_type->cft; + /* more hackery: mount type is built in so no module ref, but + * filesystem may be modular, so acquire a reference to the + * module for configfd_free to put later */ + try_module_get(cfc->cft->owner); + cfc->data = fc; + fc->cfc = cfc; + + return 0; +} + +static int fsopen_cf_set(const struct configfd_context *cfc, + struct configfd_param *p) +{ + struct fs_context *fc = cfc->data; + int ret; + + struct fs_parameter param = { + .type = fs_value_is_undefined, + .key = p->key, + }; + + /* parameter we intercept */ + if (strcmp(p->key, "mount_attrs") == 0 && + p->cmd == CONFIGFD_SET_INT) { + fc->mnt_flags = 0; + if (p->aux & ~(MOUNT_ATTR_RDONLY | + MOUNT_ATTR_NOSUID | + MOUNT_ATTR_NODEV | + MOUNT_ATTR_NOEXEC | + MOUNT_ATTR__ATIME | + MOUNT_ATTR_NODIRATIME)) + return -EINVAL; + + if (p->aux & MOUNT_ATTR_RDONLY) + fc->mnt_flags |= MNT_READONLY; + if (p->aux & MOUNT_ATTR_NOSUID) + fc->mnt_flags |= MNT_NOSUID; + if (p->aux & MOUNT_ATTR_NODEV) + fc->mnt_flags |= MNT_NODEV; + if (p->aux & MOUNT_ATTR_NOEXEC) + fc->mnt_flags |= MNT_NOEXEC; + if (p->aux & MOUNT_ATTR_NODIRATIME) + fc->mnt_flags |= MNT_NODIRATIME; + + switch (p->aux & MOUNT_ATTR__ATIME) { + case MOUNT_ATTR_STRICTATIME: + break; + case MOUNT_ATTR_NOATIME: + fc->mnt_flags |= MNT_NOATIME; + break; + case MOUNT_ATTR_RELATIME: + fc->mnt_flags |= MNT_RELATIME; + break; + default: + return -EINVAL; + } + + return 0; + } + + if (fc->ops == &legacy_fs_context_ops) { + switch (p->cmd) { + case FSCONFIG_SET_BINARY: + case FSCONFIG_SET_PATH: + case FSCONFIG_SET_PATH_EMPTY: + case FSCONFIG_SET_FD: + return -EOPNOTSUPP; + default: + break; + } + } + switch (p->cmd) { + case FSCONFIG_SET_FLAG: + param.type = fs_value_is_flag; + break; + case FSCONFIG_SET_STRING: + param.type = fs_value_is_string; + param.string = p->string; + param.size = p->size; + break; + case FSCONFIG_SET_BINARY: + param.type = fs_value_is_blob; + param.size = p->size; + param.blob = p->blob; + break; + case FSCONFIG_SET_PATH: + param.type = fs_value_is_filename; + param.name = p->name; + param.dirfd = p->aux; + param.size = p->size; + break; + case FSCONFIG_SET_PATH_EMPTY: + param.type = fs_value_is_filename; + param.name = p->name; + param.dirfd = p->aux; + param.size = p->size; + break; + case FSCONFIG_SET_FD: + param.type = fs_value_is_file; + param.file = p->file; + break; + default: + break; + } + + ret = mutex_lock_interruptible(&fc->uapi_mutex); + if (ret == 0) { + ret = vfs_fsconfig_locked(fc, p->cmd, ¶m); + mutex_unlock(&fc->uapi_mutex); + } + return ret; +} + +static int fsopen_cf_act(const struct configfd_context *cfc, + unsigned int cmd) +{ + struct fs_context *fc = cfc->data; + int ret = mutex_lock_interruptible(&fc->uapi_mutex); + + if (ret == 0) { + ret = vfs_fsconfig_locked(fc, cmd, NULL); + mutex_unlock(&fc->uapi_mutex); + } + return ret; +} + /** * sys_fsconfig - Set parameters and trigger actions on a context * @fd: The filesystem context to act upon @@ -315,155 +413,50 @@ SYSCALL_DEFINE5(fsconfig, int, fd, unsigned int, cmd, const char __user *, _key, - const void __user *, _value, + void __user *, _value, int, aux) { - struct fs_context *fc; - struct fd f; - int ret; - int lookup_flags = 0; + return ksys_configfd_action(fd, cmd, _key, _value, aux); +} - struct fs_parameter param = { - .type = fs_value_is_undefined, - }; +static struct configfd_ops fsopen_cf_ops = { + .alloc = fsopen_cf_alloc, + .free = fsopen_cf_free, + .set = fsopen_cf_set, + .act = fsopen_cf_act, + .get = fsopen_cf_get, +}; - if (fd < 0) - return -EINVAL; +static struct configfd_ops fsopen_cf_mount_ops = { + .set = fsopen_cf_mount_set, +}; - switch (cmd) { - case FSCONFIG_SET_FLAG: - if (!_key || _value || aux) - return -EINVAL; - break; - case FSCONFIG_SET_STRING: - if (!_key || !_value || aux) - return -EINVAL; - break; - case FSCONFIG_SET_BINARY: - if (!_key || !_value || aux <= 0 || aux > 1024 * 1024) - return -EINVAL; - break; - case FSCONFIG_SET_PATH: - case FSCONFIG_SET_PATH_EMPTY: - if (!_key || !_value || (aux != AT_FDCWD && aux < 0)) - return -EINVAL; - break; - case FSCONFIG_SET_FD: - if (!_key || _value || aux < 0) - return -EINVAL; - break; - case FSCONFIG_CMD_CREATE: - case FSCONFIG_CMD_RECONFIGURE: - if (_key || _value || aux) - return -EINVAL; - break; - default: - return -EOPNOTSUPP; - } +static struct configfd_type fsopen_mount_type = { + .name = "mount", + .ops = &fsopen_cf_mount_ops, +}; - f = fdget(fd); - if (!f.file) - return -EBADF; - ret = -EINVAL; - if (f.file->f_op != &fscontext_fops) - goto out_f; +int fs_context_register(struct file_system_type *fs) +{ + int res; - fc = f.file->private_data; - if (fc->ops == &legacy_fs_context_ops) { - switch (cmd) { - case FSCONFIG_SET_BINARY: - case FSCONFIG_SET_PATH: - case FSCONFIG_SET_PATH_EMPTY: - case FSCONFIG_SET_FD: - ret = -EOPNOTSUPP; - goto out_f; - } - } + fs->cft.name = fs->name; + fs->cft.ops = &fsopen_cf_ops; + fs->cft.owner = fs->owner; + res = configfd_type_register(&(fs->cft)); - if (_key) { - param.key = strndup_user(_key, 256); - if (IS_ERR(param.key)) { - ret = PTR_ERR(param.key); - goto out_f; - } - } + return res; +} - switch (cmd) { - case FSCONFIG_SET_FLAG: - param.type = fs_value_is_flag; - break; - case FSCONFIG_SET_STRING: - param.type = fs_value_is_string; - param.string = strndup_user(_value, 256); - if (IS_ERR(param.string)) { - ret = PTR_ERR(param.string); - goto out_key; - } - param.size = strlen(param.string); - break; - case FSCONFIG_SET_BINARY: - param.type = fs_value_is_blob; - param.size = aux; - param.blob = memdup_user_nul(_value, aux); - if (IS_ERR(param.blob)) { - ret = PTR_ERR(param.blob); - goto out_key; - } - break; - case FSCONFIG_SET_PATH_EMPTY: - lookup_flags = LOOKUP_EMPTY; - /* fallthru */ - case FSCONFIG_SET_PATH: - param.type = fs_value_is_filename; - param.name = getname_flags(_value, lookup_flags, NULL); - if (IS_ERR(param.name)) { - ret = PTR_ERR(param.name); - goto out_key; - } - param.dirfd = aux; - param.size = strlen(param.name->name); - break; - case FSCONFIG_SET_FD: - param.type = fs_value_is_file; - ret = -EBADF; - param.file = fget(aux); - if (!param.file) - goto out_key; - break; - default: - break; - } +void fs_context_unregister(struct file_system_type *fs) +{ + configfd_type_unregister(&(fs->cft)); +} - ret = mutex_lock_interruptible(&fc->uapi_mutex); - if (ret == 0) { - ret = vfs_fsconfig_locked(fc, cmd, ¶m); - mutex_unlock(&fc->uapi_mutex); - } +static int __init fsopen_init(void) +{ + configfd_type_register(&fsopen_mount_type); - /* Clean up the our record of any value that we obtained from - * userspace. Note that the value may have been stolen by the LSM or - * filesystem, in which case the value pointer will have been cleared. - */ - switch (cmd) { - case FSCONFIG_SET_STRING: - case FSCONFIG_SET_BINARY: - kfree(param.string); - break; - case FSCONFIG_SET_PATH: - case FSCONFIG_SET_PATH_EMPTY: - if (param.name) - putname(param.name); - break; - case FSCONFIG_SET_FD: - if (param.file) - fput(param.file); - break; - default: - break; - } -out_key: - kfree(param.key); -out_f: - fdput(f); - return ret; + return 0; } +fs_initcall(fsopen_init); diff --git a/fs/internal.h b/fs/internal.h index f3f280b952a3..507d59e9a540 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -91,6 +91,10 @@ extern int __mnt_want_write_file(struct file *); extern void __mnt_drop_write_file(struct file *); extern void dissolve_on_fput(struct vfsmount *); + +int fsopen_cf_get(const struct configfd_context *cfc, + struct configfd_param *p); + /* * fs_struct.c */ diff --git a/fs/namespace.c b/fs/namespace.c index 85b5f7bea82e..09b3220d9437 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "pnode.h" #include "internal.h" @@ -3324,70 +3325,19 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, return ret; } -/* - * Create a kernel mount representation for a new, prepared superblock - * (specified by fs_fd) and attach to an open_tree-like file descriptor. - */ -SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, - unsigned int, attr_flags) +int fsopen_cf_get(const struct configfd_context *cfc, + struct configfd_param *p) { struct mnt_namespace *ns; - struct fs_context *fc; + struct fs_context *fc = cfc->data; struct file *file; struct path newmount; struct mount *mnt; - struct fd f; - unsigned int mnt_flags = 0; long ret; - if (!may_mount()) - return -EPERM; - - if ((flags & ~(FSMOUNT_CLOEXEC)) != 0) + if (strcmp(p->key, "mountfd") != 0 || p->cmd != CONFIGFD_GET_FD) return -EINVAL; - if (attr_flags & ~(MOUNT_ATTR_RDONLY | - MOUNT_ATTR_NOSUID | - MOUNT_ATTR_NODEV | - MOUNT_ATTR_NOEXEC | - MOUNT_ATTR__ATIME | - MOUNT_ATTR_NODIRATIME)) - return -EINVAL; - - if (attr_flags & MOUNT_ATTR_RDONLY) - mnt_flags |= MNT_READONLY; - if (attr_flags & MOUNT_ATTR_NOSUID) - mnt_flags |= MNT_NOSUID; - if (attr_flags & MOUNT_ATTR_NODEV) - mnt_flags |= MNT_NODEV; - if (attr_flags & MOUNT_ATTR_NOEXEC) - mnt_flags |= MNT_NOEXEC; - if (attr_flags & MOUNT_ATTR_NODIRATIME) - mnt_flags |= MNT_NODIRATIME; - - switch (attr_flags & MOUNT_ATTR__ATIME) { - case MOUNT_ATTR_STRICTATIME: - break; - case MOUNT_ATTR_NOATIME: - mnt_flags |= MNT_NOATIME; - break; - case MOUNT_ATTR_RELATIME: - mnt_flags |= MNT_RELATIME; - break; - default: - return -EINVAL; - } - - f = fdget(fs_fd); - if (!f.file) - return -EBADF; - - ret = -EINVAL; - if (f.file->f_op != &fscontext_fops) - goto err_fsfd; - - fc = f.file->private_data; - ret = mutex_lock_interruptible(&fc->uapi_mutex); if (ret < 0) goto err_fsfd; @@ -3398,7 +3348,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, goto err_unlock; ret = -EPERM; - if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) { + if (mount_too_revealing(fc->root->d_sb, &fc->mnt_flags)) { pr_warn("VFS: Mount too revealing\n"); goto err_unlock; } @@ -3417,7 +3367,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, goto err_unlock; } newmount.dentry = dget(fc->root); - newmount.mnt->mnt_flags = mnt_flags; + newmount.mnt->mnt_flags = fc->mnt_flags; /* We've done the mount bit - now move the file context into more or * less the same state as if we'd done an fspick(). We don't want to @@ -3447,23 +3397,56 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, ret = PTR_ERR(file); goto err_path; } + ret = 0; file->f_mode |= FMODE_NEED_UNMOUNT; - - ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0); - if (ret >= 0) - fd_install(ret, file); - else - fput(file); + p->file = file; err_path: path_put(&newmount); err_unlock: mutex_unlock(&fc->uapi_mutex); err_fsfd: - fdput(f); + return ret; } +/* + * Create a kernel mount representation for a new, prepared superblock + * (specified by fs_fd) and attach to an open_tree-like file descriptor. + */ +SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, + unsigned int, attr_flags) +{ + int ret; + int oflags; + struct configfd_param p = { + .key = "mount_attrs", + .cmd = CONFIGFD_SET_INT, + .aux = attr_flags, + }; + + if (!may_mount()) + return -EPERM; + + if ((flags & ~(FSMOUNT_CLOEXEC)) != 0) + return -EINVAL; + + oflags = (flags & ~(FSMOUNT_CLOEXEC)) ? O_CLOEXEC : 0; + + ret = kern_configfd_action(fs_fd, &p); + if (ret < 0) + return ret; + p = (struct configfd_param) { + .key = "mountfd", + .cmd = CONFIGFD_GET_FD, + .aux = oflags, + }; + ret = kern_configfd_action(fs_fd, &p); + if (ret < 0) + return ret; + return p.aux; +} + /* * Move a mount from one place to another. In combination with * fsopen()/fsmount() this is used to install a new mount and in combination diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index ec73ebc4827d..64b97014c388 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -281,12 +281,11 @@ extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); extern void *ceph_kvmalloc(size_t size, gfp_t flags); struct fs_parameter; -struct fc_log; struct ceph_options *ceph_alloc_options(void); int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, - struct fc_log *l); + struct plogger *l); int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, - struct fc_log *l); + struct plogger *l); int ceph_print_client_options(struct seq_file *m, struct ceph_client *client, bool show_all); extern void ceph_destroy_options(struct ceph_options *opt); diff --git a/include/linux/fs.h b/include/linux/fs.h index 3cd4fe6b845e..4dc62a697817 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2,6 +2,7 @@ #ifndef _LINUX_FS_H #define _LINUX_FS_H +#include #include #include #include @@ -2251,6 +2252,7 @@ struct file_system_type { struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; struct lock_class_key i_mutex_dir_key; + struct configfd_type cft; }; #define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME) diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index e6c3e4c61dad..da422d99cf8a 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -73,11 +73,6 @@ struct fs_parameter { int dirfd; }; -struct p_log { - const char *prefix; - struct fc_log *log; -}; - /* * Filesystem context for holding the parameters used in the creation or * reconfiguration of a superblock. @@ -97,14 +92,15 @@ struct fs_context { struct user_namespace *user_ns; /* The user namespace for this mount */ struct net *net_ns; /* The network namespace for this mount */ const struct cred *cred; /* The mounter's credentials */ - struct p_log log; /* Logging buffer */ const char *source; /* The source name (eg. dev path) */ void *security; /* Linux S&M options */ void *s_fs_info; /* Proposed s_fs_info */ + struct configfd_context *cfc; unsigned int sb_flags; /* Proposed superblock flags (SB_*) */ unsigned int sb_flags_mask; /* Superblock flags that were changed */ unsigned int s_iflags; /* OR'd with sb->s_iflags */ unsigned int lsm_flags; /* Information flags from the fs to the LSM */ + unsigned int mnt_flags; /* mnt flags translated from MOUNT_ATTRS */ enum fs_context_purpose purpose:8; enum fs_context_phase phase:8; /* The phase the context is in */ bool need_free:1; /* Need to call ops->free() */ @@ -138,6 +134,8 @@ extern int vfs_parse_fs_string(struct fs_context *fc, const char *key, extern int generic_parse_monolithic(struct fs_context *fc, void *data); extern int vfs_get_tree(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc); +extern void fs_context_set_reconfigure(struct fs_context *fc, + struct dentry *reference); /* * sget() wrappers to be called from the ->get_tree() op. @@ -170,28 +168,28 @@ extern int get_tree_keyed(struct fs_context *fc, extern int get_tree_bdev(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc)); - -extern const struct file_operations fscontext_fops; - -/* - * Mount error, warning and informational message logging. This structure is - * shareable between a mount and a subordinate mount. - */ -struct fc_log { - refcount_t usage; - u8 head; /* Insertion index in buffer[] */ - u8 tail; /* Removal index in buffer[] */ - u8 need_free; /* Mask of kfree'able items in buffer[] */ - struct module *owner; /* Owner module for strings that don't then need freeing */ - char *buffer[8]; -}; - -extern __attribute__((format(printf, 4, 5))) -void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, ...); - -#define __logfc(fc, l, fmt, ...) logfc((fc)->log.log, NULL, \ +#define logfc(fc, p, l, fmt, ...) ({ \ + struct fs_context *fsc = (fc); \ + struct logger *log = NULL; \ + if (fsc) \ + log = fsc->cfc->log.log; \ + logger_log(log, p, l, fmt, ## __VA_ARGS__); \ +}) + +#define plogfc(fc, l, fmt, ...) ({ \ + struct fs_context *fsc = (fc); \ + struct plogger *log = NULL; \ + const char *p = NULL; \ + if (fsc) { \ + log = &fsc->cfc->log; \ + p = log->prefix; \ + } \ + logger_log(log->log, p, l, fmt, ## __VA_ARGS__); \ +}) + +#define __logfc(fc, l, fmt, ...) logfc(fc, NULL, \ l, fmt, ## __VA_ARGS__) -#define __plog(p, l, fmt, ...) logfc((p)->log, (p)->prefix, \ +#define __plog(p, l, fmt, ...) logger_log((p)->log, (p)->prefix, \ l, fmt, ## __VA_ARGS__) /** * infof - Store supplementary informational message @@ -203,7 +201,7 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, */ #define infof(fc, fmt, ...) __logfc(fc, 'i', fmt, ## __VA_ARGS__) #define info_plog(p, fmt, ...) __plog(p, 'i', fmt, ## __VA_ARGS__) -#define infofc(p, fmt, ...) __plog((&(fc)->log), 'i', fmt, ## __VA_ARGS__) +#define infofc(p, fmt, ...) plogfc(fc, 'i', fmt, ## __VA_ARGS__) /** * warnf - Store supplementary warning message @@ -215,7 +213,7 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, */ #define warnf(fc, fmt, ...) __logfc(fc, 'w', fmt, ## __VA_ARGS__) #define warn_plog(p, fmt, ...) __plog(p, 'w', fmt, ## __VA_ARGS__) -#define warnfc(fc, fmt, ...) __plog((&(fc)->log), 'w', fmt, ## __VA_ARGS__) +#define warnfc(fc, fmt, ...) plogfc(fc, 'w', fmt, ## __VA_ARGS__) /** * errorf - Store supplementary error message @@ -227,7 +225,7 @@ void logfc(struct fc_log *log, const char *prefix, char level, const char *fmt, */ #define errorf(fc, fmt, ...) __logfc(fc, 'e', fmt, ## __VA_ARGS__) #define error_plog(p, fmt, ...) __plog(p, 'e', fmt, ## __VA_ARGS__) -#define errorfc(fc, fmt, ...) __plog((&(fc)->log), 'e', fmt, ## __VA_ARGS__) +#define errorfc(fc, fmt, ...) plogfc(fc, 'e', fmt, ## __VA_ARGS__) /** * invalf - Store supplementary invalid argument error message diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h index 2eab6d5f6736..7a7302341b56 100644 --- a/include/linux/fs_parser.h +++ b/include/linux/fs_parser.h @@ -19,7 +19,7 @@ struct constant_table { struct fs_parameter_spec; struct fs_parse_result; -typedef int fs_param_type(struct p_log *, +typedef int fs_param_type(struct plogger *, const struct fs_parameter_spec *, struct fs_parameter *, struct fs_parse_result *); @@ -60,7 +60,7 @@ struct fs_parse_result { }; }; -extern int __fs_parse(struct p_log *log, +extern int __fs_parse(struct plogger *log, const struct fs_parameter_spec *desc, struct fs_parameter *value, struct fs_parse_result *result); @@ -70,7 +70,7 @@ static inline int fs_parse(struct fs_context *fc, struct fs_parameter *param, struct fs_parse_result *result) { - return __fs_parse(&fc->log, desc, param, result); + return __fs_parse(&fc->cfc->log, desc, param, result); } extern int fs_lookup_param(struct fs_context *fc, @@ -131,4 +131,7 @@ static inline bool fs_validate_description(const char *name, #define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL) #define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL) +int fs_context_register(struct file_system_type *fs); +void fs_context_unregister(struct file_system_type *fs); + #endif /* _LINUX_FS_PARSER_H */ diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index a0e97f6c1072..f7112d36e4a2 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -332,7 +332,7 @@ EXPORT_SYMBOL(ceph_destroy_options); /* get secret from key store */ static int get_secret(struct ceph_crypto_key *dst, const char *name, - struct p_log *log) + struct plogger *log) { struct key *ukey; int key_err; @@ -378,16 +378,16 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name, } int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, - struct fc_log *l) + struct plogger *l) { - struct p_log log = {.prefix = "libceph", .log = l}; int ret; + l->prefix = "libceph"; /* ip1[:port1][,ip2[:port2]...] */ ret = ceph_parse_ips(buf, buf + len, opt->mon_addr, CEPH_MAX_MON, &opt->num_mon); if (ret) { - error_plog(&log, "Failed to parse monitor IPs: %d", ret); + error_plog(l, "Failed to parse monitor IPs: %d", ret); return ret; } @@ -396,13 +396,13 @@ int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, EXPORT_SYMBOL(ceph_parse_mon_ips); int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, - struct fc_log *l) + struct plogger *l) { struct fs_parse_result result; int token, err; - struct p_log log = {.prefix = "libceph", .log = l}; - token = __fs_parse(&log, ceph_parameters, param, &result); + l->prefix = "libceph"; + token = __fs_parse(l, ceph_parameters, param, &result); dout("%s fs_parse '%s' token %d\n", __func__, param->key, token); if (token < 0) return token; @@ -414,7 +414,7 @@ int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, &opt->my_addr, 1, NULL); if (err) { - error_plog(&log, "Failed to parse ip: %d", err); + error_plog(l, "Failed to parse ip: %d", err); return err; } opt->flags |= CEPH_OPT_MYIP; @@ -423,7 +423,7 @@ int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, case Opt_fsid: err = parse_fsid(param->string, &opt->fsid); if (err) { - error_plog(&log, "Failed to parse fsid: %d", err); + error_plog(l, "Failed to parse fsid: %d", err); return err; } opt->flags |= CEPH_OPT_FSID; @@ -442,7 +442,7 @@ int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, return -ENOMEM; err = ceph_crypto_key_unarmor(opt->key, param->string); if (err) { - error_plog(&log, "Failed to parse secret: %d", err); + error_plog(l, "Failed to parse secret: %d", err); return err; } break; @@ -453,10 +453,10 @@ int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL); if (!opt->key) return -ENOMEM; - return get_secret(opt->key, param->string, &log); + return get_secret(opt->key, param->string, l); case Opt_osdtimeout: - warn_plog(&log, "Ignoring osdtimeout"); + warn_plog(l, "Ignoring osdtimeout"); break; case Opt_osdkeepalivetimeout: /* 0 isn't well defined right now, reject it */ @@ -527,7 +527,7 @@ int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, return 0; out_of_range: - return inval_plog(&log, "%s out of range", param->key); + return inval_plog(l, "%s out of range", param->key); } EXPORT_SYMBOL(ceph_parse_param); From patchwork Sat Feb 15 15:36:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383889 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9DF311580 for ; Sat, 15 Feb 2020 15:38:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7C6F52187F for ; Sat, 15 Feb 2020 15:38:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="HpDU/g5i" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726363AbgBOPix (ORCPT ); Sat, 15 Feb 2020 10:38:53 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:53118 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbgBOPiw (ORCPT ); Sat, 15 Feb 2020 10:38:52 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 9914A8EE302; Sat, 15 Feb 2020 07:38:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781132; bh=Is4gjJXUw4+JIkfLcQjjmaIbmYgT3i6jizgl3BOtdJs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HpDU/g5ixTCFS1DHV50TmTqnJiXcQaOZzGndyvsFUUSf94q6V/zRSFbIs+NRRROHP oWPiZSGK/NlZ8FL+OMnNgloqgxRoWvAWjOpzRH9bZdL/HAhfIjAFfGAJK6pQGEvB7I EFdkzR3n4cVcDUOrFiwlLqXtEB3T121V9aCH7Yxk= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id JvnIb0bEkufC; Sat, 15 Feb 2020 07:38:52 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id CC8358EE121; Sat, 15 Feb 2020 07:38:51 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 5/6] fs: expose internal interfaces open_detached_copy and do_reconfigure_mount Date: Sat, 15 Feb 2020 10:36:08 -0500 Message-Id: <20200215153609.23797-6-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org These are needed for the forthcoming bind configure type to work. Signed-off-by: James Bottomley --- fs/internal.h | 3 +++ fs/namespace.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 507d59e9a540..80d89ddb9b28 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -95,6 +95,9 @@ extern void dissolve_on_fput(struct vfsmount *); int fsopen_cf_get(const struct configfd_context *cfc, struct configfd_param *p); +extern int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags); +extern struct file *open_detached_copy(struct path *path, bool recursive); + /* * fs_struct.c */ diff --git a/fs/namespace.c b/fs/namespace.c index 09b3220d9437..69fb23ae3d8f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2318,7 +2318,7 @@ static int do_loopback(struct path *path, const char *old_name, return err; } -static struct file *open_detached_copy(struct path *path, bool recursive) +struct file *open_detached_copy(struct path *path, bool recursive) { struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; struct mnt_namespace *ns = alloc_mnt_ns(user_ns, true); @@ -2494,7 +2494,7 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount * * superblock it refers to. This is triggered by specifying MS_REMOUNT|MS_BIND * to mount(2). */ -static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags) +int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags) { struct super_block *sb = path->mnt->mnt_sb; struct mount *mnt = real_mount(path->mnt); From patchwork Sat Feb 15 15:36:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11383891 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0417F1580 for ; Sat, 15 Feb 2020 15:39:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CE7B5222C2 for ; Sat, 15 Feb 2020 15:39:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="xFd34JAm" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726486AbgBOPjY (ORCPT ); Sat, 15 Feb 2020 10:39:24 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:53152 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbgBOPjY (ORCPT ); Sat, 15 Feb 2020 10:39:24 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id AE4ED8EE302; Sat, 15 Feb 2020 07:39:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1581781163; bh=6MGZ/87iTCXTcnHfgJPrVHbR6Ng1T0HOR6zbSjR+ug4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=xFd34JAmheYAI7n4TXjVzpCCjKhfoVNJ2k9r268DIMz53czjq8Pmx303UGq++FBw0 UhJFD8k2/QVYfZ3OQqp05gDQP4DUX2G1+bGYjj5fH90hzGtqANaqB7fTrOvAht+mNp 2nVAqKdfg0kWMzQm+5jgg5gbTXxSqwCR2fGjanQs= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cy09A65H5p_5; Sat, 15 Feb 2020 07:39:23 -0800 (PST) Received: from jarvis.lan (jarvis.ext.hansenpartnership.com [153.66.160.226]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id DC45B8EE121; Sat, 15 Feb 2020 07:39:22 -0800 (PST) From: James Bottomley To: linux-fsdevel@vger.kernel.org Cc: David Howells , Christian Brauner , Al Viro , Miklos Szeredi Subject: [PATCH v3 6/6] fs: bind: add configfs type for bind mounts Date: Sat, 15 Feb 2020 10:36:09 -0500 Message-Id: <20200215153609.23797-7-James.Bottomley@HansenPartnership.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> References: <20200215153609.23797-1-James.Bottomley@HansenPartnership.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This can do the equivalent of open_tree and also do bind mount reconfiguration from ro to rw and vice versa. To get the equvalent of open tree you need to do mnt = open("/path/to/tree", O_PATH); fd = configfs_open(bind, O_CLOEXEC); configfs_action(fd, CONFIGFD_SET_FD, "pathfd", NULL, mnt); configfs_action(fd, CONFIGFD_SET_FLAG, "detached", NULL, 0); configfs_action(fd, CONFIGFD_SET_FLAG, "recursive", NULL, 0); configfs_action(fd, CONFIGFD_CMD_CREATE, NULL, NULL, 0); configfs_action(fd, CONFIGFD_GET_FD, "bindfd", &bfd, NULL, O_CLOEXEC); And bfd will now contain the file descriptor to pass to move_tree. There is a deficiency over the original implementation in that the open system call has no way of clearing the LOOKUP_AUTOMOUNT path, but that's fixable. To do a mount reconfigure to change the bind mount to readonly do mnt = open("/path/to/tree", O_PATH); fd = configfs_open(bind, O_CLOEXEC); configfs_action(fd, CONFIGFD_SET_FD, "pathfd", NULL, mnt); configfs_action(fd, CONFIGFD_SET_FLAG, "ro", NULL, 0); configfs_action(fd, CONFIGFD_CMD_RECONFIGURE, NULL, NULL, 0); And the bind properties will be changed. You can also pass the "rw" flag to reset the read only. Signed-off-by: James Bottomley --- v2: add nodev and noexec mount reconfigurations --- fs/Makefile | 2 +- fs/bind.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 fs/bind.c diff --git a/fs/Makefile b/fs/Makefile index 2c078355fdf5..59b78e19f0d1 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -14,7 +14,7 @@ obj-y := open.o read_write.o file_table.o super.o \ pnode.o splice.o sync.o utimes.o d_path.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ fs_types.o fs_context.o fs_parser.o fsopen.o \ - configfd.o + configfd.o bind.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/bind.c b/fs/bind.c new file mode 100644 index 000000000000..c1dedef40169 --- /dev/null +++ b/fs/bind.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Dummy configfd handler for doing context based configuration + * on bind mounts + * + * Copyright (C) James.Bottomley@HansenPartnership.com + */ + +#include +#include +#include +#include +#include + +#include "internal.h" +#include "mount.h" + +struct bind_data { + bool ro:1; + bool noexec:1; + bool nosuid:1; + bool nodev:1; + bool detached:1; + bool recursive:1; + struct file *file; + struct file *retfile; +}; + +struct bind_data *to_bind_data(const struct configfd_context *cfc) +{ + return cfc->data; +} + +static int bind_set_fd(const struct configfd_context *cfc, + struct configfd_param *p) +{ + struct bind_data *bd = to_bind_data(cfc); + struct path *path; + + if (strcmp(p->key, "pathfd") != 0) + return -EINVAL; + + path = &p->file->f_path; + + if (cfc->op == CONFIGFD_CMD_RECONFIGURE && + path->mnt->mnt_root != path->dentry) { + plogger_err(&cfc->log, "pathfd must be a bind mount"); + return -EINVAL; + } + bd->file = p->file; + p->file = NULL; /* we now own */ + return 0; +} + +static int bind_set_flag(const struct configfd_context *cfc, + struct configfd_param *p) +{ + struct bind_data *bd = to_bind_data(cfc); + + if (strcmp(p->key, "ro") == 0) { + bd->ro = true; + } else if (strcmp(p->key, "rw") == 0) { + bd->ro = false; + } else if (strcmp(p->key, "nosuid") == 0) { + bd->nosuid = true; + } else if (strcmp(p->key, "nodev") == 0) { + bd->nodev = true; + } else if (strcmp(p->key, "noexec") == 0) { + bd->noexec = true; + } else if (strcmp(p->key, "recursive") == 0 && + cfc->op == CONFIGFD_CMD_CREATE) { + bd->recursive = true; + } else if (strcmp(p->key, "detached") == 0 && + cfc->op == CONFIGFD_CMD_CREATE) { + if (!ns_capable(current->nsproxy->mnt_ns->user_ns, + CAP_SYS_ADMIN)) { + plogger_err(&cfc->log, "bind set: insufficient permission for detached tree"); + return -EPERM; + } + bd->detached = true; + } else { + plogger_err(&cfc->log, "bind set: invalid flag %s", p->key); + return -EINVAL; + } + return 0; +} +static int bind_set(const struct configfd_context *cfc, + struct configfd_param *p) +{ + switch (p->cmd) { + case CONFIGFD_SET_FLAG: + return bind_set_flag(cfc, p); + case CONFIGFD_SET_FD: + return bind_set_fd(cfc, p); + default: + plogger_err(&cfc->log, "bind only takes a flag or fd argument"); + return -EINVAL; + } +} + +static int bind_get(const struct configfd_context *cfc, + struct configfd_param *p) +{ + struct bind_data *bd = to_bind_data(cfc); + + if (strcmp(p->key, "bindfd") != 0 || p->cmd != CONFIGFD_GET_FD) + return -EINVAL; + + if (!bd->retfile) + return -EINVAL; + + p->file = bd->retfile; + bd->retfile = NULL; + + return 0; +} + +static int bind_get_mnt_flags(struct bind_data *bd, int mnt_flags) +{ + /* for an unprivileged bind, the ATIME will be locked so keep the same */ + mnt_flags = mnt_flags & MNT_ATIME_MASK; + if (bd->ro) + mnt_flags |= MNT_READONLY; + if (bd->nosuid) + mnt_flags |= MNT_NOSUID; + if (bd->nodev) + mnt_flags |= MNT_NODEV; + if (bd->noexec) + mnt_flags |= MNT_NOEXEC; + + return mnt_flags; +} + +static int bind_reconfigure(const struct configfd_context *cfc) +{ + struct bind_data *bd = to_bind_data(cfc); + unsigned int mnt_flags; + + if (!bd->file) { + plogger_err(&cfc->log, "bind reconfigure: fd must be set"); + return -EINVAL; + } + /* for an unprivileged bind, the ATIME will be locked so keep the same */ + mnt_flags = bd->file->f_path.mnt->mnt_flags & MNT_ATIME_MASK; + mnt_flags = bind_get_mnt_flags(bd, mnt_flags); + + return do_reconfigure_mnt(&bd->file->f_path, mnt_flags); +} + +static int bind_create(const struct configfd_context *cfc) +{ + struct bind_data *bd = to_bind_data(cfc); + struct path *p; + struct file *f; + + if (!bd->file) { + plogger_err(&cfc->log, "bind create: fd must be set"); + return -EINVAL; + } + if (bd->recursive && !bd->detached) { + plogger_err(&cfc->log, "bind create: recursive cannot be set without detached"); + return -EINVAL; + } + + if ((bd->ro || bd->nosuid || bd->noexec || bd->nodev) && + !bd->detached) { + plogger_err(&cfc->log, "bind create: to use ro,rw,nosuid or noexec, mount must be detached"); + return -EINVAL; + } + + p = &bd->file->f_path; + + if (bd->detached) + f = open_detached_copy(p, bd->recursive); + else + f = dentry_open(p, O_PATH, current_cred()); + if (IS_ERR(f)) + return PTR_ERR(f); + + if (bd->detached) { + int mnt_flags = f->f_path.mnt->mnt_flags & MNT_ATIME_MASK; + + mnt_flags = bind_get_mnt_flags(bd, mnt_flags); + + /* since this is a detached copy, we can do without locking */ + f->f_path.mnt->mnt_flags |= mnt_flags; + } + + bd->retfile = f; + return 0; +} + +static int bind_act(const struct configfd_context *cfc, unsigned int cmd) +{ + switch (cmd) { + case CONFIGFD_CMD_RECONFIGURE: + return bind_reconfigure(cfc); + case CONFIGFD_CMD_CREATE: + return bind_create(cfc); + default: + plogger_err(&cfc->log, "bind only responds to reconfigure or create actions"); + return -EINVAL; + } +} + +static void bind_free(const struct configfd_context *cfc) +{ + struct bind_data *bd = to_bind_data(cfc); + + if (bd->file) + fput(bd->file); +} + +static struct configfd_ops bind_type_ops = { + .free = bind_free, + .get = bind_get, + .set = bind_set, + .act = bind_act, +}; + +static struct configfd_type bind_type = { + .name = "bind", + .ops = &bind_type_ops, + .data_size = sizeof(struct bind_data), +}; + +static int __init bind_setup(void) +{ + configfd_type_register(&bind_type); + + return 0; +} +fs_initcall(bind_setup);