From patchwork Wed Sep 11 06:19:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsO8cmdlbiBHcm/Dnw==?= X-Patchwork-Id: 11140335 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 DBFB614DB for ; Wed, 11 Sep 2019 06:22:26 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B660821A4C for ; Wed, 11 Sep 2019 06:22:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B660821A4C Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=suse.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1i7vzX-0003ii-6d; Wed, 11 Sep 2019 06:20:35 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1i7vzV-0003iE-R0 for xen-devel@lists.xenproject.org; Wed, 11 Sep 2019 06:20:33 +0000 X-Inumbo-ID: 32145fa2-d45c-11e9-978d-bc764e2007e4 Received: from mx1.suse.de (unknown [195.135.220.15]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 32145fa2-d45c-11e9-978d-bc764e2007e4; Wed, 11 Sep 2019 06:20:05 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id DBA73AF5B; Wed, 11 Sep 2019 06:20:04 +0000 (UTC) From: Juergen Gross To: xen-devel@lists.xenproject.org Date: Wed, 11 Sep 2019 08:19:58 +0200 Message-Id: <20190911062001.25931-3-jgross@suse.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20190911062001.25931-1-jgross@suse.com> References: <20190911062001.25931-1-jgross@suse.com> Subject: [Xen-devel] [RFC PATCH 2/5] xen: add basic hypervisor filesystem support X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Juergen Gross , Stefano Stabellini , Wei Liu , Konrad Rzeszutek Wilk , George Dunlap , Andrew Cooper , Ian Jackson , Tim Deegan , Julien Grall , Jan Beulich , Volodymyr Babchuk , =?utf-8?q?Roger_Pau_Monn?= =?utf-8?q?=C3=A9?= MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" Add the infrastructure for the hypervisor filesystem. This includes the hypercall interface and the base functions for entry creation, deletion and modification. Signed-off-by: Juergen Gross --- xen/arch/arm/traps.c | 1 + xen/arch/x86/hvm/hypercall.c | 1 + xen/arch/x86/hypercall.c | 1 + xen/arch/x86/pv/hypercall.c | 1 + xen/common/Makefile | 1 + xen/common/filesystem.c | 270 ++++++++++++++++++++++++++++++++++++++++ xen/include/public/errno.h | 1 + xen/include/public/filesystem.h | 98 +++++++++++++++ xen/include/public/xen.h | 1 + xen/include/xen/filesystem.h | 34 +++++ xen/include/xen/hypercall.h | 8 ++ 11 files changed, 417 insertions(+) create mode 100644 xen/common/filesystem.c create mode 100644 xen/include/public/filesystem.h create mode 100644 xen/include/xen/filesystem.h diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c index a3b961bd06..44b027951f 100644 --- a/xen/arch/arm/traps.c +++ b/xen/arch/arm/traps.c @@ -1401,6 +1401,7 @@ static arm_hypercall_t arm_hypercall_table[] = { #ifdef CONFIG_ARGO HYPERCALL(argo_op, 5), #endif + HYPERCALL(filesystem_op, 5), }; #ifndef NDEBUG diff --git a/xen/arch/x86/hvm/hypercall.c b/xen/arch/x86/hvm/hypercall.c index 33dd2d99d2..936ad10add 100644 --- a/xen/arch/x86/hvm/hypercall.c +++ b/xen/arch/x86/hvm/hypercall.c @@ -144,6 +144,7 @@ static const hypercall_table_t hvm_hypercall_table[] = { #endif HYPERCALL(xenpmu_op), COMPAT_CALL(dm_op), + HYPERCALL(filesystem_op), HYPERCALL(arch_1) }; diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c index d483dbaa6b..640347e514 100644 --- a/xen/arch/x86/hypercall.c +++ b/xen/arch/x86/hypercall.c @@ -73,6 +73,7 @@ const hypercall_args_t hypercall_args_table[NR_hypercalls] = ARGS(hvm_op, 2), ARGS(dm_op, 3), #endif + ARGS(filesystem_op, 5), ARGS(mca, 1), ARGS(arch_1, 1), }; diff --git a/xen/arch/x86/pv/hypercall.c b/xen/arch/x86/pv/hypercall.c index 0c84c0b3a0..27c8e893c6 100644 --- a/xen/arch/x86/pv/hypercall.c +++ b/xen/arch/x86/pv/hypercall.c @@ -84,6 +84,7 @@ const hypercall_table_t pv_hypercall_table[] = { HYPERCALL(hvm_op), COMPAT_CALL(dm_op), #endif + HYPERCALL(filesystem_op), HYPERCALL(mca), HYPERCALL(arch_1), }; diff --git a/xen/common/Makefile b/xen/common/Makefile index 62b34e69e9..be43757dff 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -11,6 +11,7 @@ obj-y += domain.o obj-y += event_2l.o obj-y += event_channel.o obj-y += event_fifo.o +obj-y += filesystem.o obj-$(CONFIG_CRASH_DEBUG) += gdbstub.o obj-$(CONFIG_GRANT_TABLE) += grant_table.o obj-y += guestcopy.o diff --git a/xen/common/filesystem.c b/xen/common/filesystem.c new file mode 100644 index 0000000000..d833992ab1 --- /dev/null +++ b/xen/common/filesystem.c @@ -0,0 +1,270 @@ +/****************************************************************************** + * + * filesystem.c + * + * Simple sysfs-like file system for the hypervisor. + */ + +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(fs_lock); + +struct fs_dir fs_root = { + .list = LIST_HEAD_INIT(fs_root.list), +}; + +static struct fs_entry fs_root_entry = { + .type = fs_type_dir, + .name = "", + .list = LIST_HEAD_INIT(fs_root_entry.list), + .parent = &fs_root, + .dir = &fs_root, +}; + +static int fs_add_entry(struct fs_dir *parent, struct fs_entry *new) +{ + int ret = -ENOENT; + struct list_head *l; + + if ( !new->content ) + return -EINVAL; + + spin_lock(&fs_lock); + + list_for_each ( l, &parent->list ) + { + struct fs_entry *e = list_entry(l, struct fs_entry, list); + int cmp = strcmp(e->name, new->name); + + if ( cmp < 0 ) + { + ret = 0; + list_add_tail(&new->list, l); + break; + } + if ( cmp == 0 ) + { + ret = -EEXIST; + break; + } + } + + if ( ret == -ENOENT ) + { + ret = 0; + list_add_tail(&new->list, &parent->list); + } + + if ( !ret ) + { + unsigned int sz = strlen(new->name) + 1; + + parent->content_size += sizeof(struct xen_fs_direntry) + ROUNDUP(sz, 4); + new->parent = parent; + } + + spin_unlock(&fs_lock); + + return ret; +} + +int fs_new_entry_any(struct fs_dir *parent, const char *name, + enum fs_entry_type type, void *content) +{ + int ret = -ENOMEM; + struct fs_entry *new = xzalloc(struct fs_entry); + + if ( !new ) + return ret; + + new->name = name; + new->type = type; + new->content = content; + + ret = fs_add_entry(parent, new); + + if ( ret ) + xfree(new); + + return ret; +} + +int fs_new_entry(struct fs_dir *parent, const char *name, const char *val) +{ + return fs_new_entry_any(parent, name, fs_type_string, (void *)val); +} + +int fs_new_dir(struct fs_dir *parent, const char *name, struct fs_dir *dir) +{ + if ( !dir ) + dir = xzalloc(struct fs_dir); + + return fs_new_entry_any(parent, name, fs_type_dir, dir); +} + +static int fs_get_path_user(char *buf, XEN_GUEST_HANDLE_PARAM(void) uaddr, + unsigned long len) +{ + if ( len > XEN_FS_MAX_PATHLEN ) + return -EINVAL; + + if ( copy_from_guest(buf, uaddr, len) ) + return -EFAULT; + + buf[len - 1] = 0; + + return 0; +} + +static struct fs_entry *fs_get_entry_rel(struct fs_entry *dir, char *path) +{ + char *slash; + struct fs_entry *entry; + struct list_head *l; + unsigned int name_len; + + if ( *path == 0 ) + return dir; + + if ( dir->type != fs_type_dir ) + return NULL; + + slash = strchr(path, '/'); + if ( !slash ) + slash = strchr(path, '\0'); + name_len = slash - path; + + list_for_each ( l, &dir->dir->list ) + { + int cmp; + + entry = list_entry(l, struct fs_entry, list); + cmp = strncmp(path, entry->name, name_len); + if ( cmp < 0 ) + return NULL; + if ( cmp > 0 ) + continue; + if ( strlen(entry->name) == name_len ) + return *slash ? fs_get_entry_rel(entry, slash + 1) : entry; + } + + return NULL; +} + +struct fs_entry *fs_get_entry(char *path) +{ + if ( path[0] != '/' ) + return NULL; + + return fs_get_entry_rel(&fs_root_entry, path + 1); +} + +long do_filesystem_op(unsigned int cmd, + XEN_GUEST_HANDLE_PARAM(void) arg1, unsigned long arg2, + XEN_GUEST_HANDLE_PARAM(void) arg3, unsigned long arg4) +{ + int ret; + struct fs_entry *entry; + unsigned int len; + static char path[XEN_FS_MAX_PATHLEN]; + + if ( !is_control_domain(current->domain) && + !is_hardware_domain(current->domain) ) + return -EPERM; + + spin_lock(&fs_lock); + + ret = fs_get_path_user(path, arg1, arg2); + if ( ret ) + goto out; + + entry = fs_get_entry(path); + if ( !entry ) + { + ret = -ENOENT; + goto out; + } + + switch ( cmd ) + { + case XEN_FS_OP_read_contents: + if ( entry->type == fs_type_dir ) + { + ret = -EISDIR; + break; + } + + len = strlen(entry->val) + 1; + if ( len > arg4 ) + { + ret = len; + break; + } + + if ( copy_to_guest(arg3, entry->val, len) ) + ret = -EFAULT; + + break; + + case XEN_FS_OP_read_dir: + { + struct list_head *l; + + if ( entry->type != fs_type_dir ) + { + ret = -ENOTDIR; + break; + } + + len = entry->dir->content_size; + if ( len > arg4 ) + { + ret = len; + break; + } + + list_for_each ( l, &entry->dir->list ) + { + struct xen_fs_direntry direntry; + struct fs_entry *e = list_entry(l, struct fs_entry, list); + unsigned int e_len = strlen(e->name) + 1; + + e_len = sizeof(direntry) + ROUNDUP(e_len, 4); + direntry.flags = (e->type == fs_type_dir) ? XEN_FS_ISDIR : 0; + direntry.off_next = list_is_last(l, &entry->dir->list) ? 0 : e_len; + direntry.content_len = (e->type == fs_type_dir) + ? e->dir->content_size + : strlen(e->val) + 1; + if ( copy_to_guest(arg3, &direntry, 1) ) + { + ret = -EFAULT; + goto out; + } + + if ( copy_to_guest_offset(arg3, sizeof(direntry), e->name, + strlen(e->name) + 1) ) + { + ret = -EFAULT; + goto out; + } + + guest_handle_add_offset(arg3, e_len); + } + + break; + } + + default: + ret = -ENOSYS; + break; + } + + out: + spin_unlock(&fs_lock); + + return ret; +} diff --git a/xen/include/public/errno.h b/xen/include/public/errno.h index e1d02fcddf..5c53af6af9 100644 --- a/xen/include/public/errno.h +++ b/xen/include/public/errno.h @@ -78,6 +78,7 @@ XEN_ERRNO(EBUSY, 16) /* Device or resource busy */ XEN_ERRNO(EEXIST, 17) /* File exists */ XEN_ERRNO(EXDEV, 18) /* Cross-device link */ XEN_ERRNO(ENODEV, 19) /* No such device */ +XEN_ERRNO(ENOTDIR, 20) /* Not a directory */ XEN_ERRNO(EISDIR, 21) /* Is a directory */ XEN_ERRNO(EINVAL, 22) /* Invalid argument */ XEN_ERRNO(ENFILE, 23) /* File table overflow */ diff --git a/xen/include/public/filesystem.h b/xen/include/public/filesystem.h new file mode 100644 index 0000000000..cc790ae490 --- /dev/null +++ b/xen/include/public/filesystem.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * Xen Hypervisor Filesystem + * + * Copyright (c) 2019, SUSE Software Solutions Germany GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __XEN_PUBLIC_FILESYSTEM_H__ +#define __XEN_PUBLIC_FILESYSTEM_H__ + +#include "xen.h" + +/* + * Definitions for the __HYPERVISOR_filesystem_op hypercall. + */ + +/* Maximum length of a path in the filesystem. */ +#define XEN_FS_MAX_PATHLEN 1024 + +struct xen_fs_direntry { + uint16_t flags; +#define XEN_FS_ISDIR 0x0001 + /* Offset in bytes to next entry (0 == this is the last entry). */ + uint16_t off_next; + uint32_t content_len; + char name[XEN_FLEX_ARRAY_DIM]; +}; + +/* + * Hypercall operations. + */ + +/* + * XEN_FS_OP_read_contents + * + * Read contents of a filesystem entry. + * + * Returns the contents of an entry in the buffer supplied by the caller. + * Only text data with a trailing zero byte is returned. + * + * arg1: XEN_GUEST_HANDLE(path name) + * arg2: length of path name (including trailing zero byte) + * arg3: XEN_GUEST_HANDLE(content buffer) + * arg4: content buffer size + * + * Possible return values: + * 0: success + * -EPERM: operation not permitted + * -ENOENT: entry not found + * -EACCESS: access to entry not permitted + * -EISDIR: entry is a directory + * -EINVAL: invalid parameter + * positive value: content buffer was too small, returned value is needed size + */ +#define XEN_FS_OP_read_contents 1 + +/* + * XEN_FS_OP_read_dir + * + * Read directory entries of a directory. + * + * Returns a struct xen_fs_direntry for each entry in a directory. + * + * arg1: XEN_GUEST_HANDLE(path name) + * arg2: length of path name (including trailing zero byte) + * arg3: XEN_GUEST_HANDLE(content buffer) + * arg4: content buffer size + * + * Possible return values: + * 0: success + * -EPERM: operation not permitted + * -ENOENT: entry not found + * -EACCESS: access to entry not permitted + * -ENOTDIR: entry is not a directory + * -EINVAL: invalid parameter + * positive value: content buffer was too small, returned value is needed size + */ +#define XEN_FS_OP_read_dir 2 + +#endif /* __XEN_PUBLIC_FILESYSTEM_H__ */ diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h index cef65c38e7..232589a95b 100644 --- a/xen/include/public/xen.h +++ b/xen/include/public/xen.h @@ -130,6 +130,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); #define __HYPERVISOR_argo_op 39 #define __HYPERVISOR_xenpmu_op 40 #define __HYPERVISOR_dm_op 41 +#define __HYPERVISOR_filesystem_op 42 /* Architecture-specific hypercall definitions. */ #define __HYPERVISOR_arch_0 48 diff --git a/xen/include/xen/filesystem.h b/xen/include/xen/filesystem.h new file mode 100644 index 0000000000..24e0e2c62d --- /dev/null +++ b/xen/include/xen/filesystem.h @@ -0,0 +1,34 @@ +#ifndef __XEN_FILESYSTEM_H__ +#define __XEN_FILESYSTEM_H__ + +#include + +struct fs_dir { + unsigned int content_size; + struct list_head list; +}; + +enum fs_entry_type { + fs_type_dir, + fs_type_string +}; + +struct fs_entry { + enum fs_entry_type type; + const char *name; + struct list_head list; + struct fs_dir *parent; + union { + void *content; + struct fs_dir *dir; + const char *val; + }; +}; + +extern struct fs_dir fs_root; + +int fs_new_dir(struct fs_dir *parent, const char *name, struct fs_dir *dir); +int fs_new_entry(struct fs_dir *parent, const char *name, const char *val); +struct fs_entry *fs_get_entry(char *path); + +#endif /* __XEN_FILESYSTEM_H__ */ diff --git a/xen/include/xen/hypercall.h b/xen/include/xen/hypercall.h index ad8ad27b23..56129ef82f 100644 --- a/xen/include/xen/hypercall.h +++ b/xen/include/xen/hypercall.h @@ -150,6 +150,14 @@ do_dm_op( unsigned int nr_bufs, XEN_GUEST_HANDLE_PARAM(xen_dm_op_buf_t) bufs); +extern long +do_filesystem_op( + unsigned int cmd, + XEN_GUEST_HANDLE_PARAM(void) arg1, + unsigned long arg2, + XEN_GUEST_HANDLE_PARAM(void) arg3, + unsigned long arg4); + #ifdef CONFIG_COMPAT extern int