1
0
mirror of https://github.com/systemd/systemd synced 2025-11-19 16:54:46 +01:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Yu Watanabe
cc7ccd3c6a man/network: extend document about the default value of IPv6AcceptRA=
Prompted by #39304.
2025-10-23 11:13:36 +09:00
Yu Watanabe
a5962d3327
rereadpt: always update kernel partition tables from userspace in an incremental fashion (#39390)
Let's address #38672 comprehensively: let's avoid BLKRRPART as much as
we can, and always do careful userspace controlled, incremental updates
to the kernel partition tables.

This simply iterates through blkid's partition parsing, and turns it
into a BLKPG ioctls, adding, updating, removing partitions as necessary,
suppressing unnecessary changes. This has the major benefit that the
call becomes truly idempotent: if nothing changed then nothing is
removed/readed, like BLKRRPART is doing it.

This then ports over all code currently doing partition refreshing,
specifcially: udev, repart, and homed.

Fixes: #38672
2025-10-23 09:35:03 +09:00
Yu Watanabe
80297f75e1
Two minor id128-related cleanups (#39407)
Split out from #39210
2025-10-23 09:30:11 +09:00
Lennart Poettering
e582484789 tree-wide: open block device locks in writable mode
udev's block device locking protocol has one pitfall not even the
example in the documentation got right so far (even though this is
explained in all detail above): udev's rescanning is only triggered when
an fd that is opened for writing is closed. This means that if a
separate locking fd is opened on a block device – one that is maintained
independently of the fd actually used for writing – it must be opened for
writing too, so that closing the lock definitely triggers a rescan. This
matters in cases where the lock fd is kept for longer than the fd used
for writing to disk. (Because otherwise udev might get the
IN_CLOSE_WRITE event, but when it tries to rescan will find the device
locked, and never retry because no IN_CLOSE_WRITE is triggred anymore.)

Let's fix that across the codebase, at 4 places:

1. in makefs (a lock fd is kept, and mkfs then invoked as child, which
   uses a different fd, and the lock fd is closed only once the child
   died)

2. in udevadm lock (embarassing!): which is intended to be used to wrap tools
   that modify disk contents, very similar to the makefs case. The lock
   is also kept until after the tool exited.

3. In storagetm: the kernel nvme-tcp layer writes to the device
   directly, we just keep a lock fd.

4. the example in BLOCK_DEVICE_LOCKING.md
2025-10-22 22:56:02 +02:00
Lennart Poettering
46da450f13 repart: switch things over to our own partition reread logic 2025-10-22 22:56:02 +02:00
Lennart Poettering
78c017a8ed repart: split out that disarms automatic artifact removal 2025-10-22 22:56:01 +02:00
Lennart Poettering
aa47d8ade1 udev: switch over to rereadpt() rather than raw BLKRRPART
Fixes: #38672
2025-10-22 22:56:01 +02:00
Lennart Poettering
0ef4118c78 homed: switch from raw BLKRRPART to rereadpt_fd() 2025-10-22 22:56:01 +02:00
Lennart Poettering
757887d01d rereadpt: implement userspace-based BLKRRPART re-implementation 2025-10-22 22:56:01 +02:00
Lennart Poettering
524ebfe28a blockdev-util: split out partition device node generation from dissect-util.c 2025-10-22 22:56:01 +02:00
Lennart Poettering
bb45a893c2 blockdev-util: in blockdev_partscan_enabled() check if we are operating on block device first
The function makes no sense on any other type of fd, hence we better
check this explicitly.
2025-10-22 22:56:01 +02:00
Lennart Poettering
a251345cf5 blockdev-util: rename BlockDeviceLookupFlag to plural
This is a flags type and a flag function argument, let's name it in
plural, because it allows many flags combinations. Internally, the
implementation already used plural, but let's fix the prototypes too.
2025-10-22 22:56:01 +02:00
Lennart Poettering
b09ea23978 sd-device: add device_get_property_uint() helper
This is just like device_get_property_int() but operates on unsigned
ints.
2025-10-22 22:56:01 +02:00
Mike Yuan
7d4e8f920b
shared/bus-get-properties: use sizeof instead of hardcoded size in bus_property_get_id128()
Prompted by
https://github.com/systemd/systemd/pull/39210#discussion_r2404225907
2025-10-22 14:44:50 +02:00
Mike Yuan
fbc9f0dd4e
libsystemd-network/sd-lldp-tx: use SD_JSON_BUILD_PAIR_ID128 2025-10-22 14:44:50 +02:00
22 changed files with 775 additions and 103 deletions

View File

@ -223,11 +223,12 @@ int main(int argc, char **argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// try to take an exclusive and nonblocking BSD lock // try to take an exclusive and nonblocking BSD lock (use O_WRONLY mode to ensure udev
// rescans the device once the lock is closed)
__attribute__((cleanup(closep))) int fd = __attribute__((cleanup(closep))) int fd =
lock_whole_disk_from_devname( lock_whole_disk_from_devname(
argv[1], argv[1],
O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY,
LOCK_EX|LOCK_NB); LOCK_EX|LOCK_NB);
if (fd < 0) if (fd < 0)

View File

@ -215,7 +215,8 @@
<term><varname>Multicast=</varname></term> <term><varname>Multicast=</varname></term>
<listitem> <listitem>
<para>Takes a boolean. If set to true, the multicast flag on the device is enabled. Defaults <para>Takes a boolean. If set to true, the multicast flag on the device is enabled. Defaults
to unset.</para> to unset and the flag is unchanged. When disabled, <varname>IPv6AcceptRA=</varname> and
<varname>IPv6SendRA=</varname> cannot be enabled.</para>
<xi:include href="version-info.xml" xpointer="v239"/> <xi:include href="version-info.xml" xpointer="v239"/>
</listitem> </listitem>
@ -942,10 +943,19 @@ DuplicateAddressDetection=none</programlisting></para>
<para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the interface. <para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the interface.
If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may trigger the If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may trigger the
start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found
on the link. Defaults to false for bridge devices, when <varname>IPv6Forwarding=</varname>, on the link.</para>
<varname>IPv6SendRA=</varname>, or <varname>KeepMaster=</varname> is enabled. Otherwise, enabled by
default. Cannot be enabled on devices aggregated in a bond device or when link-local addressing is <para>This cannot be enabled on devices aggregated in a bond device, or when IPv6 link-local
disabled.</para> addressing (see <varname>LinkLocalAddressing=</varname>) or multicasting (see
<varname>Multicast=</varname>) is disabled. Note, multicasting is disabled by default for some
configurations, e.g. bridge ports. Hence, in that case <varname>Multicast=</varname> needs to be
explicitly enabled to make this feature usable. When <varname>IPv6SendRA=</varname>,
<varname>IPv6Forwarding=</varname>, or <varname>IPMasquerade=</varname> is enabled, this feature is
disabled by default, but can be overridden by explicitly enabling this setting. Note,
<varname>IPv6Forwarding=</varname> may be indirectly enabled when the global setting with the same
name is enabled, or when <varname>IPMasquerade=</varname> is enabled on <emphasis>any other
interfaces</emphasis>. See also <varname>IPv6Forwarding=</varname> and
<varname>IPMasquerade=</varname> for more details. Enabled by default otherwise.</para>
<para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA] <para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA]
section, see below.</para> section, see below.</para>

View File

@ -41,7 +41,7 @@ static int run(int argc, char *argv[]) {
if (S_ISBLK(st.st_mode)) { if (S_ISBLK(st.st_mode)) {
/* Lock the device so that udev doesn't interfere with our work */ /* Lock the device so that udev doesn't interfere with our work */
lock_fd = lock_whole_block_device(st.st_rdev, LOCK_EX); lock_fd = lock_whole_block_device(st.st_rdev, O_WRONLY, LOCK_EX);
if (lock_fd < 0) if (lock_fd < 0)
return log_error_errno(lock_fd, "Failed to lock whole block device of \"%s\": %m", device); return log_error_errno(lock_fd, "Failed to lock whole block device of \"%s\": %m", device);
} else } else

View File

@ -53,6 +53,7 @@
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "random-util.h" #include "random-util.h"
#include "reread-partition-table.h"
#include "resize-fs.h" #include "resize-fs.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -2497,7 +2498,7 @@ int home_create_luks(
if (disk_uuid_path) if (disk_uuid_path)
/* Reread partition table if this is a block device */ /* Reread partition table if this is a block device */
(void) ioctl(setup->image_fd, BLKRRPART, 0); (void) reread_partition_table_fd(setup->image_fd, /* flags= */ 0);
else { else {
assert(setup->temporary_image_path); assert(setup->temporary_image_path);
@ -3469,8 +3470,8 @@ int home_resize_luks(
if (r > 0) if (r > 0)
log_info("Growing of partition completed."); log_info("Growing of partition completed.");
if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0) if (S_ISBLK(st.st_mode))
log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m"); (void) reread_partition_table_fd(image_fd, /* flags= */ 0);
/* Tell LUKS about the new bigger size too */ /* Tell LUKS about the new bigger size too */
r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U); r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U);
@ -3569,8 +3570,8 @@ int home_resize_luks(
if (r > 0) if (r > 0)
log_info("Shrinking of partition completed."); log_info("Shrinking of partition completed.");
if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0) if (S_ISBLK(st.st_mode))
log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m"); (void) reread_partition_table_fd(image_fd, /* flags= */ 0);
} else { /* → Grow */ } else { /* → Grow */
if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) { if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) {

View File

@ -713,7 +713,7 @@ int sd_lldp_tx_describe(sd_lldp_tx *lldp_tx, sd_json_variant **ret) {
return sd_json_buildo( return sd_json_buildo(
ret, ret,
SD_JSON_BUILD_PAIR_STRING("ChassisID", SD_ID128_TO_STRING(machine_id)), SD_JSON_BUILD_PAIR_ID128("ChassisID", machine_id),
SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawChassisID", chassis_id, chassis_id_len), SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawChassisID", chassis_id, chassis_id_len),
SD_JSON_BUILD_PAIR_STRING("PortID", lldp_tx->ifname), SD_JSON_BUILD_PAIR_STRING("PortID", lldp_tx->ifname),
SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawPortID", port_id, port_id_len), SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawPortID", port_id, port_id_len),

View File

@ -14,6 +14,7 @@ int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_sysnum_unsigned(sd_device *device, unsigned *ret); int device_get_sysnum_unsigned(sd_device *device, unsigned *ret);
int device_get_property_bool(sd_device *device, const char *key); int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret); int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_property_uint(sd_device *device, const char *key, unsigned *ret);
int device_get_ifname(sd_device *device, const char **ret); int device_get_ifname(sd_device *device, const char **ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value); int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value); int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);

View File

@ -2319,6 +2319,27 @@ int device_get_property_int(sd_device *device, const char *key, int *ret) {
return 0; return 0;
} }
int device_get_property_uint(sd_device *device, const char *key, unsigned *ret) {
const char *value;
int r;
assert(device);
assert(key);
r = sd_device_get_property_value(device, key, &value);
if (r < 0)
return r;
unsigned v;
r = safe_atou(value, &v);
if (r < 0)
return r;
if (ret)
*ret = v;
return 0;
}
_public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) { _public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
const char *s; const char *s;
sd_id128_t id; sd_id128_t id;

View File

@ -65,6 +65,7 @@
#include "process-util.h" #include "process-util.h"
#include "random-util.h" #include "random-util.h"
#include "ratelimit.h" #include "ratelimit.h"
#include "reread-partition-table.h"
#include "resize-fs.h" #include "resize-fs.h"
#include "rm-rf.h" #include "rm-rf.h"
#include "set.h" #include "set.h"
@ -855,6 +856,16 @@ static Context* context_free(Context *context) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
static void context_disarm_auto_removal(Context *context) {
assert(context);
/* Make sure automatic removal of half-written artifacts is disarmed */
context->node = mfree(context->node);
LIST_FOREACH(partitions, p, context->partitions)
p->split_path = mfree(p->split_path);
}
static int context_add_free_area( static int context_add_free_area(
Context *context, Context *context,
uint64_t size, uint64_t size,
@ -7260,12 +7271,8 @@ static int context_write_partition_table(Context *context) {
else if (capable < 0) else if (capable < 0)
return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m"); return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m");
else if (capable > 0) { else if (capable > 0) {
log_info("Telling kernel to reread partition table."); log_info("Informing kernel about changed partitions...");
r = reread_partition_table_fd(fdisk_get_devfd(context->fdisk_context), /* flags= */ 0);
if (context->from_scratch)
r = fdisk_reread_partition_table(context->fdisk_context);
else
r = fdisk_reread_changes(context->fdisk_context, original_table);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to reread partition table: %m"); return log_error_errno(r, "Failed to reread partition table: %m");
} else } else
@ -10177,10 +10184,7 @@ static int run(int argc, char *argv[]) {
(void) context_dump(context, /*late=*/ true); (void) context_dump(context, /*late=*/ true);
context->node = mfree(context->node); context_disarm_auto_removal(context);
LIST_FOREACH(partitions, p, context->partitions)
p->split_path = mfree(p->split_path);
return 0; return 0;
} }

View File

@ -24,7 +24,7 @@
#include "path-util.h" #include "path-util.h"
#include "string-util.h" #include "string-util.h"
static int fd_get_devnum(int fd, BlockDeviceLookupFlag flags, dev_t *ret) { static int fd_get_devnum(int fd, BlockDeviceLookupFlags flags, dev_t *ret) {
struct stat st; struct stat st;
dev_t devnum; dev_t devnum;
int r; int r;
@ -148,7 +148,7 @@ int block_device_get_originating(sd_device *dev, sd_device **ret) {
return 0; return 0;
} }
int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flags, sd_device **ret) { int block_device_new_from_fd(int fd, BlockDeviceLookupFlags flags, sd_device **ret) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
dev_t devnum; dev_t devnum;
int r; int r;
@ -194,7 +194,7 @@ int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flags, sd_device **re
return 0; return 0;
} }
int block_device_new_from_path(const char *path, BlockDeviceLookupFlag flags, sd_device **ret) { int block_device_new_from_path(const char *path, BlockDeviceLookupFlags flags, sd_device **ret) {
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
assert(path); assert(path);
@ -340,20 +340,24 @@ int get_block_device_harder(const char *path, dev_t *ret) {
return get_block_device_harder_fd(fd, ret); return get_block_device_harder_fd(fd, ret);
} }
int lock_whole_block_device(dev_t devt, int operation) { int lock_whole_block_device(dev_t devt, int open_flags, int operation) {
_cleanup_close_ int lock_fd = -EBADF; _cleanup_close_ int lock_fd = -EBADF;
dev_t whole_devt; dev_t whole_devt;
int r; int r;
/* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */ /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING
*
* NB: it matters whether open_flags indicates open for write: only then will the eventual closing of
* the fd trigger udev's partitioning rescanning of the device (as it watches for IN_CLOSE_WRITE),
* hence make sure to pass the right value there. */
r = block_get_whole_disk(devt, &whole_devt); r = block_get_whole_disk(devt, &whole_devt);
if (r < 0) if (r < 0)
return r; return r;
lock_fd = r = device_open_from_devnum(S_IFBLK, whole_devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, NULL); lock_fd = device_open_from_devnum(S_IFBLK, whole_devt, open_flags|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, NULL);
if (r < 0) if (lock_fd < 0)
return r; return lock_fd;
if (flock(lock_fd, operation) < 0) if (flock(lock_fd, operation) < 0)
return -errno; return -errno;
@ -414,6 +418,12 @@ int blockdev_partscan_enabled(sd_device *dev) {
assert(dev); assert(dev);
r = device_in_subsystem(dev, "block");
if (r < 0)
return r;
if (r == 0)
return -ENOTBLK;
/* For v6.10 or newer. */ /* For v6.10 or newer. */
r = device_get_sysattr_bool(dev, "partscan"); r = device_get_sysattr_bool(dev, "partscan");
if (r != -ENOENT) if (r != -ENOENT)
@ -798,27 +808,6 @@ int block_device_remove_all_partitions(sd_device *dev, int fd) {
return k < 0 ? k : has_partitions; return k < 0 ? k : has_partitions;
} }
int blockdev_reread_partition_table(sd_device *dev) {
_cleanup_close_ int fd = -EBADF;
assert(dev);
/* Try to re-read the partition table. This only succeeds if none of the devices is busy. */
fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return fd;
if (flock(fd, LOCK_EX|LOCK_NB) < 0)
return -errno;
if (ioctl(fd, BLKRRPART, 0) < 0)
return -errno;
return 0;
}
int blockdev_get_sector_size(int fd, uint32_t *ret) { int blockdev_get_sector_size(int fd, uint32_t *ret) {
int ssz = 0; int ssz = 0;
@ -898,3 +887,46 @@ int blockdev_get_root(int level, dev_t *ret) {
return 1; return 1;
} }
int partition_node_of(const char *node, unsigned nr, char **ret) {
int r;
assert(node);
assert(nr > 0);
assert(ret);
/* Given a device node path to a block device returns the device node path to the partition block
* device of the specified partition */
_cleanup_free_ char *fn = NULL;
r = path_extract_filename(node, &fn);
if (r < 0)
return r;
if (r == O_DIRECTORY)
return -EISDIR;
_cleanup_free_ char *dn = NULL;
r = path_extract_directory(node, &dn);
if (r < 0 && r != -EDESTADDRREQ) /* allow if only filename is specified */
return r;
size_t l = strlen(fn);
assert(l > 0); /* underflow check for the subtraction below */
bool need_p = ascii_isdigit(fn[l-1]); /* Last char a digit? */
_cleanup_free_ char *subnode = NULL;
if (asprintf(&subnode, "%s%s%u", fn, need_p ? "p" : "", nr) < 0)
return -ENOMEM;
if (dn) {
_cleanup_free_ char *j = path_join(dn, subnode);
if (!j)
return -ENOMEM;
*ret = TAKE_PTR(j);
} else
*ret = TAKE_PTR(subnode);
return 0;
}

View File

@ -11,16 +11,16 @@
#define xsprintf_sys_block_path(buf, suffix, devno) \ #define xsprintf_sys_block_path(buf, suffix, devno) \
xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), suffix ?: "") xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), suffix ?: "")
typedef enum BlockDeviceLookupFlag { typedef enum BlockDeviceLookupFlags {
BLOCK_DEVICE_LOOKUP_WHOLE_DISK = 1 << 0, /* whole block device, e.g. sda, nvme0n1, or loop0. */ BLOCK_DEVICE_LOOKUP_WHOLE_DISK = 1 << 0, /* whole block device, e.g. sda, nvme0n1, or loop0. */
BLOCK_DEVICE_LOOKUP_BACKING = 1 << 1, /* fd may be regular file or directory on file system, in BLOCK_DEVICE_LOOKUP_BACKING = 1 << 1, /* fd may be regular file or directory on file system, in
* which case backing block device is determined. */ * which case backing block device is determined. */
BLOCK_DEVICE_LOOKUP_ORIGINATING = 1 << 2, /* Try to find the underlying layer device for stacked BLOCK_DEVICE_LOOKUP_ORIGINATING = 1 << 2, /* Try to find the underlying layer device for stacked
* block device, e.g. LUKS-style DM. */ * block device, e.g. LUKS-style DM. */
} BlockDeviceLookupFlag; } BlockDeviceLookupFlags;
int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flag, sd_device **ret); int block_device_new_from_fd(int fd, BlockDeviceLookupFlags flags, sd_device **ret);
int block_device_new_from_path(const char *path, BlockDeviceLookupFlag flag, sd_device **ret); int block_device_new_from_path(const char *path, BlockDeviceLookupFlags flags, sd_device **ret);
int block_device_is_whole_disk(sd_device *dev); int block_device_is_whole_disk(sd_device *dev);
int block_device_get_whole_disk(sd_device *dev, sd_device **ret); int block_device_get_whole_disk(sd_device *dev, sd_device **ret);
@ -35,7 +35,7 @@ int get_block_device(const char *path, dev_t *dev);
int get_block_device_harder_fd(int fd, dev_t *dev); int get_block_device_harder_fd(int fd, dev_t *dev);
int get_block_device_harder(const char *path, dev_t *dev); int get_block_device_harder(const char *path, dev_t *dev);
int lock_whole_block_device(dev_t devt, int operation); int lock_whole_block_device(dev_t devt, int open_flags, int operation);
int blockdev_partscan_enabled(sd_device *d); int blockdev_partscan_enabled(sd_device *d);
int blockdev_partscan_enabled_fd(int fd); int blockdev_partscan_enabled_fd(int fd);
@ -51,9 +51,10 @@ int block_device_remove_partition(int fd, const char *name, int nr);
int block_device_resize_partition(int fd, int nr, uint64_t start, uint64_t size); int block_device_resize_partition(int fd, int nr, uint64_t start, uint64_t size);
int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret); int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret);
int block_device_remove_all_partitions(sd_device *dev, int fd); int block_device_remove_all_partitions(sd_device *dev, int fd);
int blockdev_reread_partition_table(sd_device *dev);
int blockdev_get_sector_size(int fd, uint32_t *ret); int blockdev_get_sector_size(int fd, uint32_t *ret);
int blockdev_get_device_size(int fd, uint64_t *ret); int blockdev_get_device_size(int fd, uint64_t *ret);
int blockdev_get_root(int level, dev_t *ret); int blockdev_get_root(int level, dev_t *ret);
int partition_node_of(const char *node, unsigned nr, char **ret);

View File

@ -70,12 +70,12 @@ int bus_property_get_id128(
void *userdata, void *userdata,
sd_bus_error *reterr_error) { sd_bus_error *reterr_error) {
sd_id128_t *id = userdata; sd_id128_t *id = ASSERT_PTR(userdata);
if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */ if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
return sd_bus_message_append(reply, "ay", 0); return sd_bus_message_append(reply, "ay", 0);
else
return sd_bus_message_append_array(reply, 'y', id->bytes, 16); return sd_bus_message_append_array(reply, 'y', id->bytes, sizeof(sd_id128_t));
} }
#if __SIZEOF_SIZE_T__ != 8 #if __SIZEOF_SIZE_T__ != 8

View File

@ -627,14 +627,9 @@ static int make_partition_devname(
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
} else { } else {
size_t l = strlen(whole_devname); r = partition_node_of(whole_devname, nr, &s);
if (l < 1) /* underflow check for the subtraction below */ if (r < 0)
return -EINVAL; return r;
bool need_p = ascii_isdigit(whole_devname[l-1]); /* Last char a digit? */
if (asprintf(&s, "%s%s%i", whole_devname, need_p ? "p" : "", nr) < 0)
return -ENOMEM;
} }
} else { } else {
if (nr < 0) /* whole disk? */ if (nr < 0) /* whole disk? */

View File

@ -167,6 +167,7 @@ shared_sources = files(
'quota-util.c', 'quota-util.c',
'reboot-util.c', 'reboot-util.c',
'recovery-key.c', 'recovery-key.c',
'reread-partition-table.c',
'resize-fs.c', 'resize-fs.c',
'resolve-util.c', 'resolve-util.c',
'rm-rf.c', 'rm-rf.c',

View File

@ -0,0 +1,396 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/fs.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include "sd-device.h"
#include "alloc-util.h"
#include "blkid-util.h"
#include "blockdev-util.h"
#include "device-private.h"
#include "device-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "log.h"
#include "parse-util.h"
#include "reread-partition-table.h"
#include "set.h"
#include "string-util.h"
static int trigger_partitions(sd_device *dev, bool blkrrpart_success) {
int ret = 0, r;
assert(dev);
/* search for partitions */
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
r = partition_enumerator_new(dev, &e);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to initialize partition enumerator: %m");
/* We have partitions and re-read the table, the kernel already sent out a "change"
* event for the disk, and "remove/add" for all partitions. */
if (blkrrpart_success && sd_device_enumerator_get_device_first(e))
return 0;
/* We have partitions but re-reading the partition table did not work, synthesize
* "change" for the disk and all partitions. */
r = sd_device_trigger(dev, SD_DEVICE_CHANGE);
if (r < 0)
RET_GATHER(ret, log_device_debug_errno(dev, r, "Failed to trigger 'change' uevent, proceeding: %m"));
FOREACH_DEVICE(e, d) {
r = sd_device_trigger(d, SD_DEVICE_CHANGE);
if (r < 0)
RET_GATHER(ret, log_device_debug_errno(d, r, "Failed to trigger 'change' uevent, proceeding: %m"));
}
return ret;
}
static int fallback_ioctl(sd_device *d, int fd, RereadPartitionTableFlags flags) {
int r;
assert(d);
assert(fd >= 0);
r = RET_NERRNO(ioctl(fd, BLKRRPART, 0));
if (r < 0)
log_device_debug_errno(d, r, "Failed to reread partition table via BLKRRPART: %m");
else
log_device_debug(d, "Successfully reread partition table via BLKRRPART.");
if (FLAGS_SET(flags, REREADPT_FORCE_UEVENT))
RET_GATHER(r, trigger_partitions(d, r >= 0));
return r;
}
#if HAVE_BLKID
static int process_partition(
sd_device *d,
int fd,
blkid_partition pp,
sd_device_enumerator *e,
Set **partnos,
RereadPartitionTableFlags flags,
bool *changed) {
int r;
assert(d);
assert(fd >= 0);
assert(pp);
assert(e);
assert(partnos);
assert(changed);
const char *node;
r = sd_device_get_devname(d, &node);
if (r < 0)
return log_device_debug_errno(d, r, "Failed to acquire device node path: %m");
errno = 0;
int nr = sym_blkid_partition_get_partno(pp);
if (nr < 0)
return log_debug_errno(errno_or_else(EIO), "Failed to read partition number of partition: %m");
log_device_debug(d, "Processing partition %i...", nr);
errno = 0;
blkid_loff_t start = sym_blkid_partition_get_start(pp);
if (start < 0)
return log_debug_errno(errno_or_else(EIO), "Failed to read partition start offset of partition %i: %m", nr);
assert((uint64_t) start < UINT64_MAX / 512U);
errno = 0;
blkid_loff_t size = sym_blkid_partition_get_size(pp);
if (size < 0)
return log_debug_errno(errno_or_else(EIO), "Failed to read partition size of partition %i: %m", nr);
assert((uint64_t) size < UINT64_MAX / 512U);
if (set_ensure_put(partnos, /* hash_ops= */ NULL, UINT_TO_PTR(nr)) < 0)
return log_oom_debug();
_cleanup_free_ char *subnode = NULL;
r = partition_node_of(node, nr, &subnode);
if (r < 0)
return log_device_debug_errno(d, r, "Failed to determine partition node %i for '%s': %m", nr, node);
_cleanup_(sd_device_unrefp) sd_device *partition = NULL;
r = sd_device_new_from_devname(&partition, subnode);
if (r < 0) {
if (r != -ENODEV)
return log_device_debug_errno(d, r, "Failed to acquire device '%s': %m", subnode);
} else {
uint64_t start_kernel;
r = device_get_sysattr_u64(partition, "start", &start_kernel);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to get start of kernel partition device '%s': %m", subnode);
uint64_t size_kernel;
r = device_get_sysattr_u64(partition, "size", &size_kernel);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to get size of kernel partition device '%s': %m", subnode);
if (start_kernel == (uint64_t) start && size_kernel == (uint64_t) size) {
log_device_debug(partition, "Kernel partition device '%s' already matches partition table, not modifying.", subnode);
if (FLAGS_SET(flags, REREADPT_FORCE_UEVENT)) {
if (!*changed) {
/* Make sure to synthesize a change event on the main device, before we issue the first one on a partition device */
r = sd_device_trigger(d, SD_DEVICE_CHANGE);
if (r < 0)
return log_device_debug_errno(d, r, "Failed to issue 'change' uevent on device '%s': %m", node);
log_device_debug(d, "Successfully issued 'change' uevent on device '%s'.", node);
*changed = true;
}
r = sd_device_trigger(partition, SD_DEVICE_CHANGE);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to issue 'change' uevent on partition '%s': %m", subnode);
log_device_debug(partition, "Successfully issued 'change' uevent on partition '%s'.", subnode);
}
return 0;
}
if (start_kernel == (uint64_t) start) {
/* If the start offsize doesn't change we can just resize the partition */
log_device_debug(partition, "Resizing partition %i...", nr);
r = block_device_resize_partition(fd, nr, (uint64_t) start * 512U, (uint64_t) size * 512U);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to resize kernel partition device '%s' to partition table values: %m", subnode);
log_device_debug(partition, "Successfully resized kernel partition device '%s' to match partition table.", subnode);
*changed = true;
return 1;
}
/* If the start offset changed we need to remove and recreate the partition */
log_device_debug(partition, "Removing and recreating partition %i...", nr);
/* NB: when logging below we use the parent device now, after all the partition device ceased
* existing by now, most likely. Let's explicitly get rid of the obsolete device object now,
* just to make a point. */
partition = sd_device_unref(partition);
r = block_device_remove_partition(fd, subnode, (int) nr);
if (r < 0)
return log_device_debug_errno(d, r, "Failed to remove kernel partition device '%s' in order to recreate it: %m", subnode);
/* And now add it the partition anew*/
log_device_debug(d, "Successfully removed kernel partition device '%s' in order to recreate it.", subnode);
}
log_device_debug(d, "Adding partition %i...", nr);
r = block_device_add_partition(fd, subnode, nr, (uint64_t) start * 512U, (uint64_t) size * 512U);
if (r < 0)
return log_device_debug_errno(d, r, "Failed to add kernel partition device %i to partition table values: %m", nr);
log_device_debug(d, "Successfully added kernel partition device %i to match partition table.", nr);
*changed = true;
return 1;
}
static int remove_partitions(sd_device *d, int fd, sd_device_enumerator *e, Set *partnos, bool *changed) {
int r;
assert(d);
assert(fd >= 0);
assert(e);
assert(changed);
/* Removes all partitions of the specified device that we didn't find in the partition table (as
* listed in the specified Set object) */
int ret = 0;
FOREACH_DEVICE(e, partition) {
const char *devname;
r = sd_device_get_devname(partition, &devname);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to get name of partition: %m");
unsigned nr;
r = device_get_property_uint(partition, "PARTN", &nr);
if (r < 0)
return log_device_debug_errno(partition, r, "Failed to read partition number property: %m");
if (set_contains(partnos, UINT_TO_PTR(nr))) {
log_device_debug(partition, "Found kernel partition device %u in partition table, leaving around.", nr);
continue;
}
log_device_debug(partition, "Kernel knows partition %u which we didn't find, removing.", nr);
r = block_device_remove_partition(fd, devname, (int) nr);
if (r < 0) /* NB: when logging we use the parent device below, after all the partition device ceased existing by now, most likely */
RET_GATHER(ret, log_device_debug_errno(d, r, "Failed to remove kernel partition device '%s' that vanished from partition table: %m", devname));
else {
log_device_debug(d, "Removed partition %u from kernel.", nr);
*changed = true;
}
}
return ret;
}
#endif
static int reread_partition_table_full(sd_device *dev, int fd, RereadPartitionTableFlags flags) {
int r;
assert(dev);
assert(fd >= 0);
const char *p;
r = sd_device_get_devname(dev, &p);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get block device name: %m");
_cleanup_close_ int lock_fd = -EBADF;
if (FLAGS_SET(flags, REREADPT_BSD_LOCK)) {
lock_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (lock_fd < 0)
return log_device_debug_errno(dev, r, "Failed top open lock fd for block device '%s': %m", p);
if (flock(lock_fd, LOCK_SH|LOCK_NB) < 0)
return log_device_debug_errno(dev, errno, "Failed to take BSD lock on block device '%s': %m", p);
}
r = blockdev_partscan_enabled(dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to test if block device '%s' knows partition scanning: %m", p);
if (r == 0) {
/* No partition scanning? Generate a uevent at least, if that's requested */
if (FLAGS_SET(flags, REREADPT_FORCE_UEVENT)) {
r = sd_device_trigger(dev, SD_DEVICE_CHANGE);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to trigger 'change' uevent, proceeding: %m");
return 0;
}
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENOTTY), "Block device '%s' does not support partition scanning.", p);
}
#if HAVE_BLKID
r = dlopen_libblkid();
if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) {
log_device_debug(dev, "We don't have libblkid, falling back to BLKRRPART on '%s'.", p);
return fallback_ioctl(dev, fd, flags);
}
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to load libblkid: %m");
_cleanup_(blkid_free_probep) blkid_probe b = sym_blkid_new_probe();
if (!b)
return log_oom_debug();
errno = 0;
r = sym_blkid_probe_set_device(b, fd, /* off= */ 0, /* size= */ 0);
if (r != 0)
return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to open block device '%s': %m", p);
(void) sym_blkid_probe_enable_partitions(b, 1);
(void) sym_blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
r = sym_blkid_do_safeprobe(b);
if (r == _BLKID_SAFEPROBE_ERROR)
return log_device_debug_errno(dev, errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) {
log_device_debug(dev, "Didn't find partition table on block device '%s', falling back to BLKRRPART.", p);
return fallback_ioctl(dev, fd, flags);
}
assert(r == _BLKID_SAFEPROBE_FOUND);
const char *pttype = NULL;
(void) sym_blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
if (!streq_ptr(pttype, "gpt")) {
log_device_debug(dev, "Didn't find a GPT partition table on '%s', falling back to BLKRRPART.", p);
return fallback_ioctl(dev, fd, flags);
}
errno = 0;
blkid_partlist pl = sym_blkid_probe_get_partitions(b);
if (!pl)
return log_device_debug_errno(dev, errno_or_else(EIO), "Unable to read partition table of '%s': %m", p);
errno = 0;
int n_partitions = sym_blkid_partlist_numof_partitions(pl);
if (n_partitions < 0)
return log_device_debug_errno(dev, errno_or_else(EIO), "Unable to acquire number of entries in partition table of '%s': %m", p);
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
r = partition_enumerator_new(dev, &e);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to enumerate kernel partition devices: %m");
log_device_debug(dev, "Updating/adding kernel partition devices...");
_cleanup_(set_freep) Set *found_partnos = NULL;
bool changed = false;
int ret = 0;
for (int i = 0; i < n_partitions; i++) {
errno = 0;
blkid_partition pp = sym_blkid_partlist_get_partition(pl, i);
if (!pp)
return log_device_debug_errno(dev, errno_or_else(EIO), "Unable to get partition data of partition %i of partition table of '%s': %m", i, p);
RET_GATHER(ret, process_partition(dev, fd, pp, e, &found_partnos, flags, &changed));
}
/* Only delete unrecognized partitions if everything else worked */
if (ret < 0)
return ret;
log_device_debug(dev, "Removing old kernel partition devices...");
r = remove_partitions(dev, fd, e, found_partnos, &changed);
if (r < 0)
return r;
if (changed)
return 1;
if (FLAGS_SET(flags, REREADPT_FORCE_UEVENT)) {
/* No change? Then trigger an event manually if we were told to */
r = sd_device_trigger(dev, SD_DEVICE_CHANGE);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to issue 'change' uevent on device '%s': %m", p);
}
return 0;
#else
log_device_debug(dev, "We don't have libblkid, falling back to BLKRRPART on '%s'.", p);
return fallback_ioctl(dev, fd, flags);
#endif
}
int reread_partition_table(sd_device *dev, RereadPartitionTableFlags flags) {
assert(dev);
_cleanup_close_ int fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return log_debug_errno(fd, "Failed to open block device: %m");
return reread_partition_table_full(dev, fd, flags);
}
int reread_partition_table_fd(int fd, RereadPartitionTableFlags flags) {
int r;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
r = block_device_new_from_fd(fd, /* flags= */ 0, &dev);
if (r < 0)
return log_debug_errno(r, "Failed to get block device object: %m");
return reread_partition_table_full(dev, fd, flags);
}

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-forward.h"
#include "shared-forward.h"
typedef enum RereadPartitionTableFlags {
REREADPT_FORCE_UEVENT = 1 << 0, /* Force a "change" ueven out on partitions we didn't resize/remove/add */
REREADPT_BSD_LOCK = 1 << 1, /* Take a BSD lock on the device around the rescan operation */
} RereadPartitionTableFlags;
int reread_partition_table_fd(int fd, RereadPartitionTableFlags flags);
int reread_partition_table(sd_device *dev, RereadPartitionTableFlags flags);

View File

@ -390,7 +390,7 @@ static int nvme_subsystem_add(const char *node, int consumed_fd, sd_device *devi
return log_oom(); return log_oom();
if (fd < 0) { if (fd < 0) {
fd = RET_NERRNO(open(node, O_RDONLY|O_CLOEXEC|O_NONBLOCK)); fd = RET_NERRNO(open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK));
if (fd < 0) if (fd < 0)
return log_error_errno(fd, "Failed to open '%s': %m", node); return log_error_errno(fd, "Failed to open '%s': %m", node);
} }

View File

@ -432,6 +432,13 @@ executables += [
'dependencies' : libm, 'dependencies' : libm,
'timeout' : 120, 'timeout' : 120,
}, },
test_template + {
'sources' : files('test-reread-partition-table.c'),
},
test_template + {
'sources' : files('test-reread-partition-table-manual.c'),
'type' : 'manual',
},
test_template + { test_template + {
'sources' : files('test-sbat.c'), 'sources' : files('test-sbat.c'),
'conditions' : ['ENABLE_BOOTLOADER'], 'conditions' : ['ENABLE_BOOTLOADER'],

View File

@ -77,4 +77,30 @@ TEST(partscan_enabled) {
} }
} }
static void test_partition_node_of_one(const char *main, unsigned partition, const char *result, int retval) {
_cleanup_free_ char *s = NULL;
int r;
r = partition_node_of(main, partition, &s);
ASSERT_EQ(r, retval);
if (r < 0)
return;
ASSERT_STREQ(s, result);
log_info("%s with %u → %s", main, partition, result);
}
TEST(partition_node_of) {
test_partition_node_of_one("/dev/sda", 2, "/dev/sda2", 0);
test_partition_node_of_one("sda", 3, "sda3", 0);
test_partition_node_of_one("/dev/nvme0n1", 7, "/dev/nvme0n1p7", 0);
test_partition_node_of_one("nvme0n1", 8, "nvme0n1p8", 0);
test_partition_node_of_one("/dev/loop1", 3, "/dev/loop1p3", 0);
test_partition_node_of_one("", 1, NULL, -EINVAL);
test_partition_node_of_one("/", 1, NULL, -EADDRNOTAVAIL);
test_partition_node_of_one("/dev/", 1, NULL, -EISDIR);
test_partition_node_of_one("/sda", 1, "/sda1", 0);
test_partition_node_of_one(".", 1, NULL, -EADDRNOTAVAIL);
}
DEFINE_TEST_MAIN(LOG_INFO); DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "fd-util.h"
#include "log.h"
#include "main-func.h"
#include "reread-partition-table.h"
static int run(int argc, char *argv[]) {
int r;
log_set_max_level(LOG_DEBUG);
log_setup();
if (argc != 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected single parameter, the device node to open.");
_cleanup_close_ int fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
r = reread_partition_table_fd(fd, REREADPT_BSD_LOCK|REREADPT_FORCE_UEVENT);
if (r < 0)
return log_error_errno(r, "Failed to reread partition table: %m");
return 0;
}
DEFINE_MAIN_FUNCTION(run);

View File

@ -0,0 +1,162 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/loop.h>
#include "blockdev-util.h"
#include "fd-util.h"
#include "loop-util.h"
#include "memfd-util.h"
#include "path-util.h"
#include "process-util.h"
#include "reread-partition-table.h"
#include "tests.h"
#include "tmpfile-util.h"
#include "virt.h"
static void sfdisk(const char *sfdisk_path, LoopDevice *loop, const char *definition) {
int r;
assert(sfdisk_path);
assert(loop);
assert(definition);
_cleanup_close_ int memfd = memfd_new_and_seal("sfdisk", definition, SIZE_MAX);
ASSERT_OK(memfd);
r = safe_fork_full(
"(sfdisk)",
(int[]) { memfd, STDOUT_FILENO, STDERR_FILENO },
/* except_fds= */ NULL,
/* n_except_fds= */ 0,
FORK_CLOSE_ALL_FDS|FORK_RESET_SIGNALS|FORK_REARRANGE_STDIO|FORK_LOG|FORK_WAIT,
/* ret_pid= */ NULL);
if (r == 0) {
/* child */
execl(sfdisk_path, "fdisk", "--no-tell-kernel", "--no-reread", loop->node, NULL);
_exit(EXIT_FAILURE);
}
ASSERT_OK(r);
}
TEST(rereadpt) {
int r;
if (detect_container() > 0)
return (void) log_tests_skipped("test not available in container");
if (running_in_chroot() > 0)
return (void) log_tests_skipped("test not available in chroot()");
_cleanup_free_ char *sfdisk_path = NULL;
r = find_executable("sfdisk", &sfdisk_path);
if (r == -ENOENT)
return (void) log_tests_skipped("sfdisk not found");
ASSERT_OK(r);
_cleanup_close_ int fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR);
ASSERT_FD(fd);
ASSERT_OK_ERRNO(ftruncate(fd, 100 * 1024 * 1024));
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
r = loop_device_make(
fd,
O_RDWR,
/* offset= */ 0,
/* size= */ UINT64_MAX,
/* sector_size= */ 512U,
LO_FLAGS_PARTSCAN,
LOCK_EX, &loop);
if (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))
return (void) log_tests_skipped("loopback block devices not available");
_cleanup_free_ char *p = NULL;
ASSERT_OK(partition_node_of(loop->node, 1, &p));
ASSERT_ERROR_ERRNO(access(p, F_OK), ENOENT);
/* No change */
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
ASSERT_ERROR_ERRNO(access(p, F_OK), ENOENT);
/* Create */
log_notice("CREATING 20M");
sfdisk(sfdisk_path,
loop,
"label: gpt\n"
"start=, size=20M, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7\n");
ASSERT_ERROR_ERRNO(access(p, F_OK), ENOENT);
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
_cleanup_close_ int pfd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
ASSERT_OK_ERRNO(pfd);
uint64_t size;
ASSERT_OK(blockdev_get_device_size(pfd, &size));
ASSERT_EQ(size, 20U*1024U*1024U);
/* No change */
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
/* No change, but synthesize change anyway */
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ REREADPT_FORCE_UEVENT));
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
/* Resize */
log_notice("RESIZING TO 30M");
sfdisk(sfdisk_path,
loop,
"label: gpt\n"
"start=, size=30M, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7\n");
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
ASSERT_OK(blockdev_get_device_size(pfd, &size));
ASSERT_EQ(size, 30U*1024U*1024U);
/* No change */
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
/* Move */
log_notice("MOVING BY 50M");
sfdisk(sfdisk_path,
loop,
"label: gpt\n"
"start=50M, size=15M, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7\n");
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
ASSERT_ERROR(reread_partition_table_fd(loop->fd, /* flags= */ 0), EBUSY);
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
safe_close(pfd);
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
pfd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
ASSERT_OK_ERRNO(pfd);
ASSERT_OK(blockdev_get_device_size(pfd, &size));
ASSERT_EQ(size, 15U*1024U*1024U);
/* No change */
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
/* Remove */
log_notice("REMOVING");
sfdisk(sfdisk_path,
loop,
"label: gpt\n");
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
ASSERT_ERROR(reread_partition_table_fd(loop->fd, /* flags= */ 0), EBUSY);
ASSERT_OK_ZERO_ERRNO(access(p, F_OK));
pfd = safe_close(pfd);
ASSERT_OK(reread_partition_table_fd(loop->fd, /* flags= */ 0));
ASSERT_ERROR_ERRNO(access(p, F_OK), ENOENT);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -22,6 +22,7 @@
#include "parse-util.h" #include "parse-util.h"
#include "pidref.h" #include "pidref.h"
#include "process-util.h" #include "process-util.h"
#include "reread-partition-table.h"
#include "rm-rf.h" #include "rm-rf.h"
#include "set.h" #include "set.h"
#include "signal-util.h" #include "signal-util.h"
@ -161,36 +162,6 @@ static int synthesize_change_one(sd_device *dev, sd_device *target) {
return 0; return 0;
} }
static int synthesize_change_all(sd_device *dev) {
int r;
assert(dev);
r = blockdev_reread_partition_table(dev);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to re-read partition table, ignoring: %m");
bool part_table_read = r >= 0;
/* search for partitions */
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
r = partition_enumerator_new(dev, &e);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to initialize partition enumerator, ignoring: %m");
/* We have partitions and re-read the table, the kernel already sent out a "change"
* event for the disk, and "remove/add" for all partitions. */
if (part_table_read && sd_device_enumerator_get_device_first(e))
return 0;
/* We have partitions but re-reading the partition table did not work, synthesize
* "change" for the disk and all partitions. */
r = synthesize_change_one(dev, dev);
FOREACH_DEVICE(e, d)
RET_GATHER(r, synthesize_change_one(dev, d));
return r;
}
static int synthesize_change_child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) { static int synthesize_change_child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
Manager *manager = ASSERT_PTR(userdata); Manager *manager = ASSERT_PTR(userdata);
assert(s); assert(s);
@ -226,7 +197,7 @@ static int synthesize_change(Manager *manager, sd_device *dev) {
return r; return r;
if (r == 0) { if (r == 0) {
/* child */ /* child */
(void) synthesize_change_all(dev); (void) reread_partition_table(dev, REREADPT_FORCE_UEVENT|REREADPT_BSD_LOCK);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }

View File

@ -184,7 +184,8 @@ static int lock_device(
struct stat st; struct stat st;
int r; int r;
fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); /* We open in O_WRONLY mode here, to trigger a rescan in udev once we are done */
fd = open(path, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) if (fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", path); return log_error_errno(errno, "Failed to open '%s': %m", path);