Tak to jste si ale dost zahrával, protože dd je minové pole. tl;dr dd bez flagu fullblock prostě yolo udělá syscall read s velikostí podle nastavené velikosti bloku, a když mu kernel vrátí méně dat, tak to neřeší a zapíše je na výstup a počítá to za plný blok. Takže můžete snadno přenést méně dat než jste chtěl. Skoro mi podle popisu bugu přijde, že tohle je sice bug v rust-coreutils (kromě částečného přečtení na vstupu udělá i částečný zápis na výstupu a ztratí přečtená data), ale kdyby se situace sešla, tak problém nastane i s normálním dd.
Já se na dd vykašlal, soubory přenáším normálně catem (nebo ještě lépe pv - zobrazuje průběh, navíc celou dobu a nemusí se dráždit jak dd), a začátky a konce usekávám pomocí head a tail (-c 100, -c +100).
Průběh obecně zobrazuji nástrojem progress, podporuje spoustu programů (gzip a další *zip, md5sum a další *sum…). Funguje tak, že se podívá do /proc/pid/fd aby zjistil, který soubor je vstup a výstup, a pak do /proc/pid/fdinfo, kde se dozví aktuální pozici. Pokud jsem na systému, kde není a nechci/nemůžu ho instalovat, tak tohle podívání se udělám ručně.
Tak zrovna tohle jde naprosto v pohodě, i když nevím, proč bych to dělal (oddíly jsou typicky číslované od začátku). Poslední gigabajt:
# time tail -c $(( 1000*1000*1000 )) /dev/nvme0n1 | md5sum 90f6764444b7952a4dcf1c7bb24e5ff5 - real 0m1.063s user 0m0.966s sys 0m0.395s
Chápu, že tenhle přístup nejde použít k přepisu hodnoty uprostřed disku.
Tuhle narážku na dd (že nemusí číst celý blok) jsem o tebe před mnoha lety četl už na ABCLinuxu. Tobě se někdy reálně stalo, že by dd nenačetl celý blok a nic by neoznámilo chybu? Mě teda nikdy a to jsem pomocí dd běžně dělal image virtuálek a přenesl možná stovky TB dat. Nedovedu si představit, že by fs vrátil méně než blok a nenahlásil by chybu čtení apod.
No ne, když kopíruješ celý disk/image atd. tak je to v pohodě, protože se ve čtení pokračuje dál. Problém je když používáš "count" a chceš aby to přeneslo přesně tolik dat kolik je count*bs.
Nasimulovat se to dá takhle:
$ (while sleep 0.1; do head -c 1000 /dev/urandom ; done) | dd bs=1024 count=10 of=/dev/null dd: warning: partial read (1000 bytes); suggest iflag=fullblock 0+10 records in 0+10 records out 10000 bytes (10 kB, 9,8 KiB) copied, 1,0492 s, 9,5 kB/s
(mělo přenést 10240 a přeneslo jen 10000). Tj. stane se to pokud se čte z programu/sítě, ze kterého padají data po menších bobečcích než chceme číst.
A je to reálné při síťovém provozu, např. ssh po normálním rozumně rychlém internetu:
$ ssh f "cat /dev/urandom" | dd bs=1M count=10 of=/dev/null dd: warning: partial read (32768 bytes); suggest iflag=fullblock 0+10 records in 0+10 records out 327680 bytes (328 kB, 320 KiB) copied, 0,527688 s, 621 kB/s
A stejně tak při čtení komprimovaného archivu:
$ zstdcat openwrt-imagebuilder-24.10.2-ramips-mt7621.Linux-x86_64.tar.zst | dd bs=1M count=10 of=/dev/null dd: warning: partial read (65536 bytes); suggest iflag=fullblock 0+10 records in 0+10 records out 593920 bytes (594 kB, 580 KiB) copied, 0,00314817 s, 189 MB/s
Přímo disku se to doufejme dít nebude, a doufejme že ani např. z NFS share. Ale nesázel bych na to… Konec konců, diskutujeme pod zprávičkou, kdy měl někdo částečný zápis při zapisování do "pomalého" md5sum.
Za mě se to chová správně. Tímhle dd říkáte "udělej 10 čtení po maximálně 1024 bajtech". Pipe v jednom čtení vrací 1000 bajtů, takže není možné, aby se vrátilo 10240 bajtů. Pokud chcete, aby dd dočetl data do plného bloku, musí udělat větší počet čtení, než jste mu zadal. To zapnete pomocí iflag=fullblock, jak doporučuje.