2018-06-14 12:50:49 +02:00
/* SPDX-License-Identifier: LGPL-2.1+ */
2017-10-17 18:23:16 +02:00
# include <stdio.h>
2017-10-20 18:31:34 +02:00
# include <linux/magic.h>
2019-03-27 11:32:41 +01:00
# include <unistd.h>
2017-10-17 18:23:16 +02:00
2019-02-05 18:45:41 +01:00
# include "sd-device.h"
2018-01-11 00:39:12 +01:00
# include "sd-id128.h"
2017-10-17 18:23:16 +02:00
# include "alloc-util.h"
2017-10-20 18:31:34 +02:00
# include "blkid-util.h"
2017-10-17 18:23:16 +02:00
# include "bootspec.h"
# include "conf-files.h"
# include "def.h"
2017-10-31 11:30:40 +01:00
# include "device-nodes.h"
2019-02-06 17:49:37 +01:00
# include "dirent-util.h"
2017-10-17 18:23:16 +02:00
# include "efivars.h"
2019-02-06 17:49:37 +01:00
# include "env-file.h"
2018-06-25 15:08:24 +02:00
# include "env-util.h"
2017-10-17 18:23:16 +02:00
# include "fd-util.h"
# include "fileio.h"
2017-10-20 18:31:34 +02:00
# include "parse-util.h"
2018-11-20 04:37:01 +01:00
# include "path-util.h"
2019-02-06 17:49:37 +01:00
# include "pe-header.h"
2019-03-13 12:14:47 +01:00
# include "sort-util.h"
2017-10-20 18:31:34 +02:00
# include "stat-util.h"
2019-03-01 18:35:26 +01:00
# include "string-table.h"
2017-10-17 18:23:16 +02:00
# include "string-util.h"
# include "strv.h"
2019-02-06 17:49:37 +01:00
# include "unaligned.h"
2019-03-27 11:32:41 +01:00
# include "util.h"
2017-10-20 18:31:34 +02:00
# include "virt.h"
2017-10-17 18:23:16 +02:00
2018-06-22 19:30:12 +02:00
static void boot_entry_free ( BootEntry * entry ) {
2017-12-11 22:22:04 +01:00
assert ( entry ) ;
2017-10-17 18:23:16 +02:00
2018-06-22 12:18:43 +02:00
free ( entry - > id ) ;
2018-09-26 08:35:12 +02:00
free ( entry - > path ) ;
2019-01-28 17:57:41 +01:00
free ( entry - > root ) ;
2017-10-17 18:23:16 +02:00
free ( entry - > title ) ;
2017-10-20 17:58:13 +02:00
free ( entry - > show_title ) ;
2017-10-17 18:23:16 +02:00
free ( entry - > version ) ;
free ( entry - > machine_id ) ;
free ( entry - > architecture ) ;
strv_free ( entry - > options ) ;
free ( entry - > kernel ) ;
free ( entry - > efi ) ;
strv_free ( entry - > initrd ) ;
free ( entry - > device_tree ) ;
}
2019-01-28 17:57:41 +01:00
static int boot_entry_load (
const char * root ,
const char * path ,
BootEntry * entry ) {
2019-03-01 18:35:26 +01:00
_cleanup_ ( boot_entry_free ) BootEntry tmp = {
. type = BOOT_ENTRY_CONF ,
} ;
2017-10-17 18:23:16 +02:00
_cleanup_fclose_ FILE * f = NULL ;
unsigned line = 1 ;
2017-12-26 09:35:35 +09:00
char * b , * c ;
2017-10-17 18:23:16 +02:00
int r ;
2019-01-28 17:57:41 +01:00
assert ( root ) ;
2017-12-11 22:22:04 +01:00
assert ( path ) ;
assert ( entry ) ;
2017-12-26 09:35:35 +09:00
c = endswith_no_case ( path , " .conf " ) ;
2019-01-28 17:32:04 +01:00
if ( ! c )
2019-03-04 19:19:30 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) , " Invalid loader entry file suffix: %s " , path ) ;
2017-10-17 18:23:16 +02:00
2017-12-26 09:35:35 +09:00
b = basename ( path ) ;
2018-06-22 12:18:43 +02:00
tmp . id = strndup ( b , c - b ) ;
if ( ! tmp . id )
2017-10-17 18:23:16 +02:00
return log_oom ( ) ;
2019-03-04 19:19:30 +01:00
if ( ! efi_loader_entry_name_valid ( tmp . id ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) , " Invalid loader entry filename: %s " , path ) ;
2018-09-26 08:35:12 +02:00
tmp . path = strdup ( path ) ;
if ( ! tmp . path )
return log_oom ( ) ;
2019-01-28 17:57:41 +01:00
tmp . root = strdup ( root ) ;
if ( ! tmp . root )
return log_oom ( ) ;
2017-12-26 09:35:35 +09:00
f = fopen ( path , " re " ) ;
if ( ! f )
return log_error_errno ( errno , " Failed to open \" %s \" : %m " , path ) ;
2017-10-17 18:23:16 +02:00
for ( ; ; ) {
2018-02-19 22:26:01 +09:00
_cleanup_free_ char * buf = NULL , * field = NULL ;
const char * p ;
2017-10-17 18:23:16 +02:00
r = read_line ( f , LONG_LINE_MAX , & buf ) ;
if ( r = = 0 )
break ;
if ( r = = - ENOBUFS )
return log_error_errno ( r , " %s:%u: Line too long " , path , line ) ;
if ( r < 0 )
return log_error_errno ( r , " %s:%u: Error while reading: %m " , path , line ) ;
line + + ;
if ( IN_SET ( * strstrip ( buf ) , ' # ' , ' \0 ' ) )
continue ;
2018-02-19 22:26:01 +09:00
p = buf ;
r = extract_first_word ( & p , & field , " \t " , 0 ) ;
if ( r < 0 ) {
log_error_errno ( r , " Failed to parse config file %s line %u: %m " , path , line ) ;
continue ;
}
if ( r = = 0 ) {
2017-10-17 18:23:16 +02:00
log_warning ( " %s:%u: Bad syntax " , path , line ) ;
continue ;
}
2018-02-19 22:26:01 +09:00
if ( streq ( field , " title " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . title , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " version " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . version , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " machine-id " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . machine_id , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " architecture " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . architecture , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " options " ) )
2017-10-17 18:23:16 +02:00
r = strv_extend ( & tmp . options , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " linux " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . kernel , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " efi " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . efi , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " initrd " ) )
2017-10-17 18:23:16 +02:00
r = strv_extend ( & tmp . initrd , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " devicetree " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & tmp . device_tree , p ) ;
else {
2019-01-28 17:34:17 +01:00
log_notice ( " %s:%u: Unknown line \" %s \" , ignoring. " , path , line , field ) ;
2017-10-17 18:23:16 +02:00
continue ;
}
if ( r < 0 )
return log_error_errno ( r , " %s:%u: Error while reading: %m " , path , line ) ;
}
* entry = tmp ;
tmp = ( BootEntry ) { } ;
return 0 ;
}
void boot_config_free ( BootConfig * config ) {
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
size_t i ;
2017-10-17 18:23:16 +02:00
2017-12-11 22:22:04 +01:00
assert ( config ) ;
2017-10-17 18:23:16 +02:00
free ( config - > default_pattern ) ;
free ( config - > timeout ) ;
free ( config - > editor ) ;
2018-01-28 17:06:22 +01:00
free ( config - > auto_entries ) ;
free ( config - > auto_firmware ) ;
2019-03-31 23:30:30 +09:00
free ( config - > console_mode ) ;
2017-10-17 18:23:16 +02:00
free ( config - > entry_oneshot ) ;
free ( config - > entry_default ) ;
for ( i = 0 ; i < config - > n_entries ; i + + )
boot_entry_free ( config - > entries + i ) ;
free ( config - > entries ) ;
}
2018-06-22 19:30:12 +02:00
static int boot_loader_read_conf ( const char * path , BootConfig * config ) {
2017-10-17 18:23:16 +02:00
_cleanup_fclose_ FILE * f = NULL ;
unsigned line = 1 ;
int r ;
2017-12-11 22:22:04 +01:00
assert ( path ) ;
assert ( config ) ;
2017-10-17 18:23:16 +02:00
f = fopen ( path , " re " ) ;
2018-06-22 19:31:50 +02:00
if ( ! f ) {
if ( errno = = ENOENT )
return 0 ;
2017-10-17 18:23:16 +02:00
return log_error_errno ( errno , " Failed to open \" %s \" : %m " , path ) ;
2018-06-22 19:31:50 +02:00
}
2017-10-17 18:23:16 +02:00
for ( ; ; ) {
2018-02-19 22:26:01 +09:00
_cleanup_free_ char * buf = NULL , * field = NULL ;
const char * p ;
2017-10-17 18:23:16 +02:00
r = read_line ( f , LONG_LINE_MAX , & buf ) ;
if ( r = = 0 )
break ;
if ( r = = - ENOBUFS )
return log_error_errno ( r , " %s:%u: Line too long " , path , line ) ;
if ( r < 0 )
return log_error_errno ( r , " %s:%u: Error while reading: %m " , path , line ) ;
line + + ;
if ( IN_SET ( * strstrip ( buf ) , ' # ' , ' \0 ' ) )
continue ;
2018-02-19 22:26:01 +09:00
p = buf ;
r = extract_first_word ( & p , & field , " \t " , 0 ) ;
if ( r < 0 ) {
log_error_errno ( r , " Failed to parse config file %s line %u: %m " , path , line ) ;
continue ;
}
if ( r = = 0 ) {
2017-10-17 18:23:16 +02:00
log_warning ( " %s:%u: Bad syntax " , path , line ) ;
continue ;
}
2018-02-19 22:26:01 +09:00
if ( streq ( field , " default " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & config - > default_pattern , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " timeout " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & config - > timeout , p ) ;
2018-02-19 22:26:01 +09:00
else if ( streq ( field , " editor " ) )
2017-10-17 18:23:16 +02:00
r = free_and_strdup ( & config - > editor , p ) ;
2018-03-08 10:06:00 +01:00
else if ( streq ( field , " auto-entries " ) )
2018-01-28 17:06:22 +01:00
r = free_and_strdup ( & config - > auto_entries , p ) ;
2018-03-08 10:06:00 +01:00
else if ( streq ( field , " auto-firmware " ) )
2018-01-28 17:06:22 +01:00
r = free_and_strdup ( & config - > auto_firmware , p ) ;
2018-03-08 10:06:00 +01:00
else if ( streq ( field , " console-mode " ) )
2017-12-16 22:52:05 -02:00
r = free_and_strdup ( & config - > console_mode , p ) ;
2017-10-17 18:23:16 +02:00
else {
2019-01-28 17:34:17 +01:00
log_notice ( " %s:%u: Unknown line \" %s \" , ignoring. " , path , line , field ) ;
2017-10-17 18:23:16 +02:00
continue ;
}
if ( r < 0 )
return log_error_errno ( r , " %s:%u: Error while reading: %m " , path , line ) ;
}
2018-06-22 19:31:50 +02:00
return 1 ;
2017-10-17 18:23:16 +02:00
}
2018-09-18 08:39:24 +09:00
static int boot_entry_compare ( const BootEntry * a , const BootEntry * b ) {
2018-06-22 12:18:43 +02:00
return str_verscmp ( a - > id , b - > id ) ;
2017-10-17 18:23:16 +02:00
}
2019-01-28 17:57:41 +01:00
static int boot_entries_find (
const char * root ,
const char * dir ,
2019-01-28 18:56:53 +01:00
BootEntry * * entries ,
size_t * n_entries ) {
2019-01-28 17:57:41 +01:00
2017-10-17 18:23:16 +02:00
_cleanup_strv_free_ char * * files = NULL ;
2019-01-28 18:56:53 +01:00
size_t n_allocated = * n_entries ;
2017-10-17 18:23:16 +02:00
char * * f ;
int r ;
2019-01-28 17:57:41 +01:00
assert ( root ) ;
2017-12-11 22:22:04 +01:00
assert ( dir ) ;
2019-01-28 18:56:53 +01:00
assert ( entries ) ;
assert ( n_entries ) ;
2017-12-11 22:22:04 +01:00
2017-10-17 18:23:16 +02:00
r = conf_files_list ( & files , " .conf " , NULL , 0 , dir , NULL ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to list files in \" %s \" : %m " , dir ) ;
STRV_FOREACH ( f , files ) {
2019-01-28 18:56:53 +01:00
if ( ! GREEDY_REALLOC0 ( * entries , n_allocated , * n_entries + 1 ) )
2017-10-17 18:23:16 +02:00
return log_oom ( ) ;
2019-01-28 18:56:53 +01:00
r = boot_entry_load ( root , * f , * entries + * n_entries ) ;
2017-10-17 18:23:16 +02:00
if ( r < 0 )
continue ;
2019-01-28 18:56:53 +01:00
( * n_entries ) + + ;
2017-10-17 18:23:16 +02:00
}
return 0 ;
}
2019-02-06 17:49:37 +01:00
static int boot_entry_load_unified (
const char * root ,
const char * path ,
const char * osrelease ,
const char * cmdline ,
BootEntry * ret ) {
_cleanup_free_ char * os_pretty_name = NULL , * os_id = NULL , * version_id = NULL , * build_id = NULL ;
2019-03-01 18:35:26 +01:00
_cleanup_ ( boot_entry_free ) BootEntry tmp = {
. type = BOOT_ENTRY_UNIFIED ,
} ;
2019-02-06 17:49:37 +01:00
_cleanup_fclose_ FILE * f = NULL ;
const char * k ;
int r ;
assert ( root ) ;
assert ( path ) ;
assert ( osrelease ) ;
k = path_startswith ( path , root ) ;
if ( ! k )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) , " Path is not below root: %s " , path ) ;
f = fmemopen ( ( void * ) osrelease , strlen ( osrelease ) , " r " ) ;
if ( ! f )
return log_error_errno ( errno , " Failed to open os-release buffer: %m " ) ;
r = parse_env_file ( f , " os-release " ,
" PRETTY_NAME " , & os_pretty_name ,
" ID " , & os_id ,
" VERSION_ID " , & version_id ,
" BUILD_ID " , & build_id ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse os-release data from unified kernel image %s: %m " , path ) ;
if ( ! os_pretty_name | | ! os_id | | ! ( version_id | | build_id ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " Missing fields in os-release data from unified kernel image %s, refusing. " , path ) ;
tmp . id = strjoin ( os_id , " - " , version_id ? : build_id ) ;
if ( ! tmp . id )
return log_oom ( ) ;
2019-03-04 19:19:30 +01:00
if ( ! efi_loader_entry_name_valid ( tmp . id ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) , " Invalid loader entry: %s " , tmp . id ) ;
2019-02-06 17:49:37 +01:00
tmp . path = strdup ( path ) ;
if ( ! tmp . path )
return log_oom ( ) ;
tmp . root = strdup ( root ) ;
if ( ! tmp . root )
return log_oom ( ) ;
tmp . kernel = strdup ( skip_leading_chars ( k , " / " ) ) ;
if ( ! tmp . kernel )
return log_oom ( ) ;
tmp . options = strv_new ( skip_leading_chars ( cmdline , WHITESPACE ) ) ;
if ( ! tmp . options )
return log_oom ( ) ;
delete_trailing_chars ( tmp . options [ 0 ] , WHITESPACE ) ;
tmp . title = TAKE_PTR ( os_pretty_name ) ;
* ret = tmp ;
tmp = ( BootEntry ) { } ;
return 0 ;
}
/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
* the ones we do care about and we are willing to load into memory have this size limit . ) */
# define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
static int find_sections (
int fd ,
char * * ret_osrelease ,
char * * ret_cmdline ) {
_cleanup_free_ struct PeSectionHeader * sections = NULL ;
_cleanup_free_ char * osrelease = NULL , * cmdline = NULL ;
size_t i , n_sections ;
struct DosFileHeader dos ;
struct PeHeader pe ;
uint64_t start ;
ssize_t n ;
n = pread ( fd , & dos , sizeof ( dos ) , 0 ) ;
if ( n < 0 )
return log_error_errno ( errno , " Failed read DOS header: %m " ) ;
if ( n ! = sizeof ( dos ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EIO ) , " Short read while reading DOS header, refusing. " ) ;
if ( dos . Magic [ 0 ] ! = ' M ' | | dos . Magic [ 1 ] ! = ' Z ' )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " DOS executable magic missing, refusing. " ) ;
start = unaligned_read_le32 ( & dos . ExeHeader ) ;
n = pread ( fd , & pe , sizeof ( pe ) , start ) ;
if ( n < 0 )
return log_error_errno ( errno , " Failed to read PE header: %m " ) ;
if ( n ! = sizeof ( pe ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EIO ) , " Short read while reading PE header, refusing. " ) ;
if ( pe . Magic [ 0 ] ! = ' P ' | | pe . Magic [ 1 ] ! = ' E ' | | pe . Magic [ 2 ] ! = 0 | | pe . Magic [ 3 ] ! = 0 )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " PE executable magic missing, refusing. " ) ;
n_sections = unaligned_read_le16 ( & pe . FileHeader . NumberOfSections ) ;
if ( n_sections > 96 )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " PE header has too many sections, refusing. " ) ;
sections = new ( struct PeSectionHeader , n_sections ) ;
if ( ! sections )
return log_oom ( ) ;
n = pread ( fd , sections ,
n_sections * sizeof ( struct PeSectionHeader ) ,
start + sizeof ( pe ) + unaligned_read_le16 ( & pe . FileHeader . SizeOfOptionalHeader ) ) ;
if ( n < 0 )
return log_error_errno ( errno , " Failed to read section data: %m " ) ;
if ( ( size_t ) n ! = n_sections * sizeof ( struct PeSectionHeader ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EIO ) , " Short read while reading sections, refusing. " ) ;
for ( i = 0 ; i < n_sections ; i + + ) {
_cleanup_free_ char * k = NULL ;
uint32_t offset , size ;
char * * b ;
if ( strneq ( ( char * ) sections [ i ] . Name , " .osrel " , sizeof ( sections [ i ] . Name ) ) )
b = & osrelease ;
else if ( strneq ( ( char * ) sections [ i ] . Name , " .cmdline " , sizeof ( sections [ i ] . Name ) ) )
b = & cmdline ;
else
continue ;
if ( * b )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " Duplicate section %s, refusing. " , sections [ i ] . Name ) ;
offset = unaligned_read_le32 ( & sections [ i ] . PointerToRawData ) ;
size = unaligned_read_le32 ( & sections [ i ] . VirtualSize ) ;
if ( size > PE_SECTION_SIZE_MAX )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " Section %s too large, refusing. " , sections [ i ] . Name ) ;
k = new ( char , size + 1 ) ;
if ( ! k )
return log_oom ( ) ;
n = pread ( fd , k , size , offset ) ;
if ( n < 0 )
return log_error_errno ( errno , " Failed to read section payload: %m " ) ;
2019-03-15 11:05:26 +01:00
if ( ( size_t ) n ! = size )
2019-02-06 17:49:37 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( EIO ) , " Short read while reading section payload, refusing: " ) ;
/* Allow one trailing NUL byte, but nothing more. */
if ( size > 0 & & memchr ( k , 0 , size - 1 ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " Section contains embedded NUL byte: %m " ) ;
k [ size ] = 0 ;
* b = TAKE_PTR ( k ) ;
}
if ( ! osrelease )
return log_error_errno ( SYNTHETIC_ERRNO ( EBADMSG ) , " Image lacks .osrel section, refusing. " ) ;
if ( ret_osrelease )
* ret_osrelease = TAKE_PTR ( osrelease ) ;
if ( ret_cmdline )
* ret_cmdline = TAKE_PTR ( cmdline ) ;
return 0 ;
}
static int boot_entries_find_unified (
const char * root ,
const char * dir ,
BootEntry * * entries ,
size_t * n_entries ) {
_cleanup_ ( closedirp ) DIR * d = NULL ;
size_t n_allocated = * n_entries ;
struct dirent * de ;
int r ;
assert ( root ) ;
assert ( dir ) ;
assert ( entries ) ;
assert ( n_entries ) ;
d = opendir ( dir ) ;
if ( ! d ) {
if ( errno = = ENOENT )
return 0 ;
return log_error_errno ( errno , " Failed to open %s: %m " , dir ) ;
}
FOREACH_DIRENT ( de , d , return log_error_errno ( errno , " Failed to read %s: %m " , dir ) ) {
_cleanup_free_ char * j = NULL , * osrelease = NULL , * cmdline = NULL ;
_cleanup_close_ int fd = - 1 ;
if ( ! dirent_is_file ( de ) )
continue ;
if ( ! endswith_no_case ( de - > d_name , " .efi " ) )
continue ;
if ( ! GREEDY_REALLOC0 ( * entries , n_allocated , * n_entries + 1 ) )
return log_oom ( ) ;
fd = openat ( dirfd ( d ) , de - > d_name , O_RDONLY | O_CLOEXEC | O_NONBLOCK ) ;
if ( fd < 0 ) {
log_warning_errno ( errno , " Failed to open %s/%s, ignoring: %m " , dir , de - > d_name ) ;
continue ;
}
r = fd_verify_regular ( fd ) ;
if ( r < 0 ) {
2019-03-01 18:38:32 +01:00
log_warning_errno ( r , " File %s/%s is not regular, ignoring: %m " , dir , de - > d_name ) ;
2019-02-06 17:49:37 +01:00
continue ;
}
r = find_sections ( fd , & osrelease , & cmdline ) ;
if ( r < 0 )
continue ;
j = path_join ( dir , de - > d_name ) ;
if ( ! j )
return log_oom ( ) ;
r = boot_entry_load_unified ( root , j , osrelease , cmdline , * entries + * n_entries ) ;
if ( r < 0 )
continue ;
( * n_entries ) + + ;
}
return 0 ;
}
2017-10-20 17:58:13 +02:00
static bool find_nonunique ( BootEntry * entries , size_t n_entries , bool * arr ) {
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
size_t i , j ;
2017-10-20 17:58:13 +02:00
bool non_unique = false ;
2017-12-11 22:22:04 +01:00
assert ( entries | | n_entries = = 0 ) ;
assert ( arr | | n_entries = = 0 ) ;
2017-10-20 17:58:13 +02:00
for ( i = 0 ; i < n_entries ; i + + )
arr [ i ] = false ;
for ( i = 0 ; i < n_entries ; i + + )
for ( j = 0 ; j < n_entries ; j + + )
if ( i ! = j & & streq ( boot_entry_title ( entries + i ) ,
boot_entry_title ( entries + j ) ) )
non_unique = arr [ i ] = arr [ j ] = true ;
return non_unique ;
}
static int boot_entries_uniquify ( BootEntry * entries , size_t n_entries ) {
char * s ;
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
size_t i ;
2017-10-20 17:58:13 +02:00
int r ;
bool arr [ n_entries ] ;
2017-12-11 22:22:04 +01:00
assert ( entries | | n_entries = = 0 ) ;
2017-10-20 17:58:13 +02:00
/* Find _all_ non-unique titles */
if ( ! find_nonunique ( entries , n_entries , arr ) )
return 0 ;
/* Add version to non-unique titles */
for ( i = 0 ; i < n_entries ; i + + )
if ( arr [ i ] & & entries [ i ] . version ) {
r = asprintf ( & s , " %s (%s) " , boot_entry_title ( entries + i ) , entries [ i ] . version ) ;
if ( r < 0 )
return - ENOMEM ;
free_and_replace ( entries [ i ] . show_title , s ) ;
}
if ( ! find_nonunique ( entries , n_entries , arr ) )
return 0 ;
/* Add machine-id to non-unique titles */
for ( i = 0 ; i < n_entries ; i + + )
if ( arr [ i ] & & entries [ i ] . machine_id ) {
r = asprintf ( & s , " %s (%s) " , boot_entry_title ( entries + i ) , entries [ i ] . machine_id ) ;
if ( r < 0 )
return - ENOMEM ;
free_and_replace ( entries [ i ] . show_title , s ) ;
}
if ( ! find_nonunique ( entries , n_entries , arr ) )
return 0 ;
/* Add file name to non-unique titles */
for ( i = 0 ; i < n_entries ; i + + )
if ( arr [ i ] ) {
2018-06-22 12:18:43 +02:00
r = asprintf ( & s , " %s (%s) " , boot_entry_title ( entries + i ) , entries [ i ] . id ) ;
2017-10-20 17:58:13 +02:00
if ( r < 0 )
return - ENOMEM ;
free_and_replace ( entries [ i ] . show_title , s ) ;
}
return 0 ;
}
2017-12-07 14:23:11 +09:00
static int boot_entries_select_default ( const BootConfig * config ) {
2017-10-17 18:23:16 +02:00
int i ;
2017-12-11 22:22:04 +01:00
assert ( config ) ;
2019-03-08 14:37:26 +01:00
assert ( config - > entries | | config - > n_entries = = 0 ) ;
if ( config - > n_entries = = 0 ) {
log_debug ( " Found no default boot entry :( " ) ;
return - 1 ; /* -1 means "no default" */
}
2017-12-11 22:22:04 +01:00
2017-10-17 18:23:16 +02:00
if ( config - > entry_oneshot )
for ( i = config - > n_entries - 1 ; i > = 0 ; i - - )
2018-06-22 12:18:43 +02:00
if ( streq ( config - > entry_oneshot , config - > entries [ i ] . id ) ) {
log_debug ( " Found default: id \" %s \" is matched by LoaderEntryOneShot " ,
config - > entries [ i ] . id ) ;
2017-10-17 18:23:16 +02:00
return i ;
}
if ( config - > entry_default )
for ( i = config - > n_entries - 1 ; i > = 0 ; i - - )
2018-06-22 12:18:43 +02:00
if ( streq ( config - > entry_default , config - > entries [ i ] . id ) ) {
log_debug ( " Found default: id \" %s \" is matched by LoaderEntryDefault " ,
config - > entries [ i ] . id ) ;
2017-10-17 18:23:16 +02:00
return i ;
}
if ( config - > default_pattern )
for ( i = config - > n_entries - 1 ; i > = 0 ; i - - )
2018-06-22 12:18:43 +02:00
if ( fnmatch ( config - > default_pattern , config - > entries [ i ] . id , FNM_CASEFOLD ) = = 0 ) {
log_debug ( " Found default: id \" %s \" is matched by pattern \" %s \" " ,
config - > entries [ i ] . id , config - > default_pattern ) ;
2017-10-17 18:23:16 +02:00
return i ;
}
2019-03-08 14:37:26 +01:00
log_debug ( " Found default: last entry \" %s \" " , config - > entries [ config - > n_entries - 1 ] . id ) ;
return config - > n_entries - 1 ;
2017-10-17 18:23:16 +02:00
}
2019-01-28 18:56:53 +01:00
int boot_entries_load_config (
const char * esp_path ,
const char * xbootldr_path ,
BootConfig * config ) {
2017-10-17 18:23:16 +02:00
const char * p ;
int r ;
2017-12-11 22:22:04 +01:00
assert ( config ) ;
2019-01-28 18:56:53 +01:00
if ( esp_path ) {
p = strjoina ( esp_path , " /loader/loader.conf " ) ;
r = boot_loader_read_conf ( p , config ) ;
if ( r < 0 )
return r ;
2017-10-17 18:23:16 +02:00
2019-01-28 18:56:53 +01:00
p = strjoina ( esp_path , " /loader/entries " ) ;
r = boot_entries_find ( esp_path , p , & config - > entries , & config - > n_entries ) ;
if ( r < 0 )
return r ;
2019-02-06 17:49:37 +01:00
p = strjoina ( esp_path , " /EFI/Linux/ " ) ;
r = boot_entries_find_unified ( esp_path , p , & config - > entries , & config - > n_entries ) ;
if ( r < 0 )
return r ;
2019-01-28 18:56:53 +01:00
}
if ( xbootldr_path ) {
p = strjoina ( xbootldr_path , " /loader/entries " ) ;
r = boot_entries_find ( xbootldr_path , p , & config - > entries , & config - > n_entries ) ;
if ( r < 0 )
return r ;
2019-02-06 17:49:37 +01:00
p = strjoina ( xbootldr_path , " /EFI/Linux/ " ) ;
r = boot_entries_find_unified ( xbootldr_path , p , & config - > entries , & config - > n_entries ) ;
if ( r < 0 )
return r ;
2019-01-28 18:56:53 +01:00
}
2017-10-17 18:23:16 +02:00
2019-02-06 17:53:43 +01:00
typesafe_qsort ( config - > entries , config - > n_entries , boot_entry_compare ) ;
2017-10-20 17:58:13 +02:00
r = boot_entries_uniquify ( config - > entries , config - > n_entries ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to uniquify boot entries: %m " ) ;
2018-06-22 19:33:42 +02:00
if ( is_efi_boot ( ) ) {
r = efi_get_variable_string ( EFI_VENDOR_LOADER , " LoaderEntryOneShot " , & config - > entry_oneshot ) ;
2019-03-06 22:49:52 +01:00
if ( r < 0 & & ! IN_SET ( r , - ENOENT , - ENODATA ) ) {
log_warning_errno ( r , " Failed to read EFI variable \" LoaderEntryOneShot \" : %m " ) ;
if ( r = = - ENOMEM )
return r ;
}
2018-06-22 19:33:42 +02:00
r = efi_get_variable_string ( EFI_VENDOR_LOADER , " LoaderEntryDefault " , & config - > entry_default ) ;
2019-03-06 22:49:52 +01:00
if ( r < 0 & & ! IN_SET ( r , - ENOENT , - ENODATA ) ) {
log_warning_errno ( r , " Failed to read EFI variable \" LoaderEntryDefault \" : %m " ) ;
if ( r = = - ENOMEM )
return r ;
}
2018-06-22 19:33:42 +02:00
}
2017-10-17 18:23:16 +02:00
config - > default_entry = boot_entries_select_default ( config ) ;
return 0 ;
}
2017-10-20 18:31:34 +02:00
2019-03-04 17:52:17 +01:00
int boot_entries_load_config_auto (
const char * override_esp_path ,
const char * override_xbootldr_path ,
BootConfig * config ) {
_cleanup_free_ char * esp_where = NULL , * xbootldr_where = NULL ;
int r ;
assert ( config ) ;
/* This function is similar to boot_entries_load_config(), however we automatically search for the
* ESP and the XBOOTLDR partition unless it is explicitly specified . Also , if the user did not pass
* an ESP or XBOOTLDR path directly , let ' s see if / run / boot - loader - entries / exists . If so , let ' s
* read data from there , as if it was an ESP ( i . e . loading both entries and loader . conf data from
* it ) . This allows other boot loaders to pass boot loader entry information to our tools if they
* want to . */
if ( ! override_esp_path & & ! override_xbootldr_path ) {
2019-03-05 13:56:18 +01:00
if ( access ( " /run/boot-loader-entries/ " , F_OK ) > = 0 )
return boot_entries_load_config ( " /run/boot-loader-entries/ " , NULL , config ) ;
2019-03-04 17:52:17 +01:00
2019-03-05 13:56:18 +01:00
if ( errno ! = ENOENT )
return log_error_errno ( errno ,
" Failed to determine whether /run/boot-loader-entries/ exists: %m " ) ;
2019-03-04 17:52:17 +01:00
}
r = find_esp_and_warn ( override_esp_path , false , & esp_where , NULL , NULL , NULL , NULL ) ;
2019-03-12 20:13:29 +01:00
if ( r < 0 ) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
2019-03-04 17:52:17 +01:00
return r ;
r = find_xbootldr_and_warn ( override_xbootldr_path , false , & xbootldr_where , NULL ) ;
if ( r < 0 & & r ! = - ENOKEY )
return r ; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
2019-03-05 13:56:18 +01:00
return boot_entries_load_config ( esp_where , xbootldr_where , config ) ;
2019-03-04 17:52:17 +01:00
}
2019-03-01 18:35:26 +01:00
int boot_entries_augment_from_loader ( BootConfig * config , bool only_auto ) {
static const char * const title_table [ ] = {
/* Pretty names for a few well-known automatically discovered entries. */
" auto-osx " , " macOS " ,
" auto-windows " , " Windows Boot Manager " ,
" auto-efi-shell " , " EFI Shell " ,
" auto-efi-default " , " EFI Default Loader " ,
" auto-reboot-to-firmware-setup " , " Reboot Into Firmware Interface " ,
} ;
2019-03-31 23:31:29 +09:00
_cleanup_strv_free_ char * * found_by_loader = NULL ;
2019-03-01 18:35:26 +01:00
size_t n_allocated ;
char * * i ;
int r ;
assert ( config ) ;
/* Let's add the entries discovered by the boot loader to the end of our list, unless they are
* already included there . */
r = efi_loader_get_entries ( & found_by_loader ) ;
if ( IN_SET ( r , - ENOENT , - EOPNOTSUPP ) )
return log_debug_errno ( r , " Boot loader reported no entries. " ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to determine entries reported by boot loader: %m " ) ;
n_allocated = config - > n_entries ;
STRV_FOREACH ( i , found_by_loader ) {
2019-04-04 22:20:19 +02:00
_cleanup_free_ char * c = NULL , * t = NULL , * p = NULL ;
2019-03-01 18:35:26 +01:00
char * * a , * * b ;
if ( boot_config_has_entry ( config , * i ) )
continue ;
if ( only_auto & & ! startswith ( * i , " auto- " ) )
continue ;
c = strdup ( * i ) ;
if ( ! c )
return log_oom ( ) ;
STRV_FOREACH_PAIR ( a , b , ( char * * ) title_table )
if ( streq ( * a , * i ) ) {
t = strdup ( * b ) ;
if ( ! t )
return log_oom ( ) ;
break ;
}
2019-04-04 22:20:19 +02:00
p = efi_variable_path ( EFI_VENDOR_LOADER , " LoaderEntries " ) ;
if ( ! p )
return log_oom ( ) ;
2019-03-01 18:35:26 +01:00
if ( ! GREEDY_REALLOC0 ( config - > entries , n_allocated , config - > n_entries + 1 ) )
return log_oom ( ) ;
config - > entries [ config - > n_entries + + ] = ( BootEntry ) {
. type = BOOT_ENTRY_LOADER ,
. id = TAKE_PTR ( c ) ,
. title = TAKE_PTR ( t ) ,
2019-04-04 22:20:19 +02:00
. path = TAKE_PTR ( p ) ,
2019-03-01 18:35:26 +01:00
} ;
}
return 0 ;
}
2017-10-20 18:31:34 +02:00
/********************************************************************************/
2019-01-30 18:24:59 +01:00
static int verify_esp_blkid (
dev_t devid ,
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
bool searching ,
2017-10-20 18:31:34 +02:00
uint32_t * ret_part ,
uint64_t * ret_pstart ,
uint64_t * ret_psize ,
sd_id128_t * ret_uuid ) {
2019-01-30 18:24:59 +01:00
sd_id128_t uuid = SD_ID128_NULL ;
uint64_t pstart = 0 , psize = 0 ;
uint32_t part = 0 ;
2017-12-05 14:03:11 +09:00
# if HAVE_BLKID
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
_cleanup_ ( blkid_free_probep ) blkid_probe b = NULL ;
2018-06-29 16:49:23 +02:00
_cleanup_free_ char * node = NULL ;
2017-12-05 14:03:11 +09:00
const char * v ;
2017-10-20 18:31:34 +02:00
int r ;
2019-01-30 18:24:59 +01:00
r = device_path_make_major_minor ( S_IFBLK , devid , & node ) ;
2018-06-29 16:49:23 +02:00
if ( r < 0 )
return log_error_errno ( r , " Failed to format major/minor device path: %m " ) ;
2019-01-30 18:24:59 +01:00
2017-10-20 18:31:34 +02:00
errno = 0 ;
2018-06-29 16:49:23 +02:00
b = blkid_new_probe_from_filename ( node ) ;
2017-10-20 18:31:34 +02:00
if ( ! b )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( ENOMEM ) , " Failed to open file system \" %s \" : %m " , node ) ;
2017-10-20 18:31:34 +02:00
blkid_probe_enable_superblocks ( b , 1 ) ;
blkid_probe_set_superblocks_flags ( b , BLKID_SUBLKS_TYPE ) ;
blkid_probe_enable_partitions ( b , 1 ) ;
blkid_probe_set_partitions_flags ( b , BLKID_PARTS_ENTRY_DETAILS ) ;
errno = 0 ;
r = blkid_do_safeprobe ( b ) ;
2019-01-28 20:08:34 +01:00
if ( r = = - 2 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( ENODEV ) , " File system \" %s \" is ambiguous. " , node ) ;
2019-01-28 20:08:34 +01:00
else if ( r = = 1 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( ENODEV ) , " File system \" %s \" does not contain a label. " , node ) ;
2019-01-28 20:08:34 +01:00
else if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe file system \" %s \" : %m " , node ) ;
2017-10-20 18:31:34 +02:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " TYPE " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe file system type of \" %s \" : %m " , node ) ;
2019-01-28 20:08:34 +01:00
if ( ! streq ( v , " vfat " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
2019-01-30 18:24:59 +01:00
" File system \" %s \" is not FAT. " , node ) ;
2017-10-20 18:31:34 +02:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_SCHEME " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition scheme of \" %s \" : %m " , node ) ;
2019-01-28 20:08:34 +01:00
if ( ! streq ( v , " gpt " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
2019-01-30 18:24:59 +01:00
" File system \" %s \" is not on a GPT partition table. " , node ) ;
2017-10-20 18:31:34 +02:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_TYPE " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : EIO , " Failed to probe partition type UUID of \" %s \" : %m " , node ) ;
2019-01-28 20:08:34 +01:00
if ( ! streq ( v , " c12a7328-f81f-11d2-ba4b-00a0c93ec93b " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
2019-01-30 18:24:59 +01:00
" File system \" %s \" has wrong type for an EFI System Partition (ESP). " , node ) ;
2017-10-20 18:31:34 +02:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_UUID " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition entry UUID of \" %s \" : %m " , node ) ;
2017-10-20 18:31:34 +02:00
r = sd_id128_from_string ( v , & uuid ) ;
2019-01-28 20:08:34 +01:00
if ( r < 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( r , " Partition \" %s \" has invalid UUID \" %s \" . " , node , v ) ;
2017-10-20 18:31:34 +02:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_NUMBER " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition number of \" %s \" : m " , node ) ;
2017-10-20 18:31:34 +02:00
r = safe_atou32 ( v , & part ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_NUMBER field. " ) ;
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_OFFSET " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition offset of \" %s \" : %m " , node ) ;
2017-10-20 18:31:34 +02:00
r = safe_atou64 ( v , & pstart ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_OFFSET field. " ) ;
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_SIZE " , & v , NULL ) ;
if ( r ! = 0 )
2019-01-30 18:24:59 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition size of \" %s \" : %m " , node ) ;
2017-10-20 18:31:34 +02:00
r = safe_atou64 ( v , & psize ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_SIZE field. " ) ;
2017-12-05 14:03:11 +09:00
# endif
2017-10-20 18:31:34 +02:00
if ( ret_part )
* ret_part = part ;
if ( ret_pstart )
* ret_pstart = pstart ;
if ( ret_psize )
* ret_psize = psize ;
if ( ret_uuid )
* ret_uuid = uuid ;
return 0 ;
}
2019-02-05 18:45:41 +01:00
static int verify_esp_udev (
dev_t devid ,
bool searching ,
uint32_t * ret_part ,
uint64_t * ret_pstart ,
uint64_t * ret_psize ,
sd_id128_t * ret_uuid ) {
_cleanup_ ( sd_device_unrefp ) sd_device * d = NULL ;
_cleanup_free_ char * node = NULL ;
sd_id128_t uuid = SD_ID128_NULL ;
uint64_t pstart = 0 , psize = 0 ;
uint32_t part = 0 ;
const char * v ;
int r ;
r = device_path_make_major_minor ( S_IFBLK , devid , & node ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to format major/minor device path: %m " ) ;
r = sd_device_new_from_devnum ( & d , ' b ' , devid ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device from device number: %m " ) ;
r = sd_device_get_property_value ( d , " ID_FS_TYPE " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( ! streq ( v , " vfat " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" File system \" %s \" is not FAT. " , node ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_SCHEME " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( ! streq ( v , " gpt " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" File system \" %s \" is not on a GPT partition table. " , node ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_TYPE " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( ! streq ( v , " c12a7328-f81f-11d2-ba4b-00a0c93ec93b " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" File system \" %s \" has wrong type for an EFI System Partition (ESP). " , node ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_UUID " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
r = sd_id128_from_string ( v , & uuid ) ;
if ( r < 0 )
return log_error_errno ( r , " Partition \" %s \" has invalid UUID \" %s \" . " , node , v ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_NUMBER " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
r = safe_atou32 ( v , & part ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_NUMBER field. " ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_OFFSET " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
r = safe_atou64 ( v , & pstart ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_OFFSET field. " ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_SIZE " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
r = safe_atou64 ( v , & psize ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PART_ENTRY_SIZE field. " ) ;
if ( ret_part )
* ret_part = part ;
if ( ret_pstart )
* ret_pstart = pstart ;
if ( ret_psize )
* ret_psize = psize ;
if ( ret_uuid )
* ret_uuid = uuid ;
return 0 ;
}
2019-02-05 18:52:24 +01:00
static int verify_fsroot_dir (
const char * path ,
bool searching ,
bool unprivileged_mode ,
dev_t * ret_dev ) {
struct stat st , st2 ;
2019-02-08 17:18:48 +01:00
const char * t2 , * trigger ;
2019-02-05 18:52:24 +01:00
int r ;
assert ( path ) ;
assert ( ret_dev ) ;
2019-02-08 17:18:48 +01:00
/* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
* directory won ' t trigger it , if it is not mounted yet . Let ' s hence explicitly trigger it here ,
* before stat ( ) ing */
trigger = strjoina ( path , " /trigger " ) ; /* Filename doesn't matter... */
( void ) access ( trigger , F_OK ) ;
2019-02-05 18:52:24 +01:00
if ( stat ( path , & st ) < 0 )
return log_full_errno ( ( searching & & errno = = ENOENT ) | |
( unprivileged_mode & & errno = = EACCES ) ? LOG_DEBUG : LOG_ERR , errno ,
" Failed to determine block device node of \" %s \" : %m " , path ) ;
if ( major ( st . st_dev ) = = 0 )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" Block device node of \" %s \" is invalid. " , path ) ;
t2 = strjoina ( path , " /.. " ) ;
if ( stat ( t2 , & st2 ) < 0 ) {
if ( errno ! = EACCES )
r = - errno ;
else {
_cleanup_free_ char * parent = NULL ;
/* If going via ".." didn't work due to EACCESS, then let's determine the parent path
* directly instead . It ' s not as good , due to symlinks and such , but we can ' t do
* anything better here . */
parent = dirname_malloc ( path ) ;
if ( ! parent )
return log_oom ( ) ;
if ( stat ( parent , & st2 ) < 0 )
r = - errno ;
else
r = 0 ;
}
if ( r < 0 )
return log_full_errno ( unprivileged_mode & & r = = - EACCES ? LOG_DEBUG : LOG_ERR , r ,
" Failed to determine block device node of parent of \" %s \" : %m " , path ) ;
}
if ( st . st_dev = = st2 . st_dev )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" Directory \" %s \" is not the root of the file system. " , path ) ;
if ( ret_dev )
* ret_dev = st . st_dev ;
return 0 ;
}
2019-01-30 18:24:59 +01:00
static int verify_esp (
const char * p ,
bool searching ,
bool unprivileged_mode ,
uint32_t * ret_part ,
uint64_t * ret_pstart ,
uint64_t * ret_psize ,
sd_id128_t * ret_uuid ) {
bool relax_checks ;
2019-02-05 18:52:24 +01:00
dev_t devid ;
2019-01-30 18:24:59 +01:00
int r ;
assert ( p ) ;
/* This logs about all errors, except:
*
* - ENOENT → if ' searching ' is set , and the dir doesn ' t exist
* - EADDRNOTAVAIL → if ' searching ' is set , and the dir doesn ' t look like an ESP
* - EACESS → if ' unprivileged_mode ' is set , and we have trouble acessing the thing
*/
relax_checks = getenv_bool ( " SYSTEMD_RELAX_ESP_CHECKS " ) > 0 ;
/* Non-root user can only check the status, so if an error occured in the following, it does not cause any
* issues . Let ' s also , silence the error messages . */
if ( ! relax_checks ) {
2019-02-05 18:52:24 +01:00
struct statfs sfs ;
2019-01-30 18:24:59 +01:00
if ( statfs ( p , & sfs ) < 0 )
/* If we are searching for the mount point, don't generate a log message if we can't find the path */
return log_full_errno ( ( searching & & errno = = ENOENT ) | |
( unprivileged_mode & & errno = = EACCES ) ? LOG_DEBUG : LOG_ERR , errno ,
" Failed to check file system type of \" %s \" : %m " , p ) ;
if ( ! F_TYPE_EQUAL ( sfs . f_type , MSDOS_SUPER_MAGIC ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
SYNTHETIC_ERRNO ( searching ? EADDRNOTAVAIL : ENODEV ) ,
" File system \" %s \" is not a FAT EFI System Partition (ESP) file system. " , p ) ;
}
2019-02-05 18:52:24 +01:00
r = verify_fsroot_dir ( p , searching , unprivileged_mode , & devid ) ;
2019-01-30 18:24:59 +01:00
if ( r < 0 )
2019-02-05 18:52:24 +01:00
return r ;
2019-01-30 18:24:59 +01:00
/* In a container we don't have access to block devices, skip this part of the verification, we trust
2019-02-05 18:45:41 +01:00
* the container manager set everything up correctly on its own . */
if ( detect_container ( ) > 0 | | relax_checks )
2019-01-30 18:24:59 +01:00
goto finish ;
2019-02-05 18:45:41 +01:00
/* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
* use blkid instead . Why ? Because this code is called from ' bootctl ' which is pretty much an
* emergency recovery tool that should also work when udev isn ' t up ( i . e . from the emergency shell ) ,
* however blkid can ' t work if we have no privileges to access block devices directly , which is why
* we use udev in that case . */
if ( unprivileged_mode )
2019-02-05 18:52:24 +01:00
return verify_esp_udev ( devid , searching , ret_part , ret_pstart , ret_psize , ret_uuid ) ;
2019-02-05 18:45:41 +01:00
else
2019-02-05 18:52:24 +01:00
return verify_esp_blkid ( devid , searching , ret_part , ret_pstart , ret_psize , ret_uuid ) ;
2019-01-30 18:24:59 +01:00
finish :
if ( ret_part )
* ret_part = 0 ;
if ( ret_pstart )
* ret_pstart = 0 ;
if ( ret_psize )
* ret_psize = 0 ;
if ( ret_uuid )
* ret_uuid = SD_ID128_NULL ;
return 0 ;
}
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
int find_esp_and_warn (
const char * path ,
bool unprivileged_mode ,
char * * ret_path ,
uint32_t * ret_part ,
uint64_t * ret_pstart ,
uint64_t * ret_psize ,
sd_id128_t * ret_uuid ) {
2017-10-20 18:31:34 +02:00
int r ;
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
/* This logs about all errors except:
*
* - ENOKEY → when we can ' t find the partition
* - EACCESS → when unprivileged_mode is true , and we can ' t access something
*/
2017-10-20 18:31:34 +02:00
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
if ( path ) {
r = verify_esp ( path , false , unprivileged_mode , ret_part , ret_pstart , ret_psize , ret_uuid ) ;
2017-10-20 18:31:34 +02:00
if ( r < 0 )
return r ;
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
goto found ;
}
2018-11-20 04:37:01 +01:00
path = getenv ( " SYSTEMD_ESP_PATH " ) ;
if ( path ) {
2018-11-20 23:40:44 +01:00
if ( ! path_is_valid ( path ) | | ! path_is_absolute ( path ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) ,
" $SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s " ,
path ) ;
2018-11-20 04:37:01 +01:00
/* Note: when the user explicitly configured things with an env var we won't validate the mount
* point . After all we want this to be useful for testing . */
goto found ;
}
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
FOREACH_STRING ( path , " /efi " , " /boot " , " /boot/efi " ) {
r = verify_esp ( path , true , unprivileged_mode , ret_part , ret_pstart , ret_psize , ret_uuid ) ;
if ( r > = 0 )
goto found ;
if ( ! IN_SET ( r , - ENOENT , - EADDRNOTAVAIL ) ) /* This one is not it */
return r ;
}
/* No logging here */
return - ENOKEY ;
2019-01-23 14:19:40 +01:00
found :
if ( ret_path ) {
char * c ;
c = strdup ( path ) ;
if ( ! c )
return log_oom ( ) ;
* ret_path = c ;
}
return 0 ;
}
2019-02-05 18:17:01 +01:00
static int verify_xbootldr_blkid (
dev_t devid ,
2019-01-23 14:19:40 +01:00
bool searching ,
sd_id128_t * ret_uuid ) {
2019-02-05 18:17:01 +01:00
sd_id128_t uuid = SD_ID128_NULL ;
2019-01-23 14:19:40 +01:00
# if HAVE_BLKID
_cleanup_ ( blkid_free_probep ) blkid_probe b = NULL ;
_cleanup_free_ char * node = NULL ;
const char * v ;
int r ;
2019-02-05 18:17:01 +01:00
r = device_path_make_major_minor ( S_IFBLK , devid , & node ) ;
2019-01-23 14:19:40 +01:00
if ( r < 0 )
return log_error_errno ( r , " Failed to format major/minor device path: %m " ) ;
errno = 0 ;
b = blkid_new_probe_from_filename ( node ) ;
if ( ! b )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( ENOMEM ) , " Failed to open file system \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
blkid_probe_enable_partitions ( b , 1 ) ;
blkid_probe_set_partitions_flags ( b , BLKID_PARTS_ENTRY_DETAILS ) ;
errno = 0 ;
r = blkid_do_safeprobe ( b ) ;
if ( r = = - 2 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( ENODEV ) , " File system \" %s \" is ambiguous. " , node ) ;
2019-01-23 14:19:40 +01:00
else if ( r = = 1 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( SYNTHETIC_ERRNO ( ENODEV ) , " File system \" %s \" does not contain a label. " , node ) ;
2019-01-23 14:19:40 +01:00
else if ( r ! = 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe file system \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_SCHEME " , & v , NULL ) ;
if ( r ! = 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition scheme of \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
if ( streq ( v , " gpt " ) ) {
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_TYPE " , & v , NULL ) ;
if ( r ! = 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition type UUID of \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
if ( ! streq ( v , " bc13c2ff-59e6-4262-a352-b275fd6f7172 " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
2019-02-05 18:17:01 +01:00
" File system \" %s \" has wrong type for extended boot loader partition. " , node ) ;
2019-01-23 14:19:40 +01:00
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_UUID " , & v , NULL ) ;
if ( r ! = 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition entry UUID of \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
r = sd_id128_from_string ( v , & uuid ) ;
if ( r < 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( r , " Partition \" %s \" has invalid UUID \" %s \" . " , node , v ) ;
2019-01-23 14:19:40 +01:00
} else if ( streq ( v , " dos " ) ) {
errno = 0 ;
r = blkid_probe_lookup_value ( b , " PART_ENTRY_TYPE " , & v , NULL ) ;
if ( r ! = 0 )
2019-02-05 18:17:01 +01:00
return log_error_errno ( errno ? : SYNTHETIC_ERRNO ( EIO ) , " Failed to probe partition type UUID of \" %s \" : %m " , node ) ;
2019-01-23 14:19:40 +01:00
if ( ! streq ( v , " 0xea " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
2019-02-05 18:17:01 +01:00
" File system \" %s \" has wrong type for extended boot loader partition. " , node ) ;
2019-01-23 14:19:40 +01:00
} else
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
2019-02-05 18:17:01 +01:00
" File system \" %s \" is not on a GPT or DOS partition table. " , node ) ;
2019-01-23 14:19:40 +01:00
# endif
if ( ret_uuid )
* ret_uuid = uuid ;
return 0 ;
}
2019-02-05 19:02:03 +01:00
static int verify_xbootldr_udev (
dev_t devid ,
bool searching ,
sd_id128_t * ret_uuid ) {
_cleanup_ ( sd_device_unrefp ) sd_device * d = NULL ;
_cleanup_free_ char * node = NULL ;
sd_id128_t uuid = SD_ID128_NULL ;
const char * v ;
int r ;
r = device_path_make_major_minor ( S_IFBLK , devid , & node ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to format major/minor device path: %m " ) ;
r = sd_device_new_from_devnum ( & d , ' b ' , devid ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device from device number: %m " ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_SCHEME " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( streq ( v , " gpt " ) ) {
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_TYPE " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( ! streq ( v , " bc13c2ff-59e6-4262-a352-b275fd6f7172 " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
" File system \" %s \" has wrong type for extended boot loader partition. " , node ) ;
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_UUID " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
r = sd_id128_from_string ( v , & uuid ) ;
if ( r < 0 )
return log_error_errno ( r , " Partition \" %s \" has invalid UUID \" %s \" . " , node , v ) ;
} else if ( streq ( v , " dos " ) ) {
r = sd_device_get_property_value ( d , " ID_PART_ENTRY_TYPE " , & v ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to get device property: %m " ) ;
if ( ! streq ( v , " 0xea " ) )
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
" File system \" %s \" has wrong type for extended boot loader partition. " , node ) ;
} else
return log_full_errno ( searching ? LOG_DEBUG : LOG_ERR ,
searching ? SYNTHETIC_ERRNO ( EADDRNOTAVAIL ) : SYNTHETIC_ERRNO ( ENODEV ) ,
" File system \" %s \" is not on a GPT or DOS partition table. " , node ) ;
if ( ret_uuid )
* ret_uuid = uuid ;
return 0 ;
}
2019-02-05 18:17:01 +01:00
static int verify_xbootldr (
const char * p ,
bool searching ,
bool unprivileged_mode ,
sd_id128_t * ret_uuid ) {
bool relax_checks ;
2019-02-05 19:11:31 +01:00
dev_t devid ;
2019-02-05 18:17:01 +01:00
int r ;
assert ( p ) ;
relax_checks = getenv_bool ( " SYSTEMD_RELAX_XBOOTLDR_CHECKS " ) > 0 ;
2019-02-05 19:11:31 +01:00
r = verify_fsroot_dir ( p , searching , unprivileged_mode , & devid ) ;
2019-02-05 18:17:01 +01:00
if ( r < 0 )
2019-02-05 19:11:31 +01:00
return r ;
2019-02-05 18:17:01 +01:00
2019-02-05 19:02:03 +01:00
if ( detect_container ( ) > 0 | | relax_checks )
2019-02-05 18:17:01 +01:00
goto finish ;
2019-02-05 19:02:03 +01:00
if ( unprivileged_mode )
2019-02-05 19:11:31 +01:00
return verify_xbootldr_udev ( devid , searching , ret_uuid ) ;
2019-02-05 19:02:03 +01:00
else
2019-02-05 19:11:31 +01:00
return verify_xbootldr_blkid ( devid , searching , ret_uuid ) ;
2019-02-05 18:17:01 +01:00
finish :
if ( ret_uuid )
* ret_uuid = SD_ID128_NULL ;
return 0 ;
}
2019-01-23 14:19:40 +01:00
int find_xbootldr_and_warn (
const char * path ,
bool unprivileged_mode ,
char * * ret_path ,
sd_id128_t * ret_uuid ) {
int r ;
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
if ( path ) {
r = verify_xbootldr ( path , false , unprivileged_mode , ret_uuid ) ;
if ( r < 0 )
return r ;
goto found ;
}
path = getenv ( " SYSTEMD_XBOOTLDR_PATH " ) ;
if ( path ) {
if ( ! path_is_valid ( path ) | | ! path_is_absolute ( path ) )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) ,
" $SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s " ,
path ) ;
goto found ;
}
r = verify_xbootldr ( " /boot " , true , unprivileged_mode , ret_uuid ) ;
if ( r > = 0 ) {
path = " /boot " ;
goto found ;
}
if ( ! IN_SET ( r , - ENOENT , - EADDRNOTAVAIL ) ) /* This one is not it */
return r ;
return - ENOKEY ;
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
found :
if ( ret_path ) {
char * c ;
c = strdup ( path ) ;
if ( ! c )
2017-10-20 18:31:34 +02:00
return log_oom ( ) ;
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
* ret_path = c ;
2017-10-20 18:31:34 +02:00
}
efi: rework find_esp() error propagation/logging a bit
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:
1. Change the error that is returned when we can't find the ESP to
ENOKEY (from ENOENT). This way the error code can only mean one
thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
two: an input path and an output path. That way the memory management
is clear: we will access the input parameter only for reading, and
only write out the output parameter, using malloc() memory.
Before the calling convention were quire surprising for internal API
code, as the path parameter had to be malloc() memory and might and
might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
simple wrapper around find_esp_warn(), that basically just adds the
friendly logging for the ENOKEY case. This rework removes double
logging in a number of error cases, as we no longer log here in
anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
"unprivileged_mode", which disables logging in the EACCES case, and
skips privileged validation of the path. This makes the function less
magic, and doesn't hide this internal silencing automatism from the
caller anymore.
With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable
2017-12-11 22:04:46 +01:00
return 0 ;
2017-10-20 18:31:34 +02:00
}