diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fs_context.c | 105 | ||||
-rw-r--r-- | fs/fsopen.c | 67 |
2 files changed, 158 insertions, 14 deletions
diff --git a/fs/fs_context.c b/fs/fs_context.c index eb806fae3117..dcf3786f90f9 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> #include <linux/fs_context.h> #include <linux/fs_parser.h> #include <linux/fs.h> @@ -23,6 +24,7 @@ #include <linux/pid_namespace.h> #include <linux/user_namespace.h> #include <net/net_namespace.h> +#include <asm/sections.h> #include "mount.h" #include "internal.h" @@ -365,6 +367,8 @@ 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) + refcount_inc(&fc->log->usage); /* Can't call put until we've called ->dup */ ret = fc->ops->dup(fc, src_fc); @@ -382,7 +386,6 @@ err_fc: } EXPORT_SYMBOL(vfs_dup_fs_context); -#ifdef CONFIG_PRINTK /** * logfc - Log a message to a filesystem context * @fc: The filesystem context to log to. @@ -390,27 +393,100 @@ EXPORT_SYMBOL(vfs_dup_fs_context); */ void logfc(struct fs_context *fc, const char *fmt, ...) { + static const char store_failure[] = "OOM: Can't store error string"; + struct fc_log *log = fc ? fc->log : NULL; + const char *p; va_list va; + char *q; + u8 freeable; va_start(va, fmt); - - switch (fmt[0]) { - case 'w': - vprintk_emit(0, LOGLEVEL_WARNING, NULL, 0, fmt, va); - break; - case 'e': - vprintk_emit(0, LOGLEVEL_ERR, NULL, 0, fmt, va); - break; - default: - vprintk_emit(0, LOGLEVEL_NOTICE, NULL, 0, fmt, va); - break; + if (!strchr(fmt, '%')) { + p = fmt; + goto unformatted_string; } + if (strcmp(fmt, "%s") == 0) { + p = va_arg(va, const char *); + goto unformatted_string; + } + + q = kvasprintf(GFP_KERNEL, fmt, va); +copied_string: + if (!q) + goto store_failure; + freeable = 1; + goto store_string; + +unformatted_string: + if ((unsigned long)p >= (unsigned long)__start_rodata && + (unsigned long)p < (unsigned long)__end_rodata) + goto const_string; + if (log && within_module_core((unsigned long)p, log->owner)) + goto const_string; + q = kstrdup(p, GFP_KERNEL); + goto copied_string; + +store_failure: + p = store_failure; +const_string: + q = (char *)p; + freeable = 0; +store_string: + if (!log) { + switch (fmt[0]) { + case 'w': + printk(KERN_WARNING "%s\n", q + 2); + break; + case 'e': + printk(KERN_ERR "%s\n", q + 2); + break; + default: + printk(KERN_NOTICE "%s\n", q + 2); + break; + } + if (freeable) + kfree(q); + } else { + unsigned int logsize = ARRAY_SIZE(log->buffer); + u8 index; + + 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++; + } - pr_cont("\n"); + log->buffer[index] = q; + log->need_free &= ~(1 << index); + log->need_free |= freeable << index; + log->head++; + } va_end(va); } EXPORT_SYMBOL(logfc); -#endif + +/* + * Free a logging structure. + */ +static void put_fc_log(struct fs_context *fc) +{ + struct fc_log *log = fc->log; + int i; + + if (log) { + if (refcount_dec_and_test(&log->usage)) { + fc->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. @@ -435,6 +511,7 @@ void put_fs_context(struct fs_context *fc) put_user_ns(fc->user_ns); put_cred(fc->cred); kfree(fc->subtype); + put_fc_log(fc); put_filesystem(fc->fs_type); kfree(fc->source); kfree(fc); diff --git a/fs/fsopen.c b/fs/fsopen.c index d256f1ac9ff1..5fce6347de7a 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -20,6 +20,52 @@ #include <uapi/linux/mount.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) +{ + struct fs_context *fc = file->private_data; + struct fc_log *log = fc->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; + + 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; +} + static int fscontext_release(struct inode *inode, struct file *file) { struct fs_context *fc = file->private_data; @@ -32,6 +78,7 @@ static int fscontext_release(struct inode *inode, struct file *file) } const struct file_operations fscontext_fops = { + .read = fscontext_read, .release = fscontext_release, .llseek = no_llseek, }; @@ -50,6 +97,16 @@ static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags) return fd; } +static int fscontext_alloc_log(struct fs_context *fc) +{ + fc->log = kzalloc(sizeof(*fc->log), GFP_KERNEL); + if (!fc->log) + return -ENOMEM; + refcount_set(&fc->log->usage, 1); + fc->log->owner = fc->fs_type->owner; + return 0; +} + /* * Open a filesystem by name so that it can be configured for mounting. * @@ -62,6 +119,7 @@ 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; @@ -84,5 +142,14 @@ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags) 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; } |