[PATCH 2/2] efivarfs: rework the filesystem to make it human readable
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Fri Mar 3 07:31:37 PST 2017
today we use <varname>-<guid as %pUl>
such as Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c
now we will use a different approach where will represent first the vendor
as a directory and then inside the associates variable.
But also create symlink on the vendor with readable name.
so it will look like this:
barebox at barebox EFI payload:/ ls -l /efivars/
drwxrwxrwx 36 04b37fe8-f6ae-480b-bdd5-37d98c5e89aa
drwxrwxrwx 36 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
drwxrwxrwx 36 4c19049f-4137-4dd3-9c10-8b97a83ffdfa
drwxrwxrwx 36 5b446ed1-e30b-4faa-871a-3654eca36080
drwxrwxrwx 36 5b91f69c-8b88-4a2b-9269-5f1d802b5175
drwxrwxrwx 36 8be4df61-93ca-11d2-aa0d-00e098032b8c
lrwxrwxrwx 3 Efi -> 8be4df61-93ca-11d2-aa0d-00e098032b8c
lrwxrwxrwx 7 barebox -> 5b91f69c-8b88-4a2b-9269-5f1d802b5175
drwxrwxrwx 36 eb704011-1402-11d3-8e77-00a0c969723b
lrwxrwxrwx 7 systemd -> 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
barebox at barebox EFI payload:/ ls -l /efivars/Efi/
-rw-rw-rw- 62 Boot0000
-rw-rw-rw- 80 Boot0001
-rw-rw-rw- 84 Boot0002
-rw-rw-rw- 106 Boot0003
-rw-rw-rw- 108 Boot0004
-rw-rw-rw- 141 Boot0005
-rw-rw-rw- 88 Boot0006
-rw-rw-rw- 2 BootCurrent
-rw-rw-rw- 4 BootOptionSupport
-rw-rw-rw- 14 BootOrder
-rw-rw-rw- 107 ConIn
-rw-rw-rw- 1567 ConInDev
-rw-rw-rw- 103 ConOut
-rw-rw-rw- 1855 ConOutDev
-rw-rw-rw- 73 ErrOut
-rw-rw-rw- 1563 ErrOutDev
-rw-rw-rw- 14 Key0000
-rw-rw-rw- 14 Key0001
-rw-rw-rw- 4 Lang
-rw-rw-rw- 13 LangCodes
-rw-rw-rw- 8 OsIndicationsSupported
-rw-rw-rw- 3 PlatformLang
-rw-rw-rw- 18 PlatformLangCodes
-rw-rw-rw- 108 PlatformRecovery0000
-rw-rw-rw- 2 Timeout
barebox at barebox EFI payload:/ ls -l /efivars/barebox/
barebox at barebox EFI payload:/ ls -l /efivars/systemd/
-rw-rw-rw- 14 LoaderTimeInitUSec
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj at jcrosoft.com>
---
common/efi-guid.c | 17 ++-
common/efi/efi.c | 2 +-
fs/efivarfs.c | 379 ++++++++++++++++++++++++++++++++++++++++++------------
include/efi.h | 1 +
4 files changed, 312 insertions(+), 87 deletions(-)
diff --git a/common/efi-guid.c b/common/efi-guid.c
index 71aa21ddd..6908012df 100644
--- a/common/efi-guid.c
+++ b/common/efi-guid.c
@@ -14,11 +14,21 @@ efi_guid_t efi_systemd_vendor_guid = EFI_SYSTEMD_VENDOR_GUID;
#define EFI_GUID_STRING(guid, short, long) do { \
if (!efi_guidcmp(guid, *g)) \
- return long; \
+ return is_long ? long : short; \
} while(0)
+
const char *efi_guid_string(efi_guid_t *g)
{
+ const char *name;
+
+ name = efi_guid_string_type(g, 1);
+
+ return name ? name : "Unknown";
+}
+
+const char *efi_guid_string_type(efi_guid_t *g, int is_long)
+{
EFI_GUID_STRING(EFI_NULL_GUID, "NULL", "NULL GUID");
EFI_GUID_STRING(EFI_MPS_TABLE_GUID, "MPS Table", "MPS Table GUID in EFI System Table");
EFI_GUID_STRING(EFI_ACPI_TABLE_GUID, "ACPI Table", "ACPI 1.0 Table GUID in EFI System Table");
@@ -88,5 +98,8 @@ const char *efi_guid_string(efi_guid_t *g)
EFI_GUID_STRING(EFI_ISCSIDXE_INF_GUID, "IScsiDxe.inf", "EFI IScsiDxe.inf File GUID");
EFI_GUID_STRING(EFI_VLANCONFIGDXE_INF_GUID, "VlanConfigDxe.inf", "EFI VlanConfigDxe.inf File GUID");
- return "unknown";
+ EFI_GUID_STRING(EFI_BAREBOX_VENDOR_GUID, "barebox", "Barebox Bootloader");
+ EFI_GUID_STRING(EFI_SYSTEMD_VENDOR_GUID, "systemd", "Linux systemd");
+
+ return NULL;
}
diff --git a/common/efi/efi.c b/common/efi/efi.c
index 05c58250f..b4a5bb88e 100644
--- a/common/efi/efi.c
+++ b/common/efi/efi.c
@@ -299,7 +299,7 @@ static int efi_init(void)
defaultenv_append_directory(env_efi);
- env = xasprintf("/efivars/barebox-env-%pUl", &efi_barebox_vendor_guid);
+ env = xasprintf("/efivars/%pUl/barebox-env", &efi_barebox_vendor_guid);
default_environment_path_set(env);
return 0;
diff --git a/fs/efivarfs.c b/fs/efivarfs.c
index bf7351e6d..90aa3cfcf 100644
--- a/fs/efivarfs.c
+++ b/fs/efivarfs.c
@@ -27,6 +27,7 @@
#include <linux/stat.h>
#include <xfuncs.h>
#include <fcntl.h>
+#include <libgen.h>
#include <efi.h>
#include <wchar.h>
#include <linux/err.h>
@@ -35,21 +36,99 @@
#include <efi/efi-device.h>
struct efivarfs_inode {
+ int is_dir;
+ int is_symlink;
s16 *name;
efi_guid_t vendor;
char *full_name; /* name including vendor namespacing */
+ struct efivarfs_inode *parent;
struct list_head node;
-};
-
-struct efivarfs_dir {
- struct list_head *current;
- DIR dir;
+ struct list_head childs;
};
struct efivarfs_priv {
struct list_head inodes;
};
+static struct efivarfs_inode *efivarfs_get_by_name(
+ struct list_head *inodes, const char *full_name)
+{
+ struct efivarfs_inode *inode, *tmp;
+
+ list_for_each_entry_safe(inode, tmp, inodes, node) {
+ if (!strcmp(inode->full_name, full_name))
+ return inode;
+ }
+
+ return NULL;
+}
+
+static struct efivarfs_inode *efivarfs_get_by_vendor(
+ struct efivarfs_priv *priv, efi_guid_t *guid)
+{
+ struct efivarfs_inode *inode, *tmp;
+
+ list_for_each_entry_safe(inode, tmp, &priv->inodes, node) {
+ if (!efi_guidcmp(inode->vendor, *guid))
+ return inode;
+ }
+
+ return NULL;
+}
+
+static struct efivarfs_inode *efivarfs_add_vendor(
+ struct efivarfs_priv *priv, efi_guid_t *guid)
+{
+ struct efivarfs_inode *vendor;
+ struct efivarfs_inode *symlink;
+ const char *name;
+
+ vendor = efivarfs_get_by_vendor(priv, guid);
+
+ if (vendor)
+ return vendor;
+
+ vendor = xzalloc(sizeof(*vendor));
+ vendor->is_dir = 1;
+ vendor->full_name = basprintf("%pUl", guid);
+ vendor->vendor = *guid;
+
+ list_add_tail(&vendor->node, &priv->inodes);
+ INIT_LIST_HEAD(&vendor->childs);
+
+ name = efi_guid_string_type(guid, 0);
+ if (!name)
+ goto out;
+
+ /* add symlink if we can resolv it */
+ symlink = xzalloc(sizeof(*symlink));
+ symlink->is_symlink = 1;
+ symlink->full_name = xstrdup(name);
+ symlink->vendor = *guid;
+ symlink->parent = vendor;
+ list_add_tail(&symlink->node, &priv->inodes);
+
+out:
+ return vendor;
+}
+
+static struct efivarfs_inode *efivarfs_add_var(
+ struct efivarfs_inode *v_inode, s16 *name)
+{
+ struct efivarfs_inode *inode;
+
+ inode = xzalloc(sizeof(*inode));
+ inode->name = xstrdup_wchar(name);
+
+ inode->vendor = v_inode->vendor;
+
+ inode->full_name = xstrdup_wchar_to_char(inode->name);
+
+ list_add_tail(&inode->node, &v_inode->childs);
+
+ return inode;
+}
+
static int char_to_nibble(char c)
{
int ret = tolower(c);
@@ -93,47 +172,74 @@ int efi_guid_parse(const char *str, efi_guid_t *guid)
return 0;
}
-static int efivarfs_parse_filename(const char *filename, efi_guid_t *vendor, s16 **name)
+static struct efivarfs_inode *efivarfs_parse_path(struct efivarfs_priv *priv,
+ const char *filename, s16 **name, char **full_name)
{
- int len, ret;
- const char *guidstr;
- s16 *varname;
- int i;
+ char *dir, *file;
+ char *tmp = xstrdup(filename);
+ char *tmp2 = xstrdup(filename);
+ const char *vendor_str;
+ s16 *varname = NULL;
+ int len;
+ void *ret = ERR_PTR(-EINVAL);
+ struct efivarfs_inode *inode;
+
+ dir = dirname(tmp);
+ file = basename(tmp2);
- if (*filename == '/')
+ if (filename[0] == '/')
filename++;
- len = strlen(filename);
+ if (dir[0] == '/')
+ dir++;
- if (len < sizeof("-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"))
- return -EINVAL;
+ if (!strlen(dir)) {
+ vendor_str = filename;
+ *name = NULL;
+ *full_name = NULL;
+ } else {
+ int i;
- guidstr = filename + len - sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
- if (*guidstr != '-')
- return -EINVAL;
+ vendor_str = dir;
+ if (name) {
+ len = strlen(file);
+
+ varname = xzalloc((len + 1) * sizeof(s16));
+
+ for (i = 0; i < len; i++)
+ varname[i] = file[i];
- guidstr++;
+ *name = varname;
+ }
- ret = efi_guid_parse(guidstr, vendor);
+ if (full_name)
+ *full_name = xstrdup(file);
+ }
- varname = xzalloc((guidstr - filename) * sizeof(s16));
+ inode = efivarfs_get_by_name(&priv->inodes, vendor_str);
+ if (!inode)
+ goto err;
- for (i = 0; i < guidstr - filename - 1; i++)
- varname[i] = filename[i];
+ ret = inode;
+ goto out;
- *name = varname;
+err:
+ free(varname);
+ *name = NULL;
+out:
+ free(tmp);
+ free(tmp2);
- return 0;
+ return ret;
}
static int efivars_create(struct device_d *dev, const char *pathname, mode_t mode)
{
struct efivarfs_priv *priv = dev->priv;
struct efivarfs_inode *inode;
- efi_guid_t vendor;
+ struct efivarfs_inode *vendor;
efi_status_t efiret;
u8 dummydata;
- char *name8;
s16 *name;
int ret;
@@ -141,58 +247,73 @@ static int efivars_create(struct device_d *dev, const char *pathname, mode_t mod
pathname++;
/* deny creating files with other vendor GUID than our own */
- ret = efivarfs_parse_filename(pathname, &vendor, &name);
- if (ret)
+ vendor = efivarfs_parse_path(priv, pathname, &name, NULL);
+ if (IS_ERR(vendor))
return -ENOENT;
- if (memcmp(&vendor, &EFI_BAREBOX_VENDOR_GUID, sizeof(efi_guid_t)))
- return -EPERM;
-
- inode = xzalloc(sizeof(*inode));
- inode->name = name;
- inode->vendor = vendor;
-
-
- name8 = xstrdup_wchar_to_char(inode->name);
- inode->full_name = basprintf("%s-%pUl", name8, &inode->vendor);
- free(name8);
+ if (memcmp(&vendor->vendor, &EFI_BAREBOX_VENDOR_GUID, sizeof(efi_guid_t))) {
+ ret = -EPERM;
+ goto out;
+ }
- efiret = RT->set_variable(inode->name, &inode->vendor,
+ efiret = RT->set_variable(name, &vendor->vendor,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
1, &dummydata);
if (EFI_ERROR(efiret)) {
- free(inode);
- return -efi_errno(efiret);
+ ret = -efi_errno(efiret);
+ goto out;
}
- list_add_tail(&inode->node, &priv->inodes);
+ inode = efivarfs_add_var(vendor, name);
+ ret = 0;
- return 0;
+out:
+ free(name);
+
+ return ret;
}
static int efivars_unlink(struct device_d *dev, const char *pathname)
{
struct efivarfs_priv *priv = dev->priv;
- struct efivarfs_inode *inode, *tmp;
+ struct efivarfs_inode *vendor, *inode;
efi_status_t efiret;
+ s16 *name;
+ char *full_name;
+ int ret;
if (pathname[0] == '/')
pathname++;
- list_for_each_entry_safe(inode, tmp, &priv->inodes, node) {
- if (!strcmp(inode->full_name, pathname)) {
- efiret = RT->set_variable(inode->name, &inode->vendor,
- 0, 0, NULL);
- if (EFI_ERROR(efiret))
- return -efi_errno(efiret);
- list_del(&inode->node);
- free(inode);
- }
+ vendor = efivarfs_parse_path(priv, pathname, &name, &full_name);
+ if (IS_ERR(vendor)) {
+ ret = PTR_ERR(vendor);
+ goto out;
}
- return 0;
+ inode = efivarfs_get_by_name(&vendor->childs, full_name);
+ if (IS_ERR(inode)) {
+ ret = PTR_ERR(inode);
+ goto out;
+ }
+
+ efiret = RT->set_variable(inode->name, &inode->vendor, 0, 0, NULL);
+ if (EFI_ERROR(efiret)) {
+ ret = -efi_errno(efiret);
+ goto out;
+ }
+
+ list_del(&inode->node);
+ free(inode);
+ ret = 0;
+
+out:
+ free(full_name);
+ free(name);
+
+ return ret;
}
struct efivars_file {
@@ -206,14 +327,20 @@ struct efivars_file {
static int efivarfs_open(struct device_d *dev, FILE *f, const char *filename)
{
struct efivars_file *efile;
+ struct efivarfs_priv *priv = dev->priv;
+ struct efivarfs_inode *vendor;
efi_status_t efiret;
int ret;
efile = xzalloc(sizeof(*efile));
- ret = efivarfs_parse_filename(filename, &efile->vendor, &efile->name);
- if (ret)
- return -ENOENT;
+ vendor = efivarfs_parse_path(priv, filename, &efile->name, NULL);
+ if (IS_ERR(vendor)) {
+ ret = PTR_ERR(vendor);
+ goto out;
+ }
+
+ efile->vendor = vendor->vendor;
efiret = RT->get_variable(efile->name, &efile->vendor,
NULL, &efile->size, NULL);
@@ -243,6 +370,7 @@ static int efivarfs_open(struct device_d *dev, FILE *f, const char *filename)
out:
free(efile->buf);
+ free(efile->name);
free(efile);
return ret;
@@ -253,6 +381,7 @@ static int efivarfs_close(struct device_d *dev, FILE *f)
struct efivars_file *efile = f->priv;
free(efile->buf);
+ free(efile->name);
free(efile);
return 0;
@@ -314,31 +443,75 @@ static loff_t efivarfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
return f->pos;
}
+struct efivarfs_dir {
+ struct efivarfs_inode *vendor;
+ struct efivarfs_inode *var;
+ int empty;
+ DIR dir;
+};
+
static DIR *efivarfs_opendir(struct device_d *dev, const char *pathname)
{
struct efivarfs_priv *priv = dev->priv;
struct efivarfs_dir *edir;
+ DIR *dir;
edir = xzalloc(sizeof(*edir));
- edir->current = priv->inodes.next;
+ dir = &edir->dir;
+ dir->priv = edir;
+
+ if (pathname[0] == '/')
+ pathname++;
+
+ if (!strlen(pathname)) {
+ if (list_empty(&priv->inodes))
+ return dir;
- return &edir->dir;
+ edir->vendor = list_first_entry(&priv->inodes,
+ struct efivarfs_inode, node);
+ } else {
+ struct efivarfs_inode *inode;
+
+ inode = efivarfs_get_by_name(&priv->inodes, pathname);
+ edir->vendor = inode->is_symlink ? inode->parent : inode;
+ if (!edir->vendor)
+ return dir;
+
+ if (list_empty(&edir->vendor->childs)) {
+ edir->empty = 1;
+ return dir;
+ }
+
+ edir->var = list_first_entry(&edir->vendor->childs,
+ struct efivarfs_inode, node);
+ }
+
+ return dir;
}
static struct dirent *efivarfs_readdir(struct device_d *dev, DIR *dir)
{
struct efivarfs_priv *priv = dev->priv;
struct efivarfs_dir *edir = container_of(dir, struct efivarfs_dir, dir);
- struct efivarfs_inode *inode;
+ struct efivarfs_inode *vendor = edir->vendor;
+ struct efivarfs_inode *var = edir->var;
- if (edir->current == &priv->inodes)
+ if (!vendor)
return NULL;
- inode = list_entry(edir->current, struct efivarfs_inode, node);
+ if (!var) {
+ if (edir->empty || &vendor->node == &priv->inodes)
+ return NULL;
- strcpy(dir->d.d_name, inode->full_name);
+ strcpy(dir->d.d_name, vendor->full_name);
+ edir->vendor = list_entry(vendor->node.next, struct efivarfs_inode, node);
+ } else {
+ if (&var->node == &vendor->childs)
+ return NULL;
- edir->current = edir->current->next;
+ strcpy(dir->d.d_name, var->full_name);
+ edir->var = list_entry(var->node.next, struct efivarfs_inode, node);
+ }
return &dir->d;
}
@@ -354,26 +527,69 @@ static int efivarfs_closedir(struct device_d *dev, DIR *dir)
static int efivarfs_stat(struct device_d *dev, const char *filename, struct stat *s)
{
- efi_guid_t vendor;
+ struct efivarfs_priv *priv = dev->priv;
+ struct efivarfs_inode *vendor;
s16 *name;
efi_status_t efiret;
unsigned long size = 0;
- int ret;
+ int ret = -EINVAL;
- ret = efivarfs_parse_filename(filename, &vendor, &name);
- if (ret)
- return -ENOENT;
+ vendor = efivarfs_parse_path(priv, filename, &name, NULL);
+ if (IS_ERR(vendor))
+ return PTR_ERR(vendor);
+
+ if (filename[0] == '/')
+ filename++;
+
+ if (!name) {
+ s->st_size = strlen(filename);
+ if (vendor->is_dir)
+ s->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
+ else
+ s->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ ret = 0;
+
+ goto out;
+ }
- efiret = RT->get_variable(name, &vendor, NULL, &size, NULL);
+ efiret = RT->get_variable(name, &vendor->vendor, NULL, &size, NULL);
free(name);
- if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL)
- return -efi_errno(efiret);
+ if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL) {
+ ret = -efi_errno(efiret);
+ goto out;
+ }
s->st_mode = 00666 | S_IFREG;
s->st_size = size;
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static int efivarfs_readlink(struct device_d *dev, const char *pathname,
+ char *buf, size_t bufsiz)
+{
+ struct efivarfs_priv *priv = dev->priv;
+ struct efivarfs_inode *node;
+ int len;
+
+ if (pathname[0] == '/')
+ pathname++;
+
+ node = efivarfs_get_by_name(&priv->inodes, pathname);
+
+ if (!node || !node->parent)
+ return -ENOENT;
+
+ len = min(bufsiz, strlen(node->parent->full_name));
+
+ memcpy(buf, node->parent->full_name, len);
+
return 0;
}
@@ -382,7 +598,6 @@ static int efivarfs_probe(struct device_d *dev)
efi_status_t efiret;
efi_guid_t vendor;
s16 name[1024];
- char *name8;
unsigned long size;
struct efivarfs_priv *priv;
@@ -391,24 +606,19 @@ static int efivarfs_probe(struct device_d *dev)
priv = xzalloc(sizeof(*priv));
INIT_LIST_HEAD(&priv->inodes);
+ efivarfs_add_vendor(priv, &efi_barebox_vendor_guid);
+
while (1) {
struct efivarfs_inode *inode;
+ struct efivarfs_inode *v_inode;
size = sizeof(name);
efiret = RT->get_next_variable(&size, name, &vendor);
if (EFI_ERROR(efiret))
break;
- inode = xzalloc(sizeof(*inode));
- inode->name = xstrdup_wchar(name);
-
- inode->vendor = vendor;
-
- name8 = xstrdup_wchar_to_char(inode->name);
- inode->full_name = basprintf("%s-%pUl", name8, &vendor);
- free(name8);
-
- list_add_tail(&inode->node, &priv->inodes);
+ v_inode = efivarfs_add_vendor(priv, &vendor);
+ inode = efivarfs_add_var(v_inode, name);
}
dev->priv = priv;
@@ -442,6 +652,7 @@ static struct fs_driver_d efivarfs_driver = {
.readdir = efivarfs_readdir,
.closedir = efivarfs_closedir,
.stat = efivarfs_stat,
+ .readlink = efivarfs_readlink,
.drv = {
.probe = efivarfs_probe,
.remove = efivarfs_remove,
diff --git a/include/efi.h b/include/efi.h
index e1fc134ee..afad26314 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -668,6 +668,7 @@ char *device_path_to_str(struct efi_device_path *dev_path);
u8 device_path_to_type(struct efi_device_path *dev_path);
char *device_path_to_partuuid(struct efi_device_path *dev_path);
+const char *efi_guid_string_type(efi_guid_t *g, int is_long);
const char *efi_guid_string(efi_guid_t *g);
#endif /* _LINUX_EFI_H */
--
2.11.0
More information about the barebox
mailing list