diff --git a/src/efivar.c b/src/efivar.c index 1f9d6139..f15a9b9b 100644 --- a/src/efivar.c +++ b/src/efivar.c @@ -53,34 +53,6 @@ static const char *attribute_names[] = { "" }; -static inline void -validate_name(const char *name) -{ - if (name == NULL) { -err: - warnx("Invalid variable name \"%s\"", - (name == NULL) ? "(null)" : name); - show_errors(); - exit(1); - } - if (name[0] == '{') { - const char *next = strchr(name+1, '}'); - if (!next) - goto err; - if (next[1] != '-') - goto err; - if (next[2] == '\000') - goto err; - } else { - if (strlen(name) < 38) - goto err; - if (name[8] != '-' || name[13] != '-' || - name[18] != '-' || name[23] != '-' || - name[36] != '-') - goto err; - } -} - static void list_all_variables(void) { @@ -97,68 +69,6 @@ list_all_variables(void) } } -static void -parse_name(const char *guid_name, char **name, efi_guid_t *guid) -{ - unsigned int guid_len = sizeof("84be9c3e-8a32-42c0-891c-4cd3b072becc"); - char guid_buf[guid_len + 2]; - int rc; - off_t name_pos = 0; - - const char *left, *right; - - validate_name(guid_name); - - left = strchr(guid_name, '{'); - right = strchr(guid_name, '}'); - if (left && right) { - if (right[1] != '-' || right[2] == '\0') { -bad_name: - errno = -EINVAL; - fprintf(stderr, "efivar: invalid name \"%s\"\n", - guid_name); - show_errors(); - exit(1); - } - name_pos = right + 1 - guid_name; - - strncpy(guid_buf, guid_name, name_pos); - guid_buf[name_pos++] = '\0'; - - rc = efi_id_guid_to_guid(guid_buf, guid); - if (rc < 0) - goto bad_name; - } else { - /* it has to be at least the length of the guid, a dash, and - * one more character */ - if (strlen(guid_name) < guid_len + 2) - goto bad_name; - name_pos = guid_len - 1; - - if (guid_name[name_pos] != '-' || guid_name[name_pos+1] == '\0') - goto bad_name; - name_pos++; - - memset(guid_buf, 0, sizeof(guid_buf)); - strncpy(guid_buf, guid_name, guid_len - 1); - - rc = text_to_guid(guid_buf, guid); - if (rc < 0) - goto bad_name; - } - - char *name_buf = NULL; - int name_len; - name_len = strlen(guid_name + name_pos) + 1; - name_buf = calloc(1, name_len); - if (!name_buf) { - fprintf(stderr, "efivar: %m\n"); - exit(1); - } - strcpy(name_buf, guid_name + name_pos); - *name = name_buf; -} - static void show_variable_data(efi_guid_t guid, const char *name, uint32_t attributes, uint8_t *data, size_t data_size, @@ -233,7 +143,7 @@ show_variable(char *guid_name, int display_type) size_t data_size = 0; uint32_t attributes; - parse_name(guid_name, &name, &guid); + efi_parse_name(guid_name, &name, &guid); if (!name || efi_guid_is_empty(&guid)) { fprintf(stderr, "efivar: could not parse variable name.\n"); show_errors(); @@ -301,7 +211,7 @@ save_variable(char *guid_name, char *outfile, bool dmpstore) uint32_t attributes = 7; efi_variable_t *var; - parse_name(guid_name, &name, &guid); + efi_parse_name(guid_name, &name, &guid); if (!name || efi_guid_is_empty(&guid)) { fprintf(stderr, "efivar: could not parse variable name.\n"); show_errors(); @@ -348,7 +258,7 @@ edit_variable(const char *guid_name, void *data, size_t data_size, size_t old_data_size = 0; uint32_t old_attributes = 0; - parse_name(guid_name, &name, &guid); + efi_parse_name(guid_name, &name, &guid); if (!name || efi_guid_is_empty(&guid)) { fprintf(stderr, "efivar: could not parse variable name.\n"); show_errors(); @@ -441,7 +351,7 @@ usage(int ret) " -a, --append append to variable specified by --name\n" " -f, --datafile= load or save variable contents from \n" " -e, --export= export variable to \n" - " -i, --import= import variable from import variable from \n" " -L, --list-guids show internal guid list\n" " -w, --write write to variable specified by --name\n\n" "Help options:\n" @@ -589,7 +499,7 @@ int main(int argc, char *argv[]) efi_guid_t guid = efi_guid_zero; efi_variable_t *var; - parse_name(guid_name, &name, &guid); + efi_parse_name(guid_name, &name, &guid); prepare_data(datafile, &data, &data_size); var = efi_variable_alloc(); diff --git a/src/efivarfs.c b/src/efivarfs.c index 034d6c19..ad2a40f2 100644 --- a/src/efivarfs.c +++ b/src/efivarfs.c @@ -312,6 +312,8 @@ efivarfs_del_variable(efi_guid_t guid, const char *name) if (rc < 0) efi_error("unlink failed"); + efi_save_esp_filename(); + __typeof__(errno) errno_value = errno; free(path); errno = errno_value; @@ -442,6 +444,8 @@ efivarfs_set_variable(efi_guid_t guid, const char *name, const uint8_t *data, goto err; } + efi_save_esp_filename(); + /* we're done */ ret = 0; diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h index b9b265b6..3b3765ba 100644 --- a/src/include/efivar/efivar.h +++ b/src/include/efivar/efivar.h @@ -121,6 +121,9 @@ extern int efi_variable_get_attributes(efi_variable_t *var, uint64_t *attrs) extern int efi_variable_realize(efi_variable_t *var) __attribute__((__nonnull__ (1))); +extern void efi_parse_name(const char *guid_name, char **name, efi_guid_t *guid); +extern void efi_save_esp_filename(void); + #ifndef EFIVAR_BUILD_ENVIRONMENT extern int efi_error_get(unsigned int n, char ** const filename, diff --git a/src/lib.c b/src/lib.c index c17a54d3..297d800b 100644 --- a/src/lib.c +++ b/src/lib.c @@ -16,6 +16,16 @@ #include "efivar.h" +#define VAR2FILE "/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c" +#define EFI_RTSTORAGEVOLATILE "b2ac5fc9-92b7-4acd-aeac-11e818c3130c-RTStorageVolatile" +#define PATH_SIZE 4096 + +static const char *esp_paths[] = { + "/boot", + "/boot/efi", + "/efi" +}; + static int default_probe(void) { return 1; @@ -65,7 +75,7 @@ _efi_set_variable_variadic(efi_guid_t guid, const char *name, const uint8_t *dat VERSION(_efi_set_variable_mode,efi_set_variable@@LIBEFIVAR_0.24) int NONNULL(2, 3) PUBLIC _efi_set_variable_mode(efi_guid_t guid, const char *name, const uint8_t *data, - size_t data_size, uint32_t attributes, mode_t mode) + size_t data_size, uint32_t attributes, mode_t mode) { int rc; if (!ops->set_variable) { @@ -93,7 +103,7 @@ efi_append_variable(efi_guid_t guid, const char *name, const uint8_t *data, int rc; if (!ops->append_variable) { rc = generic_append_variable(guid, name, data, data_size, - attributes); + attributes); if (rc < 0) efi_error("generic_append_variable() failed"); else @@ -145,7 +155,7 @@ efi_get_variable(efi_guid_t guid, const char *name, uint8_t **data, int NONNULL(2, 3) PUBLIC efi_get_variable_attributes(efi_guid_t guid, const char *name, - uint32_t *attributes) + uint32_t *attributes) { int rc; if (!ops->get_variable_attributes) { @@ -227,6 +237,207 @@ efi_variables_supported(void) return 1; } +static inline void +validate_name(const char *name) +{ + if (name == NULL) { +err: + warnx("Invalid variable name \"%s\"", + (name == NULL) ? "(null)" : name); + show_errors(); + exit(1); + } + if (name[0] == '{') { + const char *next = strchr(name+1, '}'); + if (!next) + goto err; + if (next[1] != '-') + goto err; + if (next[2] == '\000') + goto err; + } else { + if (strlen(name) < 38) + goto err; + if (name[8] != '-' || name[13] != '-' || + name[18] != '-' || name[23] != '-' || + name[36] != '-') + goto err; + } +} + +void PUBLIC +efi_parse_name(const char *guid_name, char **name, efi_guid_t *guid) +{ + unsigned int guid_len = sizeof("84be9c3e-8a32-42c0-891c-4cd3b072becc"); + char guid_buf[guid_len + 2]; + int rc; + off_t name_pos = 0; + + const char *left, *right; + + validate_name(guid_name); + + left = strchr(guid_name, '{'); + right = strchr(guid_name, '}'); + if (left && right) { + if (right[1] != '-' || right[2] == '\0') { +bad_name: + errno = -EINVAL; + fprintf(stderr, "efivar: invalid name \"%s\"\n", + guid_name); + show_errors(); + exit(1); + } + name_pos = right + 1 - guid_name; + + strncpy(guid_buf, guid_name, name_pos); + guid_buf[name_pos++] = '\0'; + + rc = efi_id_guid_to_guid(guid_buf, guid); + if (rc < 0) + goto bad_name; + } else { + /* it has to be at least the length of the guid, a dash, and + * one more character */ + if (strlen(guid_name) < guid_len + 2) + goto bad_name; + name_pos = guid_len - 1; + + if (guid_name[name_pos] != '-' || guid_name[name_pos+1] == '\0') + goto bad_name; + name_pos++; + + memset(guid_buf, 0, sizeof(guid_buf)); + strncpy(guid_buf, guid_name, guid_len - 1); + + rc = text_to_guid(guid_buf, guid); + if (rc < 0) + goto bad_name; + } + + char *name_buf = NULL; + int name_len; + name_len = strlen(guid_name + name_pos) + 1; + name_buf = calloc(1, name_len); + if (!name_buf) { + fprintf(stderr, "efivar: %m\n"); + exit(1); + } + strcpy(name_buf, guid_name + name_pos); + *name = name_buf; +} + +static int +get_esp_filename(char **esp_filename) +{ + char *dst_name = NULL; + efi_guid_t dst_guid = efi_guid_empty; + size_t filename_size; + uint32_t filename_attr; + uint8_t *filename = NULL; + int rc = 0; + + efi_parse_name(EFI_RTSTORAGEVOLATILE, &dst_name, &dst_guid); + + if (dst_name && !efi_guid_is_empty(&dst_guid)) { + rc = efi_get_variable(dst_guid, dst_name, &filename, &filename_size, &filename_attr); + if (rc < 0) { + fprintf(stderr, "efivar: Error %d getting filename from RTStorageVolatile\n", rc); + return rc; + } + } + + *esp_filename = (char *)filename; + + free(dst_name); + + return 0; +} + +static void +search_esp_filename(const char *filename, char *esp_filename) +{ + size_t num_paths = sizeof(esp_paths) / sizeof(esp_paths[0]); + + for (size_t i = 0; i < num_paths; ++i) { + snprintf(esp_filename, PATH_SIZE, "%s/%s", esp_paths[i], filename); + + struct stat buffer; + if (stat(esp_filename, &buffer) == 0) { + return; + } + } + + esp_filename[0] = '\0'; +} + +static +void save_esp_filename(const char *esp_filename) { + FILE *var2file = NULL; + FILE *output_file = NULL; + unsigned char buffer[1024]; + size_t bytes_read, bytes_written; + bool fail = false; + + var2file = fopen(VAR2FILE, "rb"); + if (var2file == NULL) { + fprintf(stderr, "Error: Could not open input file '%s'\n", VAR2FILE); + exit(1); + } + + output_file = fopen(esp_filename, "wb"); + if (output_file == NULL) { + fprintf(stderr, "Error: Could not open output file '%s'\n", esp_filename); + fclose(var2file); + exit(1); + } + + if (fread(buffer, 1, 4, var2file) < 4) { + fprintf(stderr, "Error: Could not skip first 4 bytes or file is too small\n"); + fail = true; + goto clean; + } + + while ((bytes_read = fread(buffer, 1, sizeof(buffer), var2file)) > 0) { + bytes_written = fwrite(buffer, 1, bytes_read, output_file); + if (bytes_written != bytes_read) { + fprintf(stderr, "Error: Could not write data to output file\n"); + fail = true; + goto clean; + } + } + +clean: + fclose(var2file); + fclose(output_file); + if (fail) + exit(1); +} + +void PUBLIC +efi_save_esp_filename(void) +{ + char esp_filename_path[PATH_SIZE]; + char *esp_filename = NULL; + int rc = 0; + + rc = get_esp_filename(&esp_filename); + if (rc < 0 || !esp_filename) { + goto cleanup; + } + + search_esp_filename(esp_filename, esp_filename_path); + if (esp_filename_path[0] != '\0') { + save_esp_filename(esp_filename_path); + } + else { + fprintf(stderr, "efivar: no found file in ESP part to save VarToFile\n"); + } + +cleanup: + free(esp_filename); +} + static void CONSTRUCTOR libefivar_init(void); static void CONSTRUCTOR diff --git a/src/libefivar.map.in b/src/libefivar.map.in index 59f1a723..56b50b19 100644 --- a/src/libefivar.map.in +++ b/src/libefivar.map.in @@ -90,6 +90,9 @@ libefivar.so.0 { efi_guid_x509_sha384; efi_guid_x509_sha512; efi_guid_zero; + + efi_parse_name; + efi_save_esp_filename; local: *; }; diff --git a/src/vars.c b/src/vars.c index 1cd23d27..81bedb1b 100644 --- a/src/vars.c +++ b/src/vars.c @@ -435,8 +435,10 @@ vars_del_variable(efi_guid_t guid, const char *name) } rc = write(fd, buf, buf_size); - if (rc >= 0) + if (rc >= 0) { + efi_save_esp_filename(); ret = 0; + } else efi_error("write() failed"); err: @@ -594,8 +596,10 @@ vars_set_variable(efi_guid_t guid, const char *name, const uint8_t *data, rc = write(fd, &var32, sizeof(var32)); } - if (rc >= 0) + if (rc >= 0) { + efi_save_esp_filename(); ret = 0; + } else efi_error("write() failed");