Dlaczego przesyłanie strumieniowe „tar” do „dd” nie zatrzymuje się, dopóki dysk nie jest pełny?

18

Mam archiwum tar jednego obrazu dysku. Obraz w tym pliku tar ma rozmiar około 4 GB. Przesyłam dane wyjściowe do tar xfw ddcelu zapisania obrazu dysku na karcie SD. Zrzut dysku nigdy się nie zatrzymuje, dopóki karta nie jest pełna. Oto moja sesja powłoki:

$ ls -l disk.img.tgz
-rw-r--r-- 1 confus confus 192M Okt  5 00:53

$ tar -tvf disk.img.tgz
-rw-r--r-- root/root 4294968320 2018-10-05 00:52 disk.img

$ lsblk -lb /dev/sdc
NAME MAJ:MIN RM        SIZE RO TYPE MOUNTPOINT
sdc    8:32   1 16022241280  0 disk

$ tar zxf disk.img.tgz -O | sudo dd status=progress conv=sync bs=1M of=/dev/sdc
[sudo] password for user: 
15992881152 bytes (16 GB, 15 GiB) copied, 212 s, 75,4 MB/s 
dd: error writing '/dev/sdc': No space left on device
0+15281 records in
15280+0 records out
16022241280 bytes (16 GB, 15 GiB) copied, 217,67 s, 73,6 MB/s

Dlaczego? Powinno się zatrzymać po tym, jak hit zapisał obraz 4 GB do koszyka 16 GB i nigdy nie zabraknie miejsca!

dezorientować
źródło
Czy masz miejsce na dysku, aby spróbować uruchomić to ddi zapisać w innym pliku? tar zxf disk.img.tgz -O | dd status=progress conv=sync bs=1M of=/path/to/some/file/on/disk? Jeśli tak, to czy otrzymujesz dokładną kopię oryginalnego pliku?
Andy Dalton,
2
Dlaczego masz conv=sync? Czy chciałeś użyć conv=fsync?
Ralph Rönnquist
Czy jesteś pewien, że to jest prawdziwy rozmiar pliku? Wiem, że gzip ma tylko 32 bity, w których można przechowywać rozmiary plików, więc rozmiar plików przekracza 4 GB. Nie jestem pewien, czy smoła ma podobne ograniczenie.
David Conrad

Odpowiedzi:

50

To dlatego, że robisz to źle.

Używasz, bs=1Male czytanie ze standardowego, potoku będzie miało mniejsze odczyty. W rzeczywistości, według dd, nie dostałeś ani jednego pełnego odczytu.

A potem masz, conv=syncktóry uzupełnia niekompletne odczyty zerami.

0+15281 records in
15280+0 records out

ddotrzymał 0 pełnych i 15281 niepełnych odczytów i napisał 15280 pełnych bloków (conv = synchronizacja wypełniona zerem). Wyjście jest więc znacznie większe niż wejście, dopóki nie zostanie wolne miejsce.

   sync   pad  every  input  block  with  NULs to ibs-size; when used with
          block or unblock, pad with spaces rather than NULs

Aby rozwiązać ten problem, możesz usunąć conv=synci dodać iflag=fullblock.


Aby to zilustrować, zastanów się yes, która domyślnie wyrzuca nieskończone „y \ ny \ ny \ n”.

$ yes
y
y
y
^C
$ yes | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*

Z dd bs=1M conv=syncwygląda to tak:

$ yes | dd bs=1M conv=sync | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
0001e000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
00112000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

Otrzymuje więc niekompletny blok „y \ ny \ ny \ n” (0x00000 - 0x1e000, 122880 bajtów), a następnie zapisuje pozostały 1M jako zera (0x01e000 - 0x100000, 925696 bajtów). W większości przypadków nie chcesz, aby tak się stało. Wynik jest w każdym razie losowy, ponieważ nie masz rzeczywistej kontroli nad tym, jak niekompletny byłby każdy odczyt. Tak jak tutaj, drugi odczyt to już nie 122880 bajtów, ale 73728 bajtów.

dd conv=syncjest rzadko przydatny i nawet w przypadkach, w których byłoby to mile widziane, np. pisanie zer, gdy wystąpią błędy odczytu, wszystko pójdzie z tym okropnie źle.

frostschutz
źródło
W takim przypadku uruchomienie ddpolecenia pod strace(zakładając, że Linux) pokazałoby, że po każdym krótkim odczycie z potoku następuje pełny zapis 1 MB.
Andrew Henle,
2
@AndrewHenle nawet nie potrzebuje do tego miejsca, wystarczy spojrzeć na wynik. Dodano ilustrację
frostschutz
To także pokazuje, dlaczego ddpolecenie jest zasadniczo złamane i nie nadaje się do użytku. Jest dostosowane do pracy w poszczególnych readS i writeS, ale operacje te są określone tak, że zawsze mogą produkować krótkie odczytuje lub zapisuje, i to nie jest błąd. W konsekwencji zachowanie ddzależy od nieokreślonego zachowania.
R ..
Dzięki za bardzo edukacyjną odpowiedź. Jak ktoś inny zasugerował, że byłem dupkiem i pomieszałem wiele opcji dd, ale doprowadziło mnie to do nauczenia się czegoś od ciebie. Nadal nie jestem całkowicie pewien, czy i kiedy ddby się skończyło . Zakładam, że tak, ale ponieważ faktycznie zapisywał 1 część rzeczywistych danych i 9 części zer, przestałby działać po zapisaniu około 40G. Czy to jest poprawne?
con-f-use
@R .., ta funkcja jest bardzo przydatna w przypadku sterowników urządzeń, które dbają o rozmiar bloku odczytów i zapisów. Pamiętam, że korzystałem z niektórych napędów taśm, które się tym przejmowały. Chociaż w tym przypadku nie jest to oczywiście konieczne, można po prostu przekierować bezpośrednio na dysk (nie otrzymując jednak raportu z postępu na żywo)
ilkkachu