diff --git a/TODO b/TODO index bad3a7047b..8695372bc6 100644 --- a/TODO +++ b/TODO @@ -38,8 +38,6 @@ Features: * by default, in systemd --user service bump the OOMAdjust to 100, as privs allow so that systemd survives -* when dissecting images, warn about unrecognized partition flags - * honour specifiers in unit files that resolve to some very basic /etc/os-release data, such as ID, VERSION_ID, BUILD_ID, VARIANT_ID. @@ -172,10 +170,6 @@ Features: right) become genuine first class citizens, and we gain automatic, sane JSON output for them. -* dissector: invoke fsck on the file systems we encounter, after all ext4 is - still pretty popular (and we mount the ESP too with it after all, which is - fat) - * systemd-firstboot: teach it dissector magic, so that you can point it to some disk image and it will just set everything in it all behind the scenes. diff --git a/src/core/execute.c b/src/core/execute.c index 59d7714f2c..b6a4f5479b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2603,7 +2603,7 @@ static int apply_mount_namespace( needs_sandboxing ? context->protect_home : PROTECT_HOME_NO, needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO, context->mount_flags, - DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, + DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK, error_path); /* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 2fef95aa02..e1418419f7 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -11,6 +11,7 @@ #include "log.h" #include "loop-util.h" #include "main-func.h" +#include "parse-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -22,7 +23,7 @@ static enum { } arg_action = ACTION_DISSECT; static const char *arg_image = NULL; static const char *arg_path = NULL; -static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK; +static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK; static void *arg_root_hash = NULL; static size_t arg_root_hash_size = 0; @@ -36,6 +37,7 @@ static void help(void) { " --version Show package version\n" " -m --mount Mount the image to the specified directory\n" " -r --read-only Mount read-only\n" + " --fsck=BOOL Run fsck before mounting\n" " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n" " --root-hash=HASH Specify root hash for verity\n", program_invocation_short_name, @@ -48,6 +50,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_DISCARD, ARG_ROOT_HASH, + ARG_FSCK, }; static const struct option options[] = { @@ -57,6 +60,7 @@ static int parse_argv(int argc, char *argv[]) { { "read-only", no_argument, NULL, 'r' }, { "discard", required_argument, NULL, ARG_DISCARD }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, + { "fsck", required_argument, NULL, ARG_FSCK }, {} }; @@ -123,6 +127,14 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_FSCK: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --fsck= parameter: %s", optarg); + + SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r); + break; + case '?': return -EINVAL; @@ -261,6 +273,8 @@ static int run(int argc, char *argv[]) { return r; r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags); + if (r == -EUCLEAN) + return log_error_errno(r, "File system check on image failed: %m"); if (r < 0) return log_error_errno(r, "Failed to mount image: %m"); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ef6d573bb3..2a63315a4c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3309,10 +3309,12 @@ static int outer_child( r = dissected_image_mount(dissected_image, directory, arg_uid_shift, DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP| - (arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0)| + (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)| (arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0)); + if (r == -EUCLEAN) + return log_error_errno(r, "File system check for image failed: %m"); if (r < 0) - return r; + return log_error_errno(r, "Failed to mount image root file system: %m"); } r = determine_uid_shift(directory); @@ -3396,9 +3398,11 @@ static int outer_child( if (dissected_image) { /* Now we know the uid shift, let's now mount everything else that might be in the image. */ r = dissected_image_mount(dissected_image, directory, arg_uid_shift, - DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0)); + DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)); + if (r == -EUCLEAN) + return log_error_errno(r, "File system check for image failed: %m"); if (r < 0) - return r; + return log_error_errno(r, "Failed to mount image file system: %m"); } if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) { diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 087c3dc345..1ac6549ba5 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -28,6 +28,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "fsck-util.h" #include "gpt.h" #include "hexdecoct.h" #include "hostname-util.h" @@ -278,6 +279,29 @@ static int loop_wait_for_partitions_to_appear( N_DEVICE_NODE_LIST_ATTEMPTS); } +static void check_partition_flags( + const char *node, + unsigned long long pflags, + unsigned long long supported) { + + assert(node); + + /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */ + pflags &= ~(supported | GPT_FLAG_REQUIRED_PARTITION | GPT_FLAG_NO_BLOCK_IO_PROTOCOL | GPT_FLAG_LEGACY_BIOS_BOOTABLE); + + if (pflags == 0) + return; + + /* If there are other bits set, then log about it, to make things discoverable */ + for (unsigned i = 0; i < sizeof(pflags) * 8; i++) { + unsigned long long bit = 1ULL << i; + if (!FLAGS_SET(pflags, bit)) + continue; + + log_debug("Unexpected partition flag %llu set on %s!", bit, node); + } +} + #endif int dissect_image( @@ -484,6 +508,8 @@ int dissect_image( if (sd_id128_equal(type_id, GPT_HOME)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -491,6 +517,8 @@ int dissect_image( rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_SRV)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -510,6 +538,8 @@ int dissect_image( } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -519,6 +549,8 @@ int dissect_image( #ifdef GPT_ROOT_NATIVE else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -531,6 +563,8 @@ int dissect_image( rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -549,6 +583,8 @@ int dissect_image( #ifdef GPT_ROOT_SECONDARY else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -561,6 +597,8 @@ int dissect_image( rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -578,6 +616,8 @@ int dissect_image( #endif else if (sd_id128_equal(type_id, GPT_SWAP)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -585,6 +625,8 @@ int dissect_image( fstype = "swap"; } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -601,6 +643,8 @@ int dissect_image( } else if (sd_id128_equal(type_id, GPT_TMP)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -609,6 +653,8 @@ int dissect_image( } else if (sd_id128_equal(type_id, GPT_VAR)) { + check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY); + if (pflags & GPT_FLAG_NO_AUTO) continue; @@ -851,6 +897,49 @@ static int is_loop_device(const char *path) { return true; } +static int run_fsck(const char *node, const char *fstype) { + int r, exit_status; + pid_t pid; + + assert(node); + assert(fstype); + + r = fsck_exists(fstype); + if (r < 0) { + log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype); + return 0; + } + if (r == 0) { + log_debug("Not checking partition %s, as fsck for %s does not exist.", node, fstype); + return 0; + } + + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_NULL_STDIO, &pid); + if (r < 0) + return log_debug_errno(r, "Failed to fork off fsck: %m"); + if (r == 0) { + /* Child */ + execl("/sbin/fsck", "/sbin/fsck", "-aT", node, NULL); + log_debug_errno(errno, "Failed to execl() fsck: %m"); + _exit(FSCK_OPERATIONAL_ERROR); + } + + exit_status = wait_for_terminate_and_check("fsck", pid, 0); + if (exit_status < 0) + return log_debug_errno(exit_status, "Failed to fork off /sbin/fsck: %m"); + + if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) { + log_debug("fsck failed with exit status %i.", exit_status); + + if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), "File system is corrupted, refusing."); + + log_debug("Ignoring fsck error."); + } + + return 0; +} + static int mount_partition( DissectedPartition *m, const char *where, @@ -878,6 +967,12 @@ static int mount_partition( rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY); + if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) { + r = run_fsck(node, fstype); + if (r < 0) + return r; + } + if (directory) { r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased, NULL); if (r < 0) diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 359dc877d5..6a666ca7c7 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -62,6 +62,7 @@ typedef enum DissectImageFlags { DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifiable as OS images */ DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */ DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */ + DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */ } DissectImageFlags; struct DissectedImage { diff --git a/src/shared/gpt.h b/src/shared/gpt.h index 9dc649d8d9..26a4e1ea65 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -58,7 +58,9 @@ # define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY #endif +#define GPT_FLAG_REQUIRED_PARTITION (1ULL << 0) #define GPT_FLAG_NO_BLOCK_IO_PROTOCOL (1ULL << 1) +#define GPT_FLAG_LEGACY_BIOS_BOOTABLE (1ULL << 2) /* Flags we recognize on the root, swap, home and srv partitions when * doing auto-discovery. These happen to be identical to what