1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include "alloc-util.h"
14 #include "chattr-util.h"
19 #include "stdio-util.h"
21 #include "time-util.h"
26 char* efi_variable_path(sd_id128_t vendor
, const char *name
) {
30 "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR
,
31 name
, SD_ID128_FORMAT_VAL(vendor
)) < 0)
40 uint32_t *ret_attribute
,
44 _cleanup_close_
int fd
= -1;
45 _cleanup_free_
char *p
= NULL
;
46 _cleanup_free_
void *buf
= NULL
;
53 p
= efi_variable_path(vendor
, name
);
57 if (!ret_value
&& !ret_size
&& !ret_attribute
) {
58 /* If caller is not interested in anything, just check if the variable exists and is readable
60 if (access(p
, R_OK
) < 0)
66 fd
= open(p
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
70 if (fstat(fd
, &st
) < 0)
74 if (st
.st_size
> 4*1024*1024 + 4)
77 if (ret_value
|| ret_attribute
) {
78 n
= read(fd
, &a
, sizeof(a
));
86 buf
= malloc(st
.st_size
- 4 + 2);
90 n
= read(fd
, buf
, (size_t) st
.st_size
- 4);
93 if (n
!= st
.st_size
- 4)
96 /* Always NUL terminate (2 bytes, to protect UTF-16) */
97 ((char*) buf
)[st
.st_size
- 4] = 0;
98 ((char*) buf
)[st
.st_size
- 4 + 1] = 0;
101 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
102 * with a smaller value. */
108 *ret_value
= TAKE_PTR(buf
);
111 *ret_size
= (size_t) st
.st_size
- 4;
116 int efi_get_variable_string(sd_id128_t vendor
, const char *name
, char **p
) {
117 _cleanup_free_
void *s
= NULL
;
122 r
= efi_get_variable(vendor
, name
, NULL
, &s
, &ss
);
126 x
= utf16_to_utf8(s
, ss
);
134 int efi_set_variable(
143 } _packed_
* _cleanup_free_ buf
= NULL
;
144 _cleanup_free_
char *p
= NULL
;
145 _cleanup_close_
int fd
= -1;
146 bool saved_flags_valid
= false;
147 unsigned saved_flags
;
151 assert(value
|| size
== 0);
153 p
= efi_variable_path(vendor
, name
);
157 /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
158 * them for accidental removal and modification. We are not changing these variables accidentally however,
159 * hence let's unset the bit first. */
161 r
= chattr_path(p
, 0, FS_IMMUTABLE_FL
, &saved_flags
);
162 if (r
< 0 && r
!= -ENOENT
)
163 log_debug_errno(r
, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p
);
165 saved_flags_valid
= r
>= 0;
176 fd
= open(p
, O_WRONLY
|O_CREAT
|O_NOCTTY
|O_CLOEXEC
, 0644);
182 buf
= malloc(sizeof(uint32_t) + size
);
188 buf
->attr
= EFI_VARIABLE_NON_VOLATILE
|EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
189 memcpy(buf
->buf
, value
, size
);
191 r
= loop_write(fd
, buf
, sizeof(uint32_t) + size
, false);
198 if (saved_flags_valid
) {
201 /* Restore the original flags field, just in case */
203 q
= chattr_path(p
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
205 q
= chattr_fd(fd
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
207 log_debug_errno(q
, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p
);
213 int efi_set_variable_string(sd_id128_t vendor
, const char *name
, const char *v
) {
214 _cleanup_free_ char16_t
*u16
= NULL
;
216 u16
= utf8_to_utf16(v
, strlen(v
));
220 return efi_set_variable(vendor
, name
, u16
, (char16_strlen(u16
) + 1) * sizeof(char16_t
));
223 int efi_systemd_options_variable(char **line
) {
229 /* For testing purposes it is sometimes useful to be able to override this */
230 e
= secure_getenv("SYSTEMD_EFI_OPTIONS");
242 r
= efi_get_variable_string(EFI_VENDOR_SYSTEMD
, "SystemdOptions", line
);