web-dev-qa-db-ja.com

ビット同一のext2ファイルシステムを作成する

Linuxシステム用のイメージファイルを準備しています。イメージを作成し、出力を毎回ビットごとに同一にするスクリプトを実行できる必要があります。

私は通常の手順を実行します。大きなバイナリファイルを作成してパーティションを作成し、そのパーティションを使用してループデバイスを作成してから、Iファイルシステムを作成します。次に、ファイルシステムをmountし、syslinuxinitrdのものをコピーし、パーティションをアンマウントし、ループデバイスを削除すると、イメージファイルが作成されます。ディスクにddすることができ、Linuxシステムは正しく起動します。だから私はファイルシステムを正しく作っています。

上記の手順を実行するスクリプトを実行しますが、出力が異なるたびに実行します。その一部はext2データ構造のタイムスタンプです。 ext2構造体を読み込み、タイムスタンプをクリアできるプログラムを作成しました。_tune2fs_はさらにいくつかのことをクリアできますが、ビットマップデータの一部は異なり、ファイルのようです。データは毎回同じ場所にあるわけではありません。

では、どのようにして同一のファイルシステムを作成するのでしょうか?

これが私がファイルシステムを作成し、その上にファイルを置き、それをアンマウントするために使用するコマンドです。出力を保存して再度実行し、出力を比較すると、ファイル_a.txt_が別の場所に配置されます。

_dd if=/dev/zero bs=1024 count=46112 of=cf.bin
parted cf.bin <<EOF
unit
s
mklabel
msdos
mkpart
p
ext2
63s
45119s
set
1
boot
on
q
EOF

losetup -o $(expr 63 \* 512) /dev/loop0 cf.bin

mke2fs -b 1024 -t ext2 /dev/loop0 22528

#clear some parameters
tune2fs -i 0 /dev/loop0 # interval between check
tune2fs -L LABEL /dev/loop0
tune2fs -U 00000000-0000-0000-0000-000000000000 /dev/loop0 #uuid
tune2fs -c 0 /dev/loop0 #mount count

mount /dev/loop0 mnt
# make a dummy file
echo HELLO > mnt/a.txt
umount mnt

losetup -d /dev/loop0
_

更新
上記のコマンドをスクリプトに挿入した場合は、それらをコピーして貼り付けて2回目に実行し(ただし、出力を保存します)、コマンドを2回実行する前に日付を変更します(date command)、a.txtは同じディスクの場所に配置されます。ただし、スクリプトを実行して出力を保存し、コマンドラインから再度実行する場合は、出力を比較すると、a.txtは別の場所にあります。非常に奇妙な行動。ファイルの場所を生成するためにどのようなデータが使用されていますか?明らかにそれは時間ではありません。私が考えることができる唯一のことは、スクリプトを2回呼び出すことによってコマンドを2回呼び出すことと、同じスクリプトでコマンドを2回実行することの違いは、呼び出し元のプロセスのプロセスIDのようなものです。アイデアは誰ですか?

アップデート#2
ext2の使用をあきらめました。そのため、ext2に関する最初の質問に答えることはできませんが、基本的なLinuxシステムの完全に再現可能なビルドを取得するために何をしたかについて説明します。

  1. Ext2の代わりに、FATバリアントまたはISO9660を使用してください。 32MB未満のパーティションが必要な場合は、LinuxシステムパーティションにFAT16を使用します。それ以外の場合は、FAT32を使用します。 FAT16またはFAT32は、ファイルを同じ場所に繰り返し配置します。ただし、ディレクトリエントリにはいくつかのタイムスタンプがあります。
  2. 起動に必要なLinuxシステムファイルを追加します。
  3. FAT16/32ファイルシステムのディレクトリ構造をウォークし、すべてのタイムスタンプを0に設定するプログラムを作成します。
  4. Mbrのディスク署名をクリアします。タイムスタンプをクリアするプログラムでこれを行うか、ddを使用します。
  5. FATファイルシステムなので、ブートローダーにsyslinuxを使用しています。 cpioは実行ごとに同一のinitrdを生成するため、問題はありません。これは、基本的なビットごとの同一のLinuxシステムに必要なすべてです。

FATファイルシステムの問題

Linuxシステムを起動するだけの場合、FATによって問題が発生することはありません。ただし、より大きなデータパーティションの場合、FAT32にはいくつかの問題が発生する可能性があります。

  1. ディレクトリ内のファイルの最大数にぶつかることが可能です。これは問題になる可能性は低いです。 (もちろん、私の場合はそうでした)
  2. FAT32は、ファイルごとに8.3ファイル名を保存します。長いファイル名は、チルダと数字が追加された語幹に短縮されます。ただし、同じ短いステムにマップするファイルが9つを超える場合、FAT32は文書化されていない手順を使用して、代わりにファイル名に追加する一種のハッシュを生成します。 FAT32のLinuxカーネルコードを掘り下げたところ、時間をハッシュシードとして使用しています(ファイルnamei_vfat.cのfunctionvfat_create_shortname())。したがって、このフィールドは再現できません。 Microsoftの実装がどのようにそれを行うのかわかりません。 8.3の名前はDOS以外には使用されていないと思うので、このフィールドをクリアするだけでうまくいくかもしれません。または、再現できる独自の一意の番号を生成することもできます。番号が何であるかは関係ありません。一意であるだけです。

追加のパーティションにISO9660を使用する

  1. Genisoimageを使用してisoを作成します。タイムスタンプを除いて、実行ごとに同じ出力を生成します。 -lオプションを使用すると、最大31文字のファイル名を使用できます。それより長いファイル名が必要な場合は、ロックリッジ拡張子を使用してください。コマンドは

    _genisoimage -o gfx.iso -R -l -f assets/files/
    _
  2. Iso9660ファイルシステムをウォークし、ロックリッジエントリのTFフィールドを含むすべてのタイムスタンプをクリアするプログラムを作成します。

  3. Fdiskまたはpartedを使用して、ディスクイメージにパーティションを作成します。 96hは、ISO9660のMBRID番号です。
  4. 必要に応じて、パーティションテーブルにパッチを適用します。 Partedは、タイプiso9660のパーティションの作成をサポートしていません。残念ながら、partedとfdiskの両方の古いバージョンで立ち往生しており、partedの方が使いやすいです。そこで、partedを使用して2番目のパーティションをfat32として作成しました。次に、fdiskを使用してタイプを96に変更しました。
  5. Ddを使用して、パーティションの作成に使用したのと同じ番号を使用して、ディスクイメージにisoを埋め込みます。使った

    _dd bs=512 seek=$part2_start_lba conv=notrunc if=gfx.iso of=cf.bin
    _

ここで、cf.binは私のディスクイメージファイルです。 6. Linuxが起動した後、isoパーティションをマウントします。 isoが2番目のパーティションの場合、/ dev/sda2になります。最初に/ devに適切なデバイスファイルを作成するには、mknodを使用する必要がある場合があります。

4
jhufford

私見これはすべて過度に複雑になっているようです。 tar だけが明らかな解決策のように思われる場合。 tarは、cdfs(-options cd9660:*)を含むほぼすべてのファイルシステムを作成できます。また、出力ファイルに最新の-m || --modification-time--gid id || --gname name--acls || --no-acls--same-owner || --no-same-owner、...のいずれかにタイムスタンプを付けることができます。

または、ファイルシステムを作成することもできます。ファイルツリー内でchown -Rh someone:somegroup .を実行し、必要に応じてchmodを実行し、tarまたは rsync を使用して、準備したファイルツリーを配置します。ファイルシステム。そうすれば、すべてが一貫します-同じ日付、同じ所有者/グループ&&パーマ。

さて、それは私がこのようなものにアプローチする方法です。 :)

HTH

3
somebody

_e2image_ を試すこともできます:

E2imageプログラムは、デバイス上にある重要なext2、ext3、またはext4ファイルシステムのメタデータをファイルに保存します。

デフォルトでは、_e2image_はメタデータのみを保存しますが、_-a_オプションを使用すると、データも保存できます。 (リンクの「データを含める」セクションを参照してください)

-aオプションを指定して、すべてのデータを含めることができます。これにより、FS全体のクローンを作成するため、またはバックアップの目的で使用するのに適したイメージが得られます。このオプションは、raw形式またはQCOW2形式でのみ機能することに注意してください。

バックアップには、raw形式よりもQCOW2形式の方が適しています。 QCOW2イメージは、バックアップするパーティションの使用済みスペースに近いサイズの通常のファイルです。生の画像はすべての意味を持つスパースファイルです-スパースファイルを特に処理しないツールは、使用済みスペースだけでなく空きスペースも処理します。

したがって、使用例は次のとおりです。

  • ファイルとメタデータを含む_sda1_パーティションをファイル_boot-part.qcow2_にバックアップします。
_Sudo mount -o remount,ro /dev/sda1
Sudo e2image -a -Q /dev/sda1 boot-part.qcow2
_

バックアップの進行中に誰もパーティションに書き込みを行わないようにするには、読み取り専用として再マウントする必要があることに注意してください。結局のところ、必要に応じて_Sudo mount -o remount,rw /dev/sda1_によって読み取り/書き込みとして再マウントできます。

  • QCOW2イメージファイル_dev/sda1_からパーティション_boot-part.qcow2_を復元します。
_Sudo umount /dev/sda1
Sudo e2image -r boot-part.qcow2 /dev/sdd1
_

マウントされたパーティションのスーパーブロックを書き換えることはできないため、umountは必須であることに注意してください。その時点で_/dev/sda1_がマウントされていない場合は、この手順をスキップできます。 :)

シナリオの復元では、_/dev/sda1_のサイズがイメージファイル内のパーティション以上である必要があることにも注意してください。そうでない場合、エラーe2image: Invalid argument while trying to convert qcow2 image (boot-part.qcow2) into raw image (/dev/sda1)が発生します。

1
Str1ker

:これは完全な答えではありません。部分的なもの、または少なくともヒント


イメージを作成し、出力を毎回ビットごとに同一にするスクリプトを実行できる必要があります。

それを達成するために必要な最初の問題は、msdosパーティションテーブルのdisk signaturesです([〜#〜] mbr [〜#〜]の440のオフセット、4バイト長)。 MBRが異なる場合、最初のセクターだけで目標を達成できません。 mklabel内でpartedを実行するたびに、新しいdisk signatureが生成されます。次のように、同じランダム署名でこれらの4バイトを上書きすることで、これを克服できます。

printf RAMDOM_SIGNATURE | xxd -p -r | dd bs=1 count=4 seek=440 of=YOUR_DOT_BIN conv=notrunc 2> /dev/null

RANDOM_SIGNATURE'73396992'のようなものである可能性があります

この修正により、スクリプトに小さな変更を加えました。

dd if=/dev/zero bs=1024 count=46112 of="$1"
parted "$1" <<EOF
unit
s
mklabel
msdos
mkpart
p
ext2
63s
45119s
set
1
boot
on
q
EOF

printf "$2" | xxd -p -r | dd bs=1 count=4 seek=440 of="$1" conv=notrunc 2> /dev/null

losetup -o $(expr 63 \* 512) /dev/loop0 "$1"

mke2fs -b 1024 -t ext2 /dev/loop0 22528

#clear some parameters
tune2fs -i 0 /dev/loop0 # interval between check
tune2fs -L LABEL /dev/loop0
tune2fs -U 00000000-0000-0000-0000-000000000000 /dev/loop0 #uuid
tune2fs -c 0 /dev/loop0 #mount count

#mount /dev/loop0 mnt
## make a dummy file
#echo HELLO > mnt/a.txt
#umount mnt

losetup -d /dev/loop0

これで、次のようにスクリプトを呼び出すことができます

./script_name BIN_FILE_NAME RANDOM_SIGNATURE

さて、これを行うと:

./test.sh cf00.bin '73396992'
./test.sh cf01.bin '73396992'
./test.sh cf02.bin '73396992'
./test.sh cf03.bin '73396992'

そしてこれ:

dd if=cf00.bin count=63 2>/dev/null | sha1sum
dd if=cf01.bin count=63 2>/dev/null | sha1sum
dd if=cf02.bin count=63 2>/dev/null | sha1sum
dd if=cf03.bin count=63 2>/dev/null | sha1sum

これらのファイルはすべて、最初のパーティションのファイルシステムの直前まで同一であることがわかります(元のスクリプトで同じことを試してください。合計は互いに異なります)。

私のバージョンのスクリプトでは、a.txtファイルを書き込んだ行をコメントアウトしたことに気付くでしょう。私はこれを行いました。ファイルがない場合でもファイルシステムを同一にすることができない場合、それを修正しようとしても意味がありません。そして、これが当てはまります。ファイルがなくてもファイルシステムが異なるため、最初にそれを修正する必要があります。

各イメージのファイルシステムパーティションに対してdumpe2fsを実行し、それをファイルにダンプしてから、ダンプの任意のペアに対してdiffを使用すると、次のようになります。

25c25
< Filesystem created:       Sat Jun 15 07:37:32 2019
---
> Filesystem created:       Sat Jun 15 07:37:40 2019
27c27
< Last write time:          Sat Jun 15 07:37:33 2019
---
> Last write time:          Sat Jun 15 07:37:40 2019
30c30
< Last checked:             Sat Jun 15 07:37:32 2019
---
> Last checked:             Sat Jun 15 07:37:40 2019
37c37
< Directory Hash Seed:      603130ae-82de-4530-9772-f68ae3d6df5f
---
> Directory Hash Seed:      1d9c5af8-a48e-4221-9e70-8fa2ccc6936f

したがって、少なくとも、非常に高いレベルで(この後、最も低いレベルで、より深く進む必要があります。つまり、実際のバイトごと比較)ファイルシステムは、上記の詳細が異なります。最初にそれを回避してください。

マシンで日付を変更しても、タイムスタンプを改ざんして等しくすることはできません。プログラムの実行で制御できる時間のギャップがあるためです。その場合、少なくともファイルシステムの観点から作成するプログラムから、時計をフリーズする必要があります。あなたはそれを掘り下げることができます、しかし私はこれが行く方法ではないと思います、あなたは彼らが彼らのマシンであなたのスクリプトを実行する必要があるとあなたが言ったので:あなたは彼らの時計を台無しにしたくない。したがって、IMHOは、私がdisk signatureで行ったように、ファイルシステム上の正しいバイトを改ざんしている可能性があります。その周りを検索します。

また、Superblock backups...を忘れないでください。ファイルシステムごとに異なるデータが含まれている場合、それらが存在するバイト範囲の違いを伝播します。

最後に、ファイルをコピーするときは、ファイルシステム内のファイルバイトの「配布」を直接制御できないことを覚えておいてください...クローンを作成できない場合は、それを制御する方法を見つける必要があります。あまりにも。

1
matsib.dev

元の質問に対する答えは、 genext2fs というツールです。 -fスイッチを指定すると、同じ入力が与えられた場合、ビットごとに同じ出力が作成されます。これは、作成されたイメージを正しい出力の事前に計算されたmd5sumと比較する独自のテストスイートによって、またはこのテスト(ソースディレクトリ内で実行)によって証明されます。

$ ./genext2fs -f -B 1024 -b 40 -d m4 rootfs.img
$ md5sum rootfs.img
322053a8962acc599eaabb2dfde28783  rootfs.img
$ rm rootfs.img
$ ./genext2fs -f -B 1024 -b 40 -d m4 rootfs.img
$ md5sum rootfs.img
322053a8962acc599eaabb2dfde28783  rootfs.img

結果のイメージをマウントして、その内容が実際にパックされたディレクトリと同じであることを確認できます。

$ Sudo mount rootfs.img /mnt
$ ls -lha /mnt
total 27K
drwxr-xr-x  3 root  root  1.0K Jan  1  1970 .
drwxr-xr-x 25 root  root  4.0K Mar 22 14:56 ..
-rw-r--r--  1 josch josch 1.5K Mar 22 23:05 ac_func_scanf_can_malloc.m4
-rw-r--r--  1 josch josch 2.4K Mar 22 14:24 ac_func_snprintf.m4
drwx------  2 root  root   16K Jan  1  1970 lost+found
$ rmdir /mnt/lost+found
$ diff -rq m4 /mnt
$ echo $?
0

楽しんで!

0
josch