1195 lines
31 KiB
Diff
1195 lines
31 KiB
Diff
|
From 488976e782a7307cc2df4e1a8aaea1f6dfa757dd Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Jones <pjones@redhat.com>
|
||
|
Date: Mon, 17 Jun 2019 15:04:11 -0400
|
||
|
Subject: [PATCH 37/63] Implement 'efivar --export=foo.var'
|
||
|
|
||
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
||
|
---
|
||
|
.gitignore | 17 +-
|
||
|
src/Makefile | 2 +-
|
||
|
src/crc32.h | 19 ++
|
||
|
src/efivar.c | 362 ++++++++++++++++++++------
|
||
|
src/export.c | 502 +++++++++++++++++++++++++++++++++---
|
||
|
src/gpt.c | 18 --
|
||
|
src/include/efivar/efivar.h | 3 +
|
||
|
7 files changed, 783 insertions(+), 140 deletions(-)
|
||
|
|
||
|
diff --git a/.gitignore b/.gitignore
|
||
|
index 5c3fd0e3f52..947d88eec42 100644
|
||
|
--- a/.gitignore
|
||
|
+++ b/.gitignore
|
||
|
@@ -1,23 +1,26 @@
|
||
|
+.*.c.P
|
||
|
+.*.h.P
|
||
|
+.*.d
|
||
|
.*.sw?
|
||
|
*~
|
||
|
*.a
|
||
|
+*.env
|
||
|
*.E
|
||
|
*.o
|
||
|
+*.map
|
||
|
*.pc
|
||
|
*.S
|
||
|
!src/guids.S
|
||
|
*.so
|
||
|
*.so.*
|
||
|
+*.spec
|
||
|
*.tar.*
|
||
|
-.*.c.P
|
||
|
-.*.h.P
|
||
|
-.*.d
|
||
|
+*.var
|
||
|
core.*
|
||
|
-*.spec
|
||
|
+cov-int
|
||
|
+vgcore.*
|
||
|
+scan-results/
|
||
|
src/efivar
|
||
|
src/efivar-static
|
||
|
src/makeguids
|
||
|
src/guid-symbols.c
|
||
|
-*.map
|
||
|
-cov-int
|
||
|
-scan-results/
|
||
|
diff --git a/src/Makefile b/src/Makefile
|
||
|
index ecbbc02e1f7..addfaa03c85 100644
|
||
|
--- a/src/Makefile
|
||
|
+++ b/src/Makefile
|
||
|
@@ -17,7 +17,7 @@ STATICTARGETS=$(STATICLIBTARGETS) $(STATICBINTARGETS)
|
||
|
LIBEFIBOOT_SOURCES = crc32.c creator.c disk.c gpt.c loadopt.c path-helpers.c \
|
||
|
linux.c $(wildcard linux-*.c)
|
||
|
LIBEFIBOOT_OBJECTS = $(patsubst %.c,%.o,$(LIBEFIBOOT_SOURCES))
|
||
|
-LIBEFIVAR_SOURCES = dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \
|
||
|
+LIBEFIVAR_SOURCES = crc32.c dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \
|
||
|
efivarfs.c error.c export.c guid.c guids.S guid-symbols.c \
|
||
|
lib.c vars.c
|
||
|
LIBEFIVAR_OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(LIBEFIVAR_SOURCES)))
|
||
|
diff --git a/src/crc32.h b/src/crc32.h
|
||
|
index b5b975a5768..4e833aafac0 100644
|
||
|
--- a/src/crc32.h
|
||
|
+++ b/src/crc32.h
|
||
|
@@ -29,6 +29,25 @@
|
||
|
|
||
|
extern uint32_t crc32 (const void *buf, unsigned long len, uint32_t seed);
|
||
|
|
||
|
+/**
|
||
|
+ * efi_crc32() - EFI version of crc32 function
|
||
|
+ * @buf: buffer to calculate crc32 of
|
||
|
+ * @len - length of buf
|
||
|
+ *
|
||
|
+ * Description: Returns EFI-style CRC32 value for @buf
|
||
|
+ *
|
||
|
+ * This function uses the little endian Ethernet polynomial
|
||
|
+ * but seeds the function with ~0, and xor's with ~0 at the end.
|
||
|
+ * Note, the EFI Specification, v1.02, has a reference to
|
||
|
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
|
||
|
+ */
|
||
|
+static inline uint32_t
|
||
|
+efi_crc32(const void *buf, unsigned long len)
|
||
|
+{
|
||
|
+ return (crc32(buf, len, ~0L) ^ ~0L);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
#endif /* _CRC32_H */
|
||
|
|
||
|
// vim:fenc=utf-8:tw=75:noet
|
||
|
diff --git a/src/efivar.c b/src/efivar.c
|
||
|
index 7f16ab15bab..885a9af864b 100644
|
||
|
--- a/src/efivar.c
|
||
|
+++ b/src/efivar.c
|
||
|
@@ -46,6 +46,8 @@ extern int optind, opterr, optopt;
|
||
|
#define ACTION_LIST_GUIDS 0x08
|
||
|
#define ACTION_WRITE 0x10
|
||
|
#define ACTION_PRINT_DEC 0x20
|
||
|
+#define ACTION_IMPORT 0x40
|
||
|
+#define ACTION_EXPORT 0x80
|
||
|
|
||
|
#define EDIT_APPEND 0
|
||
|
#define EDIT_WRITE 1
|
||
|
@@ -173,31 +175,10 @@ bad_name:
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
-show_variable(char *guid_name, int display_type)
|
||
|
+show_variable_data(efi_guid_t guid, const char *name, uint32_t attributes,
|
||
|
+ uint8_t *data, size_t data_size,
|
||
|
+ int display_type)
|
||
|
{
|
||
|
- efi_guid_t guid = efi_guid_empty;
|
||
|
- char *name = NULL;
|
||
|
- int rc;
|
||
|
-
|
||
|
- uint8_t *data = NULL;
|
||
|
- size_t data_size = 0;
|
||
|
- uint32_t attributes;
|
||
|
-
|
||
|
- parse_name(guid_name, &name, &guid);
|
||
|
- if (!name || efi_guid_is_empty(&guid)) {
|
||
|
- fprintf(stderr, "efivar: could not parse variable name.\n");
|
||
|
- show_errors();
|
||
|
- exit(1);
|
||
|
- }
|
||
|
-
|
||
|
- errno = 0;
|
||
|
- rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
|
||
|
- if (rc < 0) {
|
||
|
- fprintf(stderr, "efivar: show variable: %m\n");
|
||
|
- show_errors();
|
||
|
- exit(1);
|
||
|
- }
|
||
|
-
|
||
|
if (display_type == SHOW_VERBOSE) {
|
||
|
printf("GUID: "GUID_FORMAT "\n",
|
||
|
guid.a, guid.b, guid.c, bswap_16(guid.d),
|
||
|
@@ -257,6 +238,117 @@ show_variable(char *guid_name, int display_type)
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+show_variable(char *guid_name, int display_type)
|
||
|
+{
|
||
|
+ efi_guid_t guid = efi_guid_empty;
|
||
|
+ char *name = NULL;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ uint8_t *data = NULL;
|
||
|
+ size_t data_size = 0;
|
||
|
+ uint32_t attributes;
|
||
|
+
|
||
|
+ parse_name(guid_name, &name, &guid);
|
||
|
+ if (!name || efi_guid_is_empty(&guid)) {
|
||
|
+ fprintf(stderr, "efivar: could not parse variable name.\n");
|
||
|
+ show_errors();
|
||
|
+ exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ errno = 0;
|
||
|
+ rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
|
||
|
+ if (rc < 0) {
|
||
|
+ fprintf(stderr, "efivar: show variable: %m\n");
|
||
|
+ show_errors();
|
||
|
+ exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ show_variable_data(guid, name, attributes,
|
||
|
+ data, data_size, display_type);
|
||
|
+
|
||
|
+ free(name);
|
||
|
+ if (data)
|
||
|
+ free(data);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+save_variable_data(efi_variable_t *var, char *outfile, bool dmpstore)
|
||
|
+{
|
||
|
+ FILE *out = NULL;
|
||
|
+ ssize_t sz;
|
||
|
+ uint8_t *data = NULL;
|
||
|
+ size_t datasz = 0;
|
||
|
+ ssize_t (*export)(efi_variable_t *var, uint8_t *data, size_t size) =
|
||
|
+ dmpstore ? efi_variable_export_dmpstore : efi_variable_export;
|
||
|
+
|
||
|
+ out = fopen(outfile, "w");
|
||
|
+ if (!out)
|
||
|
+ err(1, "Could not open \"%s\" for writing", outfile);
|
||
|
+
|
||
|
+ sz = export(var, data, datasz);
|
||
|
+ data = calloc(sz, 1);
|
||
|
+ if (!data)
|
||
|
+ err(1, "Could not allocate memory");
|
||
|
+ datasz = sz;
|
||
|
+
|
||
|
+ sz = export(var, data, datasz);
|
||
|
+ if (sz < 0)
|
||
|
+ err(1, "Could not format data");
|
||
|
+ datasz = sz;
|
||
|
+
|
||
|
+ sz = fwrite(data, 1, datasz, out);
|
||
|
+ if (sz < (ssize_t)datasz)
|
||
|
+ err(1, "Could not write to \"%s\"", outfile);
|
||
|
+
|
||
|
+ fflush(out);
|
||
|
+ fclose(out);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+save_variable(char *guid_name, char *outfile, bool dmpstore)
|
||
|
+{
|
||
|
+ efi_guid_t guid = efi_guid_empty;
|
||
|
+ char *name = NULL;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ uint8_t *data = NULL;
|
||
|
+ size_t data_size = 0;
|
||
|
+ uint32_t attributes = 7;
|
||
|
+ efi_variable_t *var;
|
||
|
+
|
||
|
+ parse_name(guid_name, &name, &guid);
|
||
|
+ if (!name || efi_guid_is_empty(&guid)) {
|
||
|
+ fprintf(stderr, "efivar: could not parse variable name.\n");
|
||
|
+ show_errors();
|
||
|
+ exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ errno = 0;
|
||
|
+ rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
|
||
|
+ if (rc < 0) {
|
||
|
+ fprintf(stderr, "efivar: show variable: %m\n");
|
||
|
+ show_errors();
|
||
|
+ exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ var = efi_variable_alloc();
|
||
|
+ if (!var) {
|
||
|
+ fprintf(stderr, "efivar: could not allocate variable storage.\n");
|
||
|
+ show_errors();
|
||
|
+ exit(1);
|
||
|
+ }
|
||
|
+
|
||
|
+ efi_variable_set_name(var, (unsigned char *)name);
|
||
|
+ efi_variable_set_guid(var, &guid);
|
||
|
+ efi_variable_set_attributes(var, attributes);
|
||
|
+ efi_variable_set_data(var, data, data_size);
|
||
|
+
|
||
|
+ save_variable_data(var, outfile, dmpstore);
|
||
|
+
|
||
|
+ efi_variable_free(var, false);
|
||
|
|
||
|
free(name);
|
||
|
if (data)
|
||
|
@@ -372,16 +464,18 @@ usage(int ret)
|
||
|
FILE *out = ret == 0 ? stdout : stderr;
|
||
|
fprintf(out,
|
||
|
"Usage: %s [OPTION...]\n"
|
||
|
+ " -t, --attributes=<attributes> attributes to use on append\n"
|
||
|
" -l, --list list current variables\n"
|
||
|
" -p, --print print variable specified by --name\n"
|
||
|
+ " -D, --dmpstore use DMPSTORE format when exporting\n"
|
||
|
" -d, --print-decimal print variable in decimal values specified\n"
|
||
|
" by --name\n"
|
||
|
" -n, --name=<guid-name> variable to manipulate, in the form\n"
|
||
|
" 8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0000\n"
|
||
|
" -a, --append append to variable specified by --name\n"
|
||
|
+ " -f, --datafile=<file> load or save variable contents from <file>\n"
|
||
|
" -e, --export=<file> export variable to <file>\n"
|
||
|
- " -f, --fromfile=<file> use data from <file>\n"
|
||
|
- " -t, --attributes=<attributes> attributes to use on append\n"
|
||
|
+ " -i, --import=<file> import variable from <file\n"
|
||
|
" -L, --list-guids show internal guid list\n"
|
||
|
" -w, --write write to variable specified by --name\n\n"
|
||
|
"Help options:\n"
|
||
|
@@ -398,55 +492,79 @@ int main(int argc, char *argv[])
|
||
|
int action = 0;
|
||
|
uint8_t *data = NULL;
|
||
|
size_t data_size = 0;
|
||
|
- char *name = NULL;
|
||
|
- char *file = NULL;
|
||
|
- uint32_t attributes = 0;
|
||
|
- char *sopts = "lpdn:af:t:Lw?";
|
||
|
- struct option lopts[] =
|
||
|
- { {"list", no_argument, 0, 'l'},
|
||
|
- {"print", no_argument, 0, 'p'},
|
||
|
- {"print-decimal", no_argument, 0, 'd'},
|
||
|
- {"name", required_argument, 0, 'n'},
|
||
|
- {"append", no_argument, 0, 'a'},
|
||
|
- {"fromfile", required_argument, 0, 'f'},
|
||
|
- {"attributes", required_argument, 0, 't'},
|
||
|
- {"list-guids", no_argument, 0, 'L'},
|
||
|
- {"write", no_argument, 0, 'w'},
|
||
|
- {"help", no_argument, 0, '?'},
|
||
|
- {"usage", no_argument, 0, 0},
|
||
|
- {0, 0, 0, 0}
|
||
|
- };
|
||
|
+ char *guid_name = NULL;
|
||
|
+ char *infile = NULL;
|
||
|
+ char *outfile = NULL;
|
||
|
+ char *datafile = NULL;
|
||
|
+ bool dmpstore = false;
|
||
|
+ int verbose = 0;
|
||
|
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE
|
||
|
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
|
||
|
+ | EFI_VARIABLE_RUNTIME_ACCESS;
|
||
|
+ char *sopts = "aA:Dde:f:i:Llpn:vw?";
|
||
|
+ struct option lopts[] = {
|
||
|
+ {"append", no_argument, 0, 'a'},
|
||
|
+ {"attributes", required_argument, 0, 'A'},
|
||
|
+ {"datafile", required_argument, 0, 'f'},
|
||
|
+ {"dmpstore", no_argument, 0, 'D'},
|
||
|
+ {"export", required_argument, 0, 'e'},
|
||
|
+ {"help", no_argument, 0, '?'},
|
||
|
+ {"import", required_argument, 0, 'i'},
|
||
|
+ {"list", no_argument, 0, 'l'},
|
||
|
+ {"list-guids", no_argument, 0, 'L'},
|
||
|
+ {"name", required_argument, 0, 'n'},
|
||
|
+ {"print", no_argument, 0, 'p'},
|
||
|
+ {"print-decimal", no_argument, 0, 'd'},
|
||
|
+ {"usage", no_argument, 0, 0},
|
||
|
+ {"verbose", no_argument, 0, 'v'},
|
||
|
+ {"write", no_argument, 0, 'w'},
|
||
|
+ {0, 0, 0, 0}
|
||
|
+ };
|
||
|
|
||
|
while ((c = getopt_long(argc, argv, sopts, lopts, &i)) != -1) {
|
||
|
switch (c) {
|
||
|
- case 'l':
|
||
|
- action |= ACTION_LIST;
|
||
|
+ case 'A':
|
||
|
+ attributes = strtoul(optarg, NULL, 10);
|
||
|
+ if (errno == ERANGE || errno == EINVAL)
|
||
|
+ err(1,
|
||
|
+ "invalid argument for -t: %s: %m\n",
|
||
|
+ optarg);
|
||
|
break;
|
||
|
- case 'p':
|
||
|
- action |= ACTION_PRINT;
|
||
|
+ case 'a':
|
||
|
+ action |= ACTION_APPEND;
|
||
|
+ break;
|
||
|
+ case 'D':
|
||
|
+ dmpstore = true;
|
||
|
break;
|
||
|
case 'd':
|
||
|
action |= ACTION_PRINT_DEC;
|
||
|
break;
|
||
|
- case 'n':
|
||
|
- name = optarg;
|
||
|
- break;
|
||
|
- case 'a':
|
||
|
- action |= ACTION_APPEND;
|
||
|
+ case 'e':
|
||
|
+ action |= ACTION_EXPORT;
|
||
|
+ outfile = optarg;
|
||
|
break;
|
||
|
case 'f':
|
||
|
- file = optarg;
|
||
|
+ datafile = optarg;
|
||
|
break;
|
||
|
- case 't':
|
||
|
- attributes = strtoul(optarg, NULL, 10);
|
||
|
- if (errno == ERANGE || errno == EINVAL)
|
||
|
- err(1,
|
||
|
- "invalid argument for -t: %s: %m\n",
|
||
|
- optarg);
|
||
|
+ case 'i':
|
||
|
+ action |= ACTION_IMPORT;
|
||
|
+ infile = optarg;
|
||
|
break;
|
||
|
case 'L':
|
||
|
action |= ACTION_LIST_GUIDS;
|
||
|
break;
|
||
|
+ case 'l':
|
||
|
+ action |= ACTION_LIST;
|
||
|
+ break;
|
||
|
+ case 'n':
|
||
|
+ guid_name = optarg;
|
||
|
+ break;
|
||
|
+ case 'p':
|
||
|
+ action |= ACTION_PRINT;
|
||
|
+ break;
|
||
|
+ case 'v':
|
||
|
+ verbose += 1;
|
||
|
+ break;
|
||
|
case 'w':
|
||
|
action |= ACTION_WRITE;
|
||
|
break;
|
||
|
@@ -460,7 +578,9 @@ int main(int argc, char *argv[])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- if (name)
|
||
|
+ efi_set_verbose(verbose, stderr);
|
||
|
+
|
||
|
+ if (guid_name && !outfile)
|
||
|
action |= ACTION_PRINT;
|
||
|
|
||
|
switch (action) {
|
||
|
@@ -468,23 +588,23 @@ int main(int argc, char *argv[])
|
||
|
list_all_variables();
|
||
|
break;
|
||
|
case ACTION_PRINT:
|
||
|
- validate_name(name);
|
||
|
- show_variable(name, SHOW_VERBOSE);
|
||
|
+ validate_name(guid_name);
|
||
|
+ show_variable(guid_name, SHOW_VERBOSE);
|
||
|
break;
|
||
|
case ACTION_PRINT_DEC | ACTION_PRINT:
|
||
|
- validate_name(name);
|
||
|
- show_variable(name, SHOW_DECIMAL);
|
||
|
+ validate_name(guid_name);
|
||
|
+ show_variable(guid_name, SHOW_DECIMAL);
|
||
|
break;
|
||
|
case ACTION_APPEND | ACTION_PRINT:
|
||
|
- validate_name(name);
|
||
|
- prepare_data(file, &data, &data_size);
|
||
|
- edit_variable(name, data, data_size, attributes,
|
||
|
+ validate_name(guid_name);
|
||
|
+ prepare_data(infile, &data, &data_size);
|
||
|
+ edit_variable(guid_name, data, data_size, attributes,
|
||
|
EDIT_APPEND);
|
||
|
break;
|
||
|
case ACTION_WRITE | ACTION_PRINT:
|
||
|
- validate_name(name);
|
||
|
- prepare_data(file, &data, &data_size);
|
||
|
- edit_variable(name, data, data_size, attributes,
|
||
|
+ validate_name(guid_name);
|
||
|
+ prepare_data(infile, &data, &data_size);
|
||
|
+ edit_variable(guid_name, data, data_size, attributes,
|
||
|
EDIT_WRITE);
|
||
|
break;
|
||
|
case ACTION_LIST_GUIDS: {
|
||
|
@@ -509,10 +629,108 @@ int main(int argc, char *argv[])
|
||
|
guid[i].symbol + strlen("efi_guid_"),
|
||
|
guid[i].symbol, guid[i].name);
|
||
|
}
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ case ACTION_EXPORT:
|
||
|
+ if (datafile) {
|
||
|
+ char *name = NULL;
|
||
|
+ efi_guid_t guid = efi_guid_zero;
|
||
|
+ efi_variable_t *var;
|
||
|
+
|
||
|
+ parse_name(guid_name, &name, &guid);
|
||
|
+ prepare_data(datafile, &data, &data_size);
|
||
|
+
|
||
|
+ var = efi_variable_alloc();
|
||
|
+ if (!var)
|
||
|
+ err(1, "Could not allocate memory");
|
||
|
+
|
||
|
+ efi_variable_set_name(var, (unsigned char *)name);
|
||
|
+ efi_variable_set_guid(var, &guid);
|
||
|
+ efi_variable_set_attributes(var, attributes);
|
||
|
+ efi_variable_set_data(var, data, data_size);
|
||
|
+
|
||
|
+ save_variable_data(var, outfile, dmpstore);
|
||
|
+
|
||
|
+ efi_variable_free(var, false);
|
||
|
+ } else {
|
||
|
+ validate_name(guid_name);
|
||
|
+ save_variable(guid_name, outfile, dmpstore);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case ACTION_IMPORT:
|
||
|
+ case ACTION_IMPORT | ACTION_PRINT:
|
||
|
+ case ACTION_IMPORT | ACTION_PRINT | ACTION_PRINT_DEC:
|
||
|
+ {
|
||
|
+ ssize_t sz;
|
||
|
+ efi_variable_t *var = NULL;
|
||
|
+ char *name;
|
||
|
+ efi_guid_t *guid;
|
||
|
+ uint64_t attributes;
|
||
|
+ int display_type = (action & ACTION_PRINT_DEC)
|
||
|
+ ? SHOW_VERBOSE|SHOW_DECIMAL
|
||
|
+ : SHOW_VERBOSE;
|
||
|
+
|
||
|
+
|
||
|
+ prepare_data(infile, &data, &data_size);
|
||
|
+ sz = efi_variable_import(data, data_size, &var);
|
||
|
+ if (sz < 0)
|
||
|
+ err(1, "Could not import data from \"%s\"", infile);
|
||
|
+
|
||
|
+ free(data);
|
||
|
+ data = NULL;
|
||
|
+ data_size = 0;
|
||
|
+
|
||
|
+ name = (char *)efi_variable_get_name(var);
|
||
|
+ efi_variable_get_guid(var, &guid);
|
||
|
+ efi_variable_get_attributes(var, &attributes);
|
||
|
+ efi_variable_get_data(var, &data, &data_size);
|
||
|
+
|
||
|
+ if (datafile) {
|
||
|
+ FILE *out;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ out = fopen(datafile, "w");
|
||
|
+ if (!out)
|
||
|
+ err(1, "Could not open \"%s\" for writing",
|
||
|
+ datafile);
|
||
|
+
|
||
|
+ rc = fwrite(data, data_size, 1, out);
|
||
|
+ if (rc < (long)data_size)
|
||
|
+ err(1, "Could not write to \"%s\"",
|
||
|
+ datafile);
|
||
|
+
|
||
|
+ fclose(out);
|
||
|
+ free(guid_name);
|
||
|
+ }
|
||
|
+ if (action & ACTION_PRINT)
|
||
|
+ show_variable_data(*guid, name,
|
||
|
+ ((uint32_t)(attributes & 0xffffffff)),
|
||
|
+ data, data_size, display_type);
|
||
|
+
|
||
|
+ efi_variable_free(var, false);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ case ACTION_IMPORT | ACTION_EXPORT:
|
||
|
+ {
|
||
|
+ efi_variable_t *var = NULL;
|
||
|
+ ssize_t sz;
|
||
|
+
|
||
|
+ if (datafile)
|
||
|
+ errx(1, "--datafile cannot be used with --import and --export");
|
||
|
+
|
||
|
+ prepare_data(infile, &data, &data_size);
|
||
|
+ sz = efi_variable_import(data, data_size, &var);
|
||
|
+ if (sz < 0)
|
||
|
+ err(1, "Could not import data from \"%s\"", infile);
|
||
|
+
|
||
|
+ save_variable_data(var, outfile, dmpstore);
|
||
|
+
|
||
|
+ efi_variable_free(var, false);
|
||
|
+ break;
|
||
|
+ }
|
||
|
case ACTION_USAGE:
|
||
|
default:
|
||
|
usage(EXIT_FAILURE);
|
||
|
- }
|
||
|
};
|
||
|
|
||
|
return 0;
|
||
|
diff --git a/src/export.c b/src/export.c
|
||
|
index 6b78412cce1..cfb021525ff 100644
|
||
|
--- a/src/export.c
|
||
|
+++ b/src/export.c
|
||
|
@@ -27,7 +27,7 @@
|
||
|
|
||
|
#include "efivar.h"
|
||
|
|
||
|
-#define EFIVAR_MAGIC 0xf3df1597
|
||
|
+#define EFIVAR_MAGIC 0xf3df1597u
|
||
|
|
||
|
#define ATTRS_UNSET 0xa5a5a5a5a5a5a5a5
|
||
|
#define ATTRS_MASK 0xffffffff
|
||
|
@@ -50,12 +50,166 @@ struct efi_variable {
|
||
|
* uint32_t data_len;
|
||
|
* char16_t name[];
|
||
|
* uint8_t data[];
|
||
|
- * uint32_t magic;
|
||
|
+ * uint32_t crc32;
|
||
|
+ * }
|
||
|
+ *
|
||
|
+ * Unfortunately the exported structure from dmpstore is:
|
||
|
+ * struct {
|
||
|
+ * uint32_t name_size; // in bytes
|
||
|
+ * uint32_t data_size; // in bytes
|
||
|
+ * char16_t name[];
|
||
|
+ * efi_guid_t guid;
|
||
|
+ * uint32_t attr;
|
||
|
+ * unit8_t data[];
|
||
|
+ * uint32_t crc32;
|
||
|
* }
|
||
|
*/
|
||
|
|
||
|
-ssize_t NONNULL(1, 3) PUBLIC
|
||
|
-efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
|
||
|
+#ifdef EFIVAR_BUILD_ENVIRONMENT
|
||
|
+#error wtf
|
||
|
+#endif
|
||
|
+
|
||
|
+ssize_t NONNULL(1, 3)
|
||
|
+efi_variable_import_dmpstore(uint8_t *data, size_t size,
|
||
|
+ efi_variable_t **var_out)
|
||
|
+{
|
||
|
+ efi_variable_t var;
|
||
|
+ uint32_t namesz;
|
||
|
+ uint32_t datasz;
|
||
|
+ size_t min = sizeof (uint32_t) /* name size */
|
||
|
+ + sizeof (uint32_t) /* data size */
|
||
|
+ + sizeof (char16_t) /* two bytes of name */
|
||
|
+ + sizeof (efi_guid_t) /* guid */
|
||
|
+ + sizeof (uint32_t) /* attr */
|
||
|
+ + 1 /* one byte of data */
|
||
|
+ + sizeof (uint32_t); /* crc32 */
|
||
|
+ size_t sz = sizeof (uint32_t) /* name size */
|
||
|
+ + sizeof (uint32_t) /* data size */
|
||
|
+ + sizeof (efi_guid_t) /* guid */
|
||
|
+ + sizeof (uint32_t) /* attr */
|
||
|
+ + sizeof (uint32_t); /* crc32 */
|
||
|
+ uint8_t *ptr = data;
|
||
|
+ uint32_t crc;
|
||
|
+ int saved_errno;
|
||
|
+
|
||
|
+ if (size <= min) {
|
||
|
+etoosmall:
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("data size is too small for dmpstore variable (%zu < %zu)",
|
||
|
+ size, min);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ memset(&var, 0, sizeof(var));
|
||
|
+
|
||
|
+ namesz = *(uint32_t *)ptr;
|
||
|
+ debug("namesz:%"PRIu32, namesz);
|
||
|
+ ptr += sizeof(uint32_t);
|
||
|
+
|
||
|
+ if (namesz <= 2) {
|
||
|
+ errno = EINVAL;
|
||
|
+ debug("name size (%"PRIu32") must be greater than 2", namesz);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (namesz % 2 != 0) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("name size (%"PRIu32") cannot be odd", namesz);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ datasz = *(uint32_t *)ptr;
|
||
|
+ ptr += sizeof(uint32_t);
|
||
|
+ debug("datasz:%"PRIu32, datasz);
|
||
|
+
|
||
|
+ if (datasz == 0) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("data size (%"PRIu32") must be nonzero", datasz);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (add(sz, namesz, &sz)) {
|
||
|
+overflow:
|
||
|
+ errno = EOVERFLOW;
|
||
|
+ efi_error("arithmetic overflow computing allocation size");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (add(sz, datasz, &min))
|
||
|
+ goto overflow;
|
||
|
+
|
||
|
+ if (size < min)
|
||
|
+ goto etoosmall;
|
||
|
+ size = min;
|
||
|
+
|
||
|
+ if (!(ptr[namesz - 1] == 0 && ptr[namesz -2] == 0)) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("variable name is not properly terminated.");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ crc = efi_crc32(data, size - sizeof(uint32_t));
|
||
|
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32", expected 0x%"PRIx32,
|
||
|
+ data, size - sizeof(uint32_t), crc,
|
||
|
+ *(uint32_t*)(data + size - sizeof(uint32_t)));
|
||
|
+
|
||
|
+ if (memcmp(data + size - sizeof(uint32_t),
|
||
|
+ &crc, sizeof(uint32_t))) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("crc32 did not match");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ var.name = ucs2_to_utf8(ptr, -1);
|
||
|
+ if (!var.name)
|
||
|
+ goto oom;
|
||
|
+ ptr += namesz;
|
||
|
+
|
||
|
+ var.guid = malloc(sizeof (efi_guid_t));
|
||
|
+ if (!var.guid)
|
||
|
+ goto oom;
|
||
|
+ memcpy(var.guid, ptr, sizeof (efi_guid_t));
|
||
|
+ ptr += sizeof (efi_guid_t);
|
||
|
+
|
||
|
+ var.attrs = *(uint32_t *)ptr;
|
||
|
+ ptr += sizeof(uint32_t);
|
||
|
+
|
||
|
+ var.data_size = datasz;
|
||
|
+ var.data = malloc(datasz);
|
||
|
+ if (!var.data) {
|
||
|
+ efi_error("Could not allocate %"PRIu32" bytes", datasz);
|
||
|
+ goto oom;
|
||
|
+ }
|
||
|
+ memcpy(var.data, ptr, datasz);
|
||
|
+
|
||
|
+ if (!*var_out) {
|
||
|
+ *var_out =malloc(sizeof (var));
|
||
|
+ if (!*var_out)
|
||
|
+ goto oom;
|
||
|
+ memcpy(*var_out, &var, sizeof (var));
|
||
|
+ } else {
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ return size;
|
||
|
+oom:
|
||
|
+ saved_errno = errno;
|
||
|
+
|
||
|
+ if (var.guid)
|
||
|
+ free(var.guid);
|
||
|
+
|
||
|
+ if (var.name)
|
||
|
+ free(var.name);
|
||
|
+
|
||
|
+ if (var.data)
|
||
|
+ free(var.data);
|
||
|
+
|
||
|
+ errno = saved_errno;
|
||
|
+ efi_error("Could not allocate memory");
|
||
|
+ return -1;
|
||
|
+}
|
||
|
+
|
||
|
+ssize_t NONNULL(1, 3)
|
||
|
+efi_variable_import_efivar(uint8_t *data, size_t datasz, efi_variable_t **var_out)
|
||
|
{
|
||
|
efi_variable_t var;
|
||
|
size_t min = sizeof (uint32_t) * 2 /* magic */
|
||
|
@@ -63,47 +217,83 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
|
||
|
+ sizeof (uint64_t) /* attr */
|
||
|
+ sizeof (efi_guid_t) /* guid */
|
||
|
+ sizeof (uint32_t) * 2 /* name_len and data_len */
|
||
|
- + sizeof (char16_t) /* two bytes of name */
|
||
|
- + 1; /* one byte of data */
|
||
|
+ + sizeof (char16_t) /* two bytes of name */
|
||
|
+ + 1 /* one byte of data */
|
||
|
+ + 4; /* crc32 */
|
||
|
+ uint32_t crc;
|
||
|
+ uint8_t *ptr = data;
|
||
|
+ uint32_t magic = EFIVAR_MAGIC;
|
||
|
+ int test;
|
||
|
+
|
||
|
errno = EINVAL;
|
||
|
- if (size <= min)
|
||
|
+ if (datasz <= min)
|
||
|
return -1;
|
||
|
|
||
|
- uint8_t *ptr = data;
|
||
|
- uint32_t magic = EFIVAR_MAGIC;
|
||
|
- if (memcmp(data, &magic, sizeof (uint32_t)) ||
|
||
|
- memcmp(data + size - sizeof (uint32_t), &magic,
|
||
|
- sizeof (uint32_t)))
|
||
|
+ test = memcmp(data, &magic, sizeof (uint32_t));
|
||
|
+ debug("test magic 0: cmp(0x%04x,0x%04x)->%d", *(uint32_t *)data, magic, test);
|
||
|
+ if (test) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("MAGIC for file format did not match.");
|
||
|
return -1;
|
||
|
- size -= sizeof (uint32_t);
|
||
|
+ }
|
||
|
+
|
||
|
ptr += sizeof (uint32_t);
|
||
|
|
||
|
+ debug("test version");
|
||
|
if (*(uint32_t *)ptr == 1) {
|
||
|
ptr += sizeof (uint32_t);
|
||
|
+ debug("version 1");
|
||
|
+
|
||
|
var.attrs = *(uint64_t *)ptr;
|
||
|
- ptr += sizeof (uint32_t);
|
||
|
+ ptr += sizeof (uint64_t);
|
||
|
+ debug("var.attrs:0x%08"PRIx64, var.attrs);
|
||
|
|
||
|
var.guid = malloc(sizeof (efi_guid_t));
|
||
|
if (!var.guid)
|
||
|
return -1;
|
||
|
*var.guid = *(efi_guid_t *)ptr;
|
||
|
ptr += sizeof (efi_guid_t);
|
||
|
+ debug("var.guid:"GUID_FORMAT,
|
||
|
+ var.guid->a, var.guid->b, var.guid->c,
|
||
|
+ bswap_16(var.guid->d),
|
||
|
+ var.guid->e[0], var.guid->e[1], var.guid->e[2],
|
||
|
+ var.guid->e[3], var.guid->e[4], var.guid->e[5]);
|
||
|
|
||
|
uint32_t name_len = *(uint32_t *)ptr;
|
||
|
ptr += sizeof (uint32_t);
|
||
|
+ debug("name_len:%"PRIu32, name_len);
|
||
|
+
|
||
|
uint32_t data_len = *(uint32_t *)ptr;
|
||
|
ptr += sizeof (uint32_t);
|
||
|
+ debug("data_len:%"PRIu32, data_len);
|
||
|
+
|
||
|
+ min -= 3;
|
||
|
+ min += name_len;
|
||
|
+ min += data_len;
|
||
|
|
||
|
- if (name_len < 1 ||
|
||
|
- name_len != ((data + size) - ptr - data_len) ||
|
||
|
+ if (name_len < 2 ||
|
||
|
+ name_len > (datasz - data_len) ||
|
||
|
data_len < 1 ||
|
||
|
- data_len != ((data + size) - ptr - name_len)) {
|
||
|
+ data_len > (datasz - name_len)) {
|
||
|
int saved_errno = errno;
|
||
|
free(var.guid);
|
||
|
errno = saved_errno;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
+ crc = efi_crc32(data, datasz - sizeof(uint32_t));
|
||
|
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32", expected 0x%"PRIx32,
|
||
|
+ data, datasz - sizeof(uint32_t), crc,
|
||
|
+ *(uint32_t*)(data + datasz - sizeof(uint32_t)));
|
||
|
+
|
||
|
+ if (memcmp(data + datasz - sizeof (uint32_t), &crc,
|
||
|
+ sizeof (uint32_t))) {
|
||
|
+ free(var.guid);
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("crc32 did not match");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
var.name = calloc(1, name_len + 1);
|
||
|
if (!var.name) {
|
||
|
int saved_errno = errno;
|
||
|
@@ -115,7 +305,8 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
|
||
|
char16_t *wname = (char16_t *)ptr;
|
||
|
for (uint32_t i = 0; i < name_len; i++)
|
||
|
var.name[i] = wname[i] & 0xff;
|
||
|
- ptr += name_len * 2;
|
||
|
+ ptr += name_len;
|
||
|
+ debug("name:%s", var.name);
|
||
|
|
||
|
var.data_size = data_len;
|
||
|
var.data = malloc(data_len);
|
||
|
@@ -143,31 +334,228 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
- return size;
|
||
|
+ return min;
|
||
|
+}
|
||
|
+
|
||
|
+ssize_t NONNULL(1, 3) PUBLIC
|
||
|
+efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
|
||
|
+{
|
||
|
+ ssize_t rc;
|
||
|
+
|
||
|
+ rc = efi_variable_import_efivar(data, size, var_out);
|
||
|
+ if (rc >= 0)
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ rc = efi_variable_import_dmpstore(data, size, var_out);
|
||
|
+ return rc;
|
||
|
}
|
||
|
|
||
|
ssize_t NONNULL(1) PUBLIC
|
||
|
-efi_variable_export(efi_variable_t *var, uint8_t *data, size_t size)
|
||
|
+efi_variable_export_dmpstore(efi_variable_t *var, uint8_t *data, size_t datasz)
|
||
|
{
|
||
|
- size_t name_len = strlen((char *)var->name);
|
||
|
-
|
||
|
- size_t needed = sizeof (uint32_t) /* magic */
|
||
|
- + sizeof (uint32_t) /* version */
|
||
|
- + sizeof (uint64_t) /* attr */
|
||
|
- + sizeof (efi_guid_t) /* guid */
|
||
|
- + sizeof (uint32_t) /* name_len */
|
||
|
- + sizeof (uint32_t) /* data_len */
|
||
|
- + sizeof (char16_t) * name_len /* name */
|
||
|
- + var->data_size /* data */
|
||
|
- + sizeof (uint32_t); /* magic again */
|
||
|
-
|
||
|
- if (!data || size == 0) {
|
||
|
+ uint32_t tmpu32;
|
||
|
+ ssize_t tmpssz;
|
||
|
+ uint32_t namesz;
|
||
|
+ uint32_t needed = sizeof (uint32_t) /* name_size */
|
||
|
+ + sizeof (uint32_t) /* data_size */
|
||
|
+ + 2 /* name */
|
||
|
+ + sizeof (efi_guid_t) /* guid */
|
||
|
+ + sizeof (uint32_t) /* attrs */
|
||
|
+ + 1 /* data */
|
||
|
+ + 4; /* crc32 */
|
||
|
+ uint8_t *ptr;
|
||
|
+ uint32_t crc;
|
||
|
+
|
||
|
+ if (!var) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ if (!var->name) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var->name cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ if (!var->data) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var->data cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ debug("data: %p datasz: %zu", data, datasz);
|
||
|
+
|
||
|
+ namesz = utf8size(var->name, -1);
|
||
|
+ debug("sizeof(uint16_t):%zd * namesz:%"PRIu32, sizeof(uint16_t), namesz);
|
||
|
+ if (mul(sizeof (uint16_t), namesz, &namesz)) {
|
||
|
+overflow:
|
||
|
+ errno = EOVERFLOW;
|
||
|
+ efi_error("arithmetic overflow computing name size");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ debug("namesz -> %"PRIu32, namesz);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Remove our stand-ins for name size and data size before we add
|
||
|
+ * them back in.
|
||
|
+ */
|
||
|
+ needed -= 3;
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" + namesz:%"PRIu32, needed, namesz);
|
||
|
+ if (add(needed, namesz, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" + var->data_size:%zd", needed, var->data_size);
|
||
|
+ if (add(needed, var->data_size, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ if (!data || datasz == 0) {
|
||
|
+ debug("data: %p datasz: %zd -> returning needed size %"PRIu32,
|
||
|
+ data, datasz, needed);
|
||
|
return needed;
|
||
|
- } else if (size < needed) {
|
||
|
- return needed - size;
|
||
|
}
|
||
|
|
||
|
- uint8_t *ptr = data;
|
||
|
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
|
||
|
+ if (datasz < needed) {
|
||
|
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zu",
|
||
|
+ needed, datasz, needed - datasz);
|
||
|
+ return needed - datasz;
|
||
|
+ }
|
||
|
+
|
||
|
+ ptr = data;
|
||
|
+
|
||
|
+ tmpssz = utf8_to_ucs2(ptr + 8, datasz - 8, true, var->name);
|
||
|
+ if (tmpssz < 0) {
|
||
|
+ efi_error("UTF-8 to UCS-2 conversion failed");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ tmpu32 = tmpssz;
|
||
|
+ tmpu32 *= sizeof(uint16_t);
|
||
|
+
|
||
|
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
|
||
|
+ if (sub(namesz, tmpu32, &tmpu32))
|
||
|
+ goto overflow;
|
||
|
+ debug("tmpu32 -> %"PRIu32, tmpu32);
|
||
|
+
|
||
|
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
|
||
|
+ if (sub(namesz, tmpu32, &namesz))
|
||
|
+ goto overflow;
|
||
|
+ debug("namesz -> %"PRIu32, namesz);
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" - tmpu32:%"PRIu32, needed, tmpu32);
|
||
|
+ if (sub(needed, tmpu32, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
|
||
|
+ if (datasz < needed) {
|
||
|
+ debug("needed: %"PRIu32" datasz: %zd -> returning needed datasz %"PRIu32,
|
||
|
+ needed, datasz, needed);
|
||
|
+ return needed;
|
||
|
+ }
|
||
|
+
|
||
|
+ *(uint32_t *)ptr = namesz;
|
||
|
+ ptr += sizeof (uint32_t);
|
||
|
+
|
||
|
+ *(uint32_t *)ptr = var->data_size;
|
||
|
+ ptr += sizeof (uint32_t);
|
||
|
+
|
||
|
+ ptr += namesz;
|
||
|
+
|
||
|
+ memcpy(ptr, var->guid, sizeof (efi_guid_t));
|
||
|
+ ptr += sizeof(efi_guid_t);
|
||
|
+
|
||
|
+ *(uint32_t *)ptr = var->attrs;
|
||
|
+ ptr += sizeof (uint32_t);
|
||
|
+
|
||
|
+ memcpy(ptr, var->data, var->data_size);
|
||
|
+ ptr += var->data_size;
|
||
|
+
|
||
|
+ crc = efi_crc32(data, needed - sizeof(uint32_t));
|
||
|
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32,
|
||
|
+ data, needed - sizeof(uint32_t), crc);
|
||
|
+ *(uint32_t *)ptr = crc;
|
||
|
+
|
||
|
+ return needed;
|
||
|
+}
|
||
|
+
|
||
|
+ssize_t NONNULL(1) PUBLIC
|
||
|
+efi_variable_export(efi_variable_t *var, uint8_t *data, size_t datasz)
|
||
|
+{
|
||
|
+ uint32_t tmpu32;
|
||
|
+ ssize_t tmpssz;
|
||
|
+ uint32_t namesz;
|
||
|
+ uint32_t needed = sizeof (uint32_t) /* magic */
|
||
|
+ + sizeof (uint32_t) /* version */
|
||
|
+ + sizeof (uint64_t) /* attr */
|
||
|
+ + sizeof (efi_guid_t) /* guid */
|
||
|
+ + sizeof (uint32_t) /* name_len */
|
||
|
+ + sizeof (uint32_t) /* data_len */
|
||
|
+ + 2 /* name */
|
||
|
+ + 1 /* data */
|
||
|
+ + 4; /* crc32 */
|
||
|
+ uint8_t *ptr;
|
||
|
+ uint32_t crc;
|
||
|
+
|
||
|
+ if (!var) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ if (!var->name) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var->name cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ if (!var->data) {
|
||
|
+ errno = EINVAL;
|
||
|
+ efi_error("var->data cannot be NULL");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ debug("data: %p datasz: %zu", data, datasz);
|
||
|
+
|
||
|
+ namesz = utf8size(var->name, -1);
|
||
|
+ debug("sizeof(uint16_t):%zd * namesz:%"PRIu32, sizeof(uint16_t), namesz);
|
||
|
+ if (mul(sizeof (uint16_t), namesz, &namesz)) {
|
||
|
+overflow:
|
||
|
+ errno = EOVERFLOW;
|
||
|
+ efi_error("arithmetic overflow computing name size");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ debug("namesz -> %"PRIu32, namesz);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Remove our stand-ins for name size and data size before we add
|
||
|
+ * them back in.
|
||
|
+ */
|
||
|
+ needed -= 3;
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" + namesz:%"PRIu32, needed, namesz);
|
||
|
+ if (add(needed, namesz, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" + var->data_size:%zd", needed, var->data_size);
|
||
|
+ if (add(needed, var->data_size, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ if (!data || datasz == 0) {
|
||
|
+ debug("data: %p datasz: %zd -> returning needed datasz %"PRIu32,
|
||
|
+ data, datasz, needed);
|
||
|
+ return needed;
|
||
|
+ }
|
||
|
+
|
||
|
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
|
||
|
+ if (datasz < needed) {
|
||
|
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zd",
|
||
|
+ needed, datasz, needed - datasz);
|
||
|
+ return needed - datasz;
|
||
|
+ }
|
||
|
+
|
||
|
+ ptr = data;
|
||
|
|
||
|
*(uint32_t *)ptr = EFIVAR_MAGIC;
|
||
|
ptr += sizeof (uint32_t);
|
||
|
@@ -181,21 +569,51 @@ efi_variable_export(efi_variable_t *var, uint8_t *data, size_t size)
|
||
|
memcpy(ptr, var->guid, sizeof (efi_guid_t));
|
||
|
ptr += sizeof (efi_guid_t);
|
||
|
|
||
|
- *(uint32_t *)ptr = (uint32_t)(sizeof (char16_t) * name_len);
|
||
|
+ tmpssz = utf8_to_ucs2(ptr + 8, datasz - 8, true, var->name);
|
||
|
+ if (tmpssz < 0) {
|
||
|
+ efi_error("UTF-8 to UCS-2 conversion failed");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ tmpu32 = tmpssz;
|
||
|
+ tmpu32 *= sizeof(uint16_t);
|
||
|
+
|
||
|
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
|
||
|
+ if (sub(namesz, tmpu32, &tmpu32))
|
||
|
+ goto overflow;
|
||
|
+ debug("tmpu32 -> %"PRIu32, tmpu32);
|
||
|
+
|
||
|
+ debug("needed:%"PRIu32" - tmpu32:%"PRIu32, needed, tmpu32);
|
||
|
+ if (sub(needed, tmpu32, &needed))
|
||
|
+ goto overflow;
|
||
|
+ debug("needed -> %"PRIu32, needed);
|
||
|
+
|
||
|
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
|
||
|
+ if (sub(namesz, tmpu32, &namesz))
|
||
|
+ goto overflow;
|
||
|
+ debug("namesz -> %"PRIu32, namesz);
|
||
|
+
|
||
|
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
|
||
|
+ if (datasz < needed) {
|
||
|
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zd",
|
||
|
+ needed, datasz, needed - datasz);
|
||
|
+ return needed - datasz;
|
||
|
+ }
|
||
|
+
|
||
|
+ *(uint32_t *)ptr = namesz;
|
||
|
ptr += sizeof (uint32_t);
|
||
|
|
||
|
*(uint32_t *)ptr = var->data_size;
|
||
|
ptr += sizeof (uint32_t);
|
||
|
|
||
|
- for (uint32_t i = 0; i < name_len; i++) {
|
||
|
- *(char16_t *)ptr = var->name[i];
|
||
|
- ptr += sizeof (char16_t);
|
||
|
- }
|
||
|
+ ptr += namesz;
|
||
|
|
||
|
memcpy(ptr, var->data, var->data_size);
|
||
|
ptr += var->data_size;
|
||
|
|
||
|
- *(uint32_t *)ptr = EFIVAR_MAGIC;
|
||
|
+ crc = efi_crc32(data, needed - sizeof(uint32_t));
|
||
|
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32,
|
||
|
+ data, needed - sizeof(uint32_t), crc);
|
||
|
+ *(uint32_t *)ptr = crc;
|
||
|
|
||
|
return needed;
|
||
|
}
|
||
|
diff --git a/src/gpt.c b/src/gpt.c
|
||
|
index aa4055b9812..8babafeb588 100644
|
||
|
--- a/src/gpt.c
|
||
|
+++ b/src/gpt.c
|
||
|
@@ -48,24 +48,6 @@ struct blkdev_ioctl_param {
|
||
|
char * block_contents;
|
||
|
};
|
||
|
|
||
|
-/**
|
||
|
- * efi_crc32() - EFI version of crc32 function
|
||
|
- * @buf: buffer to calculate crc32 of
|
||
|
- * @len - length of buf
|
||
|
- *
|
||
|
- * Description: Returns EFI-style CRC32 value for @buf
|
||
|
- *
|
||
|
- * This function uses the little endian Ethernet polynomial
|
||
|
- * but seeds the function with ~0, and xor's with ~0 at the end.
|
||
|
- * Note, the EFI Specification, v1.02, has a reference to
|
||
|
- * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
|
||
|
- */
|
||
|
-static inline uint32_t
|
||
|
-efi_crc32(const void *buf, unsigned long len)
|
||
|
-{
|
||
|
- return (crc32(buf, len, ~0L) ^ ~0L);
|
||
|
-}
|
||
|
-
|
||
|
/**
|
||
|
* is_pmbr_valid(): test Protective MBR for validity
|
||
|
* @mbr: pointer to a legacy mbr structure
|
||
|
diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h
|
||
|
index 729b6fe80f7..8ad14b9be57 100644
|
||
|
--- a/src/include/efivar/efivar.h
|
||
|
+++ b/src/include/efivar/efivar.h
|
||
|
@@ -139,6 +139,9 @@ extern ssize_t efi_variable_import(uint8_t *data, size_t size,
|
||
|
extern ssize_t efi_variable_export(efi_variable_t *var, uint8_t *data,
|
||
|
size_t size)
|
||
|
__attribute__((__nonnull__ (1)));
|
||
|
+extern ssize_t efi_variable_export_dmpstore(efi_variable_t *var, uint8_t *data,
|
||
|
+ size_t size)
|
||
|
+ __attribute__((__nonnull__ (1)));
|
||
|
|
||
|
extern efi_variable_t *efi_variable_alloc(void)
|
||
|
__attribute__((__visibility__ ("default")));
|
||
|
--
|
||
|
2.26.2
|
||
|
|