mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
bootctl: add new --print-root-device option
We already have this nice code in system that determines the block device backing the root file system, but it's only used internally in systemd-gpt-generator. Let's make this more accessible and expose it directly in bootctl. It doesn't fit immediately into the topic of bootctl, but I think it's close enough and behaves very similar to the existing "bootctl --print-boot-path" and "--print-esp-path" tools. If --print-root-device (or -R) is specified once, will show the block device backing the root fs, and if specified twice (probably easier: -RR) it will show the whole block device that block device belongs to in case it is a partition block device. Suggested use: # cfdisk `bootctl -RR` To get access to the partition table, behind the OS install, for whatever it might be.
This commit is contained in:
parent
b469b969f3
commit
c56be2c294
@ -341,6 +341,22 @@
|
||||
<command>systemd-boot</command> being installed.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-R</option></term>
|
||||
<term><option>--print-root-device</option></term>
|
||||
|
||||
<listitem><para>Print the path to the block device node backing the root file system of the local
|
||||
OS. This prints a path such as <filename>/dev/nvme0n1p5</filename>. If the root file system is backed
|
||||
by dm-crypt/LUKS or dm-verity the underlying block device is returned. If the root file system is
|
||||
backed by multiple block devices (as supported by btrfs) the operation will fail. If the switch is
|
||||
specified twice (i.e. <option>-RR</option>) and the discovered block device is a partition device the
|
||||
"whole" block device it belongs to is determined and printed
|
||||
(e.g. <filename>/dev/nvme0n1</filename>). If the root file system is <literal>tmpfs</literal> (or a
|
||||
similar in-memory file system), the block device backing <filename>/usr/</filename> is returned if
|
||||
applicable. If the root file system is a network file system (e.g. NFS, CIFS) the operation will
|
||||
fail.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--no-variables</option></term>
|
||||
<listitem><para>Do not touch the firmware's boot loader list stored in EFI variables.</para></listitem>
|
||||
@ -462,7 +478,9 @@
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
|
||||
<para>On success, 0 is returned, a non-zero failure code otherwise. <command>bootctl
|
||||
--print-root-device</command> returns exit status 80 in case the root file system is not backed by single
|
||||
block device, and other non-zero exit statusses on other errors.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "blockdev-util.h"
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-install.h"
|
||||
#include "bootctl-random-seed.h"
|
||||
@ -11,6 +12,7 @@
|
||||
#include "bootctl-systemd-efi-options.h"
|
||||
#include "bootctl-uki.h"
|
||||
#include "build.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "escape.h"
|
||||
#include "find-esp.h"
|
||||
@ -33,6 +35,7 @@ char *arg_esp_path = NULL;
|
||||
char *arg_xbootldr_path = NULL;
|
||||
bool arg_print_esp_path = false;
|
||||
bool arg_print_dollar_boot_path = false;
|
||||
unsigned arg_print_root_device = 0;
|
||||
bool arg_touch_variables = true;
|
||||
PagerFlags arg_pager_flags = 0;
|
||||
bool arg_graceful = false;
|
||||
@ -167,8 +170,10 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --install-source=auto|image|host\n"
|
||||
" Where to pick files when using --root=/--image=\n"
|
||||
" -p --print-esp-path Print path to the EFI System Partition\n"
|
||||
" -x --print-boot-path Print path to the $BOOT partition\n"
|
||||
" -p --print-esp-path Print path to the EFI System Partition mount point\n"
|
||||
" -x --print-boot-path Print path to the $BOOT partition mount point\n"
|
||||
" -R --print-root-device\n"
|
||||
" Print path to the root device node\n"
|
||||
" --no-variables Don't touch EFI variables\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --graceful Don't fail when the ESP cannot be found or EFI\n"
|
||||
@ -227,6 +232,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "print-esp-path", no_argument, NULL, 'p' },
|
||||
{ "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
|
||||
{ "print-boot-path", no_argument, NULL, 'x' },
|
||||
{ "print-root-device", no_argument, NULL, 'R' },
|
||||
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
|
||||
@ -247,7 +253,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hpx", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "hpxR", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
@ -295,19 +301,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (arg_print_dollar_boot_path)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--print-boot-path/-x cannot be combined with --print-esp-path/-p");
|
||||
arg_print_esp_path = true;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
if (arg_print_esp_path)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--print-boot-path/-x cannot be combined with --print-esp-path/-p");
|
||||
arg_print_dollar_boot_path = true;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
arg_print_root_device ++;
|
||||
break;
|
||||
|
||||
case ARG_NO_VARIABLES:
|
||||
arg_touch_variables = false;
|
||||
break;
|
||||
@ -398,6 +402,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined.");
|
||||
|
||||
if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
|
||||
"install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
@ -458,6 +466,32 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_print_root_device > 0) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
dev_t devno;
|
||||
|
||||
r = blockdev_get_root(LOG_ERR, &devno);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_error("Root file system not backed by a (single) whole block device.");
|
||||
return 80; /* some recognizable error code */
|
||||
}
|
||||
|
||||
if (arg_print_root_device > 1) {
|
||||
r = block_get_whole_disk(devno, &devno);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
|
||||
}
|
||||
|
||||
r = device_path_make_canonical(S_IFBLK, devno, &path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
puts(path);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Open up and mount the image */
|
||||
if (arg_image) {
|
||||
assert(!arg_root);
|
||||
|
@ -24,6 +24,7 @@ extern char *arg_esp_path;
|
||||
extern char *arg_xbootldr_path;
|
||||
extern bool arg_print_esp_path;
|
||||
extern bool arg_print_dollar_boot_path;
|
||||
extern unsigned arg_print_root_device;
|
||||
extern bool arg_touch_variables;
|
||||
extern PagerFlags arg_pager_flags;
|
||||
extern bool arg_graceful;
|
||||
|
@ -773,40 +773,15 @@ static int enumerate_partitions(dev_t devnum) {
|
||||
}
|
||||
|
||||
static int add_mounts(void) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
dev_t devno;
|
||||
int r;
|
||||
|
||||
/* If the root mount has been replaced by some form of volatile file system (overlayfs), the
|
||||
* original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
|
||||
* here. */
|
||||
r = readlink_malloc("/run/systemd/volatile-root", &p);
|
||||
if (r == -ENOENT) { /* volatile-root not found */
|
||||
r = get_block_device_harder("/", &devno);
|
||||
if (r == -EUCLEAN)
|
||||
return btrfs_log_dev_root(LOG_ERR, r, "root file system");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine block device of root file system: %m");
|
||||
if (r == 0) { /* Not backed by a single block device. (Could be NFS or so, or could be multi-device RAID or so) */
|
||||
r = get_block_device_harder("/usr", &devno);
|
||||
if (r == -EUCLEAN)
|
||||
return btrfs_log_dev_root(LOG_ERR, r, "/usr");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine block device of /usr/ file system: %m");
|
||||
if (r == 0) { /* /usr/ not backed by single block device, either. */
|
||||
log_debug("Neither root nor /usr/ file system are on a (single) block device.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
|
||||
else {
|
||||
mode_t m;
|
||||
r = device_path_parse_major_minor(p, &m, &devno);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse major/minor device node: %m");
|
||||
if (!S_ISBLK(m))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
|
||||
r = blockdev_get_root(LOG_ERR, &devno);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("Skipping automatic GPT dissection logic, root file system not backed by a (single) whole block device.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return enumerate_partitions(devno);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
@ -777,3 +778,53 @@ int blockdev_get_sector_size(int fd, uint32_t *ret) {
|
||||
*ret = ssz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blockdev_get_root(int level, dev_t *ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
dev_t devno;
|
||||
int r;
|
||||
|
||||
/* Returns the device node backing the root file system. Traces through
|
||||
* dm-crypt/dm-verity/... Returns > 0 and the devno of the device on success. If there's no block
|
||||
* device (or multiple) returns 0 and a devno of 0. Failure otherwise.
|
||||
*
|
||||
* If the root mount has been replaced by some form of volatile file system (overlayfs), the original
|
||||
* root block device node is symlinked in /run/systemd/volatile-root. Let's read that here. */
|
||||
r = readlink_malloc("/run/systemd/volatile-root", &p);
|
||||
if (r == -ENOENT) { /* volatile-root not found */
|
||||
r = get_block_device_harder("/", &devno);
|
||||
if (r == -EUCLEAN)
|
||||
return btrfs_log_dev_root(level, r, "root file system");
|
||||
if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to determine block device of root file system: %m");
|
||||
if (r == 0) { /* Not backed by a single block device. (Could be NFS or so, or could be multi-device RAID or so) */
|
||||
r = get_block_device_harder("/usr", &devno);
|
||||
if (r == -EUCLEAN)
|
||||
return btrfs_log_dev_root(level, r, "/usr");
|
||||
if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to determine block device of /usr/ file system: %m");
|
||||
if (r == 0) { /* /usr/ not backed by single block device, either. */
|
||||
log_debug("Neither root nor /usr/ file system are on a (single) block device.");
|
||||
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to read symlink /run/systemd/volatile-root: %m");
|
||||
else {
|
||||
mode_t m;
|
||||
r = device_path_parse_major_minor(p, &m, &devno);
|
||||
if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to parse major/minor device node: %m");
|
||||
if (!S_ISBLK(m))
|
||||
return log_full_errno(level, SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = devno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -56,3 +56,5 @@ int block_device_has_partitions(sd_device *dev);
|
||||
int blockdev_reread_partition_table(sd_device *dev);
|
||||
|
||||
int blockdev_get_sector_size(int fd, uint32_t *ret);
|
||||
|
||||
int blockdev_get_root(int level, dev_t *ret);
|
||||
|
@ -22,3 +22,20 @@ command -v jq >/dev/null || {
|
||||
|
||||
"$bootctl" list --json=pretty | jq . >/dev/null
|
||||
"$bootctl" list --json=short | jq . >/dev/null
|
||||
|
||||
# bootctl --print-root-device should either succeed or fail with exit status 80
|
||||
# (because not backed by a single block device), but not fail otherwise.
|
||||
"$bootctl" -R || test "$?" -eq 80
|
||||
"$bootctl" -RR || test "$?" -eq 80
|
||||
|
||||
if "$bootctl" -R > /dev/null ; then
|
||||
P=$("$bootctl" -R)
|
||||
PP=$("$bootctl" -RR)
|
||||
|
||||
echo "$P vs $PP"
|
||||
test -b "$P"
|
||||
test -b "$PP"
|
||||
|
||||
# $P must be a prefix of $PP
|
||||
[[ $P = $PP* ]]
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user