@@ -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
@@ -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)
};
@@ -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),
};
@@ -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),
};
@@ -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
new file mode 100644
@@ -0,0 +1,270 @@
+/******************************************************************************
+ *
+ * filesystem.c
+ *
+ * Simple sysfs-like file system for the hypervisor.
+ */
+
+#include <xen/lib.h>
+#include <xen/filesystem.h>
+#include <xen/guest_access.h>
+#include <xen/hypercall.h>
+#include <public/filesystem.h>
+
+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;
+}
@@ -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 */
new file mode 100644
@@ -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__ */
@@ -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
new file mode 100644
@@ -0,0 +1,34 @@
+#ifndef __XEN_FILESYSTEM_H__
+#define __XEN_FILESYSTEM_H__
+
+#include <xen/list.h>
+
+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__ */
@@ -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
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 <jgross@suse.com> --- 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