Как можно использовать `dd` для смещения блоков данных вправо?

10

Рассмотрим простое блочное устройство объемом 100 МБ в качестве простого примера. Это 204800 блоков по 512 байт каждый на общую сумму 102760448 байт.

Задача состоит в том, чтобы переместить первые 98 МБ (блоки 200704), чтобы перед ним был разрыв в 2 МБ (4096 блоков). Для этого требуется, чтобы ничего не было записано в сектор, который не был прочитан. Одним из способов достижения этого является введение буфера:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 | dd of=/dev/sdj2 seek=4096

Предполагается, что mbufferперед передачей чего-либо записывающему устройству будет сохранено 4096 блоков, что гарантирует, что ничего не будет записано в область, которая не была прочитана, и что записывающее устройство отстает от читателя по размеру буфера. Буфер должен позволять читателю и записывающему устройству работать как можно быстрее в пределах этих констант.

Однако, это, кажется, не работает надежно. Я пытался использовать реальные устройства, но они никогда не работают на них, тогда как эксперименты с файлом работали на моем 64-разрядном компьютере, но не на моем 32-разрядном устройстве.

Сначала немного подготовки:

$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile

Это не работает:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=/dev/sdj2 seek=4096
summary: 98.0 MiByte in  4.4sec - average of 22.0 MiB/s
md5 hash: 3cbf1ca59a250d19573285458e320ade

Это работает на 64-битной системе, но не на 32-битной системе:

$ dd if=testfile count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=testfile seek=4096 conv=notrunc
summary: 98.0 MiByte in  0.9sec - average of  111 MiB/s
md5 hash: 0f0727f6644dac7a6ec60ea98ffc6da9

Как это можно сделать надежно?


заметки

Я прочитал другие вопросы о буферизации и посмотрел pv, bufferи mbuffer. Я мог только заставить последний работать с требуемым размером буфера.

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

Тестовые платформы под управлением Arch Linux с mbufferверсией 20140302.

starfry
источник
Я не думаю, что это решило бы проблему, но из любопытства зачем mbufferвообще использовать ? Почему бы вместо этого не сделать ddчтение всего содержимого блочного устройства за один раз dd bs=102760448? Конечно, так или иначе он буферизируется в оперативной памяти.
Celada
@Celada - пример размером 100 МБ был просто примером. Например, чтение 1 ТБ за один раз было бы не очень хорошей идеей.
звездный день
2
Ах, теперь я понимаю, спасибо. mbufferДолжны фактически заставить второй ddотстать для первого и вам нужна только достаточно оперативной памяти для буферизации размера сдвига. Жаль, ddчто не поддерживается чтение и запись блоков в обратном порядке, поскольку это устранит проблему!
Селада
Вы не перечислили, как вычислили вторую md5sum
psusi
@psusi, второй md5 выводится mbuffer (его -Hаргумент включает эту функцию).
звездный день

Ответы:

2

Без буфера вы можете вернуться назад, по одному блоку за раз.

for i in $(seq 100 -1 0)
do
    dd if=/dev/thing of=/dev/thing \
       bs=1M skip=$i seek=$(($i+2)) count=1
done

Обратите внимание, что этот пример опасен из-за отсутствия проверки ошибок.

Это также медленно из-за количества ddзвонков. Если у вас есть свободная память, вы можете использовать больший размер блока.

С буфером, остерегайтесь ловушек . Это не достаточно , чтобы гарантировать 100% предварительное заполнение с. Что вам нужно, так это минимальное заполнение на протяжении всего процесса. Буфер никогда не должен опускаться ниже, 2Mпотому что иначе вы перезапишете данные, которые еще предстоит прочитать.

Так что теоретически вы можете обойтись без буфера и цепочки dd:

dd if=/dev/thing bs=1M | \
dd bs=1M iflag=fullblock | \
dd bs=1M iflag=fullblock | \
dd of=/dev/thing bs=1M seek=2

На практике это не работает надежно, потому что нет гарантии, что первому ddудастся продолжить чтение данных, в то время как последний dd2M«буфером» между) уже записывает.

Вы можете значительно увеличить свои шансы, значительно увеличив промежуточный буфер, но даже в этом случае это ненадежно.

К сожалению, я не знаю хорошей буферной программы с минимальным свойством заполнения. Вам нужен тот, который останавливает вывод, если в буфере меньше вашего запаса прочности.

frostschutz
источник
Я принял это, потому что он отвечает на первоначальный вопрос, демонстрируя, как ddможно использовать. Я думаю, однако, что реальное решение не в том, чтобы использовать, ddа вместо этого выбрать что-то, что предназначено для работы в обратном направлении ddrescue. Я описал способ сделать это в ответе.
звездный день
1
@starfry: конечно, программа, которая просто делает это, будет хорошим решением. Однако я не совсем уверен насчет ddrescueздесь. Нет, если он рассчитывает работать на разных устройствах, и вы должны обмануть его, чтобы принять ваши аргументы. Он также может не иметь свойства «минимальное заполнение буфера» внутри (поскольку на разных устройствах он не нужен), поэтому он может повредить ваши данные. Вы должны проверить в исходном коде, действительно ли он предназначен для вашего случая использования.
frostschutz
1

Вы читаете 4096 блоков, а затем записываете эти 4096 блоков на следующие 4096 блоков диска, таким образом перезаписывая вторые 4096 блоков, прежде чем их можно будет прочитать. Вам нужно прочитать 8129 блоков, чтобы получить эти 4096 секунд, прежде чем начинать запись, а затем вам нужно написать только 4096 блоков, прежде чем читать следующие 4096.

Вы не упомянули, что это за файловая система. Если это ext [234], и у вас есть последняя версия e2fsprogs, то вы можете использовать e2image -ra -O 512 /dev/sdj2. Это также имеет дополнительное преимущество, заключающееся в том, что вы достаточно умны, чтобы пропустить свободное место в томе.

psusi
источник
Это имеет смысл, когда я читаю это, и я собираюсь взглянуть на это еще раз. Но это не объясняет, почему это сработало с тестовым файлом.
звездный день
Относительно файловой системы, вы имеете в виду файловую систему, содержащую мой тестовый файл? Это ext4для блочной копии устройства, любая файловая система должна быть неактуальной.
звездный день
@starfry, единственный известный мне способ сделать это в общем виде - это использовать алгоритм, предложенный Эммануэлем (работать в обратном направлении с конца), что и делает gparted.
Псуси
Что касается размера блока, я пробовал большие блоки (я должен был написать это в вопросе). Я обнаружил, что он не стал более надежным даже для буфера сектора 64K. Надежное решение - бежать задом наперед, что ddне делает.
Звездный день
1

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

ddrescueИнструмент может работать в обратном направлении , но он отказывается работать с входными и выходными данными являются одинаковыми. Однако это можно обмануть, дублируя узел устройства.

Я провел несколько быстрых экспериментов, и это похоже на работу. Командная строка:

$ ddrescue -f -R -s 200704s -o 4096s /dev/sdj11 /dev/sdj11_copy

Аргументы

  • -f необходимо заставить его записать в существующее устройство вывода
  • -R говорит ему работать в обратном направлении
  • -sговорит ему, сколько входных данных копировать (я использовал sсуффикс для указания количества секторов)
  • -oговорит ему искать вперед в устройстве вывода перед записью (снова указывается в секторах с sсуффиксом)
  • /dev/sdj11 это блочное устройство для чтения
  • /dev/sdj11_copy это блочное устройство для записи

Я создал /dev/sdj11_copyс, mknodчтобы соответствовать параметрам /dev/sdj11.

Я только сделал несколько очень быстрых тестов, но, похоже, это нормально работает для копирования необработанного устройства. Он не работает с файлом (я не мог обмануть его, чтобы выйти за пределы файлов, являющихся одинаковыми)

Это не отвечает на мой первоначальный вопрос, в котором спрашивалось, как этого добиться, ddно я думаю, что, прочитав другие ответы, я ddне смог этого сделать.

starfry
источник
Что произойдет, если ddrescueобнаружит плохой блок в этом сценарии? Если он перейдет в другую область диска (чтобы избежать плохих блоков) и продолжит копирование оттуда, он снова перезапишет еще не скопированные части ваших данных. Если он не рассчитывает на работу с одним и тем же устройством, у него нет причин принимать какие-либо специальные меры для предотвращения возможных случаев повреждения данных.
frostschutz
Я согласен, что это потенциальная проблема, но я не рассматривал крайние случаи, так как я мог использовать это, чтобы сделать то, что мне было нужно. Существуют ddrescueварианты ограничения попыток восстановить плохие данные, но я не стал их использовать.
Starfry
Тот факт, что он отказывается работать, если вход и выход совпадают, вероятно, является хорошим признаком того, что это небезопасно.
psusi