static void delete_variable(char *name) { char *vname; efi_guid_t guid; breakdown_name(name, &guid, &vname); if (efi_del_variable(guid, vname) < 0) err(1, "efi_del_variable"); }
static int efivarfs_set_variable(efi_guid_t guid, const char *name, uint8_t *data, size_t data_size, uint32_t attributes, mode_t mode) { uint8_t buf[sizeof (attributes) + data_size]; typeof(errno) errno_value; int ret = -1; if (strlen(name) > 1024) { errno = EINVAL; return -1; } char *path; int rc = make_efivarfs_path(&path, guid, name); if (rc < 0) return -1; int fd = -1; if (!access(path, F_OK) && !(attributes & EFI_VARIABLE_APPEND_WRITE)) { rc = efi_del_variable(guid, name); if (rc < 0) goto err; } fd = open(path, O_WRONLY|O_CREAT, mode); if (fd < 0) goto err; efivarfs_set_fd_immutable(fd, 0); memcpy(buf, &attributes, sizeof (attributes)); memcpy(buf + sizeof (attributes), data, data_size); rc = write(fd, buf, sizeof (attributes) + data_size); if (rc >= 0) { ret = 0; efivarfs_set_fd_immutable(fd, 1); } else { efivarfs_set_fd_immutable(fd, 0); unlink(path); } err: errno_value = errno; if (path) free(path); if (fd >= 0) close(fd); errno = errno_value; return ret; }
static int vars_set_variable(efi_guid_t guid, const char *name, uint8_t *data, size_t data_size, uint32_t attributes) { int errno_value; int ret = -1; int i = 0; if (strlen(name) > 1024) { errno = EINVAL; return -1; } if (data_size > 1024) { errno = ENOSPC; return -1; } char *path; int rc = asprintf(&path, VARS_PATH "%s-" GUID_FORMAT "/data", name, guid.a, guid.b, guid.c, bswap_16(guid.d), guid.e[0], guid.e[1], guid.e[2], guid.e[3], guid.e[4], guid.e[5]); if (rc < 0) return -1; int fd = -1; if (!access(path, F_OK)) { rc = efi_del_variable(guid, name); if (rc < 0) goto err; } efi_variable_t var = { .VendorGuid = guid, .DataSize = data_size, .Status = 0, .Attributes = attributes }; for (i = 0; name[i] != '\0'; i++) var.VariableName[i] = name[i]; memcpy(var.Data, data, data_size); fd = open(VARS_PATH "new_var", O_WRONLY); if (fd < 0) goto err; rc = write(fd, &var, sizeof(var)); if (rc >= 0) ret = 0; err: errno_value = errno; if (path) free(path); if (fd >= 0) close(fd); errno = errno_value; return ret; } static int vars_get_next_variable_name(efi_guid_t **guid, char **name) { return generic_get_next_variable_name(VARS_PATH, guid, name); } struct efi_var_operations vars_ops = { .probe = vars_probe, .set_variable = vars_set_variable, .del_variable = vars_del_variable, .get_variable = vars_get_variable, .get_variable_attributes = vars_get_variable_attributes, .get_variable_size = vars_get_variable_size, .get_next_variable_name = vars_get_next_variable_name, };
static int get_info(efi_guid_t *guid, uint64_t hw_inst, update_info **info) { efi_guid_t varguid = FWUPDATE_GUID; char *varname = NULL; char *guidstr = NULL; int rc; update_info *local; int error; rc = efi_guid_to_str(guid, &guidstr); if (rc < 0) return -1; guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, hw_inst); if (rc < 0) return -1; varname = onstack(varname, strlen(varname)+1); uint8_t *data = NULL; size_t data_size = 0; uint32_t attributes; rc = efi_get_variable(varguid, varname, &data, &data_size, &attributes); if (rc < 0) { if (errno != ENOENT) return -1; local = calloc(1, sizeof (*local)); if (!local) return -1; local->update_info_version = UPDATE_INFO_VERSION; local->guid = *guid; local->hw_inst = hw_inst; local->dp_ptr = calloc(1, 1024); if (!local->dp_ptr) { alloc_err: error = errno; free_info(local); errno = error; return -1; } ssize_t sz; sz = efidp_make_end_entire((uint8_t *)local->dp_ptr, 1024); if (sz < 0) { rc = sz; goto alloc_err; } *info = local; return 0; } /* If our size is wrong, or our data is otherwise bad, try to delete * the variable and create a new one. */ if (data_size < sizeof (*local) || !data) { if (data) free(data); get_err: rc = efi_del_variable(varguid, varname); if (rc < 0) return -1; return get_info(guid, hw_inst, info); } local = (update_info *)data; if (local->update_info_version != UPDATE_INFO_VERSION) goto get_err; ssize_t sz = efidp_size((efidp)local->dp); if (sz < 0) { free(data); errno = EINVAL; return -1; } efidp_header *dp = malloc((size_t)sz); if (!dp) { free(data); errno = ENOMEM; return -1; } memcpy(dp, local->dp, (size_t)sz); local->dp_ptr = dp; *info = local; return 0; }