[WIP RFC PATCH 2/6] fwvarfs: a generic firmware variable filesystem
Daniel Axtens
dja at axtens.net
Mon May 20 16:25:49 AEST 2019
Sometimes it is helpful to be able to access firmware variables as
file, like efivarfs, but not all firmware is EFI. Create a framework
that allows generic access to firmware variables exposed by a
implementations of a simple backend API.
Add a demonstration memory-based backend.
Signed-off-by: Daniel Axtens <dja at axtens.net>
---
Documentation/filesystems/fwvarfs.txt | 146 +++++++++++++
fs/Kconfig | 1 +
fs/Makefile | 1 +
fs/fwvarfs/Kconfig | 25 +++
fs/fwvarfs/Makefile | 8 +
fs/fwvarfs/fwvarfs.c | 289 ++++++++++++++++++++++++++
fs/fwvarfs/fwvarfs.h | 116 +++++++++++
fs/fwvarfs/mem.c | 113 ++++++++++
fs/kernfs/dir.c | 1 -
include/uapi/linux/magic.h | 1 +
10 files changed, 700 insertions(+), 1 deletion(-)
create mode 100644 Documentation/filesystems/fwvarfs.txt
create mode 100644 fs/fwvarfs/Kconfig
create mode 100644 fs/fwvarfs/Makefile
create mode 100644 fs/fwvarfs/fwvarfs.c
create mode 100644 fs/fwvarfs/fwvarfs.h
create mode 100644 fs/fwvarfs/mem.c
diff --git a/Documentation/filesystems/fwvarfs.txt b/Documentation/filesystems/fwvarfs.txt
new file mode 100644
index 000000000000..bf1bccba6ab9
--- /dev/null
+++ b/Documentation/filesystems/fwvarfs.txt
@@ -0,0 +1,146 @@
+fwvarfs
+=======
+
+fwvarfs is a generic firmware variable file-system. A platform
+provides a backend implementing a few very simple callbacks, and
+fwvarfs handles all the details required to present the variables as a
+filesystem.
+
+The minimum functionality for a backend is the ability to enumerate
+existing variables. Backends can optionally also allow:
+ - reading of variables
+ - writing of variables
+ - creation of variables
+ - deletion of variables
+
+Key assumptions
+---------------
+
+ * Variables for each backend live in a single, flat directory -
+ there's no concept of subdirectories.
+
+ * Files are created with mode 600 if the backend provides a write
+ hook, and 400 otherwise, and are owned by root:root.
+
+ * The set of variables stored can be determined at boot time, and
+ nothing outside of fwvarfs creates new variables after boot.
+
+Supported backends
+------------------
+
+ * mem - a memory-backed example filesystem supporting all
+ operations. Files created persist across mount/unmount but as no
+ hardware is involved they do not persist across reboots.
+
+Usage
+-----
+
+mount -t fwvarfs <backend> <dir>
+
+For example:
+
+mount -t fwvarfs mem /fw/mem/
+
+API
+---
+
+A backend is installed by creating a fwvarfs_backend struct,
+containing the name of the backend and various callbacks. The backend
+must be registered by adding it to the list at the top of
+fs/fwvarfs/fwvarfs.c
+
+The fwvarfs infrastructure provides the following function to backends:
+
+ int fwvarfs_register_var(struct fwvarfs_backend *backend, const char *name, void *variable, size_t size);
+
+ Register a variable with fwvarfs to allow it to be seen by users.
+
+ backend: the backend to which to add this variable
+
+ name: the name of file representing the variable. Must be a valid
+ filename, so no nulls or slashes.
+
+ variable: data private to the backend representing the variable -
+ will be passed back to every callback
+
+ size: the initial size of the variable
+
+
+The backend must then provide the following functions:
+
+ int (*enumerate)(void);
+
+ Mandatory and called at init time, a backend must call
+ fwvarfs_register_var for all variables it wants to expose to
+ the user.
+
+ void (*destroy)(void *variable);
+
+ Mandatory if you provide a create or unlink hook, and may
+ become mandatory in the future for cleanup.
+
+ Free backend data associated with variable. It will not be
+ referenced after this point by fwvarfs.
+
+
+ ssize_t (*read)(void *variable, char *dest, size_t bytes, loff_t off);
+
+ Read from variable into the a kernel buffer. Similar semantics
+ to a usual read operation, except that off is not a pointer
+ (unlike the usual ppos).
+
+ variable: the variable to read
+ dest: kernel buffer to read into
+ bytes: maximum number of bytes to read
+ off: offset to read from
+
+ Returns the number of bytes read or an error.
+ If this hook is not provided, all reads will fail with -EPERM.
+
+ ssize_t (*write)(void *variable, const char *src, size_t bytes, loff_t off);
+
+ Write into the variable from the given kernel buffer.
+
+ variable: the variable to write
+ src: kernel buffer with contents
+ bytes: write at most this many bytes
+ off: offset into the file to write at.
+
+ Returns the number of bytes written or an error.
+ If this hook is not provided, all writes will fail with -EPERM.
+
+
+ void* (*create)(const char *name);
+
+ Create a variable with the supplied name, and return the
+ associated private data or an error pointer. Do not return
+ NULL on failure.
+
+ If the variable created cannot be registered for any reason,
+ destroy() will be called on the variable returned.
+
+ If the hook is not provided, all attempts to create a file will
+ fail with -EPERM.
+
+ int (*unlink)(void *variable);
+
+ Delete the variable supplied from the backing store. Do not
+ free it yet, if you return success destroy() will be called on
+ the variable.
+
+ If an error is returned, the unlink will be aborted and the file
+ will still be present in the filesystem.
+
+ If the hook is not provided, all attempts to unlink a file will
+ fail with -EPERM.
+
+TODOs
+-----
+
+Perhaps a different registration scheme?
+Currently size is not updated after write
+Should standardise on whether writes must cover the whole file if partial writes are supported.
+Various TODOs in the code
+Convert API documentation to kerndoc
+perhaps better cleanup/removal, although kernfs doesn't seem to provide anything for this so difficult to do with out leaking memory
+check error handling with kernfs create and O_EXCL
diff --git a/fs/Kconfig b/fs/Kconfig
index cbbffc8b9ef5..6fb6e6cbd7b6 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -219,6 +219,7 @@ config ARCH_HAS_GIGANTIC_PAGE
source "fs/configfs/Kconfig"
source "fs/efivarfs/Kconfig"
+source "fs/fwvarfs/Kconfig"
endmenu
diff --git a/fs/Makefile b/fs/Makefile
index c9aea23aba56..2a0c593dfc0f 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -130,3 +130,4 @@ obj-$(CONFIG_F2FS_FS) += f2fs/
obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
+obj-$(CONFIG_FWVAR_FS) += fwvarfs/
diff --git a/fs/fwvarfs/Kconfig b/fs/fwvarfs/Kconfig
new file mode 100644
index 000000000000..62a47cddd4b5
--- /dev/null
+++ b/fs/fwvarfs/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config FWVAR_FS
+ bool "Generic Firmware Variable Filesystem"
+ help
+ fwvarfs is a generic file system for access to firmware
+ variables.
+
+ It is pluggable: you will need to select a backend below in
+ order to actually access anything.
+
+ This cannot currently be built as a module. (TODO: see if
+ kernfs can be exported or if there are technical obstacles.)
+
+ If unsure, say N.
+
+config FWVAR_FS_MEM_BACKEND
+ bool "In-memory testing backend"
+ depends on FWVAR_FS
+ help
+ Include a backend where firmware variables are just
+ elements of an in-memory list. This is helpful mostly as a
+ demonstration of fwvarfs.
+
+ You can safely say N here unless you're exploring fwvarfs.
diff --git a/fs/fwvarfs/Makefile b/fs/fwvarfs/Makefile
new file mode 100644
index 000000000000..f1585baccabe
--- /dev/null
+++ b/fs/fwvarfs/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the fwvarfs filesytem
+#
+
+obj-$(CONFIG_FWVAR_FS) += fwvarfs.o
+
+obj-$(CONFIG_FWVAR_FS_MEM_BACKEND) += mem.o
diff --git a/fs/fwvarfs/fwvarfs.c b/fs/fwvarfs/fwvarfs.c
new file mode 100644
index 000000000000..99b7f2fd0f14
--- /dev/null
+++ b/fs/fwvarfs/fwvarfs.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Daniel Axtens
+ *
+ * Thanks to efivarfs, rdt, and cgroupfs for the kernfs example.
+ */
+
+#include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/kernfs.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/user_namespace.h>
+#include <uapi/linux/magic.h>
+#include "fwvarfs.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+static struct fwvarfs_backend *fwvarfs_backends[] = {
+#if CONFIG_FWVAR_FS_MEM_BACKEND
+ &fwvarfs_mem_backend,
+#endif
+
+ NULL,
+};
+
+struct fwvarfs_file {
+ struct kernfs_node *kn;
+ struct fwvarfs_backend *backend;
+ void *backend_data;
+};
+
+static ssize_t fwvarfs_file_read(struct kernfs_open_file *of, char *buf,
+ size_t bytes, loff_t off)
+{
+ struct fwvarfs_file *file_data = of->kn->priv;
+
+ if (file_data->backend->read)
+ return file_data->backend->read(file_data->backend_data, buf,
+ bytes, off);
+ else
+ return -EPERM;
+}
+
+static ssize_t fwvarfs_file_write(struct kernfs_open_file *of, char *buf,
+ size_t bytes, loff_t off)
+{
+ struct fwvarfs_file *file_data = of->kn->priv;
+
+ if (file_data->backend->write)
+ return file_data->backend->write(file_data->backend_data, buf,
+ bytes, off);
+ else
+ return -EPERM;
+}
+
+
+static struct kernfs_ops fwvarfs_kf_ops = {
+ .atomic_write_len = PAGE_SIZE,
+ .read = fwvarfs_file_read,
+ .write = fwvarfs_file_write,
+};
+
+struct kernfs_node *fwvarfs_create(struct kernfs_node *parent,
+ const char *name, umode_t mode)
+{
+ struct kernfs_node *kn;
+ struct fwvarfs_file *parent_file = parent->priv;
+ struct fwvarfs_file *file_data;
+ void *backend_data;
+
+ if (!parent_file->backend->create)
+ return ERR_PTR(-EPERM);
+
+ file_data = kzalloc(sizeof(struct fwvarfs_file), GFP_KERNEL);
+ if (!file_data)
+ return ERR_PTR(-ENOMEM);
+
+ file_data->backend = parent_file->backend;
+
+ backend_data = parent_file->backend->create(name);
+
+ if (IS_ERR(backend_data)) {
+ kfree(file_data);
+ return backend_data;
+ }
+
+ file_data->backend_data = backend_data;
+
+ kn = kernfs_create_file(parent, name,
+ (!!parent_file->backend->write ? 0600 : 0400), 0,
+ &fwvarfs_kf_ops, file_data);
+
+ if (IS_ERR(kn)) {
+ parent_file->backend->destroy(backend_data);
+ kfree(file_data);
+ return kn;
+ }
+
+ file_data->kn = kn;
+
+ return kn;
+}
+
+int fwvarfs_unlink(struct kernfs_node *kn)
+{
+
+ struct fwvarfs_file *file_data = kn->priv;
+ int ret;
+
+ if (!file_data->backend->unlink)
+ return -EPERM;
+
+ ret = file_data->backend->unlink(file_data->backend_data);
+
+ if (ret)
+ return ret;
+
+ kernfs_remove(file_data->kn);
+
+ file_data->backend->destroy(file_data->backend_data);
+
+ kfree(file_data);
+ return 0;
+}
+
+static struct kernfs_syscall_ops fwvarfs_scops = {
+ .create = fwvarfs_create,
+ .unlink = fwvarfs_unlink,
+};
+
+int fwvarfs_register_var(struct fwvarfs_backend *backend, const char *name,
+ void *variable, size_t size)
+{
+ struct fwvarfs_file *file_data;
+ struct kernfs_node *kn;
+
+ file_data = kzalloc(sizeof(struct fwvarfs_file), GFP_KERNEL);
+ if (!file_data)
+ return -ENOMEM;
+
+ file_data->backend = backend;
+ file_data->backend_data = variable;
+
+ kn = kernfs_create_file(backend->kf_root->kn, name,
+ (!!backend->write ? 0600 : 0400), size,
+ &fwvarfs_kf_ops, file_data);
+
+ if (IS_ERR(kn)) {
+ kfree(file_data);
+ return PTR_ERR(kn);
+ }
+
+ file_data->kn = kn;
+
+ return 0;
+
+}
+
+static int fwvarfs_get_tree(struct fs_context *fc)
+{
+ int ret = -ENODEV;
+ struct fwvarfs_backend *backend;
+ struct kernfs_fs_context *kfc = fc->fs_private;
+ int i;
+
+ for (i = 0; (backend = fwvarfs_backends[i]); i++) {
+ if (!backend->is_active)
+ continue;
+
+ if (strcasecmp(fc->source, backend->name) == 0) {
+ kfc->root = backend->kf_root;
+ ret = 0;
+ }
+ }
+ if (ret)
+ return ret;
+
+ return kernfs_get_tree(fc);
+}
+
+static void fwvarfs_free_fs_context(struct fs_context *fc)
+{
+ kernfs_free_fs_context(fc);
+ kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations fwvarfs_fs_context_ops = {
+ .get_tree = fwvarfs_get_tree,
+ .free = fwvarfs_free_fs_context,
+};
+
+static int fwvarfs_init_fs_context(struct fs_context *fc)
+{
+ struct kernfs_fs_context *ctx;
+
+ ctx = kzalloc(sizeof(struct kernfs_fs_context), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->magic = FWVARFS_SUPER_MAGIC;
+ fc->fs_private = ctx;
+
+ fc->ops = &fwvarfs_fs_context_ops;
+ if (fc->user_ns)
+ put_user_ns(fc->user_ns);
+ fc->user_ns = get_user_ns(&init_user_ns);
+ fc->global = true;
+ return 0;
+}
+
+static struct file_system_type fwvarfs_type = {
+ .owner = THIS_MODULE,
+ .name = "fwvarfs",
+ .init_fs_context = fwvarfs_init_fs_context,
+ .kill_sb = kernfs_kill_sb,
+};
+
+static __init int fwvarfs_init(void)
+{
+ struct fwvarfs_backend *backend;
+ struct fwvarfs_file *file_data;
+ int ret, i;
+
+ for (i = 0; (backend = fwvarfs_backends[i]); i++) {
+ file_data = kzalloc(sizeof(struct fwvarfs_file), GFP_KERNEL);
+ if (!file_data)
+ return -ENOMEM;
+
+ file_data->backend = backend;
+
+ backend->kf_root = kernfs_create_root(&fwvarfs_scops,
+ KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
+ file_data);
+
+ if (IS_ERR(backend->kf_root)) {
+ pr_err("kernfs_create_root failed for %s: %ld",
+ backend->name, PTR_ERR(backend->kf_root));
+ kfree(file_data);
+ continue;
+ }
+
+ file_data->kn = backend->kf_root->kn;
+
+ ret = backend->enumerate();
+ if (ret) {
+ pr_err("enumerate failed for %s: %d",
+ backend->name, ret);
+
+ /*
+ * TODO: we make no attempt to clean up partially
+ * created files at this point
+ */
+ kernfs_destroy_root(backend->kf_root);
+ kfree(file_data);
+ continue;
+ }
+
+ backend->is_active = true;
+ }
+
+ return register_filesystem(&fwvarfs_type);
+}
+
+
+/*
+ * kernfs doesn't support being called from a module atm
+ * and we also have no obvious way to remove all the created variables, so
+ * atm even if you did this you would leak memory. TODO
+ * static __exit void fwvarfs_exit(void)
+ * {
+ * unregister_filesystem(&fwvarfs_type);
+ * }
+ */
+
+
+/*
+ * again, kernfs blocks module-ising this atm but it's still a neat way
+ * to handle initialisation
+ */
+MODULE_AUTHOR("Daniel Axtens");
+MODULE_DESCRIPTION("Generic Firmware Variable Filesystem");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_FS("fwvarfs");
+
+module_init(fwvarfs_init);
+/* module_exit(fwvarfs_exit); */
diff --git a/fs/fwvarfs/fwvarfs.h b/fs/fwvarfs/fwvarfs.h
new file mode 100644
index 000000000000..b2944a3baaf7
--- /dev/null
+++ b/fs/fwvarfs/fwvarfs.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Daniel Axtens
+ */
+
+
+#ifndef FWVARFS_H
+#define FWVARFS_H
+
+#include <linux/kernfs.h>
+
+struct fwvarfs_backend {
+ /* name of backend */
+ const char *name;
+
+ /*
+ * Mandatory and called at init time, a backend must call
+ * fwvarfs_register_var for all variables it wants to expose to
+ * the user.
+ */
+ int (*enumerate)(void);
+
+ /*
+ * Mandatory if you provide a create or unlink hook, and may
+ * become mandatory in the future for cleanup.
+ *
+ * Free backend data associated with variable. It will not be
+ * referenced after this point by fwvarfs.
+ */
+ void (*destroy)(void *variable);
+
+ /*
+ * Read from variable into the a kernel buffer. Similar semantics
+ * to a usual read operation, except that off is not a pointer
+ * (unlike the usual ppos).
+ *
+ * variable: the variable to read
+ * dest: kernel buffer to read into
+ * bytes: maximum number of bytes to read
+ * off: offset to read from
+ *
+ * Returns the number of bytes read or an error.
+ * If this hook is not provided, all reads will fail with -EPERM.
+ */
+ ssize_t (*read)(void *variable, char *dest, size_t bytes, loff_t off);
+
+ /*
+ * Write into the variable from the given kernel buffer.
+ *
+ * variable: the variable to write
+ * src: kernel buffer with contents
+ * bytes: write at most this many bytes
+ * off: offset into the file to write at.
+ *
+ * Returns the number of bytes written or an error.
+ * If this hook is not provided, all writes will fail with -EPERM.
+ */
+ ssize_t (*write)(void *variable, const char *src, size_t bytes,
+ loff_t off);
+
+ /*
+ * Create a variable with the supplied name, and return the
+ * associated private data or an error pointer. Do not return
+ * NULL on failure.
+ *
+ * If the variable created cannot be registered for any reason,
+ * destroy() will be called on the variable returned.
+ *
+ * If the hook is not provided, all attempts to create a file will
+ * fail with -EPERM.
+ */
+ void* (*create)(const char *name);
+
+ /*
+ * Delete the variable supplied from the backing store. Do not
+ * free it yet, if you return success destroy() will be called on
+ * the variable.
+ *
+ * If an error is returned, the unlink will be aborted and the file
+ * will still be present in the filesystem.
+ *
+ * If the hook is not provided, all attempts to unlink a file will
+ * fail with -EPERM.
+ */
+ int (*unlink)(void *variable);
+
+ /* private to fwvarfs generic code */
+ struct kernfs_root *kf_root;
+ /* did enumerate succeed? */
+ bool is_active;
+};
+
+/*
+ * Register a variable with fwvarfs to allow it to be seen by users.
+ *
+ * backend: the backend to which to add this variable
+ *
+ * name: the name of file representing the variable. Must be a valid
+ * filename, so no nulls or slashes.
+ *
+ * variable: data private to the backend representing the variable -
+ * will be passed back to every callback
+ *
+ * size: the initial size of the variable
+ */
+int fwvarfs_register_var(struct fwvarfs_backend *backend, const char *name,
+ void *variable, size_t size);
+
+
+/* Backends go here */
+#if defined(CONFIG_FWVAR_FS_MEM_BACKEND)
+extern struct fwvarfs_backend fwvarfs_mem_backend;
+#endif
+
+#endif /* FWVARFS_H */
diff --git a/fs/fwvarfs/mem.c b/fs/fwvarfs/mem.c
new file mode 100644
index 000000000000..5c90ea856f8e
--- /dev/null
+++ b/fs/fwvarfs/mem.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Daniel Axtens
+ *
+ * Thanks to efivarfs, and cgroupfs for the kernfs example.
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "fwvarfs.h"
+
+static LIST_HEAD(mem_file_list);
+
+struct fwvarfs_mem_file {
+ size_t length;
+ char data[PAGE_SIZE];
+ struct list_head list;
+};
+
+static ssize_t fwvarfs_mem_file_read(void *var, char *buf, size_t bytes,
+ loff_t off)
+{
+ struct fwvarfs_mem_file *file_data = var;
+ loff_t ppos = off;
+
+ return memory_read_from_buffer(buf, bytes, &ppos, file_data->data,
+ file_data->length);
+}
+
+static ssize_t simple_write_to_kernel_buffer(void *to, size_t available,
+ loff_t *ppos, const void *from,
+ size_t count)
+{
+ loff_t pos = *ppos;
+
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= available)
+ return -ENOSPC;
+ if (!count)
+ return 0;
+ if (count > available - pos)
+ count = available - pos;
+ memcpy(to, from, count);
+ *ppos = pos + count;
+ return count;
+}
+
+static ssize_t fwvarfs_mem_file_write(void *var, const char *buf,
+ size_t bytes, loff_t off)
+{
+ struct fwvarfs_mem_file *file_data = var;
+ loff_t ppos = off;
+ int rc;
+
+ // todo - update size of file
+ rc = simple_write_to_kernel_buffer(file_data->data, PAGE_SIZE, &ppos,
+ buf, bytes);
+ if (rc)
+ file_data->length = ppos;
+ return rc;
+}
+
+
+static void *fwvarfs_mem_create(const char *name)
+{
+ struct fwvarfs_mem_file *file_data;
+
+ file_data = kzalloc(sizeof(struct fwvarfs_mem_file), GFP_KERNEL);
+ if (!file_data)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&file_data->list);
+ list_add(&mem_file_list, &file_data->list);
+
+ return file_data;
+}
+
+static void fwvarfs_mem_destroy(void *var)
+{
+ struct fwvarfs_mem_file *file_data = var;
+
+ list_del(&file_data->list);
+ kfree(file_data);
+}
+
+static int fwvarfs_mem_unlink(void *var)
+{
+ /*
+ * This always succeeds and there's nothing we need to do.
+ * We free the memory in destroy() which is called after
+ * this by fwvarfs.
+ */
+ return 0;
+}
+
+static int fwvarfs_mem_enumerate(void)
+{
+ /* Nothing to do, we always start from a blank slate */
+ return 0;
+}
+
+struct fwvarfs_backend fwvarfs_mem_backend = {
+ .name = "mem",
+ .read = fwvarfs_mem_file_read,
+ .write = fwvarfs_mem_file_write,
+ .create = fwvarfs_mem_create,
+ .destroy = fwvarfs_mem_destroy,
+ .unlink = fwvarfs_mem_unlink,
+ .enumerate = fwvarfs_mem_enumerate,
+};
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 74fe51dbd027..211366ecf5a8 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1201,7 +1201,6 @@ static int kernfs_iop_create(struct inode *dir, struct dentry *dentry,
return PTR_ERR(kn);
d_instantiate(dentry, kernfs_get_inode(dir->i_sb, kn));
-
return 0;
}
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index f8c00045d537..61f2f5532366 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -34,6 +34,7 @@
#define EFIVARFS_MAGIC 0xde5e81e4
#define HOSTFS_SUPER_MAGIC 0x00c0ffee
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
+#define FWVARFS_SUPER_MAGIC 0x66777672 /* "fwvr" */
#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
--
2.19.1
More information about the Linuxppc-dev
mailing list