Skip to content

sdmcryptfs: uncompressed save path copies 4096x too much (dd count is in 4K blocks but bs=16M), and the failure goes unnoticed #403

Description

@masaki-kuraya

Ran into this on a Pi 5 (8GB) running Raspberry Pi OS Lite (trixie), encrypting the SD card rootfs with sdmcryptfs /dev/mmcblk0 /dev/sda. With 8GB of RAM and a ~5.2GB rootfs the RAM disk path gets selected, and it fails every time. Here's the output from the failed run:

bash-5.2# sdmcryptfs /dev/mmcblk0 /dev/sda
> 2026-07-31 17:38:20 Start sdmcryptfs on a 8255888 kB 'Raspberry Pi 5 Model B Rev 1.0'
> 2026-07-31 17:38:22 Shrink partition '/dev/mmcblk0p2' and get its size
> 2026-07-31 17:39:04 Device '/dev/mmcblk0' rootfs size: 1274977 4K blocks (5.2GB, 4.9GiB)
> 2026-07-31 17:39:04 ** NOTE: save and restore time estimates are pessimistic
> 2026-07-31 17:39:04 ** Exact speed measurements dependent on CPU, memory, and disk speeds of this system
> 2026-07-31 17:39:04 Save rootfs '/dev/mmcblk0p2' to RAM disk
> 2026-07-31 17:39:04 rootfs save should take less than 00:01:55
dd: error writing '/scratch/uncompressed-rootfs.img': No space left on device
374+0 records in
373+0 records out
> 2026-07-31 17:40:13 rootfs Save elapsed time: 00:01:09
> 2026-07-31 17:40:13 Expand rootfs partition with expandpartition
> 2026-07-31 17:40:13 Expand root partition 'mmcblk0p2' (ext4) on device '/dev/mmcblk0' from (31.4GB, 29.2GiB) to (63.3GB, 58.9GiB)
Warning: Shrinking a partition can cause data loss, are you sure you want to
continue?
Yes/No?

The cause is in save_rootfs(), the uncompressed branch:

dd bs=16M count=$partsize if=$rootfs of=/scratch/uncompressed-rootfs.img

$partsize is a count of 4K blocks (runall() does pbytes=$((partsize*4096))), so with bs=16M this dd tries to copy 4096 times the actual rootfs size. Since the tmpfs is sized at 1.2x the rootfs (5976M in my case), it always runs out of space. The compressed branch a few lines down converts correctly with fullblocks=$((partsize/4096)); the uncompressed one is missing that conversion.

What worries me more is that the script didn't notice. dd's exit status is never checked, so after the ENOSPC it just kept going, as you can see above. The only thing that stopped my run was parted's interactive prompt, where I answered No. If I'd answered Yes, it would have carried on to luksFormat and restored the truncated image onto the freshly encrypted partition. Checking dd's status in save_rootfs (and restore_rootfs) and bailing out before the disk gets reformatted would avoid turning a save failure into data loss.

The "from (31.4GB, 29.2GiB) to (63.3GB, 58.9GiB)" line above (on a 32GB card) is a third, cosmetic issue: in expandpartition(), nbytes=$(((devsize*512)-partend+pbytes)) uses $partend, which is declared local but never assigned in that branch, so it's 0. The actual resizepart uses newend and is correct -- but watching the partition apparently double past the device size mid-run is unsettling.

Workaround in the meantime: I patched the raspberrypi match on /sys/firmware/devicetree/base/compatible out of the script so it takes the USB scratch disk + compression path instead of the RAM disk. With that, the whole run completed cleanly on the same setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions