util-lib: split shared/efivars into basic/efivars and shared/efi-loader

I want to use efivars.[ch] in proc-cmdline.c, but most of the efivars stuff is
not needed in basic/. Move the file from shared/ to basic/, but then move back
most of the higher-level functions to the new shared/efi-loader.c file.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2019-08-01 16:28:29 +02:00
parent 2bfa8466c0
commit 0bb2f0f10e
18 changed files with 297 additions and 263 deletions

225
src/basic/efivars.c Normal file
View File

@@ -0,0 +1,225 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "sd-id128.h"
#include "alloc-util.h"
#include "chattr-util.h"
#include "efivars.h"
#include "fd-util.h"
#include "io-util.h"
#include "macro.h"
#include "stdio-util.h"
#include "strv.h"
#include "time-util.h"
#include "utf8.h"
#if ENABLE_EFI
char* efi_variable_path(sd_id128_t vendor, const char *name) {
char *p;
if (asprintf(&p,
"/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
return NULL;
return p;
}
int efi_get_variable(
sd_id128_t vendor,
const char *name,
uint32_t *ret_attribute,
void **ret_value,
size_t *ret_size) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *p = NULL;
_cleanup_free_ void *buf = NULL;
struct stat st;
uint32_t a;
ssize_t n;
assert(name);
p = efi_variable_path(vendor, name);
if (!p)
return -ENOMEM;
if (!ret_value && !ret_size && !ret_attribute) {
/* If caller is not interested in anything, just check if the variable exists and is readable
* to us. */
if (access(p, R_OK) < 0)
return -errno;
return 0;
}
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
if (fstat(fd, &st) < 0)
return -errno;
if (st.st_size < 4)
return -ENODATA;
if (st.st_size > 4*1024*1024 + 4)
return -E2BIG;
if (ret_value || ret_attribute) {
n = read(fd, &a, sizeof(a));
if (n < 0)
return -errno;
if (n != sizeof(a))
return -EIO;
}
if (ret_value) {
buf = malloc(st.st_size - 4 + 2);
if (!buf)
return -ENOMEM;
n = read(fd, buf, (size_t) st.st_size - 4);
if (n < 0)
return -errno;
if (n != st.st_size - 4)
return -EIO;
/* Always NUL terminate (2 bytes, to protect UTF-16) */
((char*) buf)[st.st_size - 4] = 0;
((char*) buf)[st.st_size - 4 + 1] = 0;
}
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
* with a smaller value. */
if (ret_attribute)
*ret_attribute = a;
if (ret_value)
*ret_value = TAKE_PTR(buf);
if (ret_size)
*ret_size = (size_t) st.st_size - 4;
return 0;
}
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
int r;
char *x;
r = efi_get_variable(vendor, name, NULL, &s, &ss);
if (r < 0)
return r;
x = utf16_to_utf8(s, ss);
if (!x)
return -ENOMEM;
*p = x;
return 0;
}
int efi_set_variable(
sd_id128_t vendor,
const char *name,
const void *value,
size_t size) {
struct var {
uint32_t attr;
char buf[];
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
bool saved_flags_valid = false;
unsigned saved_flags;
int r;
assert(name);
assert(value || size == 0);
p = efi_variable_path(vendor, name);
if (!p)
return -ENOMEM;
/* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
* them for accidental removal and modification. We are not changing these variables accidentally however,
* hence let's unset the bit first. */
r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
saved_flags_valid = r >= 0;
if (size == 0) {
if (unlink(p) < 0) {
r = -errno;
goto finish;
}
return 0;
}
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
if (fd < 0) {
r = -errno;
goto finish;
}
buf = malloc(sizeof(uint32_t) + size);
if (!buf) {
r = -ENOMEM;
goto finish;
}
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
memcpy(buf->buf, value, size);
r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
if (r < 0)
goto finish;
r = 0;
finish:
if (saved_flags_valid) {
int q;
/* Restore the original flags field, just in case */
if (fd < 0)
q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
else
q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
if (q < 0)
log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
}
return r;
}
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
_cleanup_free_ char16_t *u16 = NULL;
u16 = utf8_to_utf16(v, strlen(v));
if (!u16)
return -ENOMEM;
return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
}
#endif

52
src/basic/efivars.h Normal file
View File

@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#if !ENABLE_EFI
# include <errno.h>
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "sd-id128.h"
#include "efi/loader-features.h"
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
#if ENABLE_EFI
char* efi_variable_path(sd_id128_t vendor, const char *name);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
#else
static inline char* efi_variable_path(sd_id128_t vendor, const char *name) {
return NULL;
}
static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
return -EOPNOTSUPP;
}
static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
return -EOPNOTSUPP;
}
static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
return -EOPNOTSUPP;
}
static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
return -EOPNOTSUPP;
}
#endif

View File

@@ -39,6 +39,8 @@ basic_sources = files('''
device-nodes.h
dirent-util.c
dirent-util.h
efivars.c
efivars.h
env-file.c
env-file.h
env-util.c

View File

@@ -4,7 +4,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include "efivars.h"
#include "efi-loader.h"
#include "generator.h"
#include "log.h"
#include "mkdir.h"

View File

@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "bootspec.h"
#include "efi-loader.h"
#include "efivars.h"
#include "fd-util.h"
#include "fs-util.h"

View File

@@ -24,6 +24,7 @@
#include "bootspec.h"
#include "copy.h"
#include "dirent-util.h"
#include "efi-loader.h"
#include "efivars.h"
#include "env-util.h"
#include "escape.h"

View File

@@ -13,7 +13,8 @@
#include "conf-files.h"
#include "cgroup-setup.h"
#include "dev-setup.h"
#include "efivars.h"
#include "dirent-util.h"
#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"

View File

@@ -15,7 +15,7 @@
#include "device-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "efivars.h"
#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"

View File

@@ -20,6 +20,7 @@
#include "device-util.h"
#include "dirent-util.h"
#include "efivars.h"
#include "efi-loader.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"

View File

@@ -2,7 +2,7 @@
#include "acpi-fpdt.h"
#include "boot-timestamps.h"
#include "efivars.h"
#include "efi-loader.h"
#include "macro.h"
#include "time-util.h"

View File

@@ -15,6 +15,7 @@
#include "device-nodes.h"
#include "dirent-util.h"
#include "efivars.h"
#include "efi-loader.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"

View File

@@ -22,7 +22,7 @@
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
#include "efivars.h"
#include "efi-loader.h"
#include "env-file.h"
#include "extract-word.h"
#include "fd-util.h"

View File

@@ -1,30 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "sd-id128.h"
#include "alloc-util.h"
#include "chattr-util.h"
#include "dirent-util.h"
#include "efi-loader.h"
#include "efivars.h"
#include "fd-util.h"
#include "io-util.h"
#include "macro.h"
#include "parse-util.h"
#include "sort-util.h"
#include "stdio-util.h"
#include "strv.h"
#include "time-util.h"
#include "string-util.h"
#include "utf8.h"
#include "virt.h"
@@ -193,202 +181,6 @@ int efi_set_reboot_to_firmware(bool value) {
return 0;
}
char* efi_variable_path(sd_id128_t vendor, const char *name) {
char *p;
if (asprintf(&p,
"/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
return NULL;
return p;
}
int efi_get_variable(
sd_id128_t vendor,
const char *name,
uint32_t *ret_attribute,
void **ret_value,
size_t *ret_size) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *p = NULL;
_cleanup_free_ void *buf = NULL;
struct stat st;
uint32_t a;
ssize_t n;
assert(name);
p = efi_variable_path(vendor, name);
if (!p)
return -ENOMEM;
if (!ret_value && !ret_size && !ret_attribute) {
/* If caller is not interested in anything, just check if the variable exists and is readable
* to us. */
if (access(p, R_OK) < 0)
return -errno;
return 0;
}
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
if (fstat(fd, &st) < 0)
return -errno;
if (st.st_size < 4)
return -ENODATA;
if (st.st_size > 4*1024*1024 + 4)
return -E2BIG;
if (ret_value || ret_attribute) {
n = read(fd, &a, sizeof(a));
if (n < 0)
return -errno;
if (n != sizeof(a))
return -EIO;
}
if (ret_value) {
buf = malloc(st.st_size - 4 + 2);
if (!buf)
return -ENOMEM;
n = read(fd, buf, (size_t) st.st_size - 4);
if (n < 0)
return -errno;
if (n != st.st_size - 4)
return -EIO;
/* Always NUL terminate (2 bytes, to protect UTF-16) */
((char*) buf)[st.st_size - 4] = 0;
((char*) buf)[st.st_size - 4 + 1] = 0;
}
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
* with a smaller value. */
if (ret_attribute)
*ret_attribute = a;
if (ret_value)
*ret_value = TAKE_PTR(buf);
if (ret_size)
*ret_size = (size_t) st.st_size - 4;
return 0;
}
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
int r;
char *x;
r = efi_get_variable(vendor, name, NULL, &s, &ss);
if (r < 0)
return r;
x = utf16_to_utf8(s, ss);
if (!x)
return -ENOMEM;
*p = x;
return 0;
}
int efi_set_variable(
sd_id128_t vendor,
const char *name,
const void *value,
size_t size) {
struct var {
uint32_t attr;
char buf[];
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
bool saved_flags_valid = false;
unsigned saved_flags;
int r;
assert(name);
assert(value || size == 0);
p = efi_variable_path(vendor, name);
if (!p)
return -ENOMEM;
/* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
* them for accidental removal and modification. We are not changing these variables accidentally however,
* hence let's unset the bit first. */
r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
saved_flags_valid = r >= 0;
if (size == 0) {
if (unlink(p) < 0) {
r = -errno;
goto finish;
}
return 0;
}
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
if (fd < 0) {
r = -errno;
goto finish;
}
buf = malloc(sizeof(uint32_t) + size);
if (!buf) {
r = -ENOMEM;
goto finish;
}
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
memcpy(buf->buf, value, size);
r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
if (r < 0)
goto finish;
r = 0;
finish:
if (saved_flags_valid) {
int q;
/* Restore the original flags field, just in case */
if (fd < 0)
q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
else
q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
if (q < 0)
log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
}
return r;
}
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
_cleanup_free_ char16_t *u16 = NULL;
u16 = utf8_to_utf16(v, strlen(v));
if (!u16)
return -ENOMEM;
return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
}
static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
size_t l = 0;

View File

@@ -1,23 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#if ! ENABLE_EFI
#include <errno.h>
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "sd-id128.h"
#include "efi/loader-features.h"
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
#include "efivars.h"
#if ENABLE_EFI
@@ -28,12 +12,6 @@ int efi_reboot_to_firmware_supported(void);
int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
char* efi_variable_path(sd_id128_t vendor, const char *name);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
int efi_remove_boot_option(uint16_t id);
@@ -74,26 +52,6 @@ static inline int efi_set_reboot_to_firmware(bool value) {
return -EOPNOTSUPP;
}
static inline char* efi_variable_path(sd_id128_t vendor, const char *name) {
return NULL;
}
static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
return -EOPNOTSUPP;
}
static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
return -EOPNOTSUPP;
}
static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
return -EOPNOTSUPP;
}
static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
return -EOPNOTSUPP;
}

View File

@@ -60,8 +60,8 @@ shared_sources = files('''
dns-domain.h
dropin.c
dropin.h
efivars.c
efivars.h
efi-loader.c
efi-loader.h
enable-mempool.c
env-file-label.c
env-file-label.h

View File

@@ -5,7 +5,7 @@
#include "acpi-fpdt.h"
#include "boot-timestamps.h"
#include "efivars.h"
#include "efi-loader.h"
#include "log.h"
#include "tests.h"
#include "util.h"

View File

@@ -14,7 +14,7 @@
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
#include "efivars.h"
#include "efi-loader.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "ima-util.h"

View File

@@ -19,7 +19,7 @@
#include "alloc-util.h"
#include "blkid-util.h"
#include "device-util.h"
#include "efivars.h"
#include "efi-loader.h"
#include "errno-util.h"
#include "fd-util.h"
#include "gpt.h"