[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