]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysupdate/sysupdate-resource.c
fd-uitl: rename PIPE_EBADF → EBADF_PAIR, and add EBADF_TRIPLET
[thirdparty/systemd.git] / src / sysupdate / sysupdate-resource.c
CommitLineData
43cc7a3e
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <fcntl.h>
4#include <sys/stat.h>
5#include <unistd.h>
6
7#include "alloc-util.h"
8#include "blockdev-util.h"
f461a28d 9#include "chase.h"
ca822829 10#include "device-util.h"
f8a7112c 11#include "devnum-util.h"
43cc7a3e
LP
12#include "dirent-util.h"
13#include "env-util.h"
14#include "fd-util.h"
15#include "fileio.h"
0470f919 16#include "find-esp.h"
43cc7a3e
LP
17#include "glyph-util.h"
18#include "gpt.h"
19#include "hexdecoct.h"
20#include "import-util.h"
21#include "macro.h"
22#include "process-util.h"
23#include "sort-util.h"
43cc7a3e
LP
24#include "string-table.h"
25#include "sysupdate-cache.h"
26#include "sysupdate-instance.h"
27#include "sysupdate-pattern.h"
28#include "sysupdate-resource.h"
29#include "sysupdate.h"
30#include "utf8.h"
31
32void resource_destroy(Resource *rr) {
33 assert(rr);
34
35 free(rr->path);
36 strv_free(rr->patterns);
37
38 for (size_t i = 0; i < rr->n_instances; i++)
39 instance_free(rr->instances[i]);
40 free(rr->instances);
41}
42
43static int resource_add_instance(
44 Resource *rr,
45 const char *path,
46 const InstanceMetadata *f,
47 Instance **ret) {
48
49 Instance *i;
50 int r;
51
52 assert(rr);
53 assert(path);
54 assert(f);
55 assert(f->version);
56
57 if (!GREEDY_REALLOC(rr->instances, rr->n_instances + 1))
58 return log_oom();
59
60 r = instance_new(rr, path, f, &i);
61 if (r < 0)
62 return r;
63
64 rr->instances[rr->n_instances++] = i;
65
66 if (ret)
67 *ret = i;
68
69 return 0;
70}
71
8b051623 72static int resource_load_from_directory_recursive(
43cc7a3e 73 Resource *rr,
8b051623
VD
74 DIR* d,
75 const char* relpath,
43cc7a3e 76 mode_t m) {
43cc7a3e
LP
77 int r;
78
43cc7a3e
LP
79 for (;;) {
80 _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
8b051623 81 _cleanup_free_ char *joined = NULL, *rel_joined = NULL;
43cc7a3e
LP
82 Instance *instance;
83 struct dirent *de;
84 struct stat st;
85
86 errno = 0;
87 de = readdir_no_dot(d);
88 if (!de) {
89 if (errno != 0)
90 return log_error_errno(errno, "Failed to read directory '%s': %m", rr->path);
91 break;
92 }
93
94 switch (de->d_type) {
95
96 case DT_UNKNOWN:
97 break;
98
99 case DT_DIR:
8b051623 100 if (!IN_SET(m, S_IFDIR, S_IFREG))
43cc7a3e
LP
101 continue;
102
103 break;
104
105 case DT_REG:
106 if (m != S_IFREG)
107 continue;
108 break;
109
110 default:
111 continue;
112 }
113
114 if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT) < 0) {
115 if (errno == ENOENT) /* Gone by now? */
116 continue;
117
118 return log_error_errno(errno, "Failed to stat %s/%s: %m", rr->path, de->d_name);
119 }
120
8b051623 121 if (!(S_ISDIR(st.st_mode) && S_ISREG(m)) && ((st.st_mode & S_IFMT) != m))
43cc7a3e
LP
122 continue;
123
8b051623
VD
124 rel_joined = path_join(relpath, de->d_name);
125 if (!rel_joined)
126 return log_oom();
127
128 r = pattern_match_many(rr->patterns, rel_joined, &extracted_fields);
129 if (r == PATTERN_MATCH_RETRY) {
130 _cleanup_closedir_ DIR *subdir = NULL;
131
132 subdir = xopendirat(dirfd(d), rel_joined, 0);
133 if (!subdir)
134 continue;
135
136 r = resource_load_from_directory_recursive(rr, subdir, rel_joined, m);
137 if (r < 0)
138 return r;
139 if (r == 0)
140 continue;
141 }
142 else if (r < 0)
43cc7a3e 143 return log_error_errno(r, "Failed to match pattern: %m");
8b051623
VD
144 else if (r == PATTERN_MATCH_NO)
145 continue;
146
147 if (de->d_type == DT_DIR && m != S_IFDIR)
43cc7a3e
LP
148 continue;
149
8b051623 150 joined = path_join(rr->path, rel_joined);
43cc7a3e
LP
151 if (!joined)
152 return log_oom();
153
154 r = resource_add_instance(rr, joined, &extracted_fields, &instance);
155 if (r < 0)
156 return r;
157
158 /* Inherit these from the source, if not explicitly overwritten */
159 if (instance->metadata.mtime == USEC_INFINITY)
160 instance->metadata.mtime = timespec_load(&st.st_mtim) ?: USEC_INFINITY;
161
162 if (instance->metadata.mode == MODE_INVALID)
163 instance->metadata.mode = st.st_mode & 0775; /* mask out world-writability and suid and stuff, for safety */
164 }
165
166 return 0;
167}
168
8b051623
VD
169static int resource_load_from_directory(
170 Resource *rr,
171 mode_t m) {
172 _cleanup_closedir_ DIR *d = NULL;
173
174 assert(rr);
175 assert(IN_SET(rr->type, RESOURCE_TAR, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
176 assert(IN_SET(m, S_IFREG, S_IFDIR));
177
178 d = opendir(rr->path);
179 if (!d) {
180 if (errno == ENOENT) {
181 log_debug_errno(errno, "Directory %s does not exist, not loading any resources: %m", rr->path);
182 return 0;
183 }
184
185 return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path);
186 }
187
188 return resource_load_from_directory_recursive(rr, d, NULL, m);
189}
190
43cc7a3e
LP
191static int resource_load_from_blockdev(Resource *rr) {
192 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
193 _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
194 size_t n_partitions;
195 int r;
196
197 assert(rr);
198
4492eb11 199 r = fdisk_new_context_at(AT_FDCWD, rr->path, /* read_only= */ true, /* sector_size= */ UINT32_MAX, &c);
43cc7a3e 200 if (r < 0)
4492eb11 201 return log_error_errno(r, "Failed to create fdisk context from '%s': %m", rr->path);
43cc7a3e
LP
202
203 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
204 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", rr->path);
205
206 r = fdisk_get_partitions(c, &t);
207 if (r < 0)
208 return log_error_errno(r, "Failed to acquire partition table: %m");
209
210 n_partitions = fdisk_table_get_nents(t);
211 for (size_t i = 0; i < n_partitions; i++) {
212 _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
213 _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL;
214 Instance *instance;
215
216 r = read_partition_info(c, t, i, &pinfo);
217 if (r < 0)
218 return r;
219 if (r == 0) /* not assigned */
220 continue;
221
222 /* Check if partition type matches */
22e932f4 223 if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type.uuid))
43cc7a3e
LP
224 continue;
225
226 /* A label of "_empty" means "not used so far" for us */
227 if (streq_ptr(pinfo.label, "_empty")) {
228 rr->n_empty++;
229 continue;
230 }
231
232 r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields);
233 if (r < 0)
234 return log_error_errno(r, "Failed to match pattern: %m");
8b051623 235 if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY))
43cc7a3e
LP
236 continue;
237
238 r = resource_add_instance(rr, pinfo.device, &extracted_fields, &instance);
239 if (r < 0)
240 return r;
241
242 instance->partition_info = pinfo;
243 pinfo = (PartitionInfo) PARTITION_INFO_NULL;
244
245 /* Inherit data from source if not configured explicitly */
246 if (!instance->metadata.partition_uuid_set) {
247 instance->metadata.partition_uuid = instance->partition_info.uuid;
248 instance->metadata.partition_uuid_set = true;
249 }
250
251 if (!instance->metadata.partition_flags_set) {
252 instance->metadata.partition_flags = instance->partition_info.flags;
253 instance->metadata.partition_flags_set = true;
254 }
255
256 if (instance->metadata.read_only < 0)
257 instance->metadata.read_only = instance->partition_info.read_only;
258 }
259
260 return 0;
261}
262
263static int download_manifest(
264 const char *url,
265 bool verify_signature,
266 char **ret_buffer,
267 size_t *ret_size) {
268
269 _cleanup_free_ char *buffer = NULL, *suffixed_url = NULL;
71136404 270 _cleanup_close_pair_ int pfd[2] = EBADF_PAIR;
43cc7a3e
LP
271 _cleanup_fclose_ FILE *manifest = NULL;
272 size_t size = 0;
273 pid_t pid;
274 int r;
275
276 assert(url);
277 assert(ret_buffer);
278 assert(ret_size);
279
280 /* Download a SHA256SUMS file as manifest */
281
282 r = import_url_append_component(url, "SHA256SUMS", &suffixed_url);
283 if (r < 0)
284 return log_error_errno(r, "Failed to append SHA256SUMS to URL: %m");
285
286 if (pipe2(pfd, O_CLOEXEC) < 0)
287 return log_error_errno(errno, "Failed to allocate pipe: %m");
288
28e5e1e9
DT
289 log_info("%s Acquiring manifest file %s%s", special_glyph(SPECIAL_GLYPH_DOWNLOAD),
290 suffixed_url, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
43cc7a3e 291
0c2aedb4
YW
292 r = safe_fork_full("(sd-pull)",
293 (int[]) { -EBADF, pfd[1], STDERR_FILENO },
294 NULL, 0,
295 FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG,
296 &pid);
43cc7a3e
LP
297 if (r < 0)
298 return r;
299 if (r == 0) {
300 /* Child */
301
302 const char *cmdline[] = {
303 "systemd-pull",
304 "raw",
305 "--direct", /* just download the specified URL, don't download anything else */
306 "--verify", verify_signature ? "signature" : "no", /* verify the manifest file */
307 suffixed_url,
308 "-", /* write to stdout */
309 NULL
310 };
311
43cc7a3e
LP
312 execv(pull_binary_path(), (char *const*) cmdline);
313 log_error_errno(errno, "Failed to execute %s tool: %m", pull_binary_path());
314 _exit(EXIT_FAILURE);
315 };
316
317 pfd[1] = safe_close(pfd[1]);
318
319 /* We'll first load the entire manifest into memory before parsing it. That's because the
320 * systemd-pull tool can validate the download only after its completion, but still pass the data to
321 * us as it runs. We thus need to check the return value of the process *before* parsing, to be
322 * reasonably safe. */
323
324 manifest = fdopen(pfd[0], "r");
325 if (!manifest)
326 return log_error_errno(errno, "Failed allocate FILE object for manifest file: %m");
327
328 TAKE_FD(pfd[0]);
329
330 r = read_full_stream(manifest, &buffer, &size);
331 if (r < 0)
332 return log_error_errno(r, "Failed to read manifest file from child: %m");
333
334 manifest = safe_fclose(manifest);
335
336 r = wait_for_terminate_and_check("(sd-pull)", pid, WAIT_LOG);
337 if (r < 0)
338 return r;
339 if (r != 0)
340 return -EPROTO;
341
342 *ret_buffer = TAKE_PTR(buffer);
343 *ret_size = size;
344
345 return 0;
346}
347
348static int resource_load_from_web(
349 Resource *rr,
350 bool verify,
351 Hashmap **web_cache) {
352
353 size_t manifest_size = 0, left = 0;
354 _cleanup_free_ char *buf = NULL;
355 const char *manifest, *p;
356 size_t line_nr = 1;
357 WebCacheItem *ci;
358 int r;
359
360 assert(rr);
361
362 ci = web_cache ? web_cache_get_item(*web_cache, rr->path, verify) : NULL;
363 if (ci) {
364 log_debug("Manifest web cache hit for %s.", rr->path);
365
366 manifest = (char*) ci->data;
367 manifest_size = ci->size;
368 } else {
369 log_debug("Manifest web cache miss for %s.", rr->path);
370
371 r = download_manifest(rr->path, verify, &buf, &manifest_size);
372 if (r < 0)
373 return r;
374
375 manifest = buf;
376 }
377
378 if (memchr(manifest, 0, manifest_size))
379 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file has embedded NUL byte, refusing.");
380 if (!utf8_is_valid_n(manifest, manifest_size))
381 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file is not valid UTF-8, refusing.");
382
383 p = manifest;
384 left = manifest_size;
385
386 while (left > 0) {
387 _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
388 _cleanup_free_ char *fn = NULL;
389 _cleanup_free_ void *h = NULL;
390 Instance *instance;
391 const char *e;
392 size_t hlen;
393
394 /* 64 character hash + separator + filename + newline */
395 if (left < 67)
396 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Corrupt manifest at line %zu, refusing.", line_nr);
397
398 if (p[0] == '\\')
399 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "File names with escapes not supported in manifest at line %zu, refusing.", line_nr);
400
401 r = unhexmem(p, 64, &h, &hlen);
402 if (r < 0)
403 return log_error_errno(r, "Failed to parse digest at manifest line %zu, refusing.", line_nr);
404
405 p += 64, left -= 64;
406
407 if (*p != ' ')
408 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing space separator at manifest line %zu, refusing.", line_nr);
409 p++, left--;
410
411 if (!IN_SET(*p, '*', ' '))
412 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing binary/text input marker at manifest line %zu, refusing.", line_nr);
413 p++, left--;
414
415 e = memchr(p, '\n', left);
416 if (!e)
417 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Truncated manifest file at line %zu, refusing.", line_nr);
418 if (e == p)
419 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty filename specified at manifest line %zu, refusing.", line_nr);
420
421 fn = strndup(p, e - p);
422 if (!fn)
423 return log_oom();
424
425 if (!filename_is_valid(fn))
426 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid filename specified at manifest line %zu, refusing.", line_nr);
427 if (string_has_cc(fn, NULL))
428 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Filename contains control characters at manifest line %zu, refusing.", line_nr);
429
430 r = pattern_match_many(rr->patterns, fn, &extracted_fields);
431 if (r < 0)
432 return log_error_errno(r, "Failed to match pattern: %m");
8b051623 433 if (r == PATTERN_MATCH_YES) {
43cc7a3e
LP
434 _cleanup_free_ char *path = NULL;
435
436 r = import_url_append_component(rr->path, fn, &path);
437 if (r < 0)
438 return log_error_errno(r, "Failed to build instance URL: %m");
439
440 r = resource_add_instance(rr, path, &extracted_fields, &instance);
441 if (r < 0)
442 return r;
443
444 assert(hlen == sizeof(instance->metadata.sha256sum));
445
446 if (instance->metadata.sha256sum_set) {
447 if (memcmp(instance->metadata.sha256sum, h, hlen) != 0)
448 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "SHA256 sum parsed from filename and manifest don't match at line %zu, refusing.", line_nr);
449 } else {
450 memcpy(instance->metadata.sha256sum, h, hlen);
451 instance->metadata.sha256sum_set = true;
452 }
453 }
454
455 left -= (e - p) + 1;
456 p = e + 1;
457
458 line_nr++;
459 }
460
461 if (!ci && web_cache) {
462 r = web_cache_add_item(web_cache, rr->path, verify, manifest, manifest_size);
463 if (r < 0)
464 log_debug_errno(r, "Failed to add manifest '%s' to cache, ignoring: %m", rr->path);
465 else
466 log_debug("Added manifest '%s' to cache.", rr->path);
467 }
468
469 return 0;
470}
471
472static int instance_cmp(Instance *const*a, Instance *const*b) {
473 int r;
474
475 assert(a);
476 assert(b);
477 assert(*a);
478 assert(*b);
479 assert((*a)->metadata.version);
480 assert((*b)->metadata.version);
481
482 /* Newest version at the beginning */
483 r = strverscmp_improved((*a)->metadata.version, (*b)->metadata.version);
484 if (r != 0)
485 return -r;
486
487 /* Instances don't have to be uniquely named (uniqueness on partition tables is not enforced at all,
488 * and since we allow multiple matching patterns not even in directories they are unique). Hence
489 * let's order by path as secondary ordering key. */
490 return path_compare((*a)->path, (*b)->path);
491}
492
493int resource_load_instances(Resource *rr, bool verify, Hashmap **web_cache) {
494 int r;
495
496 assert(rr);
497
498 switch (rr->type) {
499
500 case RESOURCE_TAR:
501 case RESOURCE_REGULAR_FILE:
502 r = resource_load_from_directory(rr, S_IFREG);
503 break;
504
505 case RESOURCE_DIRECTORY:
506 case RESOURCE_SUBVOLUME:
507 r = resource_load_from_directory(rr, S_IFDIR);
508 break;
509
510 case RESOURCE_PARTITION:
511 r = resource_load_from_blockdev(rr);
512 break;
513
514 case RESOURCE_URL_FILE:
515 case RESOURCE_URL_TAR:
516 r = resource_load_from_web(rr, verify, web_cache);
517 break;
518
519 default:
520 assert_not_reached();
521 }
522 if (r < 0)
523 return r;
524
525 typesafe_qsort(rr->instances, rr->n_instances, instance_cmp);
526 return 0;
527}
528
529Instance* resource_find_instance(Resource *rr, const char *version) {
530 Instance key = {
531 .metadata.version = (char*) version,
532 }, *k = &key;
533
534 return typesafe_bsearch(&k, rr->instances, rr->n_instances, instance_cmp);
535}
536
537int resource_resolve_path(
538 Resource *rr,
539 const char *root,
540 const char *node) {
541
542 _cleanup_free_ char *p = NULL;
543 dev_t d;
544 int r;
545
546 assert(rr);
547
0470f919
AV
548 if (IN_SET(rr->path_relative_to, PATH_RELATIVE_TO_ESP, PATH_RELATIVE_TO_XBOOTLDR, PATH_RELATIVE_TO_BOOT) &&
549 !IN_SET(rr->type, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY))
550 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
551 "Paths relative to %s are only allowed for regular-file or directory resources.",
552 path_relative_to_to_string(rr->path_relative_to));
553
43cc7a3e 554 if (rr->path_auto) {
f8a7112c 555 struct stat orig_root_stats;
43cc7a3e 556
f8a7112c 557 /* NB: If the root mount has been replaced by some form of volatile file system (overlayfs),
558 * the original root block device node is symlinked in /run/systemd/volatile-root. Let's
559 * follow that link here. If that doesn't exist, we check the backing device of "/usr". We
560 * don't actually check the backing device of the root fs "/", in order to support
561 * environments where the root fs is a tmpfs, and the OS itself placed exclusively in
562 * /usr/. */
43cc7a3e
LP
563
564 if (rr->type != RESOURCE_PARTITION)
565 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
566 "Automatic root path discovery only supported for partition resources.");
567
568 if (node) { /* If --image= is specified, directly use the loopback device */
569 r = free_and_strdup_warn(&rr->path, node);
570 if (r < 0)
571 return r;
572
573 return 0;
574 }
575
576 if (root)
577 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
578 "Block device is not allowed when using --root= mode.");
579
f8a7112c 580 r = stat("/run/systemd/volatile-root", &orig_root_stats);
581 if (r < 0) {
d56afce6 582 if (errno == ENOENT) /* volatile-root not found */
f8a7112c 583 r = get_block_device_harder("/usr/", &d);
584 else
585 return log_error_errno(r, "Failed to stat /run/systemd/volatile-root: %m");
586 } else if (!S_ISBLK(orig_root_stats.st_mode)) /* symlink was present but not block device */
587 return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "/run/systemd/volatile-root is not linked to a block device.");
588 else /* symlink was present and a block device */
589 d = orig_root_stats.st_rdev;
43cc7a3e
LP
590
591 } else if (rr->type == RESOURCE_PARTITION) {
254d1313 592 _cleanup_close_ int fd = -EBADF, real_fd = -EBADF;
43cc7a3e
LP
593 _cleanup_free_ char *resolved = NULL;
594 struct stat st;
595
f461a28d 596 r = chase(rr->path, root, CHASE_PREFIX_ROOT, &resolved, &fd);
43cc7a3e
LP
597 if (r < 0)
598 return log_error_errno(r, "Failed to resolve '%s': %m", rr->path);
599
600 if (fstat(fd, &st) < 0)
7a692931 601 return log_error_errno(errno, "Failed to stat '%s': %m", resolved);
43cc7a3e
LP
602
603 if (S_ISBLK(st.st_mode) && root)
604 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "When using --root= or --image= access to device nodes is prohibited.");
605
606 if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) {
607 /* Not a directory, hence no need to find backing block device for the path */
608 free_and_replace(rr->path, resolved);
609 return 0;
610 }
611
612 if (!S_ISDIR(st.st_mode))
613 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "Target path '%s' does not refer to regular file, directory or block device, refusing.", rr->path);
614
615 if (node) { /* If --image= is specified all file systems are backed by the same loopback device, hence shortcut things. */
616 r = free_and_strdup_warn(&rr->path, node);
617 if (r < 0)
618 return r;
619
620 return 0;
621 }
622
623 real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
624 if (real_fd < 0)
625 return log_error_errno(real_fd, "Failed to convert O_PATH file descriptor for %s to regular file descriptor: %m", rr->path);
626
627 r = get_block_device_harder_fd(fd, &d);
628
0470f919
AV
629 } else if (RESOURCE_IS_FILESYSTEM(rr->type)) {
630 _cleanup_free_ char *resolved = NULL, *relative_to = NULL;
631 ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
632
633 if (rr->path_relative_to == PATH_RELATIVE_TO_ROOT) {
634 relative_to = strdup(empty_to_root(root));
635 if (!relative_to)
636 return log_oom();
637 } else { /* boot, esp, or xbootldr */
638 r = 0;
639 if (IN_SET(rr->path_relative_to, PATH_RELATIVE_TO_BOOT, PATH_RELATIVE_TO_XBOOTLDR))
640 r = find_xbootldr_and_warn(root, NULL, /* unprivileged_mode= */ -1, &relative_to, NULL, NULL);
641 if (r == -ENOKEY || rr->path_relative_to == PATH_RELATIVE_TO_ESP)
642 r = find_esp_and_warn(root, NULL, -1, &relative_to, NULL, NULL, NULL, NULL, NULL);
643 if (r < 0)
644 return log_error_errno(r, "Failed to resolve $BOOT: %m");
645 log_debug("Resolved $BOOT to '%s'", relative_to);
646
647 /* Since this partition is read from EFI, there should be no symlinks */
648 chase_flags |= CHASE_PROHIBIT_SYMLINKS;
649 }
43cc7a3e 650
0470f919 651 r = chase(rr->path, relative_to, chase_flags, &resolved, NULL);
43cc7a3e 652 if (r < 0)
0470f919 653 return log_error_errno(r, "Failed to resolve '%s' (relative to '%s'): %m", rr->path, relative_to);
43cc7a3e
LP
654
655 free_and_replace(rr->path, resolved);
656 return 0;
657 } else
658 return 0; /* Otherwise assume there's nothing to resolve */
659
660 if (r < 0)
661 return log_error_errno(r, "Failed to determine block device of file system: %m");
662
663 r = block_get_whole_disk(d, &d);
664 if (r < 0)
665 return log_error_errno(r, "Failed to find whole disk device for partition backing file system: %m");
666 if (r == 0)
667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
668 "File system is not placed on a partition block device, cannot determine whole block device backing root file system.");
669
4fe46c34 670 r = devname_from_devnum(S_IFBLK, d, &p);
43cc7a3e
LP
671 if (r < 0)
672 return r;
673
674 if (rr->path)
675 log_info("Automatically discovered block device '%s' from '%s'.", p, rr->path);
676 else
677 log_info("Automatically discovered root block device '%s'.", p);
678
679 free_and_replace(rr->path, p);
680 return 1;
681}
682
683static const char *resource_type_table[_RESOURCE_TYPE_MAX] = {
684 [RESOURCE_URL_FILE] = "url-file",
685 [RESOURCE_URL_TAR] = "url-tar",
686 [RESOURCE_TAR] = "tar",
687 [RESOURCE_PARTITION] = "partition",
688 [RESOURCE_REGULAR_FILE] = "regular-file",
689 [RESOURCE_DIRECTORY] = "directory",
690 [RESOURCE_SUBVOLUME] = "subvolume",
691};
692
693DEFINE_STRING_TABLE_LOOKUP(resource_type, ResourceType);
0470f919
AV
694
695static const char *path_relative_to_table[_PATH_RELATIVE_TO_MAX] = {
696 [PATH_RELATIVE_TO_ROOT] = "root",
697 [PATH_RELATIVE_TO_ESP] = "esp",
698 [PATH_RELATIVE_TO_XBOOTLDR] = "xbootldr",
699 [PATH_RELATIVE_TO_BOOT] = "boot",
700};
701
702DEFINE_STRING_TABLE_LOOKUP(path_relative_to, PathRelativeTo);