1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "device-path-util.h"
6 #include "proto/device-path.h"
9 static char *write_cpio_word(char *p
, uint32_t v
) {
10 static const char hex
[] = "0123456789abcdef";
14 /* Writes a CPIO header 8 character hex value */
16 for (size_t i
= 0; i
< 8; i
++)
17 p
[7-i
] = hex
[(v
>> (4 * i
)) & 0xF];
22 static char *mangle_filename(char *p
, const char16_t
*f
) {
28 /* Basically converts UTF-16 to plain ASCII (note that we filtered non-ASCII filenames beforehand, so
29 * this operation is always safe) */
31 for (w
= p
; *f
!= 0; f
++) {
41 static char *pad4(char *p
, const char *start
) {
46 /* Appends NUL bytes to 'p', until the address is divisible by 4, when taken relative to 'start' */
48 while ((p
- start
) % 4 != 0)
54 static EFI_STATUS
pack_cpio_one(
55 const char16_t
*fname
,
58 const char *target_dir_prefix
,
60 uint32_t *inode_counter
,
62 size_t *cpio_buffer_size
) {
64 size_t l
, target_dir_prefix_size
, fname_size
, q
;
68 assert(contents_size
|| contents_size
== 0);
69 assert(target_dir_prefix
);
70 assert(inode_counter
);
72 assert(cpio_buffer_size
);
74 /* Serializes one file in the cpio format understood by the kernel initrd logic.
76 * See: https://docs.kernel.org/driver-api/early-userspace/buffer-format.html */
78 if (contents_size
> UINT32_MAX
) /* cpio cannot deal with > 32-bit file sizes */
79 return EFI_LOAD_ERROR
;
81 if (*inode_counter
== UINT32_MAX
) /* more than 2^32-1 inodes? yikes. cpio doesn't support that either */
82 return EFI_OUT_OF_RESOURCES
;
84 l
= 6 + 13*8 + 1 + 1; /* Fixed CPIO header size, slash separator, and NUL byte after the file name */
86 target_dir_prefix_size
= strlen8(target_dir_prefix
);
87 if (l
> SIZE_MAX
- target_dir_prefix_size
)
88 return EFI_OUT_OF_RESOURCES
;
89 l
+= target_dir_prefix_size
;
91 fname_size
= strlen16(fname
);
92 if (l
> SIZE_MAX
- fname_size
)
93 return EFI_OUT_OF_RESOURCES
;
94 l
+= fname_size
; /* append space for file name */
96 /* CPIO can't deal with fnames longer than 2^32-1 */
97 if (target_dir_prefix_size
+ fname_size
>= UINT32_MAX
)
98 return EFI_OUT_OF_RESOURCES
;
100 /* Align the whole header to 4 byte size */
102 if (l
== SIZE_MAX
) /* overflow check */
103 return EFI_OUT_OF_RESOURCES
;
105 /* Align the contents to 4 byte size */
106 q
= ALIGN4(contents_size
);
107 if (q
== SIZE_MAX
) /* overflow check */
108 return EFI_OUT_OF_RESOURCES
;
110 if (l
> SIZE_MAX
- q
) /* overflow check */
111 return EFI_OUT_OF_RESOURCES
;
112 l
+= q
; /* Add contents to header */
114 if (*cpio_buffer_size
> SIZE_MAX
- l
) /* overflow check */
115 return EFI_OUT_OF_RESOURCES
;
116 a
= xrealloc(*cpio_buffer
, *cpio_buffer_size
, *cpio_buffer_size
+ l
);
119 a
= (char *) *cpio_buffer
+ *cpio_buffer_size
;
121 a
= mempcpy(a
, "070701", 6); /* magic ID */
123 a
= write_cpio_word(a
, (*inode_counter
)++); /* inode */
124 a
= write_cpio_word(a
, access_mode
| 0100000 /* = S_IFREG */); /* mode */
125 a
= write_cpio_word(a
, 0); /* uid */
126 a
= write_cpio_word(a
, 0); /* gid */
127 a
= write_cpio_word(a
, 1); /* nlink */
129 /* Note: we don't make any attempt to propagate the mtime here, for two reasons: it's a mess given
130 * that FAT usually is assumed to operate with timezoned timestamps, while UNIX does not. More
131 * importantly though: the modifications times would hamper our goals of providing stable
132 * measurements for the same boots. After all we extend the initrds we generate here into TPM2
134 a
= write_cpio_word(a
, 0); /* mtime */
135 a
= write_cpio_word(a
, contents_size
); /* size */
136 a
= write_cpio_word(a
, 0); /* major(dev) */
137 a
= write_cpio_word(a
, 0); /* minor(dev) */
138 a
= write_cpio_word(a
, 0); /* major(rdev) */
139 a
= write_cpio_word(a
, 0); /* minor(rdev) */
140 a
= write_cpio_word(a
, target_dir_prefix_size
+ fname_size
+ 2); /* fname size */
141 a
= write_cpio_word(a
, 0); /* "crc" */
143 a
= mempcpy(a
, target_dir_prefix
, target_dir_prefix_size
);
145 a
= mangle_filename(a
, fname
);
147 /* Pad to next multiple of 4 */
148 a
= pad4(a
, *cpio_buffer
);
150 a
= mempcpy(a
, contents
, contents_size
);
152 /* Pad to next multiple of 4 */
153 a
= pad4(a
, *cpio_buffer
);
155 assert(a
== (char *) *cpio_buffer
+ *cpio_buffer_size
+ l
);
156 *cpio_buffer_size
+= l
;
161 static EFI_STATUS
pack_cpio_dir(
163 uint32_t access_mode
,
164 uint32_t *inode_counter
,
166 size_t *cpio_buffer_size
) {
172 assert(inode_counter
);
174 assert(cpio_buffer_size
);
176 /* Serializes one directory inode in cpio format. Note that cpio archives must first create the dirs
177 * they want to place files in. */
179 if (*inode_counter
== UINT32_MAX
)
180 return EFI_OUT_OF_RESOURCES
;
182 l
= 6 + 13*8 + 1; /* Fixed CPIO header size, and NUL byte after the file name */
184 path_size
= strlen8(path
);
185 if (l
> SIZE_MAX
- path_size
)
186 return EFI_OUT_OF_RESOURCES
;
189 /* Align the whole header to 4 byte size */
191 if (l
== SIZE_MAX
) /* overflow check */
192 return EFI_OUT_OF_RESOURCES
;
194 if (*cpio_buffer_size
> SIZE_MAX
- l
) /* overflow check */
195 return EFI_OUT_OF_RESOURCES
;
197 *cpio_buffer
= a
= xrealloc(*cpio_buffer
, *cpio_buffer_size
, *cpio_buffer_size
+ l
);
198 a
= (char *) *cpio_buffer
+ *cpio_buffer_size
;
200 a
= mempcpy(a
, "070701", 6); /* magic ID */
202 a
= write_cpio_word(a
, (*inode_counter
)++); /* inode */
203 a
= write_cpio_word(a
, access_mode
| 0040000 /* = S_IFDIR */); /* mode */
204 a
= write_cpio_word(a
, 0); /* uid */
205 a
= write_cpio_word(a
, 0); /* gid */
206 a
= write_cpio_word(a
, 1); /* nlink */
207 a
= write_cpio_word(a
, 0); /* mtime */
208 a
= write_cpio_word(a
, 0); /* size */
209 a
= write_cpio_word(a
, 0); /* major(dev) */
210 a
= write_cpio_word(a
, 0); /* minor(dev) */
211 a
= write_cpio_word(a
, 0); /* major(rdev) */
212 a
= write_cpio_word(a
, 0); /* minor(rdev) */
213 a
= write_cpio_word(a
, path_size
+ 1); /* fname size */
214 a
= write_cpio_word(a
, 0); /* "crc" */
216 a
= mempcpy(a
, path
, path_size
+ 1);
218 /* Pad to next multiple of 4 */
219 a
= pad4(a
, *cpio_buffer
);
221 assert(a
== (char *) *cpio_buffer
+ *cpio_buffer_size
+ l
);
223 *cpio_buffer_size
+= l
;
227 static EFI_STATUS
pack_cpio_prefix(
230 uint32_t *inode_counter
,
232 size_t *cpio_buffer_size
) {
237 assert(inode_counter
);
239 assert(cpio_buffer_size
);
241 /* Serializes directory inodes of all prefix paths of the specified path in cpio format. Note that
242 * (similar to mkdir -p behaviour) all leading paths are created with 0555 access mode, only the
243 * final dir is created with the specified directory access mode. */
245 for (const char *p
= path
;;) {
253 _cleanup_free_
char *t
= NULL
;
255 t
= xstrndup8(path
, e
- path
);
257 return EFI_OUT_OF_RESOURCES
;
259 err
= pack_cpio_dir(t
, 0555, inode_counter
, cpio_buffer
, cpio_buffer_size
);
260 if (err
!= EFI_SUCCESS
)
267 return pack_cpio_dir(path
, dir_mode
, inode_counter
, cpio_buffer
, cpio_buffer_size
);
270 static EFI_STATUS
pack_cpio_trailer(
272 size_t *cpio_buffer_size
) {
274 static const char trailer
[] =
289 "TRAILER!!!\0\0\0"; /* There's a fourth NUL byte appended here, because this is a string */
291 /* Generates the cpio trailer record that indicates the end of our initrd cpio archive */
294 assert(cpio_buffer_size
);
295 assert_cc(sizeof(trailer
) % 4 == 0);
297 *cpio_buffer
= xrealloc(*cpio_buffer
, *cpio_buffer_size
, *cpio_buffer_size
+ sizeof(trailer
));
298 memcpy((uint8_t*) *cpio_buffer
+ *cpio_buffer_size
, trailer
, sizeof(trailer
));
299 *cpio_buffer_size
+= sizeof(trailer
);
304 EFI_STATUS
pack_cpio(
305 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
,
306 const char16_t
*dropin_dir
,
307 const char16_t
*match_suffix
,
308 const char *target_dir_prefix
,
310 uint32_t access_mode
,
312 const char16_t
*tpm_description
,
314 size_t *ret_buffer_size
,
315 bool *ret_measured
) {
317 _cleanup_(file_closep
) EFI_FILE
*root
= NULL
, *extra_dir
= NULL
;
318 size_t dirent_size
= 0, buffer_size
= 0, n_items
= 0, n_allocated
= 0;
319 _cleanup_free_ char16_t
*rel_dropin_dir
= NULL
;
320 _cleanup_free_ EFI_FILE_INFO
*dirent
= NULL
;
321 _cleanup_(strv_freep
) char16_t
**items
= NULL
;
322 _cleanup_free_
void *buffer
= NULL
;
323 uint32_t inode
= 1; /* inode counter, so that each item gets a new inode */
326 assert(loaded_image
);
327 assert(target_dir_prefix
);
329 assert(ret_buffer_size
);
331 if (!loaded_image
->DeviceHandle
)
334 err
= open_volume(loaded_image
->DeviceHandle
, &root
);
335 if (err
== EFI_UNSUPPORTED
)
336 /* Error will be unsupported if the bootloader doesn't implement the file system protocol on
337 * its file handles. */
339 if (err
!= EFI_SUCCESS
)
340 return log_error_status(err
, "Unable to open root directory: %m");
343 dropin_dir
= rel_dropin_dir
= get_extra_dir(loaded_image
->FilePath
);
345 err
= open_directory(root
, dropin_dir
, &extra_dir
);
346 if (err
== EFI_NOT_FOUND
)
347 /* No extra subdir, that's totally OK */
349 if (err
!= EFI_SUCCESS
)
350 return log_error_status(err
, "Failed to open extra directory of loaded image: %m");
353 _cleanup_free_ char16_t
*d
= NULL
;
355 err
= readdir(extra_dir
, &dirent
, &dirent_size
);
356 if (err
!= EFI_SUCCESS
)
357 return log_error_status(err
, "Failed to read extra directory of loaded image: %m");
358 if (!dirent
) /* End of directory */
361 if (dirent
->FileName
[0] == '.')
363 if (FLAGS_SET(dirent
->Attribute
, EFI_FILE_DIRECTORY
))
365 if (match_suffix
&& !endswith_no_case(dirent
->FileName
, match_suffix
))
367 if (!is_ascii(dirent
->FileName
))
369 if (strlen16(dirent
->FileName
) > 255) /* Max filename size on Linux */
372 d
= xstrdup16(dirent
->FileName
);
374 if (n_items
+2 > n_allocated
) {
375 /* We allocate 16 entries at a time, as a matter of optimization */
376 if (n_items
> (SIZE_MAX
/ sizeof(uint16_t)) - 16) /* Overflow check, just in case */
379 size_t m
= n_items
+ 16;
380 items
= xrealloc(items
, n_allocated
* sizeof(uint16_t *), m
* sizeof(uint16_t *));
384 items
[n_items
++] = TAKE_PTR(d
);
385 items
[n_items
] = NULL
; /* Let's always NUL terminate, to make freeing via strv_free() easy */
389 /* Empty directory */
392 /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
393 * are not dependent on read order) */
394 sort_pointer_array((void**) items
, n_items
, (compare_pointer_func_t
) strcmp16
);
396 /* Generate the leading directory inodes right before adding the first files, to the
397 * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
398 err
= pack_cpio_prefix(target_dir_prefix
, dir_mode
, &inode
, &buffer
, &buffer_size
);
399 if (err
!= EFI_SUCCESS
)
400 return log_error_status(err
, "Failed to pack cpio prefix: %m");
402 for (size_t i
= 0; i
< n_items
; i
++) {
403 _cleanup_free_
char *content
= NULL
;
404 size_t contentsize
= 0; /* avoid false maybe-uninitialized warning */
406 err
= file_read(extra_dir
, items
[i
], 0, 0, &content
, &contentsize
);
407 if (err
!= EFI_SUCCESS
) {
408 log_error_status(err
, "Failed to read %ls, ignoring: %m", items
[i
]);
414 content
, contentsize
,
418 &buffer
, &buffer_size
);
419 if (err
!= EFI_SUCCESS
)
420 return log_error_status(err
, "Failed to pack cpio file %ls: %m", dirent
->FileName
);
423 err
= pack_cpio_trailer(&buffer
, &buffer_size
);
424 if (err
!= EFI_SUCCESS
)
425 return log_error_status(err
, "Failed to pack cpio trailer: %m");
428 tpm_pcr
, POINTER_TO_PHYSICAL_ADDRESS(buffer
), buffer_size
, tpm_description
, ret_measured
);
429 if (err
!= EFI_SUCCESS
)
430 return log_error_status(
432 "Unable to add cpio TPM measurement for PCR %u (%ls), ignoring: %m",
436 *ret_buffer
= TAKE_PTR(buffer
);
437 *ret_buffer_size
= buffer_size
;
443 *ret_buffer_size
= 0;
446 *ret_measured
= false;
451 EFI_STATUS
pack_cpio_literal(
454 const char *target_dir_prefix
,
455 const char16_t
*target_filename
,
457 uint32_t access_mode
,
459 const char16_t
*tpm_description
,
461 size_t *ret_buffer_size
,
462 bool *ret_measured
) {
464 uint32_t inode
= 1; /* inode counter, so that each item gets a new inode */
465 _cleanup_free_
void *buffer
= NULL
;
466 size_t buffer_size
= 0;
469 assert(data
|| data_size
== 0);
470 assert(target_dir_prefix
);
471 assert(target_filename
);
473 assert(ret_buffer_size
);
475 /* Generate the leading directory inodes right before adding the first files, to the
476 * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
478 err
= pack_cpio_prefix(target_dir_prefix
, dir_mode
, &inode
, &buffer
, &buffer_size
);
479 if (err
!= EFI_SUCCESS
)
480 return log_error_status(err
, "Failed to pack cpio prefix: %m");
488 &buffer
, &buffer_size
);
489 if (err
!= EFI_SUCCESS
)
490 return log_error_status(err
, "Failed to pack cpio file %ls: %m", target_filename
);
492 err
= pack_cpio_trailer(&buffer
, &buffer_size
);
493 if (err
!= EFI_SUCCESS
)
494 return log_error_status(err
, "Failed to pack cpio trailer: %m");
497 tpm_pcr
, POINTER_TO_PHYSICAL_ADDRESS(buffer
), buffer_size
, tpm_description
, ret_measured
);
498 if (err
!= EFI_SUCCESS
)
499 return log_error_status(
501 "Unable to add cpio TPM measurement for PCR %u (%ls), ignoring: %m",
505 *ret_buffer
= TAKE_PTR(buffer
);
506 *ret_buffer_size
= buffer_size
;