Mengapa pemipaan `tar` ke` dd` tidak berhenti sampai disk penuh?

18

Saya memiliki arsip tar dari gambar disk tunggal. Gambar di dalam file tar ini berukuran sekitar 4GB. Saya menyalurkan output tar xfke dduntuk menulis gambar disk ke kartu SD. Diskdump tidak pernah berhenti sampai kartu penuh. Inilah sesi shell saya:

$ 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

Mengapa? Seharusnya berhenti setelah hit telah menulis gambar 4GB ke keranjang 16GB dan tidak pernah kehabisan ruang!

membingungkan
sumber
Apakah Anda memiliki ruang disk untuk mencoba menjalankan ini dddan menulisnya ke file lain? tar zxf disk.img.tgz -O | dd status=progress conv=sync bs=1M of=/path/to/some/file/on/disk? Jika demikian, apakah itu memberi Anda salinan yang tepat dari file asli?
Andy Dalton
2
Mengapa Anda memiliki conv=sync? Apakah Anda bermaksud menggunakan conv=fsyncmungkin?
Ralph Rönnquist
Apakah Anda yakin itu ukuran sebenarnya dari file tersebut? Saya tahu gzip hanya memiliki 32 bit untuk menyimpan ukuran file, sehingga ukuran file lebih dari 4GB salah. Saya tidak yakin apakah tar memiliki batasan yang sama.
David Conrad

Jawaban:

50

Itu karena Anda salah melakukannya.

Anda menggunakan bs=1Mtetapi membaca dari stdin, pipa, akan memiliki bacaan yang lebih kecil. Bahkan, menurut dd, Anda tidak mendapatkan satu pun bacaan penuh.

Dan kemudian Anda memiliki conv=synckomplemen yang tidak lengkap dibaca dengan nol.

0+15281 records in
15280+0 records out

ddmenerima 0 penuh dan 15281 membaca tidak lengkap, dan menulis 15280 blok penuh (conv = sync zero filled). Jadi outputnya jauh lebih besar daripada input, sampai Anda tidak punya ruang tersisa.

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

Untuk mengatasi ini, Anda dapat menghapus conv=syncdan menambahkan iflag=fullblock.


Sebagai ilustrasi, pertimbangkan yes, yang secara default memuntahkan infinite "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.|
*

Dengan dd bs=1M conv=synctampilannya seperti ini:

$ 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  |................|
*

Jadi ia mendapat blok tidak lengkap dari "y \ ny \ ny \ n" (0x00000 - 0x1e000, 122880 Bytes) kemudian menulis 1M sisanya sebagai nol (0x01e000 - 0x100000, 925696 Bytes). Dalam kebanyakan kasus, Anda tidak ingin ini terjadi. Bagaimanapun hasilnya adalah acak karena Anda tidak memiliki kontrol nyata atas bagaimana tidak lengkapnya setiap pembacaan. Seperti di sini bacaan kedua tidak lagi 122880 Bytes tetapi 73728 Bytes.

dd conv=syncjarang berguna dan bahkan dalam kasus-kasus di mana itu akan diterima, seperti menulis nol ketika Anda mendapatkan kesalahan membaca, hal-hal buruk akan terjadi dengannya.

frostschutz
sumber
Dalam hal ini, menjalankan ddperintah di bawah strace(dengan asumsi Linux) akan menunjukkan bahwa setiap pembacaan singkat dari pipa diikuti oleh penulisan 1MB penuh.
Andrew Henle
2
@AndrewHenle bahkan tidak perlu strace untuk ini, hanya melihat output yang akan dilakukan. Menambahkan ilustrasi
frostschutz
Ini juga menggambarkan mengapa ddperintah pada dasarnya rusak dan tidak dapat digunakan. Ini ditentukan untuk beroperasi di masing-masing readdan write, tetapi operasi tersebut ditentukan sedemikian rupa sehingga mereka selalu dapat menghasilkan bacaan pendek atau tulis, dan itu bukan kesalahan. Akibatnya, perilaku ddtergantung pada perilaku yang tidak ditentukan.
R ..
Terima kasih atas jawaban yang sangat mendidik. Seperti yang orang lain sarankan saya menjadi keledai dan mencampuradukkan banyak pilihan dd, tetapi itu membuat saya belajar sesuatu dari Anda. Yang saya masih tidak yakin tentang adalah, jika dan kapan ddakan berakhir. Saya berasumsi, memang harus, tetapi karena sebenarnya menulis 1 bagian data aktual dan 9 bagian nol, itu akan berhenti setelah menulis sekitar 40G. Apakah itu benar?
gunakan con
@R .., fitur itu sangat berguna dengan driver perangkat yang peduli dengan ukuran blok baca dan tulis. Saya ingat menggunakan beberapa tape drive yang peduli. Meskipun dalam kasus ini, itu jelas tidak perlu, orang hanya bisa mengarahkan langsung ke disk (meskipun tidak mendapatkan laporan perkembangan langsung)
ilkkachu