LCOV - code coverage report
Current view: top level - shared - bootspec.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 804 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 24 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <stdio.h>
       4             : #include <linux/magic.h>
       5             : #include <unistd.h>
       6             : 
       7             : #include "sd-device.h"
       8             : #include "sd-id128.h"
       9             : 
      10             : #include "alloc-util.h"
      11             : #include "blkid-util.h"
      12             : #include "bootspec.h"
      13             : #include "conf-files.h"
      14             : #include "def.h"
      15             : #include "device-nodes.h"
      16             : #include "dirent-util.h"
      17             : #include "efivars.h"
      18             : #include "env-file.h"
      19             : #include "env-util.h"
      20             : #include "fd-util.h"
      21             : #include "fileio.h"
      22             : #include "parse-util.h"
      23             : #include "path-util.h"
      24             : #include "pe-header.h"
      25             : #include "sort-util.h"
      26             : #include "stat-util.h"
      27             : #include "string-table.h"
      28             : #include "string-util.h"
      29             : #include "strv.h"
      30             : #include "unaligned.h"
      31             : #include "util.h"
      32             : #include "virt.h"
      33             : 
      34           0 : static void boot_entry_free(BootEntry *entry) {
      35           0 :         assert(entry);
      36             : 
      37           0 :         free(entry->id);
      38           0 :         free(entry->path);
      39           0 :         free(entry->root);
      40           0 :         free(entry->title);
      41           0 :         free(entry->show_title);
      42           0 :         free(entry->version);
      43           0 :         free(entry->machine_id);
      44           0 :         free(entry->architecture);
      45           0 :         strv_free(entry->options);
      46           0 :         free(entry->kernel);
      47           0 :         free(entry->efi);
      48           0 :         strv_free(entry->initrd);
      49           0 :         free(entry->device_tree);
      50           0 : }
      51             : 
      52           0 : static int boot_entry_load(
      53             :                 const char *root,
      54             :                 const char *path,
      55             :                 BootEntry *entry) {
      56             : 
      57           0 :         _cleanup_(boot_entry_free) BootEntry tmp = {
      58             :                 .type = BOOT_ENTRY_CONF,
      59             :         };
      60             : 
      61           0 :         _cleanup_fclose_ FILE *f = NULL;
      62           0 :         unsigned line = 1;
      63             :         char *b, *c;
      64             :         int r;
      65             : 
      66           0 :         assert(root);
      67           0 :         assert(path);
      68           0 :         assert(entry);
      69             : 
      70           0 :         c = endswith_no_case(path, ".conf");
      71           0 :         if (!c)
      72           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
      73             : 
      74           0 :         b = basename(path);
      75           0 :         tmp.id = strndup(b, c - b);
      76           0 :         if (!tmp.id)
      77           0 :                 return log_oom();
      78             : 
      79           0 :         if (!efi_loader_entry_name_valid(tmp.id))
      80           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
      81             : 
      82           0 :         tmp.path = strdup(path);
      83           0 :         if (!tmp.path)
      84           0 :                 return log_oom();
      85             : 
      86           0 :         tmp.root = strdup(root);
      87           0 :         if (!tmp.root)
      88           0 :                 return log_oom();
      89             : 
      90           0 :         f = fopen(path, "re");
      91           0 :         if (!f)
      92           0 :                 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
      93             : 
      94           0 :         for (;;) {
      95           0 :                 _cleanup_free_ char *buf = NULL, *field = NULL;
      96             :                 const char *p;
      97             : 
      98           0 :                 r = read_line(f, LONG_LINE_MAX, &buf);
      99           0 :                 if (r == 0)
     100           0 :                         break;
     101           0 :                 if (r == -ENOBUFS)
     102           0 :                         return log_error_errno(r, "%s:%u: Line too long", path, line);
     103           0 :                 if (r < 0)
     104           0 :                         return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
     105             : 
     106           0 :                 line++;
     107             : 
     108           0 :                 if (IN_SET(*strstrip(buf), '#', '\0'))
     109           0 :                         continue;
     110             : 
     111           0 :                 p = buf;
     112           0 :                 r = extract_first_word(&p, &field, " \t", 0);
     113           0 :                 if (r < 0) {
     114           0 :                         log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
     115           0 :                         continue;
     116             :                 }
     117           0 :                 if (r == 0) {
     118           0 :                         log_warning("%s:%u: Bad syntax", path, line);
     119           0 :                         continue;
     120             :                 }
     121             : 
     122           0 :                 if (streq(field, "title"))
     123           0 :                         r = free_and_strdup(&tmp.title, p);
     124           0 :                 else if (streq(field, "version"))
     125           0 :                         r = free_and_strdup(&tmp.version, p);
     126           0 :                 else if (streq(field, "machine-id"))
     127           0 :                         r = free_and_strdup(&tmp.machine_id, p);
     128           0 :                 else if (streq(field, "architecture"))
     129           0 :                         r = free_and_strdup(&tmp.architecture, p);
     130           0 :                 else if (streq(field, "options"))
     131           0 :                         r = strv_extend(&tmp.options, p);
     132           0 :                 else if (streq(field, "linux"))
     133           0 :                         r = free_and_strdup(&tmp.kernel, p);
     134           0 :                 else if (streq(field, "efi"))
     135           0 :                         r = free_and_strdup(&tmp.efi, p);
     136           0 :                 else if (streq(field, "initrd"))
     137           0 :                         r = strv_extend(&tmp.initrd, p);
     138           0 :                 else if (streq(field, "devicetree"))
     139           0 :                         r = free_and_strdup(&tmp.device_tree, p);
     140             :                 else {
     141           0 :                         log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
     142           0 :                         continue;
     143             :                 }
     144           0 :                 if (r < 0)
     145           0 :                         return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
     146             :         }
     147             : 
     148           0 :         *entry = tmp;
     149           0 :         tmp = (BootEntry) {};
     150           0 :         return 0;
     151             : }
     152             : 
     153           0 : void boot_config_free(BootConfig *config) {
     154             :         size_t i;
     155             : 
     156           0 :         assert(config);
     157             : 
     158           0 :         free(config->default_pattern);
     159           0 :         free(config->timeout);
     160           0 :         free(config->editor);
     161           0 :         free(config->auto_entries);
     162           0 :         free(config->auto_firmware);
     163           0 :         free(config->console_mode);
     164             : 
     165           0 :         free(config->entry_oneshot);
     166           0 :         free(config->entry_default);
     167             : 
     168           0 :         for (i = 0; i < config->n_entries; i++)
     169           0 :                 boot_entry_free(config->entries + i);
     170           0 :         free(config->entries);
     171           0 : }
     172             : 
     173           0 : static int boot_loader_read_conf(const char *path, BootConfig *config) {
     174           0 :         _cleanup_fclose_ FILE *f = NULL;
     175           0 :         unsigned line = 1;
     176             :         int r;
     177             : 
     178           0 :         assert(path);
     179           0 :         assert(config);
     180             : 
     181           0 :         f = fopen(path, "re");
     182           0 :         if (!f) {
     183           0 :                 if (errno == ENOENT)
     184           0 :                         return 0;
     185             : 
     186           0 :                 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
     187             :         }
     188             : 
     189           0 :         for (;;) {
     190           0 :                 _cleanup_free_ char *buf = NULL, *field = NULL;
     191             :                 const char *p;
     192             : 
     193           0 :                 r = read_line(f, LONG_LINE_MAX, &buf);
     194           0 :                 if (r == 0)
     195           0 :                         break;
     196           0 :                 if (r == -ENOBUFS)
     197           0 :                         return log_error_errno(r, "%s:%u: Line too long", path, line);
     198           0 :                 if (r < 0)
     199           0 :                         return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
     200             : 
     201           0 :                 line++;
     202             : 
     203           0 :                 if (IN_SET(*strstrip(buf), '#', '\0'))
     204           0 :                         continue;
     205             : 
     206           0 :                 p = buf;
     207           0 :                 r = extract_first_word(&p, &field, " \t", 0);
     208           0 :                 if (r < 0) {
     209           0 :                         log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
     210           0 :                         continue;
     211             :                 }
     212           0 :                 if (r == 0) {
     213           0 :                         log_warning("%s:%u: Bad syntax", path, line);
     214           0 :                         continue;
     215             :                 }
     216             : 
     217           0 :                 if (streq(field, "default"))
     218           0 :                         r = free_and_strdup(&config->default_pattern, p);
     219           0 :                 else if (streq(field, "timeout"))
     220           0 :                         r = free_and_strdup(&config->timeout, p);
     221           0 :                 else if (streq(field, "editor"))
     222           0 :                         r = free_and_strdup(&config->editor, p);
     223           0 :                 else if (streq(field, "auto-entries"))
     224           0 :                         r = free_and_strdup(&config->auto_entries, p);
     225           0 :                 else if (streq(field, "auto-firmware"))
     226           0 :                         r = free_and_strdup(&config->auto_firmware, p);
     227           0 :                 else if (streq(field, "console-mode"))
     228           0 :                         r = free_and_strdup(&config->console_mode, p);
     229             :                 else {
     230           0 :                         log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
     231           0 :                         continue;
     232             :                 }
     233           0 :                 if (r < 0)
     234           0 :                         return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
     235             :         }
     236             : 
     237           0 :         return 1;
     238             : }
     239             : 
     240           0 : static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
     241           0 :         return str_verscmp(a->id, b->id);
     242             : }
     243             : 
     244           0 : static int boot_entries_find(
     245             :                 const char *root,
     246             :                 const char *dir,
     247             :                 BootEntry **entries,
     248             :                 size_t *n_entries) {
     249             : 
     250           0 :         _cleanup_strv_free_ char **files = NULL;
     251           0 :         size_t n_allocated = *n_entries;
     252             :         char **f;
     253             :         int r;
     254             : 
     255           0 :         assert(root);
     256           0 :         assert(dir);
     257           0 :         assert(entries);
     258           0 :         assert(n_entries);
     259             : 
     260           0 :         r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL);
     261           0 :         if (r < 0)
     262           0 :                 return log_error_errno(r, "Failed to list files in \"%s\": %m", dir);
     263             : 
     264           0 :         STRV_FOREACH(f, files) {
     265           0 :                 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
     266           0 :                         return log_oom();
     267             : 
     268           0 :                 r = boot_entry_load(root, *f, *entries + *n_entries);
     269           0 :                 if (r < 0)
     270           0 :                         continue;
     271             : 
     272           0 :                 (*n_entries) ++;
     273             :         }
     274             : 
     275           0 :         return 0;
     276             : }
     277             : 
     278           0 : static int boot_entry_load_unified(
     279             :                 const char *root,
     280             :                 const char *path,
     281             :                 const char *osrelease,
     282             :                 const char *cmdline,
     283             :                 BootEntry *ret) {
     284             : 
     285           0 :         _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL;
     286           0 :         _cleanup_(boot_entry_free) BootEntry tmp = {
     287             :                 .type = BOOT_ENTRY_UNIFIED,
     288             :         };
     289           0 :         _cleanup_fclose_ FILE *f = NULL;
     290             :         const char *k;
     291             :         int r;
     292             : 
     293           0 :         assert(root);
     294           0 :         assert(path);
     295           0 :         assert(osrelease);
     296             : 
     297           0 :         k = path_startswith(path, root);
     298           0 :         if (!k)
     299           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path);
     300             : 
     301           0 :         f = fmemopen_unlocked((void*) osrelease, strlen(osrelease), "r");
     302           0 :         if (!f)
     303           0 :                 return log_error_errno(errno, "Failed to open os-release buffer: %m");
     304             : 
     305           0 :         r = parse_env_file(f, "os-release",
     306             :                            "PRETTY_NAME", &os_pretty_name,
     307             :                            "ID", &os_id,
     308             :                            "VERSION_ID", &version_id,
     309             :                            "BUILD_ID", &build_id);
     310           0 :         if (r < 0)
     311           0 :                 return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
     312             : 
     313           0 :         if (!os_pretty_name || !os_id || !(version_id || build_id))
     314           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
     315             : 
     316           0 :         tmp.id = strjoin(os_id, "-", version_id ?: build_id);
     317           0 :         if (!tmp.id)
     318           0 :                 return log_oom();
     319             : 
     320           0 :         if (!efi_loader_entry_name_valid(tmp.id))
     321           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id);
     322             : 
     323           0 :         tmp.path = strdup(path);
     324           0 :         if (!tmp.path)
     325           0 :                 return log_oom();
     326             : 
     327           0 :         tmp.root = strdup(root);
     328           0 :         if (!tmp.root)
     329           0 :                 return log_oom();
     330             : 
     331           0 :         tmp.kernel = strdup(skip_leading_chars(k, "/"));
     332           0 :         if (!tmp.kernel)
     333           0 :                 return log_oom();
     334             : 
     335           0 :         tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE));
     336           0 :         if (!tmp.options)
     337           0 :                 return log_oom();
     338             : 
     339           0 :         delete_trailing_chars(tmp.options[0], WHITESPACE);
     340             : 
     341           0 :         tmp.title = TAKE_PTR(os_pretty_name);
     342             : 
     343           0 :         *ret = tmp;
     344           0 :         tmp = (BootEntry) {};
     345           0 :         return 0;
     346             : }
     347             : 
     348             : /* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
     349             :  * the ones we do care about and we are willing to load into memory have this size limit.) */
     350             : #define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
     351             : 
     352           0 : static int find_sections(
     353             :                 int fd,
     354             :                 char **ret_osrelease,
     355             :                 char **ret_cmdline) {
     356             : 
     357           0 :         _cleanup_free_ struct PeSectionHeader *sections = NULL;
     358           0 :         _cleanup_free_ char *osrelease = NULL, *cmdline = NULL;
     359             :         size_t i, n_sections;
     360             :         struct DosFileHeader dos;
     361             :         struct PeHeader pe;
     362             :         uint64_t start;
     363             :         ssize_t n;
     364             : 
     365           0 :         n = pread(fd, &dos, sizeof(dos), 0);
     366           0 :         if (n < 0)
     367           0 :                 return log_error_errno(errno, "Failed read DOS header: %m");
     368           0 :         if (n != sizeof(dos))
     369           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading DOS header, refusing.");
     370             : 
     371           0 :         if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z')
     372           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing.");
     373             : 
     374           0 :         start = unaligned_read_le32(&dos.ExeHeader);
     375           0 :         n = pread(fd, &pe, sizeof(pe), start);
     376           0 :         if (n < 0)
     377           0 :                 return log_error_errno(errno, "Failed to read PE header: %m");
     378           0 :         if (n != sizeof(pe))
     379           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading PE header, refusing.");
     380             : 
     381           0 :         if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0)
     382           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing.");
     383             : 
     384           0 :         n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections);
     385           0 :         if (n_sections > 96)
     386           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing.");
     387             : 
     388           0 :         sections = new(struct PeSectionHeader, n_sections);
     389           0 :         if (!sections)
     390           0 :                 return log_oom();
     391             : 
     392           0 :         n = pread(fd, sections,
     393             :                   n_sections * sizeof(struct PeSectionHeader),
     394           0 :                   start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader));
     395           0 :         if (n < 0)
     396           0 :                 return log_error_errno(errno, "Failed to read section data: %m");
     397           0 :         if ((size_t) n != n_sections * sizeof(struct PeSectionHeader))
     398           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing.");
     399             : 
     400           0 :         for (i = 0; i < n_sections; i++) {
     401           0 :                 _cleanup_free_ char *k = NULL;
     402             :                 uint32_t offset, size;
     403             :                 char **b;
     404             : 
     405           0 :                 if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name)))
     406           0 :                         b = &osrelease;
     407           0 :                 else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name)))
     408           0 :                         b = &cmdline;
     409             :                 else
     410           0 :                         continue;
     411             : 
     412           0 :                 if (*b)
     413           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section %s, refusing.", sections[i].Name);
     414             : 
     415           0 :                 offset = unaligned_read_le32(&sections[i].PointerToRawData);
     416           0 :                 size = unaligned_read_le32(&sections[i].VirtualSize);
     417             : 
     418           0 :                 if (size > PE_SECTION_SIZE_MAX)
     419           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section %s too large, refusing.", sections[i].Name);
     420             : 
     421           0 :                 k = new(char, size+1);
     422           0 :                 if (!k)
     423           0 :                         return log_oom();
     424             : 
     425           0 :                 n = pread(fd, k, size, offset);
     426           0 :                 if (n < 0)
     427           0 :                         return log_error_errno(errno, "Failed to read section payload: %m");
     428           0 :                 if ((size_t) n != size)
     429           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading section payload, refusing:");
     430             : 
     431             :                 /* Allow one trailing NUL byte, but nothing more. */
     432           0 :                 if (size > 0 && memchr(k, 0, size - 1))
     433           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section contains embedded NUL byte: %m");
     434             : 
     435           0 :                 k[size] = 0;
     436           0 :                 *b = TAKE_PTR(k);
     437             :         }
     438             : 
     439           0 :         if (!osrelease)
     440           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Image lacks .osrel section, refusing.");
     441             : 
     442           0 :         if (ret_osrelease)
     443           0 :                 *ret_osrelease = TAKE_PTR(osrelease);
     444           0 :         if (ret_cmdline)
     445           0 :                 *ret_cmdline = TAKE_PTR(cmdline);
     446             : 
     447           0 :         return 0;
     448             : }
     449             : 
     450           0 : static int boot_entries_find_unified(
     451             :                 const char *root,
     452             :                 const char *dir,
     453             :                 BootEntry **entries,
     454             :                 size_t *n_entries) {
     455             : 
     456           0 :         _cleanup_(closedirp) DIR *d = NULL;
     457           0 :         size_t n_allocated = *n_entries;
     458             :         struct dirent *de;
     459             :         int r;
     460             : 
     461           0 :         assert(root);
     462           0 :         assert(dir);
     463           0 :         assert(entries);
     464           0 :         assert(n_entries);
     465             : 
     466           0 :         d = opendir(dir);
     467           0 :         if (!d) {
     468           0 :                 if (errno == ENOENT)
     469           0 :                         return 0;
     470             : 
     471           0 :                 return log_error_errno(errno, "Failed to open %s: %m", dir);
     472             :         }
     473             : 
     474           0 :         FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", dir)) {
     475           0 :                 _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
     476           0 :                 _cleanup_close_ int fd = -1;
     477             : 
     478           0 :                 if (!dirent_is_file(de))
     479           0 :                         continue;
     480             : 
     481           0 :                 if (!endswith_no_case(de->d_name, ".efi"))
     482           0 :                         continue;
     483             : 
     484           0 :                 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
     485           0 :                         return log_oom();
     486             : 
     487           0 :                 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
     488           0 :                 if (fd < 0) {
     489           0 :                         log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name);
     490           0 :                         continue;
     491             :                 }
     492             : 
     493           0 :                 r = fd_verify_regular(fd);
     494           0 :                 if (r < 0) {
     495           0 :                         log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
     496           0 :                         continue;
     497             :                 }
     498             : 
     499           0 :                 r = find_sections(fd, &osrelease, &cmdline);
     500           0 :                 if (r < 0)
     501           0 :                         continue;
     502             : 
     503           0 :                 j = path_join(dir, de->d_name);
     504           0 :                 if (!j)
     505           0 :                         return log_oom();
     506             : 
     507           0 :                 r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries);
     508           0 :                 if (r < 0)
     509           0 :                         continue;
     510             : 
     511           0 :                 (*n_entries) ++;
     512             :         }
     513             : 
     514           0 :         return 0;
     515             : }
     516             : 
     517           0 : static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
     518             :         size_t i, j;
     519           0 :         bool non_unique = false;
     520             : 
     521           0 :         assert(entries || n_entries == 0);
     522           0 :         assert(arr || n_entries == 0);
     523             : 
     524           0 :         for (i = 0; i < n_entries; i++)
     525           0 :                 arr[i] = false;
     526             : 
     527           0 :         for (i = 0; i < n_entries; i++)
     528           0 :                 for (j = 0; j < n_entries; j++)
     529           0 :                         if (i != j && streq(boot_entry_title(entries + i),
     530             :                                             boot_entry_title(entries + j)))
     531           0 :                                 non_unique = arr[i] = arr[j] = true;
     532             : 
     533           0 :         return non_unique;
     534             : }
     535             : 
     536           0 : static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
     537             :         char *s;
     538             :         size_t i;
     539             :         int r;
     540           0 :         bool arr[n_entries];
     541             : 
     542           0 :         assert(entries || n_entries == 0);
     543             : 
     544             :         /* Find _all_ non-unique titles */
     545           0 :         if (!find_nonunique(entries, n_entries, arr))
     546           0 :                 return 0;
     547             : 
     548             :         /* Add version to non-unique titles */
     549           0 :         for (i = 0; i < n_entries; i++)
     550           0 :                 if (arr[i] && entries[i].version) {
     551           0 :                         r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
     552           0 :                         if (r < 0)
     553           0 :                                 return -ENOMEM;
     554             : 
     555           0 :                         free_and_replace(entries[i].show_title, s);
     556             :                 }
     557             : 
     558           0 :         if (!find_nonunique(entries, n_entries, arr))
     559           0 :                 return 0;
     560             : 
     561             :         /* Add machine-id to non-unique titles */
     562           0 :         for (i = 0; i < n_entries; i++)
     563           0 :                 if (arr[i] && entries[i].machine_id) {
     564           0 :                         r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
     565           0 :                         if (r < 0)
     566           0 :                                 return -ENOMEM;
     567             : 
     568           0 :                         free_and_replace(entries[i].show_title, s);
     569             :                 }
     570             : 
     571           0 :         if (!find_nonunique(entries, n_entries, arr))
     572           0 :                 return 0;
     573             : 
     574             :         /* Add file name to non-unique titles */
     575           0 :         for (i = 0; i < n_entries; i++)
     576           0 :                 if (arr[i]) {
     577           0 :                         r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
     578           0 :                         if (r < 0)
     579           0 :                                 return -ENOMEM;
     580             : 
     581           0 :                         free_and_replace(entries[i].show_title, s);
     582             :                 }
     583             : 
     584           0 :         return 0;
     585             : }
     586             : 
     587           0 : static int boot_entries_select_default(const BootConfig *config) {
     588             :         int i;
     589             : 
     590           0 :         assert(config);
     591           0 :         assert(config->entries || config->n_entries == 0);
     592             : 
     593           0 :         if (config->n_entries == 0) {
     594           0 :                 log_debug("Found no default boot entry :(");
     595           0 :                 return -1; /* -1 means "no default" */
     596             :         }
     597             : 
     598           0 :         if (config->entry_oneshot)
     599           0 :                 for (i = config->n_entries - 1; i >= 0; i--)
     600           0 :                         if (streq(config->entry_oneshot, config->entries[i].id)) {
     601           0 :                                 log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
     602             :                                           config->entries[i].id);
     603           0 :                                 return i;
     604             :                         }
     605             : 
     606           0 :         if (config->entry_default)
     607           0 :                 for (i = config->n_entries - 1; i >= 0; i--)
     608           0 :                         if (streq(config->entry_default, config->entries[i].id)) {
     609           0 :                                 log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
     610             :                                           config->entries[i].id);
     611           0 :                                 return i;
     612             :                         }
     613             : 
     614           0 :         if (config->default_pattern)
     615           0 :                 for (i = config->n_entries - 1; i >= 0; i--)
     616           0 :                         if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
     617           0 :                                 log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
     618             :                                           config->entries[i].id, config->default_pattern);
     619           0 :                                 return i;
     620             :                         }
     621             : 
     622           0 :         log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
     623           0 :         return config->n_entries - 1;
     624             : }
     625             : 
     626           0 : int boot_entries_load_config(
     627             :                 const char *esp_path,
     628             :                 const char *xbootldr_path,
     629             :                 BootConfig *config) {
     630             : 
     631             :         const char *p;
     632             :         int r;
     633             : 
     634           0 :         assert(config);
     635             : 
     636           0 :         if (esp_path) {
     637           0 :                 p = strjoina(esp_path, "/loader/loader.conf");
     638           0 :                 r = boot_loader_read_conf(p, config);
     639           0 :                 if (r < 0)
     640           0 :                         return r;
     641             : 
     642           0 :                 p = strjoina(esp_path, "/loader/entries");
     643           0 :                 r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
     644           0 :                 if (r < 0)
     645           0 :                         return r;
     646             : 
     647           0 :                 p = strjoina(esp_path, "/EFI/Linux/");
     648           0 :                 r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries);
     649           0 :                 if (r < 0)
     650           0 :                         return r;
     651             :         }
     652             : 
     653           0 :         if (xbootldr_path) {
     654           0 :                 p = strjoina(xbootldr_path, "/loader/entries");
     655           0 :                 r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries);
     656           0 :                 if (r < 0)
     657           0 :                         return r;
     658             : 
     659           0 :                 p = strjoina(xbootldr_path, "/EFI/Linux/");
     660           0 :                 r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries);
     661           0 :                 if (r < 0)
     662           0 :                         return r;
     663             :         }
     664             : 
     665           0 :         typesafe_qsort(config->entries, config->n_entries, boot_entry_compare);
     666             : 
     667           0 :         r = boot_entries_uniquify(config->entries, config->n_entries);
     668           0 :         if (r < 0)
     669           0 :                 return log_error_errno(r, "Failed to uniquify boot entries: %m");
     670             : 
     671           0 :         if (is_efi_boot()) {
     672           0 :                 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
     673           0 :                 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
     674           0 :                         log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m");
     675           0 :                         if (r == -ENOMEM)
     676           0 :                                 return r;
     677             :                 }
     678             : 
     679           0 :                 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
     680           0 :                 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
     681           0 :                         log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m");
     682           0 :                         if (r == -ENOMEM)
     683           0 :                                 return r;
     684             :                 }
     685             :         }
     686             : 
     687           0 :         config->default_entry = boot_entries_select_default(config);
     688           0 :         return 0;
     689             : }
     690             : 
     691           0 : int boot_entries_load_config_auto(
     692             :                 const char *override_esp_path,
     693             :                 const char *override_xbootldr_path,
     694             :                 BootConfig *config) {
     695             : 
     696           0 :         _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
     697             :         int r;
     698             : 
     699           0 :         assert(config);
     700             : 
     701             :         /* This function is similar to boot_entries_load_config(), however we automatically search for the
     702             :          * ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass
     703             :          * an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's
     704             :          * read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from
     705             :          * it). This allows other boot loaders to pass boot loader entry information to our tools if they
     706             :          * want to. */
     707             : 
     708           0 :         if (!override_esp_path && !override_xbootldr_path) {
     709           0 :                 if (access("/run/boot-loader-entries/", F_OK) >= 0)
     710           0 :                         return boot_entries_load_config("/run/boot-loader-entries/", NULL, config);
     711             : 
     712           0 :                 if (errno != ENOENT)
     713           0 :                         return log_error_errno(errno,
     714             :                                                "Failed to determine whether /run/boot-loader-entries/ exists: %m");
     715             :         }
     716             : 
     717           0 :         r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
     718           0 :         if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
     719           0 :                 return r;
     720             : 
     721           0 :         r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL);
     722           0 :         if (r < 0 && r != -ENOKEY)
     723           0 :                 return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
     724             : 
     725           0 :         return boot_entries_load_config(esp_where, xbootldr_where, config);
     726             : }
     727             : 
     728             : #if ENABLE_EFI
     729           0 : int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
     730             :         static const char * const title_table[] = {
     731             :                 /* Pretty names for a few well-known automatically discovered entries. */
     732             :                 "auto-osx",                      "macOS",
     733             :                 "auto-windows",                  "Windows Boot Manager",
     734             :                 "auto-efi-shell",                "EFI Shell",
     735             :                 "auto-efi-default",              "EFI Default Loader",
     736             :                 "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
     737             :         };
     738             : 
     739           0 :         _cleanup_strv_free_ char **found_by_loader = NULL;
     740             :         size_t n_allocated;
     741             :         char **i;
     742             :         int r;
     743             : 
     744           0 :         assert(config);
     745             : 
     746             :         /* Let's add the entries discovered by the boot loader to the end of our list, unless they are
     747             :          * already included there. */
     748             : 
     749           0 :         r = efi_loader_get_entries(&found_by_loader);
     750           0 :         if (IN_SET(r, -ENOENT, -EOPNOTSUPP))
     751           0 :                 return log_debug_errno(r, "Boot loader reported no entries.");
     752           0 :         if (r < 0)
     753           0 :                 return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
     754             : 
     755           0 :         n_allocated = config->n_entries;
     756             : 
     757           0 :         STRV_FOREACH(i, found_by_loader) {
     758           0 :                 _cleanup_free_ char *c = NULL, *t = NULL, *p = NULL;
     759             :                 char **a, **b;
     760             : 
     761           0 :                 if (boot_config_has_entry(config, *i))
     762           0 :                         continue;
     763             : 
     764           0 :                 if (only_auto && !startswith(*i, "auto-"))
     765           0 :                         continue;
     766             : 
     767           0 :                 c = strdup(*i);
     768           0 :                 if (!c)
     769           0 :                         return log_oom();
     770             : 
     771           0 :                 STRV_FOREACH_PAIR(a, b, (char**) title_table)
     772           0 :                         if (streq(*a, *i)) {
     773           0 :                                 t = strdup(*b);
     774           0 :                                 if (!t)
     775           0 :                                         return log_oom();
     776           0 :                                 break;
     777             :                         }
     778             : 
     779           0 :                 p = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntries");
     780           0 :                 if (!p)
     781           0 :                         return log_oom();
     782             : 
     783           0 :                 if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1))
     784           0 :                         return log_oom();
     785             : 
     786           0 :                 config->entries[config->n_entries++] = (BootEntry) {
     787             :                         .type = BOOT_ENTRY_LOADER,
     788           0 :                         .id = TAKE_PTR(c),
     789           0 :                         .title = TAKE_PTR(t),
     790           0 :                         .path = TAKE_PTR(p),
     791             :                 };
     792             :         }
     793             : 
     794           0 :         return 0;
     795             : }
     796             : #endif
     797             : 
     798             : /********************************************************************************/
     799             : 
     800           0 : static int verify_esp_blkid(
     801             :                 dev_t devid,
     802             :                 bool searching,
     803             :                 uint32_t *ret_part,
     804             :                 uint64_t *ret_pstart,
     805             :                 uint64_t *ret_psize,
     806             :                 sd_id128_t *ret_uuid) {
     807             : 
     808           0 :         sd_id128_t uuid = SD_ID128_NULL;
     809           0 :         uint64_t pstart = 0, psize = 0;
     810           0 :         uint32_t part = 0;
     811             : 
     812             : #if HAVE_BLKID
     813           0 :         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
     814           0 :         _cleanup_free_ char *node = NULL;
     815             :         const char *v;
     816             :         int r;
     817             : 
     818           0 :         r = device_path_make_major_minor(S_IFBLK, devid, &node);
     819           0 :         if (r < 0)
     820           0 :                 return log_error_errno(r, "Failed to format major/minor device path: %m");
     821             : 
     822           0 :         errno = 0;
     823           0 :         b = blkid_new_probe_from_filename(node);
     824           0 :         if (!b)
     825           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
     826             : 
     827           0 :         blkid_probe_enable_superblocks(b, 1);
     828           0 :         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
     829           0 :         blkid_probe_enable_partitions(b, 1);
     830           0 :         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
     831             : 
     832           0 :         errno = 0;
     833           0 :         r = blkid_do_safeprobe(b);
     834           0 :         if (r == -2)
     835           0 :                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
     836           0 :         else if (r == 1)
     837           0 :                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
     838           0 :         else if (r != 0)
     839           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
     840             : 
     841           0 :         errno = 0;
     842           0 :         r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
     843           0 :         if (r != 0)
     844           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
     845           0 :         if (!streq(v, "vfat"))
     846           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     847             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     848             :                                       "File system \"%s\" is not FAT.", node);
     849             : 
     850           0 :         errno = 0;
     851           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
     852           0 :         if (r != 0)
     853           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
     854           0 :         if (!streq(v, "gpt"))
     855           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     856             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     857             :                                       "File system \"%s\" is not on a GPT partition table.", node);
     858             : 
     859           0 :         errno = 0;
     860           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
     861           0 :         if (r != 0)
     862           0 :                 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
     863           0 :         if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
     864           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     865             :                                        SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     866             :                                        "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
     867             : 
     868           0 :         errno = 0;
     869           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
     870           0 :         if (r != 0)
     871           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
     872           0 :         r = sd_id128_from_string(v, &uuid);
     873           0 :         if (r < 0)
     874           0 :                 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
     875             : 
     876           0 :         errno = 0;
     877           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
     878           0 :         if (r != 0)
     879           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node);
     880           0 :         r = safe_atou32(v, &part);
     881           0 :         if (r < 0)
     882           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
     883             : 
     884           0 :         errno = 0;
     885           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
     886           0 :         if (r != 0)
     887           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
     888           0 :         r = safe_atou64(v, &pstart);
     889           0 :         if (r < 0)
     890           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
     891             : 
     892           0 :         errno = 0;
     893           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
     894           0 :         if (r != 0)
     895           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
     896           0 :         r = safe_atou64(v, &psize);
     897           0 :         if (r < 0)
     898           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
     899             : #endif
     900             : 
     901           0 :         if (ret_part)
     902           0 :                 *ret_part = part;
     903           0 :         if (ret_pstart)
     904           0 :                 *ret_pstart = pstart;
     905           0 :         if (ret_psize)
     906           0 :                 *ret_psize = psize;
     907           0 :         if (ret_uuid)
     908           0 :                 *ret_uuid = uuid;
     909             : 
     910           0 :         return 0;
     911             : }
     912             : 
     913           0 : static int verify_esp_udev(
     914             :                 dev_t devid,
     915             :                 bool searching,
     916             :                 uint32_t *ret_part,
     917             :                 uint64_t *ret_pstart,
     918             :                 uint64_t *ret_psize,
     919             :                 sd_id128_t *ret_uuid) {
     920             : 
     921           0 :         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
     922           0 :         _cleanup_free_ char *node = NULL;
     923           0 :         sd_id128_t uuid = SD_ID128_NULL;
     924           0 :         uint64_t pstart = 0, psize = 0;
     925           0 :         uint32_t part = 0;
     926             :         const char *v;
     927             :         int r;
     928             : 
     929           0 :         r = device_path_make_major_minor(S_IFBLK, devid, &node);
     930           0 :         if (r < 0)
     931           0 :                 return log_error_errno(r, "Failed to format major/minor device path: %m");
     932             : 
     933           0 :         r = sd_device_new_from_devnum(&d, 'b', devid);
     934           0 :         if (r < 0)
     935           0 :                 return log_error_errno(r, "Failed to get device from device number: %m");
     936             : 
     937           0 :         r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
     938           0 :         if (r < 0)
     939           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     940           0 :         if (!streq(v, "vfat"))
     941           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     942             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     943             :                                       "File system \"%s\" is not FAT.", node );
     944             : 
     945           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
     946           0 :         if (r < 0)
     947           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     948           0 :         if (!streq(v, "gpt"))
     949           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     950             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     951             :                                       "File system \"%s\" is not on a GPT partition table.", node);
     952             : 
     953           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
     954           0 :         if (r < 0)
     955           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     956           0 :         if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
     957           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
     958             :                                        SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
     959             :                                        "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
     960             : 
     961           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
     962           0 :         if (r < 0)
     963           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     964           0 :         r = sd_id128_from_string(v, &uuid);
     965           0 :         if (r < 0)
     966           0 :                 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
     967             : 
     968           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
     969           0 :         if (r < 0)
     970           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     971           0 :         r = safe_atou32(v, &part);
     972           0 :         if (r < 0)
     973           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
     974             : 
     975           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
     976           0 :         if (r < 0)
     977           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     978           0 :         r = safe_atou64(v, &pstart);
     979           0 :         if (r < 0)
     980           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
     981             : 
     982           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
     983           0 :         if (r < 0)
     984           0 :                 return log_error_errno(r, "Failed to get device property: %m");
     985           0 :         r = safe_atou64(v, &psize);
     986           0 :         if (r < 0)
     987           0 :                 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
     988             : 
     989           0 :         if (ret_part)
     990           0 :                 *ret_part = part;
     991           0 :         if (ret_pstart)
     992           0 :                 *ret_pstart = pstart;
     993           0 :         if (ret_psize)
     994           0 :                 *ret_psize = psize;
     995           0 :         if (ret_uuid)
     996           0 :                 *ret_uuid = uuid;
     997             : 
     998           0 :         return 0;
     999             : }
    1000             : 
    1001           0 : static int verify_fsroot_dir(
    1002             :                 const char *path,
    1003             :                 bool searching,
    1004             :                 bool unprivileged_mode,
    1005             :                 dev_t *ret_dev) {
    1006             : 
    1007             :         struct stat st, st2;
    1008             :         const char *t2, *trigger;
    1009             :         int r;
    1010             : 
    1011           0 :         assert(path);
    1012           0 :         assert(ret_dev);
    1013             : 
    1014             :         /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
    1015             :          * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
    1016             :          * before stat()ing */
    1017           0 :         trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
    1018           0 :         (void) access(trigger, F_OK);
    1019             : 
    1020           0 :         if (stat(path, &st) < 0)
    1021           0 :                 return log_full_errno((searching && errno == ENOENT) ||
    1022             :                                       (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
    1023             :                                       "Failed to determine block device node of \"%s\": %m", path);
    1024             : 
    1025           0 :         if (major(st.st_dev) == 0)
    1026           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1027             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
    1028             :                                       "Block device node of \"%s\" is invalid.", path);
    1029             : 
    1030           0 :         t2 = strjoina(path, "/..");
    1031           0 :         if (stat(t2, &st2) < 0) {
    1032           0 :                 if (errno != EACCES)
    1033           0 :                         r = -errno;
    1034             :                 else {
    1035           0 :                         _cleanup_free_ char *parent = NULL;
    1036             : 
    1037             :                         /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
    1038             :                          * directly instead. It's not as good, due to symlinks and such, but we can't do
    1039             :                          * anything better here. */
    1040             : 
    1041           0 :                         parent = dirname_malloc(path);
    1042           0 :                         if (!parent)
    1043           0 :                                 return log_oom();
    1044             : 
    1045           0 :                         if (stat(parent, &st2) < 0)
    1046           0 :                                 r = -errno;
    1047             :                         else
    1048           0 :                                 r = 0;
    1049             :                 }
    1050             : 
    1051           0 :                 if (r < 0)
    1052           0 :                         return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
    1053             :                                               "Failed to determine block device node of parent of \"%s\": %m", path);
    1054             :         }
    1055             : 
    1056           0 :         if (st.st_dev == st2.st_dev)
    1057           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1058             :                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
    1059             :                                       "Directory \"%s\" is not the root of the file system.", path);
    1060             : 
    1061           0 :         if (ret_dev)
    1062           0 :                 *ret_dev = st.st_dev;
    1063             : 
    1064           0 :         return 0;
    1065             : }
    1066             : 
    1067           0 : static int verify_esp(
    1068             :                 const char *p,
    1069             :                 bool searching,
    1070             :                 bool unprivileged_mode,
    1071             :                 uint32_t *ret_part,
    1072             :                 uint64_t *ret_pstart,
    1073             :                 uint64_t *ret_psize,
    1074             :                 sd_id128_t *ret_uuid) {
    1075             : 
    1076             :         bool relax_checks;
    1077             :         dev_t devid;
    1078             :         int r;
    1079             : 
    1080           0 :         assert(p);
    1081             : 
    1082             :         /* This logs about all errors, except:
    1083             :          *
    1084             :          *  -ENOENT        → if 'searching' is set, and the dir doesn't exist
    1085             :          *  -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
    1086             :          *  -EACESS        → if 'unprivileged_mode' is set, and we have trouble accessing the thing
    1087             :          */
    1088             : 
    1089           0 :         relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
    1090             : 
    1091             :         /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
    1092             :          * issues. Let's also, silence the error messages. */
    1093             : 
    1094           0 :         if (!relax_checks) {
    1095             :                 struct statfs sfs;
    1096             : 
    1097           0 :                 if (statfs(p, &sfs) < 0)
    1098             :                         /* If we are searching for the mount point, don't generate a log message if we can't find the path */
    1099           0 :                         return log_full_errno((searching && errno == ENOENT) ||
    1100             :                                               (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
    1101             :                                               "Failed to check file system type of \"%s\": %m", p);
    1102             : 
    1103           0 :                 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
    1104           0 :                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1105             :                                               SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
    1106             :                                               "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
    1107             :         }
    1108             : 
    1109           0 :         r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
    1110           0 :         if (r < 0)
    1111           0 :                 return r;
    1112             : 
    1113             :         /* In a container we don't have access to block devices, skip this part of the verification, we trust
    1114             :          * the container manager set everything up correctly on its own. */
    1115           0 :         if (detect_container() > 0 || relax_checks)
    1116           0 :                 goto finish;
    1117             : 
    1118             :         /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
    1119             :          * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
    1120             :          * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
    1121             :          * however blkid can't work if we have no privileges to access block devices directly, which is why
    1122             :          * we use udev in that case. */
    1123           0 :         if (unprivileged_mode)
    1124           0 :                 return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
    1125             :         else
    1126           0 :                 return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
    1127             : 
    1128           0 : finish:
    1129           0 :         if (ret_part)
    1130           0 :                 *ret_part = 0;
    1131           0 :         if (ret_pstart)
    1132           0 :                 *ret_pstart = 0;
    1133           0 :         if (ret_psize)
    1134           0 :                 *ret_psize = 0;
    1135           0 :         if (ret_uuid)
    1136           0 :                 *ret_uuid = SD_ID128_NULL;
    1137             : 
    1138           0 :         return 0;
    1139             : }
    1140             : 
    1141           0 : int find_esp_and_warn(
    1142             :                 const char *path,
    1143             :                 bool unprivileged_mode,
    1144             :                 char **ret_path,
    1145             :                 uint32_t *ret_part,
    1146             :                 uint64_t *ret_pstart,
    1147             :                 uint64_t *ret_psize,
    1148             :                 sd_id128_t *ret_uuid) {
    1149             : 
    1150             :         int r;
    1151             : 
    1152             :         /* This logs about all errors except:
    1153             :          *
    1154             :          *    -ENOKEY → when we can't find the partition
    1155             :          *   -EACCESS → when unprivileged_mode is true, and we can't access something
    1156             :          */
    1157             : 
    1158           0 :         if (path) {
    1159           0 :                 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
    1160           0 :                 if (r < 0)
    1161           0 :                         return r;
    1162             : 
    1163           0 :                 goto found;
    1164             :         }
    1165             : 
    1166           0 :         path = getenv("SYSTEMD_ESP_PATH");
    1167           0 :         if (path) {
    1168           0 :                 if (!path_is_valid(path) || !path_is_absolute(path))
    1169           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1170             :                                                "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
    1171             :                                                path);
    1172             : 
    1173             :                 /* Note: when the user explicitly configured things with an env var we won't validate the mount
    1174             :                  * point. After all we want this to be useful for testing. */
    1175           0 :                 goto found;
    1176             :         }
    1177             : 
    1178           0 :         FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
    1179             : 
    1180           0 :                 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
    1181           0 :                 if (r >= 0)
    1182           0 :                         goto found;
    1183           0 :                 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
    1184           0 :                         return r;
    1185             :         }
    1186             : 
    1187             :         /* No logging here */
    1188           0 :         return -ENOKEY;
    1189             : 
    1190           0 : found:
    1191           0 :         if (ret_path) {
    1192             :                 char *c;
    1193             : 
    1194           0 :                 c = strdup(path);
    1195           0 :                 if (!c)
    1196           0 :                         return log_oom();
    1197             : 
    1198           0 :                 *ret_path = c;
    1199             :         }
    1200             : 
    1201           0 :         return 0;
    1202             : }
    1203             : 
    1204           0 : static int verify_xbootldr_blkid(
    1205             :                 dev_t devid,
    1206             :                 bool searching,
    1207             :                 sd_id128_t *ret_uuid) {
    1208             : 
    1209           0 :         sd_id128_t uuid = SD_ID128_NULL;
    1210             : 
    1211             : #if HAVE_BLKID
    1212           0 :         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
    1213           0 :         _cleanup_free_ char *node = NULL;
    1214             :         const char *v;
    1215             :         int r;
    1216             : 
    1217           0 :         r = device_path_make_major_minor(S_IFBLK, devid, &node);
    1218           0 :         if (r < 0)
    1219           0 :                 return log_error_errno(r, "Failed to format major/minor device path: %m");
    1220           0 :         errno = 0;
    1221           0 :         b = blkid_new_probe_from_filename(node);
    1222           0 :         if (!b)
    1223           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
    1224             : 
    1225           0 :         blkid_probe_enable_partitions(b, 1);
    1226           0 :         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
    1227             : 
    1228           0 :         errno = 0;
    1229           0 :         r = blkid_do_safeprobe(b);
    1230           0 :         if (r == -2)
    1231           0 :                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
    1232           0 :         else if (r == 1)
    1233           0 :                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
    1234           0 :         else if (r != 0)
    1235           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
    1236             : 
    1237           0 :         errno = 0;
    1238           0 :         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
    1239           0 :         if (r != 0)
    1240           0 :                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
    1241           0 :         if (streq(v, "gpt")) {
    1242             : 
    1243           0 :                 errno = 0;
    1244           0 :                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
    1245           0 :                 if (r != 0)
    1246           0 :                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
    1247           0 :                 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
    1248           0 :                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1249             :                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1250             :                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
    1251             : 
    1252           0 :                 errno = 0;
    1253           0 :                 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
    1254           0 :                 if (r != 0)
    1255           0 :                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
    1256           0 :                 r = sd_id128_from_string(v, &uuid);
    1257           0 :                 if (r < 0)
    1258           0 :                         return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
    1259             : 
    1260           0 :         } else if (streq(v, "dos")) {
    1261             : 
    1262           0 :                 errno = 0;
    1263           0 :                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
    1264           0 :                 if (r != 0)
    1265           0 :                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
    1266           0 :                 if (!streq(v, "0xea"))
    1267           0 :                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1268             :                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1269             :                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
    1270             : 
    1271             :         } else
    1272           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1273             :                                       searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1274             :                                       "File system \"%s\" is not on a GPT or DOS partition table.", node);
    1275             : #endif
    1276             : 
    1277           0 :         if (ret_uuid)
    1278           0 :                 *ret_uuid = uuid;
    1279             : 
    1280           0 :         return 0;
    1281             : }
    1282             : 
    1283           0 : static int verify_xbootldr_udev(
    1284             :                 dev_t devid,
    1285             :                 bool searching,
    1286             :                 sd_id128_t *ret_uuid) {
    1287             : 
    1288           0 :         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
    1289           0 :         _cleanup_free_ char *node = NULL;
    1290           0 :         sd_id128_t uuid = SD_ID128_NULL;
    1291             :         const char *v;
    1292             :         int r;
    1293             : 
    1294           0 :         r = device_path_make_major_minor(S_IFBLK, devid, &node);
    1295           0 :         if (r < 0)
    1296           0 :                 return log_error_errno(r, "Failed to format major/minor device path: %m");
    1297             : 
    1298           0 :         r = sd_device_new_from_devnum(&d, 'b', devid);
    1299           0 :         if (r < 0)
    1300           0 :                 return log_error_errno(r, "Failed to get device from device number: %m");
    1301             : 
    1302           0 :         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
    1303           0 :         if (r < 0)
    1304           0 :                 return log_error_errno(r, "Failed to get device property: %m");
    1305             : 
    1306           0 :         if (streq(v, "gpt")) {
    1307             : 
    1308           0 :                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
    1309           0 :                 if (r < 0)
    1310           0 :                         return log_error_errno(r, "Failed to get device property: %m");
    1311           0 :                 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
    1312           0 :                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1313             :                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1314             :                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
    1315             : 
    1316           0 :                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
    1317           0 :                 if (r < 0)
    1318           0 :                         return log_error_errno(r, "Failed to get device property: %m");
    1319           0 :                 r = sd_id128_from_string(v, &uuid);
    1320           0 :                 if (r < 0)
    1321           0 :                         return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
    1322             : 
    1323           0 :         } else if (streq(v, "dos")) {
    1324             : 
    1325           0 :                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
    1326           0 :                 if (r < 0)
    1327           0 :                         return log_error_errno(r, "Failed to get device property: %m");
    1328           0 :                 if (!streq(v, "0xea"))
    1329           0 :                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1330             :                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1331             :                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
    1332             :         } else
    1333           0 :                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
    1334             :                                       searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
    1335             :                                       "File system \"%s\" is not on a GPT or DOS partition table.", node);
    1336             : 
    1337           0 :         if (ret_uuid)
    1338           0 :                 *ret_uuid = uuid;
    1339             : 
    1340           0 :         return 0;
    1341             : }
    1342             : 
    1343           0 : static int verify_xbootldr(
    1344             :                 const char *p,
    1345             :                 bool searching,
    1346             :                 bool unprivileged_mode,
    1347             :                 sd_id128_t *ret_uuid) {
    1348             : 
    1349             :         bool relax_checks;
    1350             :         dev_t devid;
    1351             :         int r;
    1352             : 
    1353           0 :         assert(p);
    1354             : 
    1355           0 :         relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
    1356             : 
    1357           0 :         r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
    1358           0 :         if (r < 0)
    1359           0 :                 return r;
    1360             : 
    1361           0 :         if (detect_container() > 0 || relax_checks)
    1362           0 :                 goto finish;
    1363             : 
    1364           0 :         if (unprivileged_mode)
    1365           0 :                 return verify_xbootldr_udev(devid, searching, ret_uuid);
    1366             :         else
    1367           0 :                 return verify_xbootldr_blkid(devid, searching, ret_uuid);
    1368             : 
    1369           0 : finish:
    1370           0 :         if (ret_uuid)
    1371           0 :                 *ret_uuid = SD_ID128_NULL;
    1372             : 
    1373           0 :         return 0;
    1374             : }
    1375             : 
    1376           0 : int find_xbootldr_and_warn(
    1377             :                 const char *path,
    1378             :                 bool unprivileged_mode,
    1379             :                 char **ret_path,
    1380             :                 sd_id128_t *ret_uuid) {
    1381             : 
    1382             :         int r;
    1383             : 
    1384             :         /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
    1385             : 
    1386           0 :         if (path) {
    1387           0 :                 r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
    1388           0 :                 if (r < 0)
    1389           0 :                         return r;
    1390             : 
    1391           0 :                 goto found;
    1392             :         }
    1393             : 
    1394           0 :         path = getenv("SYSTEMD_XBOOTLDR_PATH");
    1395           0 :         if (path) {
    1396           0 :                 if (!path_is_valid(path) || !path_is_absolute(path))
    1397           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1398             :                                                "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
    1399             :                                                path);
    1400             : 
    1401           0 :                 goto found;
    1402             :         }
    1403             : 
    1404           0 :         r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
    1405           0 :         if (r >= 0) {
    1406           0 :                 path = "/boot";
    1407           0 :                 goto found;
    1408             :         }
    1409           0 :         if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
    1410           0 :                 return r;
    1411             : 
    1412           0 :         return -ENOKEY;
    1413             : 
    1414           0 : found:
    1415           0 :         if (ret_path) {
    1416             :                 char *c;
    1417             : 
    1418           0 :                 c = strdup(path);
    1419           0 :                 if (!c)
    1420           0 :                         return log_oom();
    1421             : 
    1422           0 :                 *ret_path = c;
    1423             :         }
    1424             : 
    1425           0 :         return 0;
    1426             : }

Generated by: LCOV version 1.14