Commit Graph
162 Commits
Author SHA1 Message Date
SymbX b5d8617b03 revert changes to apply with system where kernel is 4.19
Make a Github release / release (push) Skipped
2026-09-26 19:04:39 +03:00
Daan De MeyerandLuca Boccassi 3978b076af loop-util: shortcut block device fd when it carries no partition table
663f0bf5cb stopped reusing the original block device fd whenever
partition scanning was requested (LO_FLAGS_PARTSCAN) but couldn't be
enabled on the device, so that nested partition tables on devices the
kernel won't scan (e.g. the pmOS/android case) get exposed via a real
loop device.

However that also forced a pointless loop device for any partition that
carries a file system directly, e.g. a btrfs subvolume mounted via
MountImages=. For multi-device btrfs this is fatal: the kernel rejects
seeing the same member via both the original partition and the loop
device, and the mount fails.

A loop device is only ever needed here to expose a nested partition
table. So only refuse the shortcut when the device actually carries one,
probed via gpt_probe(), instead of whenever partition scanning is
disabled. Devices carrying a file system directly (or nothing) take the
shortcut as before.

Add an integration test to cover the failure scenario of the original
issue.

Fixes: https://github.com/systemd/systemd/issues/42520
Replaces: https://github.com/systemd/systemd/pull/42576
Follow-up for 663f0bf5cb79ecaf6dd71441ecdc9dc401e7eae6

Co-Authored-By: Luca Boccassi <luca.boccassi@gmail.com>
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 00:16:05 +01:00
Yu Watanabe 06d687ffac tree-wide: use device_get_sysattr_safe_string()
The obtained strings are passed to another function, e,g, handled as a
path and opened, printed to the terminal, written to a file, saved to
udev database as udev property, exposed through DBus, passed to logger,
and so on. Hence, these should not contain any malicious characters.
2026-05-18 02:55:56 +09:00
Clayton Craft 47d408163b loop-util: don't reuse partition fd when partscan needed
Some devices (e.g. android phones running pmOS) cannot have their OEM
partition table altered without breaking the firmware, so the distros's
partitions live inside a nested GPT carved into one of the OEM
partitions. Exposing these subpartitions requires wrapping the outer
partition in a loop device with partscan enabled, since the kernel does
not go into nested partition tables.

systemd already detects this case in udev-builtin-blkid
(ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP) and acts on with
systemd-loop@.service, but this fails towards the end.
loop_device_make_internal has an optimization where if the input is
already a block device with a matching sector size, it skips creating
a loop and just hands back the original fd. That's fine for whole disks
but wrong for partitions, which don't support partscan, so this causes
dissect_image to fail with EPROTONOSUPPORT.

This patch changes the behavior to only take the shortcut when the input
is a whole disk, or when partscan was not requested.

Co-Authored-By: Clayton Craft <clayton@craftyguy.net>
2026-04-28 15:25:07 +01:00
Lennart Poettering bda7e91337 stat-util: also introduce stat_verify_block() and use it everywhere 2026-04-13 13:20:43 +02:00
Lennart Poettering 6b0a3ef8c7 stat-util: introduce a common (stat|fd)_verify_regular_or_block() helper
We already had one in repart.c, let's generalize it, and use it
everywhere.
2026-04-13 13:20:42 +02:00
Daan De Meyer ba259f5a78 loop-util: Skip loop_device_set_autoclear() if foreign 2026-04-04 20:03:44 +02:00
Zbigniew Jędrzejewski-Szmek 926167301c many: final set of coccinelle check-pointer-deref tweaks (#41426)
This is a followup to https://github.com/systemd/systemd/pull/41400 with
the final set of tweaks so that the new coccinelle `check-pinters-deref`
checker runs without failures.

It also includes a commit with some fixes for redundant asserts and the
final commit to make the assert() in the inner loop of qsort
POINTER_MAY_BE_NULL which will become a no-op - that is probably the
most controversial one, I hope the commit message explains the
trade-offs, I'm happy to drop it, I have no strong opinion either way.

Please see the individual commits/commit messages for more details, most
is (hopefully) relatively boring/mechanical.
2026-04-03 19:27:09 +02:00
Daan De Meyer eb584f164f loop-util: create loop device for block devices with sector size mismatch
Previously, loop_device_make_internal() always used the block device
directly (via loop_device_open_from_fd()) for whole-device access,
regardless of sector size. This is incorrect when the GPT partition
table was written with a different sector size than the device reports,
as happens with CD-ROM/ISO boot via El Torito: the device has
2048-byte blocks but the GPT uses 512-byte sectors.

Restructure the sector size handling in loop_device_make_internal():

- Move GPT sector size probing (UINT32_MAX case) before the
  block-vs-regular-file split so both paths share the same logic and
  O_DIRECT handling. Check f_flags instead of loop_flags for O_DIRECT
  detection, since we're probing the original fd before any reopening.

- For block devices, get the device sector size and compare it against
  the resolved sector_size. Only use the block device directly when
  sector sizes match. When they differ (probed GPT mismatch or explicit
  sector size request), fall through to create a real loop device with
  the correct sector size.

- Default sector_size=0 to the device sector size for block devices
  (instead of always 512), so "no preference" correctly matches the
  device's sector size.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 18:08:25 +02:00
Daan De Meyer d3cb7a4e0f loop-util: work around kernel loop driver partition scan race
The kernel loop driver has a race condition in LOOP_CONFIGURE when
LO_FLAGS_PARTSCAN is set: it sends a KOBJ_CHANGE uevent (with
GD_NEED_PART_SCAN set) before calling loop_reread_partitions(). If
udev opens the device in response to the uevent before
loop_reread_partitions() runs, the kernel's blkdev_get_whole() sees
GD_NEED_PART_SCAN and triggers a first partition scan. Then
loop_reread_partitions() runs a second scan that drops all partitions
from the first scan (via blk_drop_partitions()) before re-adding them.
This causes partition devices to briefly disappear (plugged -> dead ->
plugged), which breaks systemd units with BindsTo= on the partition
device: systemd observes the dead transition, fails the dependent
units with 'dependency', and does not retry when the device reappears.

Work around this in loop_device_make_internal() by splitting the loop
device setup into two steps: first LOOP_CONFIGURE without
LO_FLAGS_PARTSCAN, then LOOP_SET_STATUS64 to enable partscan. This
avoids the race because:

1. LOOP_CONFIGURE without partscan: disk_force_media_change() sets
   GD_NEED_PART_SCAN, but GD_SUPPRESS_PART_SCAN remains set. If udev
   opens the device, blkdev_get_whole() calls bdev_disk_changed()
   which clears GD_NEED_PART_SCAN, but blk_add_partitions() returns
   early because disk_has_partscan() is false — no partitions appear,
   the flag is drained harmlessly.

2. Between the two ioctls, we open and close the device to ensure
   GD_NEED_PART_SCAN is drained regardless of whether udev processed
   the uevent yet.

3. LOOP_SET_STATUS64 with LO_FLAGS_PARTSCAN: clears
   GD_SUPPRESS_PART_SCAN and calls loop_reread_partitions() for a
   single clean scan. Crucially, loop_set_status() does not call
   disk_force_media_change(), so GD_NEED_PART_SCAN is never set again.

A proper kernel fix has been submitted:
https://lore.kernel.org/linux-block/20260330081819.652890-1-daan@amutable.com/T/#u

This workaround should be dropped once the fix is widely available.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 12:32:10 +01:00
Daan De Meyer 614509699a loop-util: use auto-detect open mode for loop device setup
When callers do not explicitly request read-only mode, pass open_flags
as -1 (auto-detect) instead of hardcoding O_RDWR. This enables the
existing O_RDWR-to-O_RDONLY retry logic in loop_device_make_by_path_at()
which falls back to O_RDONLY when opening the backing device with O_RDWR
fails with EROFS or similar errors.

Previously, callers passed O_RDWR explicitly when read-only mode was not
requested, which bypassed the retry logic entirely. This meant that
inherently read-only block devices (such as CD-ROMs) would fail to open
instead of gracefully falling back to read-only mode.

Also propagate the unresolved open_flags through
loop_device_make_by_path_at() into loop_device_make_internal() instead
of resolving it to O_RDWR early. For loop_device_make_by_path_memory(),
resolve to O_RDWR immediately since memfds are always writable.

In mstack, switch from loop_device_make() to
loop_device_make_by_path_at() with a NULL path, which reopens the
O_PATH file descriptor with the appropriate access mode. This is
necessary because the backing file descriptor is opened with O_PATH,
which prevents loop_device_make_internal() from auto-detecting the
access mode via fcntl(F_GETFL).
2026-04-01 10:02:15 +02:00
Michael Vogt 029335ea74 shared: make check-pointer-deref clean
Add the needed assert changes to make the code clean
for the new check-pointer-deref script.
2026-03-31 18:30:40 +02:00
Lennart Poettering ca088266bc loop-util: when open_flags is unspecified derive it from passed in fd 2026-01-24 20:51:45 +01:00
DaanDeMeyer ebbc333880 loop-util: Make path optional in loop_device_make_by_path_at() 2026-01-21 12:03:07 +01:00
Yu Watanabe 93378148d4 tree-wide: normalize comment style 2025-12-15 08:50:13 +09:00
Lennart Poettering 342084b035 loop-util: explain why we propagate discard_max_bytes from backing block device to loopback block device
Follow-up for: 29ee9c6fb7
2025-10-20 12:33:50 +01:00
nkraetzschmar 29ee9c6fb7 loop-util: mirror discard limits from backing device 2025-10-20 12:49:39 +02:00
Lennart Poettering ec3139db8b loop-util: fully suppress unnecessary size changes
Typically we set the offset argument to UINT64_MAX in most calls, but
this caused us to not detect no-change invocations. Fix that.
2025-09-25 08:49:25 +02:00
Yu Watanabe e28d408c6b errno-util: introduce ERRNO_IS_NEG_FS_WRITE_REFUSED() 2025-07-10 14:02:00 +09:00
Daan De Meyer 69a283c5f2 shared: Clean up includes
Split out of #37344.
2025-05-24 14:00:44 +02:00
Daan De Meyer 1cf40697e3 tree-wide: Sort includes
This was done by running a locally built clang-format with
https://github.com/llvm/llvm-project/pull/137617 and
https://github.com/llvm/llvm-project/pull/137840 applied on all .c
and .h files.
2025-04-30 09:30:51 +02:00
Yu Watanabe 3a03b97d6f tree-wide: drop unnecessary inclusion of tmpfile-util.h 2025-04-30 05:38:48 +09:00
Stefan Hansson b1236ce38b missing_fcntl: Introduce O_ACCMODE_STRICT
On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which
defines it as (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is
simply defined as O_PATH.

This causes problems for systemd on musl, as it changes the
behaviour of open_mkdir_at_full() to return -EINVAL if O_PATH is
included in flags due to the fact that O_ACCMODE includes O_SEARCH
(i.e. O_PATH). Consequently, this makes the test-fs-util test fail.

Upstream musl seems content with this behaviour and doesn't seem
interested in matching glibc's behaviour due to that defining it this
way allows for O_SEARCH to match POSIX better by allowing it to open
directories where read permission is missing. Apparently musl does some
emulation in other places to make this work more consistently as well.

Initially I took the approach of working around this by redefining
O_SEARCH as O_RDONLY if O_SEARCH == O_PATH. This fixes the test and is
the approach taken by both XZ[1] and Gzip[2][3], but was not taken as
redefining system headers potentially could be problematic.

Instead, introduce O_ACCMODE_STRICT which just is a copy of glibc's
O_ACCMODE and use it everywhere. This way we don't have to deal with
unusual definitions of O_ACCMODE from C standard libraries other than
glibc.

 [1]: https://git.tukaani.org/?p=xz.git;a=blob;f=src/xz/file_io.c;h=8c83269b13fa31284f7ea5f3627a1dfbce7d6e14;hb=HEAD#l72
 [2]: https://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/fcntl.in.h
      (lines 380 and 396, commit d7f551b30f3f2a0fa57c1b10c12f4eea41a9b89e)
 [3]: https://lists.gnu.org/archive/html/bug-gzip/2025-01/msg00000.html
2025-04-18 05:22:06 +09:00
Yu Watanabe 5e3887e6c1 loop-util: drop workaround for kernel older than v5.0
Now our baseline on the kernel is v5.4.
2025-04-14 21:57:53 +09:00
Yu Watanabe ec32732043 basic: introduce our own sys/mount.h implementation
To resolve conflict with sys/mount.h and linux/mount.h or linux/fs.h.

The conflict between sys/mount.h and linux/mount.h is resolved in
glibc-2.37 (774058d72942249f71d74e7f2b639f77184160a6), but our baseline
is still glibc-2.31. Also, even with the version or newer, still
sys/mount.h conflicts with linux/fs.h, which is included by
linux/btrfs.h.

This introduces our own implementation of sys/mount.h, that can be
simultaneously included with linux/mount.h and linux/fs.h. This also
imports linux/fs.h, linux/mount.h, and several other dependent headers.
The introduced sys/mount.h header itself may not be enough simple, but
by using the header, we can drop most of workarounds in other source files.
2025-03-04 02:24:49 +09:00
Yu Watanabe dc1a78a462 basic/linux: import loop.h from kernel 6.14-rc4, and drop missing_loop.h 2025-03-04 02:24:49 +09:00
Mike Yuan 9a7b20b6a6 errno-util: add ERRNO_IS_IOCTL_NOT_SUPPORTED that checks EINVAL additionally 2025-01-04 17:07:58 +01:00
Lennart Poettering bff5d2fd5a blockdev-util: add partscan check function that takes an sd_device* 2024-07-01 16:35:56 +02:00
Yu Watanabe e40b11be04 fs-util: rename xopenat() -> xopanat_full() 2024-02-16 00:25:37 +09:00
Yu Watanabe 6383abd62c loop-util: fix error handling
Follow-up for 972c8db589.
2024-02-16 00:25:37 +09:00
Franck Bui 7ba1816dfe loop-util: drop unused .uevent_seqnum_not_before and .timestamp_not_before fields 2024-01-25 16:10:36 +01:00
Lennart Poettering 6bc201343f loop-util: remember if we created the LoopDevice
Let's store in a bool whether a LoopDevice object was created via
loop_device_open() or loop_device_make().
2023-12-13 17:35:22 +01:00
Lennart Poettering c961a8c605 loop-util: also store the device size in LoopDevice
That makes the field easily accessible, just as the sector size.
2023-12-13 17:35:22 +01:00
Luca Boccassi d579c42e9d loop-util: use the right error variable in log_debug_errno after fd_reopen 2023-11-16 21:13:10 +00:00
Luca Boccassi f5bb0a3163 loop-util: restart loop_configure and reopen FD when O_DIRECT fails
On kernel 5.10.178, when a squashfs file is stored on an EXT4 filesystem
backed by a dm-crypt volume, dissecting fails:

$ SYSTEMD_LOG_LEVEL=debug systemd-dissect /var/foo/bar.raw
Opened '/var/foo/bar.raw' in O_RDONLY access mode, with O_DIRECT enabled.
Couldn't find any partition table to derive sector size of.
loop2: Acquired exclusive lock.
Could not enable direct IO mode, proceeding in buffered IO mode.
Successfully acquired /dev/loop2, devno=7:2, nr=2, diskseq=87
Opened /dev/loop2 (fd=3, whole_block_devnum=7:2, diskseq=87).
      Name: bar.raw
      Size: 67.2M
 Sec. Size: 512
     Arch.: n/a

Successfully forked off '(sd-dissect)' as PID 4110.
Mounting /proc/self/fd/3 (squashfs) on /tmp/dissect-Zk3K5F (MS_RDONLY|MS_NODEV "")...
Failed to mount /proc/self/fd/3 (type squashfs) on /tmp/dissect-Zk3K5F (MS_RDONLY|MS_NODEV ""): Input/output error
Failed to mount dissected image: Input/output error
Failed to read /etc/hostname of image: No such file or directory
/etc/machine-id file of image is empty.
Failed to read has-init-system boolean: Input/output error
(sd-dissect) failed with exit status 1.
Failed to acquire image metadata: Input/output error

The kernel shows I/O errors:

kernel: blk_update_request: I/O error, dev loop2, sector 0 op 0x0:(READ) flags 0x800 phys_seg 1 prio class 0
kernel: SQUASHFS error: Failed to read block 0x0: -5
kernel: unable to read squashfs_super_block

This is independent of a particular filesystem and can be reproduced
reliably in my setup, starting from freshly formatted disks.

Instead of continuing when O_DIRECT fails, start over the setup
process without the flag, including opening a new FD, to make the
kernel happy.
2023-11-16 21:12:40 +00:00
Lennart Poettering ac110243a8 bsod,loop-util: fix fd validity check 2023-11-01 15:21:20 +01:00
Yu Watanabe 7a05926fbe udev: re-introduce symlinks for loopback block device
But the directories are changed from /dev/loop/by-ref/ -> /dev/disk/by-loop-ref/
and /dev/loop/by-inode/ -> /dev/disk/by-loop-inode/.
As /dev/loop/ is used by losetup command for other purpose.
See issue #28475.

This effectively reverts commits 9915cc6086,
5022fab15f, and
c0d998248e.
2023-08-10 07:05:47 +09:00
Yu Watanabe c0d998248e tree-wide: drop references to /dev/loop/by-ref
Follow-up for #28476.
2023-07-21 10:24:20 +09:00
Yu Watanabe 4251512ea9 time-util: introduce usleep_safe()
We use usec_t for storing time value, which is 64bit.
However, usleep() takes useconds_t that is (typically?) 32bit.
Also, usleep() may only support the range [0, 1000000].

This introduce usleep_safe() which takes usec_t.
2023-06-22 15:33:56 +09:00
Daan De Meyer 05d1cbb33c Merge pull request #26969 from DaanDeMeyer/xopenat-label
fs-util: Add labelling support to xopenat()
2023-05-31 14:35:50 +02:00
Daan De Meyer 420d2e3136 fs-util: Add XOpenFlags with XO_LABEL flag to have xopenat() MAC label files/dirs 2023-05-31 13:15:56 +02:00
Zbigniew Jędrzejewski-Szmek 6483bcef54 shared/loop-util: add comment
Requested in https://github.com/systemd/systemd/commit/afbe20b7d4ed4c3aebbd1b3de00b9b7385dd0ae8#r115653459.
2023-05-30 16:04:35 +02:00
Zbigniew Jędrzejewski-Szmek afbe20b7d4 shared/loop-util: use longer delay when waiting for loop device
The kernel may be syncing a file system or doing something else that requires
more time. So make the delay a bit longer, but provide some feedback and also
grow the delay exponentially (though with a long exponent). If the kernel is
doing something else, no need to repeat so often. With 38 attempts, we get a
total of slightly above 5000 ms.

I wrote this when I thought that the the delay is not long enough. It turned
out that we were blocking the file system on the loop device, so waiting longer
wasn't helpful. But I think it's nicer to do it this way anyway.
2023-05-30 13:41:56 +02:00
Lennart Poettering 8e398254ba loop-util: port some code over to ASSERT_FD() 2023-04-24 20:52:52 +02:00
Daan De Meyer 972c8db589 loop-util: Add loop_device_make_by_path_at()
On top of taking a directory file descriptor, we use xopenat() so
that the function can also be used to work on existing file
descriptors to image files including all the logic to use O_DIRECT
and fallback to O_RDONLY if needed.
2023-03-23 17:36:17 +01:00
Yu Watanabe 432f1fa8c7 loop-util: add more debugging logs in loop_configure() 2023-03-19 20:32:09 +09:00
Lennart Poettering 999ac3e2b0 loop-util: add API for selecting "lo_file_name" field for a loopback device 2023-03-09 16:40:55 +01:00
Lennart Poettering d2430d5097 loop-util: add call for setting the autoclear flag at arbitrary times 2023-03-09 16:40:55 +01:00
Lennart Poettering 4d2a9e3ea6 loop-util: keep track of inode/devnum of backing file 2023-03-09 16:40:55 +01:00
David Tardon 92651a7a2d tree-wide: initialize fds with -EBADF 2023-02-23 09:48:54 +01:00