[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v1 2/6] xen: add basic hypervisor filesystem support



Add the infrastructure for the hypervisor filesystem.

This includes the hypercall interface and the base functions for
entry creation, deletion and modification.

Initially we support string and unsigned integer entry types. The saved
entry size is an upper bound, so for unsigned integer entries we always
set the value "11".

Signed-off-by: Juergen Gross <jgross@xxxxxxxx>
---
V1:
- rename files from filesystem.* to hypfs.*
- add dummy write entry support
- rename hypercall filesystem_op to hypfs_op
- add support for unsigned integer entries
---
 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/hypfs.c           | 314 +++++++++++++++++++++++++++++++++++++++++++
 xen/include/public/errno.h   |   1 +
 xen/include/public/hypfs.h   | 123 +++++++++++++++++
 xen/include/public/xen.h     |   1 +
 xen/include/xen/hypercall.h  |   8 ++
 xen/include/xen/hypfs.h      |  40 ++++++
 11 files changed, 492 insertions(+)
 create mode 100644 xen/common/hypfs.c
 create mode 100644 xen/include/public/hypfs.h
 create mode 100644 xen/include/xen/hypfs.h

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index a3b961bd06..4b1acd67a2 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(hypfs_op, 5),
 };
 
 #ifndef NDEBUG
diff --git a/xen/arch/x86/hvm/hypercall.c b/xen/arch/x86/hvm/hypercall.c
index 33dd2d99d2..210dda4f38 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(hypfs_op),
     HYPERCALL(arch_1)
 };
 
diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c
index d483dbaa6b..8a8f223481 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(hypfs_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..e6860247cc 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(hypfs_op),
     HYPERCALL(mca),
     HYPERCALL(arch_1),
 };
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 62b34e69e9..a3f66aa0c0 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 += hypfs.o
 obj-$(CONFIG_CRASH_DEBUG) += gdbstub.o
 obj-$(CONFIG_GRANT_TABLE) += grant_table.o
 obj-y += guestcopy.o
diff --git a/xen/common/hypfs.c b/xen/common/hypfs.c
new file mode 100644
index 0000000000..7d9595dfa7
--- /dev/null
+++ b/xen/common/hypfs.c
@@ -0,0 +1,314 @@
+/******************************************************************************
+ *
+ * hypfs.c
+ *
+ * Simple sysfs-like file system for the hypervisor.
+ */
+
+#include <xen/lib.h>
+#include <xen/hypfs.h>
+#include <xen/guest_access.h>
+#include <xen/hypercall.h>
+#include <public/hypfs.h>
+
+static DEFINE_SPINLOCK(hypfs_lock);
+
+struct hypfs_dir hypfs_root = {
+    .list = LIST_HEAD_INIT(hypfs_root.list),
+};
+
+static struct hypfs_entry hypfs_root_entry = {
+    .type = hypfs_type_dir,
+    .name = "",
+    .list = LIST_HEAD_INIT(hypfs_root_entry.list),
+    .parent = &hypfs_root,
+    .dir = &hypfs_root,
+};
+
+static int hypfs_add_entry(struct hypfs_dir *parent, struct hypfs_entry *new)
+{
+    int ret = -ENOENT;
+    struct list_head *l;
+
+    if ( !new->content )
+        return -EINVAL;
+
+    spin_lock(&hypfs_lock);
+
+    list_for_each ( l, &parent->list )
+    {
+        struct hypfs_entry *e = list_entry(l, struct hypfs_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_hypfs_direntry) +
+                                ROUNDUP(sz, 4);
+        new->parent = parent;
+    }
+
+    spin_unlock(&hypfs_lock);
+
+    return ret;
+}
+
+int hypfs_new_entry_any(struct hypfs_dir *parent, const char *name,
+                        enum hypfs_entry_type type, void *content)
+{
+    int ret = -ENOMEM;
+    struct hypfs_entry *new = xzalloc(struct hypfs_entry);
+
+    if ( !new )
+        return ret;
+
+    new->name = name;
+    new->type = type;
+    new->content = content;
+
+    ret = hypfs_add_entry(parent, new);
+
+    if ( ret )
+        xfree(new);
+
+    return ret;
+}
+
+int hypfs_new_entry_string(struct hypfs_dir *parent, const char *name,
+                           char *val)
+{
+    return hypfs_new_entry_any(parent, name, hypfs_type_string, val);
+}
+
+int hypfs_new_entry_uint(struct hypfs_dir *parent, const char *name,
+                         unsigned int *val)
+{
+    return hypfs_new_entry_any(parent, name, hypfs_type_uint, val);
+}
+
+int hypfs_new_dir(struct hypfs_dir *parent, const char *name,
+                  struct hypfs_dir *dir)
+{
+    if ( !dir )
+        dir = xzalloc(struct hypfs_dir);
+
+    return hypfs_new_entry_any(parent, name, hypfs_type_dir, dir);
+}
+
+static int hypfs_get_path_user(char *buf, XEN_GUEST_HANDLE_PARAM(void) uaddr,
+                               unsigned long len)
+{
+    if ( len > XEN_HYPFS_MAX_PATHLEN )
+        return -EINVAL;
+
+    if ( copy_from_guest(buf, uaddr, len) )
+        return -EFAULT;
+
+    buf[len - 1] = 0;
+
+    return 0;
+}
+
+static struct hypfs_entry *hypfs_get_entry_rel(struct hypfs_entry *dir,
+                                               char *path)
+{
+    char *slash;
+    struct hypfs_entry *entry;
+    struct list_head *l;
+    unsigned int name_len;
+
+    if ( *path == 0 )
+        return dir;
+
+    if ( dir->type != hypfs_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 hypfs_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 ? hypfs_get_entry_rel(entry, slash + 1) : entry;
+    }
+
+    return NULL;
+}
+
+struct hypfs_entry *hypfs_get_entry(char *path)
+{
+    if ( path[0] != '/' )
+        return NULL;
+
+    return hypfs_get_entry_rel(&hypfs_root_entry, path + 1);
+}
+
+static unsigned int hypfs_get_entry_len(struct hypfs_entry *entry)
+{
+    unsigned int len = 0;
+
+    switch ( entry->type )
+    {
+    case hypfs_type_dir:
+        len = entry->dir->content_size;
+        break;
+    case hypfs_type_string:
+        len = strlen(entry->str_val) + 1;
+        break;
+    case hypfs_type_uint:
+        len = 11;      /* longest possible printed value + 1 */
+        break;
+    }
+
+    return len;
+}
+
+long do_hypfs_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 hypfs_entry *entry;
+    unsigned int len;
+    static char path[XEN_HYPFS_MAX_PATHLEN];
+
+    if ( !is_control_domain(current->domain) &&
+         !is_hardware_domain(current->domain) )
+        return -EPERM;
+
+    spin_lock(&hypfs_lock);
+
+    ret = hypfs_get_path_user(path, arg1, arg2);
+    if ( ret )
+        goto out;
+
+    entry = hypfs_get_entry(path);
+    if ( !entry )
+    {
+        ret = -ENOENT;
+        goto out;
+    }
+
+    switch ( cmd )
+    {
+    case XEN_HYPFS_OP_read_contents:
+    {
+        char buf[12];
+        char *val = buf;
+
+        len = hypfs_get_entry_len(entry);
+        if ( len > arg4 )
+        {
+            ret = len;
+            break;
+        }
+
+        switch ( entry->type )
+        {
+        case hypfs_type_dir:
+            ret = -EISDIR;
+            break;
+        case hypfs_type_string:
+            val = entry->str_val;
+            break;
+        case hypfs_type_uint:
+            len = snprintf(buf, sizeof(buf), "%u", *entry->uint_val) + 1;
+            break;
+        }
+
+        if ( !ret && copy_to_guest(arg3, val, len) )
+            ret = -EFAULT;
+
+        break;
+    }
+
+    case XEN_HYPFS_OP_read_dir:
+    {
+        struct list_head *l;
+
+        if ( entry->type != hypfs_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_hypfs_direntry direntry;
+            struct hypfs_entry *e = list_entry(l, struct hypfs_entry, list);
+            unsigned int e_len = strlen(e->name) + 1;
+
+            e_len = sizeof(direntry) + ROUNDUP(e_len, 4);
+            direntry.flags = (e->type == hypfs_type_dir) ? XEN_HYPFS_ISDIR : 0;
+            direntry.off_next = list_is_last(l, &entry->dir->list) ? 0 : e_len;
+            direntry.content_len = hypfs_get_entry_len(e);
+            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;
+    }
+
+    case XEN_HYPFS_OP_write_contents:
+        ret = -EACCES;
+        break;
+
+    default:
+        ret = -ENOSYS;
+        break;
+    }
+
+ out:
+    spin_unlock(&hypfs_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/hypfs.h b/xen/include/public/hypfs.h
new file mode 100644
index 0000000000..8822e2fb15
--- /dev/null
+++ b/xen/include/public/hypfs.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+ * 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_HYPFS_H__
+#define __XEN_PUBLIC_HYPFS_H__
+
+#include "xen.h"
+
+/*
+ * Definitions for the __HYPERVISOR_hypfs_op hypercall.
+ */
+
+/* Maximum length of a path in the filesystem. */
+#define XEN_HYPFS_MAX_PATHLEN 1024
+
+struct xen_hypfs_direntry {
+    uint16_t flags;
+#define XEN_HYPFS_ISDIR      0x0001
+#define XEN_HYPFS_WRITEABLE  0x0002
+    /* 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_HYPFS_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_HYPFS_OP_read_contents     1
+
+/*
+ * XEN_HYPFS_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_HYPFS_OP_read_dir          2
+
+/*
+ * XEN_HYPFS_OP_read_contents
+ *
+ * Write contents of a filesystem entry.
+ *
+ * Writes an entry with the contents of a buffer supplied by the caller.
+ * Only text data with a trailing zero byte can be written.
+ *
+ * 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
+ * -ENOMEM:  memory shortage in the hypervisor
+ */
+#define XEN_HYPFS_OP_write_contents    3
+
+#endif /* __XEN_PUBLIC_HYPFS_H__ */
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index d2198dffad..bf80f1da8c 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_hypfs_op             42
 
 /* Architecture-specific hypercall definitions. */
 #define __HYPERVISOR_arch_0               48
diff --git a/xen/include/xen/hypercall.h b/xen/include/xen/hypercall.h
index ad8ad27b23..349a0f6487 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_hypfs_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
diff --git a/xen/include/xen/hypfs.h b/xen/include/xen/hypfs.h
new file mode 100644
index 0000000000..d2537211f6
--- /dev/null
+++ b/xen/include/xen/hypfs.h
@@ -0,0 +1,40 @@
+#ifndef __XEN_HYPFS_H__
+#define __XEN_HYPFS_H__
+
+#include <xen/list.h>
+
+struct hypfs_dir {
+    unsigned int content_size;
+    struct list_head list;
+};
+
+enum hypfs_entry_type {
+    hypfs_type_dir,
+    hypfs_type_string,
+    hypfs_type_uint
+};
+
+struct hypfs_entry {
+    enum hypfs_entry_type type;
+    const char *name;
+    struct list_head list;
+    struct hypfs_dir *parent;
+    union {
+        void *content;
+        struct hypfs_dir *dir;
+        char *str_val;
+        unsigned int *uint_val;
+    };
+};
+
+extern struct hypfs_dir hypfs_root;
+
+int hypfs_new_dir(struct hypfs_dir *parent, const char *name,
+                  struct hypfs_dir *dir);
+int hypfs_new_entry_string(struct hypfs_dir *parent, const char *name,
+                           char *val);
+int hypfs_new_entry_uint(struct hypfs_dir *parent, const char *name,
+                         unsigned int *val);
+struct hypfs_entry *hypfs_get_entry(char *path);
+
+#endif /* __XEN_HYPFS_H__ */
-- 
2.16.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.