
Мне нужно создать в коде, подходящем для скриптинга/make/автоматизации, образ диска (два раздела, файловая система FAT и файловая система ext4, для загрузки UEFI/Syslinux USB Linux [установщик ОС], который никогда не изменится в процессе производства) размером, который содержит точно (или очень близко) набор файлов произвольного размера. Другими словами, имея набор созданных файлов, мне нужно знать, как генерировать образы файловых систем FAT и ext4, а также образ диска, который разбивается на разделы для их хранения, размеры которых вычисляются так, чтобы в результате получить как можно более близкое к нулю свободное пространство. Допустимо ошибиться в сторону небольшого дополнительного места, но недопустимо потерпеть неудачу, когда кто-то добавит N+1 байт в файл через два года. Это должно подходить для makefile, т. е. метод проб и ошибок не подходит (хотя я полагаю, что в худшем случае итеративное решение с пороговым значением может сработать). Это что-то вроде образа ISO-9660 (который использовался в предыдущем проекте, но Syslinux не поддерживает образы ISO-9660 в UEFI).
Я создаю файлы, используя dd
(для выделения образа диска), parted
, dd
(для выделения файловой системы FAT), mkfs.vfat
, dd
(для ext4), mkfs.ext4
, kpartx
(для сопоставления разделов), dd
(для записи раздела FAT), dd
(для записи раздела ext4) и, наконец, dd
для записи образа диска на USB для загрузки на реальном оборудовании.
Моя текущая идея заключается в том, чтобы использовать du
для определения того, сколько места файлы занимают на диске сборки, а затем добавить некоторый запас для дополнительных накладных расходов файловой системы и раздела, а также запас погрешности. Поэтому мне нужно знать количество блоков для каждого из , dd
учитывая du
выходные данные.
Другой вариант — создать большой образ фиксированного размера, записать файлы, затем изменить размер FAT, ext4 и разделов до минимального размера. Файловые системы ext4 можно сжать, и я вижу, что файловые системы FAT можно сжать. Но тогда у вас все еще остается проблема вычисления того, насколько ее сжать. Интересно, делал ли кто-нибудь это раньше и есть ли у кого-нибудь конкретные идеи (или пример кода).
решение1
В итоге я использовал комбинацию факторов подтасовки и итеративный подход (в качестве защиты от сбоев). Это не должно быть таким сложным, как я изначально представлял. Оказалось, что на данный момент для FAT требовалось немного подтасовки, но для ext требовалось огромное количество отрицательной подтасовки; с нулевым фактором подтасовки для ext4 у меня было много свободного места (более 21 МБ). Я перешел на ext2 (кому нужен этот вонючий журнал?!), увеличил размер блока и тщательно подсчитал нужные мне иноды и получил еще больше мегабайт свободного места. Полагаю, я мог бы получить «реальный размер» из du и работать с этим, но я решил, что подсчет накладных расходов, даже если это разные файловые системы, будет более точным приближением.
# Estimated filesystem overhead, in 512-byte blocks
FS_ESP_FUDGE=256
FS_ISO_FUDGE=-80000 # Wow!
FS_FUDGE_INCR=1024
...
read ESP_RSIZE d < <(du --summarize --block-size=512 $ESP)
read ISO_RSIZE d < <(du --summarize --block-size=512 $ISO)
success=false
until $success; do
let ESP_SIZE=ESP_RSIZE+FS_ESP_FUDGE
let ISO_SIZE=ISO_RSIZE+FS_ISO_FUDGE
let ESP_START=2048
let IMG_SIZE=ESP_SIZE+ISO_SIZE+ESP_START
let ESP_END=ESP_START+ESP_SIZE-1
let ISO_START=ESP_END+1
success=true
...
sudo /sbin/mkfs.vfat /dev/mapper/$p1 -F 16 \
|| error_exit "mkfs.vfat failed" 5
# -N: Count the inodes (all files, plus . and .. for each directory,
# which I can't get "find" to include).
sudo /sbin/mke2fs -b 4096 -N $(( $(find $ISO | wc -l ) + 2 * $(find $ISO -type d | wc -l))) -m 0 -q /dev/mapper/$p2 \
|| error_exit "mke2fs failed" 6
...
if ! tar -C $ESP -c --exclude-vcs --exclude-backups . | \
sudo tar -C mnt/esp -x; then
{
read
read fs onek used avail use rest
} < <(df mnt/esp)
# Are we out of disk space? If not, bail, else increase margin, retry
[[ $onek -ne $used || $avail -ne 0 || $use != "100%" ]] && \
error_exit "esp tar failed" 9
let FS_ESP_FUDGE=FS_ESP_FUDGE+FS_FUDGE_INCR
success=false
fi
if ! tar -C $ISO -c --exclude-vcs --exclude-backups . | \
sudo tar -C mnt/iso --owner=root --group=root -x ; then
{
read
read fs onek used avail use rest
} < <(df mnt/iso)
# Are we out of disk space? If not, bail, else increase margin, retry
[[ $onek -ne $used || $avail -ne 0 || $use != "100%" ]] && \
error_exit "iso tar failed" 10
let FS_ISO_FUDGE=FS_ISO_FUDGE+FS_FUDGE_INCR
success=false
fi
$success || echo "Whoops, I guessed too small; please adjust fudge factor. Retrying ..."
...
done