]> git.ozlabs.org Git - petitboot/blobdiff - lib/efi/efivar.c
lib/efi: Add new struct efi_mount
[petitboot] / lib / efi / efivar.c
index 1ac69908d1f62f87fb770f0cc547c606f8537cfc..f1dd0021cea0c8070a8da86ac303ac07723b82ee 100644 (file)
@@ -17,6 +17,7 @@
  *  Author: Ge Song <ge.song@hxt-semitech.com>
  */
 
+#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 
 #include <linux/fs.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
 
 #include "efivar.h"
 #include "log/log.h"
 #include "talloc/talloc.h"
 
-static const char *efivarfs_path;
+void efi_init_mount(struct efi_mount *efi_mount, const char *path,
+       const char *guid)
+{
+       assert(efi_mount);
+
+       efi_mount->path = path;
+       efi_mount->guid = guid;
+
+       pb_debug_fn("%s--%s", efi_mount->path, efi_mount->guid);
+}
 
-inline void set_efivarfs_path(const char *path)
+bool efi_check_mount_magic(const struct efi_mount *efi_mount, bool check_magic)
 {
-       efivarfs_path = path;
+       struct statfs s;
+
+       assert(efi_mount);
+
+       if (!efi_mount->guid) {
+               pb_debug_fn("guid not set\n");
+               return false;
+       }
+
+       if (access(efi_mount->path, R_OK | W_OK)) {
+               pb_debug_fn("Can't access %s\n", efi_mount->path);
+               return false;
+       }
+
+       memset(&s, '\0', sizeof(s));
+       if (statfs(efi_mount->path, &s)) {
+               pb_debug_fn("statfs failed: %s: (%d) %s\n", efi_mount->path,
+                       errno, strerror(errno));
+               return false;
+       }
+
+       if (check_magic && s.f_type != EFIVARFS_MAGIC) {
+               pb_debug_fn("Bad magic = 0x%lx\n", (unsigned long)s.f_type);
+               return false;
+       }
+
+       return true;
 }
 
-inline const char *get_efivarfs_path(void)
+static int efi_open(const struct efi_mount *efi_mount, const char *name,
+       int flags, mode_t mode, char **path)
 {
+       int fd;
 
-       return efivarfs_path;
+       assert(efi_mount);
+
+       *path = NULL;
+
+       if (!efi_mount->path || !efi_mount->guid)
+               return -1;
+
+       *path = talloc_asprintf(NULL, "%s/%s-%s", efi_mount->path, name,
+               efi_mount->guid);
+       if (!*path)
+               return -1;
+
+       flags = flags ? flags : O_RDONLY | O_NONBLOCK;
+
+       fd = open(*path, flags, mode);
+
+       if (fd < 0) {
+               pb_log("%s: open failed '%s': (%d) %s\n", __func__, *path,
+                       errno, strerror(errno));
+               talloc_free(*path);
+               *path = NULL;
+               return -1;
+       }
+
+       return fd;
 }
 
-int efi_del_variable(void *ctx, const char *guidstr,
-               const char *name)
+int efi_del_variable(const struct efi_mount *efi_mount, const char *name)
 {
-       int fd, flag, errno_value;
+       int fd, flag;
        int rc = -1;
-       const char *dir;
        char *path;
 
-       dir = get_efivarfs_path();
-       if (!dir)
-               return -1;
+       assert(efi_mount);
 
-       path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
-       if (!path)
+       fd = efi_open(efi_mount, name, 0, 0, &path);
+       if (fd < 0)
                return -1;
 
-       fd = open(path, O_RDONLY|O_NONBLOCK);
-       if (fd == -1)
-               goto err;
-
        rc = ioctl(fd, FS_IOC_GETFLAGS, &flag);
-       if (rc == -1)
-               goto err;
+       if (rc == -1 && errno == ENOTTY) {
+               pb_debug_fn("'%s' does not support ioctl_iflags.\n",
+                       efi_mount->path);
+               goto delete;
+       } else if (rc == -1) {
+               pb_log_fn("FS_IOC_GETFLAGS failed: (%d) %s\n", errno,
+                       strerror(errno));
+               goto exit;
+       }
 
        flag &= ~FS_IMMUTABLE_FL;
        rc = ioctl(fd, FS_IOC_SETFLAGS, &flag);
-       if (rc == -1)
-               goto err;
+       if (rc == -1) {
+               pb_log_fn("FS_IOC_SETFLAGS failed: (%d) %s\n", errno,
+                       strerror(errno));
+               goto exit;
+       }
 
+delete:
        close(fd);
+       fd = 0;
        rc = unlink(path);
-
-err:
-       errno_value = errno;
-       if (fd > 0)
-               close(fd);
-
-       errno = errno_value;
+       if (rc == -1) {
+               pb_log_fn("unlink failed: (%d) %s\n", errno, strerror(errno));
+               goto exit;
+       }
+       pb_debug_fn("Deleted: '%s'\n", name);
+exit:
+       talloc_free(path);
+       close(fd);
        return rc;
 }
 
-int efi_get_variable(void *ctx, const char *guidstr, const char *name,
-               uint8_t **data, size_t *data_size, uint32_t *attributes)
+int efi_get_variable(void *ctx, const struct efi_mount *efi_mount,
+       const char *name, struct efi_data **efi_data)
 {
-       int fd, errno_value;
+       int fd;
        int rc = -1;
-       void *p, *buf;
-       size_t bufsize = 4096;
-       size_t filesize = 0;
-       ssize_t sz;
-       const char *dir;
+       char *p;
+       char buf[4096];
+       ssize_t total;
+       ssize_t count;
        char *path;
 
-       dir = get_efivarfs_path();
-       if (!dir)
-               return EFAULT;
+       assert(efi_mount);
 
-       path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
-       if (!path)
-               return ENOMEM;
+       *efi_data = NULL;
 
-       fd = open(path, O_RDONLY|O_NONBLOCK);
+       fd = efi_open(efi_mount, name, 0, 0, &path);
        if (fd < 0)
-               goto err;
+               return -1;
 
-       buf = talloc_size(ctx, bufsize);
-       if (!buf)
-               goto err;
-
-       do {
-               p = buf + filesize;
-               sz = read(fd, p, bufsize);
-               if (sz < 0 && errno == EAGAIN) {
-                       continue;
-               } else if (sz == 0) {
-                       break;
+       for (p = buf, total = 0; ; p = buf + count) {
+               count = read(fd, p, sizeof(buf) - total);
+               if (count < 0) {
+                       if (errno == EAGAIN || errno == EWOULDBLOCK)
+                               continue;
+
+                       pb_log("%s: read failed %s: (%ld) (%d) %s\n", __func__, path,
+                               count, errno, strerror(errno));
+                       goto exit;
                }
-               filesize += sz;
-       } while (1);
+               if (p >= (buf + sizeof(buf))) {
+                       pb_log("%s: buffer full %s: (%ld)\n", __func__, path,
+                               sizeof(buf));
+                       goto exit;
+               }
+               if (count == 0)
+                       break;
+               total += count;
+       };
 
-       *attributes = *(uint32_t *)buf;
-       *data = (uint8_t *)(buf + sizeof(uint32_t));
-       *data_size = strlen(buf + sizeof(uint32_t));
-       rc = 0;
+       *efi_data = (void*)talloc_zero_array(ctx, char,
+               sizeof (struct efi_data) + total);
 
-err:
-       errno_value = errno;
-       if (fd > 0)
-               close(fd);
+       (*efi_data)->attributes = *(uint32_t *)buf;
+       (*efi_data)->data_size = total;
+       (*efi_data)->data = (*efi_data)->fill;
+       memcpy((*efi_data)->data, buf + sizeof (uint32_t), total);
+       pb_debug_fn("Found: '%s'='%s'\n", name, (const char *)(*efi_data)->data);
 
-       errno = errno_value;
+       rc = 0;
+exit:
+       talloc_free(path);
+       close(fd);
        return rc;
 }
 
-int efi_set_variable(void *ctx, const char *guidstr, const char *name,
-               uint8_t *data, size_t data_size, uint32_t attributes)
+int efi_set_variable(const struct efi_mount *efi_mount, const char *name,
+               const struct efi_data *efi_data)
 {
-       int rc = -1, errno_value;
-       int fd = -1;
-       ssize_t len;
-       const char *dir;
-       char *path;
+       int rc = -1;
+       int fd;
+       ssize_t count;
        void *buf;
        size_t bufsize;
-       mode_t mask = 0644;
+       char *path;
 
-       dir = get_efivarfs_path();
-       if (!dir)
-               return EFAULT;
+       assert(efi_mount);
 
-       path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
-       if (!path)
-               return ENOMEM;
+       efi_del_variable(efi_mount, name);
 
-       if (!access(path, F_OK)) {
-               rc = efi_del_variable(ctx, guidstr, name);
-               if (rc < 0) {
-                       goto err;
-               }
-       }
-
-       fd = open(path, O_CREAT|O_WRONLY, mask);
+       fd = efi_open(efi_mount, name, O_CREAT | O_WRONLY,
+               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &path);
        if (fd < 0)
-               goto err;
+               return -1;
 
-       bufsize = sizeof(uint32_t) + data_size;
-       buf = talloc_size(ctx, bufsize);
+       bufsize = sizeof(uint32_t) + efi_data->data_size;
+       buf = talloc_size(path, bufsize);
        if (!buf)
-               goto err;
-
-       *(uint32_t *)buf = attributes;
-       memcpy(buf + sizeof(uint32_t), data, data_size);
+               goto exit;
 
-       len = write(fd, buf, bufsize);
-       if ((size_t)len != bufsize)
-               goto err;
-       else
-               rc = 0;
+       *(uint32_t *)buf = efi_data->attributes;
+       memcpy(buf + sizeof(uint32_t), efi_data->data, efi_data->data_size);
 
-err:
-       errno_value = errno;
-       if (fd > 0)
-               close(fd);
+       count = write(fd, buf, bufsize);
+       if ((size_t)count != bufsize) {
+               pb_log("%s: write failed %s: (%ld) (%d) %s\n", __func__, name,
+                       count, errno, strerror(errno));
+               goto exit;
+       }
+       rc = 0;
+       pb_debug_fn("Set: '%s'='%s'\n", name,  (const char *)efi_data->data);
 
-       errno = errno_value;
+exit:
+       talloc_free(path);
+       close(fd);
        return rc;
 }