1 /* SPDX-License-Identifier: LGPL-2.1+ */
14 #include "alloc-util.h"
15 #include "chattr-util.h"
20 #include "stdio-util.h"
22 #include "time-util.h"
27 char* efi_variable_path(sd_id128_t vendor
, const char *name
) {
31 "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR
,
32 name
, SD_ID128_FORMAT_VAL(vendor
)) < 0)
41 uint32_t *ret_attribute
,
45 _cleanup_close_
int fd
= -1;
46 _cleanup_free_
char *p
= NULL
;
47 _cleanup_free_
void *buf
= NULL
;
54 p
= efi_variable_path(vendor
, name
);
58 if (!ret_value
&& !ret_size
&& !ret_attribute
) {
59 /* If caller is not interested in anything, just check if the variable exists and is readable
61 if (access(p
, R_OK
) < 0)
67 fd
= open(p
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
71 if (fstat(fd
, &st
) < 0)
75 if (st
.st_size
> 4*1024*1024 + 4)
78 if (ret_value
|| ret_attribute
) {
79 n
= read(fd
, &a
, sizeof(a
));
87 buf
= malloc(st
.st_size
- 4 + 2);
91 n
= read(fd
, buf
, (size_t) st
.st_size
- 4);
94 if (n
!= st
.st_size
- 4)
97 /* Always NUL terminate (2 bytes, to protect UTF-16) */
98 ((char*) buf
)[st
.st_size
- 4] = 0;
99 ((char*) buf
)[st
.st_size
- 4 + 1] = 0;
102 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
103 * with a smaller value. */
109 *ret_value
= TAKE_PTR(buf
);
112 *ret_size
= (size_t) st
.st_size
- 4;
117 int efi_get_variable_string(sd_id128_t vendor
, const char *name
, char **p
) {
118 _cleanup_free_
void *s
= NULL
;
123 r
= efi_get_variable(vendor
, name
, NULL
, &s
, &ss
);
127 x
= utf16_to_utf8(s
, ss
);
135 int efi_set_variable(
144 } _packed_
* _cleanup_free_ buf
= NULL
;
145 _cleanup_free_
char *p
= NULL
;
146 _cleanup_close_
int fd
= -1;
147 bool saved_flags_valid
= false;
148 unsigned saved_flags
;
152 assert(value
|| size
== 0);
154 p
= efi_variable_path(vendor
, name
);
158 /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
159 * them for accidental removal and modification. We are not changing these variables accidentally however,
160 * hence let's unset the bit first. */
162 r
= chattr_path(p
, 0, FS_IMMUTABLE_FL
, &saved_flags
);
163 if (r
< 0 && r
!= -ENOENT
)
164 log_debug_errno(r
, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p
);
166 saved_flags_valid
= r
>= 0;
177 fd
= open(p
, O_WRONLY
|O_CREAT
|O_NOCTTY
|O_CLOEXEC
, 0644);
183 buf
= malloc(sizeof(uint32_t) + size
);
189 buf
->attr
= EFI_VARIABLE_NON_VOLATILE
|EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
190 memcpy(buf
->buf
, value
, size
);
192 r
= loop_write(fd
, buf
, sizeof(uint32_t) + size
, false);
199 if (saved_flags_valid
) {
202 /* Restore the original flags field, just in case */
204 q
= chattr_path(p
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
206 q
= chattr_fd(fd
, saved_flags
, FS_IMMUTABLE_FL
, NULL
);
208 log_debug_errno(q
, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p
);
214 int efi_set_variable_string(sd_id128_t vendor
, const char *name
, const char *v
) {
215 _cleanup_free_ char16_t
*u16
= NULL
;
217 u16
= utf8_to_utf16(v
, strlen(v
));
221 return efi_set_variable(vendor
, name
, u16
, (char16_strlen(u16
) + 1) * sizeof(char16_t
));
224 int efi_systemd_options_variable(char **line
) {
230 /* For testing purposes it is sometimes useful to be able to override this */
231 e
= secure_getenv("SYSTEMD_EFI_OPTIONS");
243 r
= efi_get_variable_string(EFI_VENDOR_SYSTEMD
, "SystemdOptions", line
);