Почему передача `tar` в` dd` не останавливается, пока диск не заполнится?

18

У меня есть tar-архив одного образа диска. Размер изображения в этом tar-файле составляет около 4 ГБ. Я передаю вывод tar xfв, ddчтобы записать образ диска на SD-карту. Диск-дамп никогда не останавливается, пока карта не заполнится. Вот мой сеанс оболочки:

$ 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

Почему? Он должен остановиться после того, как хит записал изображение объемом 4 ГБ в корзину на 16 ГБ и никогда не закончится!

кон-е использование
источник
У вас есть место на диске, чтобы попытаться выполнить это ddи записать его в другой файл? tar zxf disk.img.tgz -O | dd status=progress conv=sync bs=1M of=/path/to/some/file/on/disk? Если так, это дает вам точную копию оригинального файла?
Энди Далтон
2
Почему у тебя conv=sync? Возможно, вы хотели использовать conv=fsync?
Ральф Реннквист
Вы уверены, что это истинный размер файла? Я знаю, что gzip имеет только 32 бита для хранения размеров файлов, поэтому он неправильно принимает размер файлов более 4 ГБ. Я не уверен, что у tar есть подобное ограничение.
Дэвид Конрад

Ответы:

50

Это потому что ты делаешь это неправильно.

Вы используете, bs=1Mно чтение из stdin, pipe, будет иметь меньшее чтение. На самом деле, по словам дд, вы не получили ни одного полного чтения.

И тогда у вас есть, conv=syncчто дополняет неполное чтение с нулями.

0+15281 records in
15280+0 records out

ddполучил 0 полных и 15281 неполных чтений и записал 15280 полных блоков (conv = синхронизация заполнена нулями). Таким образом, вывод будет намного больше, чем ввод, пока не останется свободного места.

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

Чтобы решить эту проблему, вы можете удалить conv=syncи добавить iflag=fullblock.


Чтобы проиллюстрировать это, рассмотрим yes, который по умолчанию выбрасывает бесконечный "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.|
*

С dd bs=1M conv=syncэтим выглядит так:

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

Таким образом, он получает неполный блок "y \ ny \ ny \ n" (0x00000 - 0x1e000, 122880 байт), а затем записывает оставшиеся 1M как нули (0x01e000 - 0x100000, 925696 байт). В большинстве случаев вы не хотите, чтобы это произошло. В любом случае результат является случайным, поскольку у вас нет реального контроля над тем, насколько неполным будет каждое чтение. Как и здесь, второе чтение больше не 122880 байт, а 73728 байт.

dd conv=syncредко бывает полезным, и даже в тех случаях, когда это будет приветствоваться, например, при записи нулей, когда вы получаете ошибки чтения, с этим все пойдет не так.

frostschutz
источник
В этом случае выполнение ddкоманды в strace(при условии Linux) показало бы, что за каждым коротким чтением из канала следовала полная запись в 1 МБ.
Эндрю Хенле
2
@AndrewHenle даже не нуждается в strace для этого, достаточно просто посмотреть на результат. Добавил иллюстрацию
frostschutz
Это также иллюстрирует, почему ddкоманда в корне сломана и непригодна для использования. Он определен для работы с отдельными reads и writes, но эти операции определены так, что они всегда могут производить короткие чтения или записи, и это не ошибка. Как следствие, поведение ddзависит от неопределенного поведения.
R ..
Спасибо за очень образовательный ответ. Как кто-то другой предположил, что я был ослом и перепутал множество вариантов dd, но это привело меня к чему-то научиться у вас. В чем я до сих пор не совсем уверен, так это в том случае, если и когда ddэто закончится. Я предполагаю, что это произойдет, но так как он фактически записывал 1 часть фактических данных и 9 частей нулей, он остановился бы после записи около 40G. Это верно?
Con-F-
@R .., эта функция очень полезна для драйверов устройств, которые заботятся о размере блоков чтения и записи. Я помню, как использовал некоторые стримеры, которые заботились об этом. Хотя в этом случае, очевидно, в этом нет необходимости, можно просто перенаправить прямо на диск (но не получить отчет о ходе работы в реальном времени)
ilkkachu