236 Views
August 23, 26
スライド概要
2026年8月22日さくらインターネットさんにて
動画: https://www.youtube.com/watch?v=rPn_-pkNgyY
プログラマー
jus共催 第76回ストレージバイナリシェル芸勉強会 やべえさんいつもjusへのレポートありがとうございます 上田 隆一 2026年8月22日 © 2026 上田隆一 第76回シェル芸勉強会
近況 しぇるげいすきー(招待制シェル芸Misskeyサーバー) ぼちぼち ロボットやさん向けLinuxの本 ストレージのところだいたい書いたので本日還元 第76回シェル芸勉強会 2
Software Designの連載 「魅惑の自作シェルの世界」 8月号でキリが悪いところで終わったので9月号以降のキリが悪い 8月号(45回): 部分文字列展開(前半) 9月号(46回): 部分文字列展開の実装(後半)と文字列の削除の実装(前半) 10月号(47回): パターン削除の実装(後編)とパターン置換の実装(前編) その後の予定: ${A:-unko} とか 第76回シェル芸勉強会 3
シェル芸とは マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる 調査・計算・テキスト処理をCLI端末へのコマンド入力一撃で終わらすこと。あるいは そのときのコマンド入力のこと。 第76回シェル芸勉強会 4
今回の座学と問題 ストレージの解析 出題 ほぼバイナリシェル芸 第76回シェル芸勉強会 5
準備(1/2) USBメモリとかを準備できなかった人へ ラズパイのmicroSDのイメージをダウンロード ファイル名: ubuntu24.04_raspimouse_ros2.img.xz リンクは8月いっぱい有効です この資料はこのイメージにもとづいて作ります 第76回シェル芸勉強会 6
準備(2/2) 展開 $ unxz ubuntu24.04_raspimouse_ros2.img.xz $ sudo losetup -f -P --show ubuntu24.04_raspimouse_ros2.img /dev/loop41 #これがストレージのデバイスファイルの代わり $ ls /dev/loop41* /dev/loop41 /dev/loop41p1 /dev/loop41p2 #ストレージとパーティション $ cd /mnt $ sudo mkdir /mnt/system-boot $ sudo mkdir /mnt/writable $ sudo mount /dev/loop41p1 /mnt/system-boot $ sudo mount /dev/loop41p2 /mnt/writable $ ls /mnt/system-boot/ README bcm2711-rpi-400.dtb.bak boot.scr.bak ... $ ls /mnt/writable/ bin boot etc lib lost+found mnt proc run ... bin.usr-is-merged dev home lib.usr-is-merged media opt root sbin... 第76回シェル芸勉強会 7
ストレージの区画整理(昔話) ストレージ(補助記憶装置) 淡々と2進数を記録する装置 しばらく円盤のものが主流だった HDD、フロッピー、・・・ ストレージの特定の場所へのアクセス方法(当初) CHS: シリンダ番号、ヘッド番号、セクタ番号 問題が起きた(90年代なかば〜) 桁足らん、外側が非効率、円盤じゃねえ・・・ 物理のことはストレージ側でやるわ (図 : LionKimbro, Public domain, via Wikimedia Commons ) 第76回シェル芸勉強会 Track/ Cylinder Sector Heads 8 Heads, 4 Platters 8
ストレージの区画整理(現在) 「セクタ番号」だけ生き残っている Logical Block Addressing(LBA) 1セクタごとにLBA 0、LBA 1、・・・と番号がつく 1セクタのサイズ OSからはたいてい512バイトに見える(論理セクタ) 内部(物理セクタ)は違うかもしれないが今の技術なら変換は容易 物理セクタ: ストレージが一度に読み書きする単位だそうです 正直よくわかりません 第76回シェル芸勉強会 9
問題1 手持ちのストレージ or p.6のイメージについて、あるセクタのデータを取り出して みましょう 例: p.6のイメージの0番目と2048番目と1050624番目 $ sudo fdisk -l /dev/loop41 ディスク /dev/loop41: 14.87 GiB, 15962472448 バイト, 31176704 セクタ 単位: セクタ (1 * 512 = 512 バイト) セクタサイズ (論理 / 物理): 512 バイト / 512 バイト I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト ディスクラベルのタイプ: dos ディスク識別子: 0xfac95764 デバイス 起動 開始位置 終了位置 セクタ サイズ Id タイプ /dev/loop41p1 * 2048 1050623 1048576 512M c W95 FAT32 (LBA) /dev/loop41p2 1050624 31176670 30126047 14.4G 83 Linux 第76回シェル芸勉強会 10
解答例(1/2) 先頭(LBA 0)は head などでよい $ sudo head -c 512 /dev/loop41 | xxd ・・・ずっと0・・・ 000001b0: 0000 0000 0000 0000 6457 c9fa 0000 8000 ・・・ 000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa ........dW...... ..............U. 2048番目 dd を使う $ sudo dd bs=512 if=/dev/loop41 count=1 skip=2048 | xxd 00000000: eb58 906d 6b66 732e 6661 7400 0201 2000 .X.mkfs.fat... . ・・・ パーティションの先頭であることを利用 $ sudo head -c 512 /dev/loop41p1 | xxd 第76回シェル芸勉強会 11
解答例(1/2) 1050624番目 $ sudo dd bs=512 if=/dev/loop41 count=1 skip=1050624 | xxd ・・・ぜんぶ0・・・ $ sudo head -c 512 /dev/loop41p2 | xxd ・・・ぜんぶ0・・・ 第76回シェル芸勉強会 12
ストレージの使い方の情報 大抵のストレージは、ブートに関する情報やパーティションに関する情報が先頭 のほうに書かれている 無いものもある(あとで紹介) MBR方式とGPT方式 MBR(master boot record): 古い方式 ラズパイはまだこれ GPT(GUID partition table)方式 これでよく混乱する場面: Windowsのインストール 第76回シェル芸勉強会 13
3文字の略語多すぎるので整理 セクタの住所の方式: CHSとLBA パーティションの住所の方式: MBRとGPT お気持ち いつもこういう仕事してるなら大丈夫だけど そうでないと急に言われても分かるか generative pre-trained transformerはGPTTにしろ 第76回シェル芸勉強会 14
MBR、GPTの説明の前に・・・ 最近のGUIツールはUSBメモリなど外部ストレージ全体を1パーティションとして 扱うことがある こうなるとMBRもGPTも関係ない LBA 0からいきなりファイルシステム固有のデータが始まる 例 ueda@raspi5:~$ ls /dev/sda* /dev/sda #パーティションがない ueda@raspi5:~$ sudo parted -l モデル: Imation Nano (scsi) ディスク /dev/sda: 8017MB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: loop #ループバックデバイスとして(外部からパーティション情報を与えて)扱う ディスクフラグ: 番号 1 第76回シェル芸勉強会 開始 終了 サイズ ファイルシステム 0.00B 8017MB 8017MB ext4 フラグ 15
MBR方式 最初のセクタにブートやパーティションの情報などが入る レイアウト 最初の446バイト: ブートのためのプログラム タイムスタンプやストレージのシグネチャが入ることも(新しいほ うの規格) 次の16バイトx4: パーティションテーブル 最後の2バイト: ブートシグネチャ( 55aa ) Q2: パーティションテーブルを1行1エントリーに並べてみましょう 第76回シェル芸勉強会 16
Q2解答例 $ sudo head -c 512 /dev/loop41 | tail -c 66 | xxd 00000000: 8000 0120 0c03 d0ff 0008 0000 0000 1000 00000010: 0003 d0ff 8303 d0ff 0008 1000 dfaf cb01 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 00000030: 0000 0000 0000 0000 0000 0000 0000 0000 00000040: 55aa ... ............ ................ ................ ................ U. 読み方 最初の1バイト: ブート可かどうか(0x80が可) 次の3バイト: CHS用(わけわかめ。情報求む) 次の1バイト: タイプ(0x0C: LBA対応FAT32、0x83: ext2以降) 次の3バイト: CHS用(わけわかめ。情報求む) 次の4バイトと最後の4バイト: 開始セクタとセクタ数(リトルエンディアン) 17 第76回シェル芸勉強会 Q3: 上のデータを4バイトずつまとめて10進数にしてください
Q3解答
$ sudo head -c 512 /dev/loop41 | tail -c 66 | xxd -g 4 -e | awk '{for(i=2;i<=5;i++)print strtonum("0x"$i)}' |
xargs -n 4
536936576 4291822348 2048 1048576
4291822336 4291822467 1050624 30126047
0 0 0 0
0 0 0 0
43605 0 0 0
$ sudo fdisk -l /dev/loop41
ディスク /dev/loop41: 14.87 GiB, 15962472448 バイト, 31176704 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0xfac95764
デバイス
起動 開始位置 終了位置
セクタ サイズ Id タイプ
/dev/loop41p1 *
2048 1050623 1048576
512M c W95 FAT32 (LBA)
/dev/loop41p2
1050624 31176670 30126047 14.4G 83 Linux
第76回シェル芸勉強会
18
GPT方式 MBAの様々な制限を撤廃 MBR: 1セクタに情報押し込め GPT: LBA 1〜LBA 33まで使用 確認は fdisk ではなく parted で ueda@x1gen13:~$ sudo parted -l モデル: WD_BLACK SN850X 8000GB (nvme) ディスク /dev/nvme0n1: 8002GB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: gpt #←GPT形式との記述 ディスクフラグ: 番号 1 2 3 4 7 5 6 開始 終了 サイズ ファイルシステム 名前 1049kB 274MB 273MB fat32 EFI system partition 274MB 290MB 16.8MB Microsoft reserved partition 290MB 3771GB 3771GB ntfs Basic data partition 3771GB 3772GB 1024MB ext4 3772GB 7702GB 3930GB ext4 7702GB 7703GB 1024MB ext4 7703GB 8002GB 299GB ext4 第76回シェル芸勉強会 フラグ boot, hidden, esp msftres msftdata 19
GPTのLBA 0(protective MBR)とLBA 1(GPTヘッダ)
LBA 0: MBRと思って見に来たプログラムを追い返す
最後に 55aa (MBRとも解釈可能)
最初のパーティションのところに 0000 0200 eeff ...
ee がGPTを表す(=GPTだから出直せ)
他のデータは0
LBA 1: ここにGPTの情報
ueda@x1gen13:~$ sudo head -c 1024 /dev/nvme0n1 | tail -c 512 | xxd
00000000: 4546 4920 5041 5254 0000 0100 5c00 0000 EFI PART....\...
00000010: 547d a131 0000 0000 0100 0000 0000 0000 T}.1............
00000020: af2a 81a3 0300 0000 2200 0000 0000 0000 .*......".......
00000030: 8e2a 81a3 0300 0000 2628 b6ba 4def c142 .*......&(..M..B
00000040: 902c 1c3b a14b 287b 0200 0000 0000 0000 .,.;.K({........
00000050: 8000 0000 8000 0000 b30e 83da 0000 0000 ................
(以後0)
第76回シェル芸勉強会
20
GPTヘッダ(1/2: GPTの構造に関するもの) データ 何のデータか 4546 4920 5041 5254 文字列「EFI PART」 、後半: サイズ(92byte) 0000 0100 5c00 0000 前半: ヘッダのバージョン(1.0) 547d a131 0000 0000 前半: このヘッダのチェックサム、後半: 予約領域 0100 0000 0000 0000 このLBAの番号(LBA 1) af2a 81a3 0300 0000 セカンダリGPTヘッダの場所(LBA 15628053167) セカンダリGPT: 要はバックアップ (続く) 第76回シェル芸勉強会 21
GPTヘッダ(2/2: ほぼパーティションに関するもの) データ 何のデータか 2200 0000 0000 0000 先頭のパーティションの先頭(LBA 34) 8e2a 81a3 0300 0000 最後のパーティションの末尾(LBA 15628053134) 2628 b6ba 4def c142 このSSDのGUID(bab62826-ef4d-42c1-902c902c 1c3b a14b 287b 1c3ba14b287b) 0200 0000 0000 0000 パーティションテーブルの先頭のセクタ(LBA 2) 前半 : パーティションエントリの数( = パーティション数) 8000 0000 8000 0000 (128個)、後半: 各エントリの長さ(128byte) b30e 83da パーティションテーブルのチェックサム 補足 : セカンダリ GPT ヘッダの前のセクタにバックアップあり 22 第76回シェル芸勉強会
GPTのパーティションエントリの例(LBA 2の先頭128バイト) ueda@x1gen13:~$ sudo head -c $((1024 + 128)) /dev/nvme0n1 | tail -c 128 | xxd 00000000: 2873 2ac1 1ff8 d211 ba4b 00a0 c93e c93b (s*......K...>.; #タイプを表すGUID 00000010: b0d6 1ca2 a5c5 1a4d b4b4 d8f7 79ee e40e .......M....y... #このpartitionのGUID 00000020: 0008 0000 0000 0000 ff27 0800 0000 0000 .........'...... #開始/最後のLBA 00000030: 0100 0000 0000 0000 4500 4600 4900 2000 ........E.F.I. . #後半〜: 00000040: 7300 7900 7300 7400 6500 6d00 2000 7000 s.y.s.t.e.m. .p. # partitionの名前 00000050: 6100 7200 7400 6900 7400 6900 6f00 6e00 a.r.t.i.t.i.o.n. # Windowsのもの 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ # なのでUTF-16 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 補足 1行目のタイプ: EFIシステムパーティション 00000030の行の前半: 最初の 0x01 の下3ビットがフラグであとは予約領域 最初の3ビット(下から 1 、 0 、 0 ): パーティションいじるなという警 告、No Block IO Protocol、Legacy BIOS Bootable 第76回シェル芸勉強会 23
各パーティション 各パーティションの中身はMBR形式でもGPT形式でも同じ それぞれのファイルシステムの決まりでデータが並んでいる 以後はext4だけ扱います 第76回シェル芸勉強会 24
ext4 基本的な構造 数セクタごとにまとめてブロックを定義 標準は8セクタ(4,096バイト) ブロックをまとめたブロックグループが構成される $ sudo dumpe2fs /dev/loop41p2 | grep グループ dumpe2fs 1.47.2 (1-Jan-2025) グループ 0: (ブロック 0-32767) csum 0x7c76 [ITABLE_ZEROED] グループ 1: (ブロック 32768-65535) csum 0xf287 [ITABLE_ZEROED] グループ 2: (ブロック 65536-98303) csum 0x4afa [ITABLE_ZEROED] ・・・ ファイル/ディレクトリをiノード単位で管理 iノードからデータのあるブロックへのポインタを張る 第76回シェル芸勉強会 25
ブロックの役割分担 種別 説明 スーパーブロック 当該するPTに関する情報を保持 GDT(group descriptor table) 各BGのグループディスクリプタの配列 予約GDTブロック PT拡張に備えたBGの予約領域 ブロックビットマップ BG内のブロックの使用状況のフラグの列挙 iノードビットマップ BG内のiノードの使用状況のフラグの列挙 iノードテーブル iノードの格納用のブロック データブロック データ(つまりファイルの内容)の記録用 PT、BG: それぞれパーティション、ブロックグループの略 第76回シェル芸勉強会 26
スーパーブロック パーティション全体に関するデータが記述されている iノード数、ブロック数、空きiノード数、空きブロック数・・・ 0番目のブロック(グループ 0の先頭のブロック)の1024〜2047バイト目 ext4のルール(partition boot record)で先頭の1024バイトは空けてある 普段の確認方法 dumpe2fs -h を使用 第76回シェル芸勉強会 27
スーパーブロックの観察 レイアウト(kernel.org で確認可能) $ sudo head -c 4096 /dev/loop41p2 | tail -c $((1024*3)) | head -c 1024 | xxd 00000000: f010 0e00 fb75 3900 d269 0200 4150 1b00 .....u9..i..AP.. #ここらへんは 00000010: 29bc 0a00 0000 0000 0200 0000 0200 0000 )............... #ブロックと 00000020: 0080 0000 0080 0000 501f 0000 f29d 776a ........P.....wj #iノードの情報(f2...はマウントの時刻) 00000030: f2d0 776a 1500 ffff 53ef 0100 0100 0000 ..wj....S....... #f2...: 書き込み時刻、53ef: マジックNo. 00000040: 594d b067 0000 0000 0000 0000 0100 0000 YM.g............ 00000050: 0000 0000 0b00 0000 0001 0000 3c00 0000 ............<... 00000060: c202 0000 6b04 0000 5624 f709 7cae 4bfd ....k...V$..|.K. 00000070: 81cd 3c8f af14 7752 7772 6974 6162 6c65 ..<...wRwritable #ボリュームラベル 00000080: 0000 0000 0000 0000 2f00 0000 0000 0000 ......../....... #マウント場所 ・・・ 000003f0: 0000 0000 0000 0000 0000 0000 cdcb 681d ..............h. #チェックサム 第76回シェル芸勉強会 28
スーパーブロックのバックアップ 番目( )の先頭のグループに存在 $ sudo dumpe2fs /dev/loop41p2 | grep -B1 バックアップ dumpe2fs 1.47.2 (1-Jan-2025) グループ 1: (ブロック 32768-65535) csum 0xf287 [ITABLE_ZEROED] バックアップ superblock at 32768, Group descriptors at 32769-32770 -グループ 3: (ブロック 98304-131071) csum 0x3c23 [ITABLE_ZEROED] バックアップ superblock at 98304, Group descriptors at 98305-98306 -グループ 5: (ブロック 163840-196607) csum 0xe013 [ITABLE_ZEROED] バックアップ superblock at 163840, Group descriptors at 163841-163842 -- Q4: バイナリレベルで確認してみましょう ( )に当該する数字を列挙( まででよいです) 列挙した数字のグループの先頭のブロックの先頭のバイナリを比較 第76回シェル芸勉強会 29
解答例
$ seq 0 4 | awk '{print 3**$1; print 5**$1; print 7**$1}' | sort -nu | awk '$1<=81' |
while read sb ; do sudo dd if=/dev/loop41p2 bs=4096 skip=$((32768*sb)) count=1 2> /dev/null | xxd |
head -n 1 ; done
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
00000000: f010 0e00 fb75 3900 d269 0200 f324 3000 .....u9..i...$0.
第76回シェル芸勉強会
30
GDT 割愛 第76回シェル芸勉強会 31
iノード/ブロックビットマップ 使っているiノード/ブロックを記録 使っていると1、使っていないと0 当該のブロックグループにないことがある HDDのため? $ sudo dumpe2fs /dev/loop41p2 | grep -A6 "グループ 8:" dumpe2fs 1.47.2 (1-Jan-2025) グループ 8: (ブロック 262144-294911) csum 0x842c [ITABLE_ZEROED] Block bitmap at 416 (bg #0 + 416), csum 0x1221c4d1 #このグループにいない Inode bitmap at 432 (bg #0 + 432), csum 0x4c718f9f #これも Inode table at 4448-4948 (bg #0 + 4448) 296 free blocks, 31 free inodes, 122 directories Free blocks: 263494-263519, 263584-263585, 263587-263593, 263602, ... Free inodes: 68613, 68690, 68696, 68703, 68712, 68811, 69910, 69915, ... 第76回シェル芸勉強会 32
Q5 前ページのグループ8について、 dumpe2fs の Free blocks/inodes が本当かビッ トマップを解析して確かめてみましょう。 ブロックビットマップに関する補足 このグループの最初のブロックがビットマップの最初のビットに対応(ブ ロックの種別関係なし) iノードビットマップに関する補足 グループあたりのiノードの数は 個 $ sudo dumpe2fs -h /dev/loop41p2 2> /dev/null | grep -i "inodes per" Inodes per group: 8016 1ノード番号は1番から 第76回シェル芸勉強会 33
解答例
ブロックビットマップ
$ sudo dd if=/dev/loop41p2 bs=4096 skip=416 count=1 2> /dev/null | xxd -b |
awk '{for(i=2;i<=7;i++)print $i}' | awk -F '' '{for(i=8;i>=1;i--)print $i}' |
nl -v 262144 | awk '!$2{print $1"-"}$2{print "@"}' | tr -d \\n | sed 's/@@*/ /g' |
sed -E 's/([0-9]+)-[^ ]*-([0-9]+)/\1-\2/g' | sed 's/- /, /g' | sed 's/---*//g'
263494-263519, 263584-263585, 263587-263593, 263602, 263687, 294038, 294046,
294055-294063, 294065-294079, 294141-294143, 294240-294294, 294320-294321, 294430-294431,
294528-294655, 294684-294687, 294720-294745, 294747-294750, 294752-294756, 294908-294911,
iノードビットマップ
$ sudo dd if=/dev/loop41p2 bs=4096 skip=432 count=1 2> /dev/null | xxd -b |
awk '{for(i=2;i<=7;i++)print $i}' | awk -F '' '{for(i=8;i>=1;i--)print $i}' | nl -v $((8016*8+1)) |
awk '!$2{print $1"-"}$2{print "@"}' | tr -d \\n | sed 's/@@*/ /g' |
sed -E 's/([0-9]+)-[^ ]*-([0-9]+)/\1-\2/g' | sed 's/- /, /g' | sed 's/---*//g'
68613, 68690, 68696, 68703, 68712, 68811, 69910, 69915, 69920, 69931, 70143,
70145, 70182, 70187, 70635-70642, 70644-70645, 70649, 70739, 70873, 70876, 71954-71955, 71957, ,
第76回シェル芸勉強会
34
iノードテーブル/iノードテーブルエントリ iノードの情報 iノードテーブル専用のブロックにiノードテーブルエントリとして並ぶ 先述のように例のイメージではグループあたりのiノードの数は 個 (注意: イレギュラーな値) 256バイト。1つのブロックに16個 例のイメージでは501個のブロックを使う iノードテーブルも当該のブロックグループにいないことがある $ sudo dumpe2fs /dev/loop41p2 2> /dev/null | grep -A3 "グループ 8:" グループ 8: (ブロック 262144-294911) csum 0x842c [ITABLE_ZEROED] Block bitmap at 416 (bg #0 + 416), csum 0x1221c4d1 Inode bitmap at 432 (bg #0 + 432), csum 0x4c718f9f Inode table at 4448-4948 (bg #0 + 4448) #グループ 0にある 第76回シェル芸勉強会 35
iノードテーブルエントリの例(2番のiノード) 2番: ルートディレクトリのもの $ sudo dd if=/dev/loop41p2 bs=4096 skip=440 count=1 2> /dev/null | head -c 512 | tail -c 256 | xxd 00000000: ed41 0000 0010 0000 0a9e 776a c947 4b69 .A........wj.GKi 00000010: c947 4b69 0000 0000 0000 1600 0800 0000 .GKi............ 00000020: 0000 0800 1200 0000 0af3 0100 0400 0000 ................ 00000030: 0000 0000 0000 0000 0100 0000 0821 0000 .............!.. ・・・0が並ぶ・・・ 00000070: 0000 0000 0000 0000 0000 0000 1845 0000 .............E.. 00000080: 2000 acd5 7841 c8e3 7841 c8e3 6c7e f51b ...xA..xA..l~.. 00000090: 594d b067 0000 0000 0000 0000 0000 0000 YM.g............ ・・・0が並ぶ・・・ ファイルの構造として一番重要なのは0x28から12バイトのエクステントヘッダー 0af3 0100 0400 0000 0000 0000 2バイトずつ、マジックナンバー( 0xF30A )、このヘッダにあるエントリ (データへのポインタ)数、最大のエントリ数、木構造の深さ エクステントって何という話はあとで 36 第76回シェル芸勉強会 (次ページに続く)
iノードテーブルエントリの例(2番のiノード。続き) 次の12バイト( 0000 0000 0100 0000 0821 0000 ): エクステント 最初の4バイト+2バイト: ファイル内での開始ブロックの位置と個数 次の2バイト: 開始ブロック番号の上位の桁 最後の4バイトが下位の桁( 0x2108 =8,456がデータブロックの位置) 次のように8,456番目のブロックにディレクトリのデータがある $ sudo dd if=/dev/loop41p2 bs=4096 skip=8456 count=1 2> /dev/null | xxd [sudo: authenticate] パスワード: 00000000: 0200 0000 0c00 0102 2e00 0000 0200 0000 ................ 00000010: 0c00 0202 2e2e 0000 0b00 0000 1400 0a02 ................ 00000020: 6c6f 7374 2b66 6f75 6e64 0000 0c00 0000 lost+found...... #ディレクトリ名が見える 00000030: 0c00 0307 6269 6e00 0d00 0000 1c00 1102 ....bin......... 00000040: 6269 6e2e 7573 722d 6973 2d6d 6572 6765 bin.usr-is-merge 00000050: 6400 0000 0e00 0000 0c00 0402 626f 6f74 d...........boot ・・・ ディレクトリの数だけディレクトリエントリがある 第76回シェル芸勉強会 37
ディレクトリエントリ iノードと名前を結びつける可変長のデータ 位置 何 最初の4バイト iノード番号 次の2バイト このエントリの長さ 次の1バイト ファイル/ディレクトリ名の長さ 次の1バイト ファイル/ディレクトリのタイプ 以後 ファイル/ディレクトリ名 例: 最初のエントリ 0200 0000 0c00 0102 2e00 0000 2番で1文字( 0x2e =カレントディレクトリの「 . 」) Q6: 前ページのバイナリを1エントリ1行にしてください 第76回シェル芸勉強会 38
解答例
$ sudo dd if=/dev/loop41p2 bs=4096 skip=8456 count=1 2> /dev/null | xxd -p | tr -d \\n | sed 's/../&\n/g' |
awk 'BEGIN{ll=12}{n+=1; if(n==5){ll=strtonum("0x"$1)};if(n==6){ll+=256*strtonum("0x"$1)};
printf $0" "; if(n==ll){n=0;ll=12;print ""}}' |
awk '{for(i=1;i<=7;i++){printf $i" "}for(i=8;i<=NF;i++){a="0x"$i;printf("%c", strtonum(a))}print ""}'
02 00 00 00 0c 00 01 .
02 00 00 00 0c 00 02 ..
0b 00 00 00 14 00 0a lost+found
0c 00 00 00 0c 00 03 bin
0d 00 00 00 1c 00 11 bin.usr-is-merged
0e 00 00 00 0c 00 04 boot
17 00 00 00 0c 00 03 dev
18 00 00 00 0c 00 03 etc
7f 06 00 00 0c 00 04 home
80 06 00 00 0c 00 03 lib
81 06 00 00 1c 00 11 lib.usr-is-merged
82 06 00 00 10 00 05 media
83 06 00 00 0c 00 03 mnt
84 06 00 00 0c 00 03 opt
85 06 00 00 0c 00 04 proc
86 06 00 00 0c 00 04 root
8a 06 00 00 0c 00 03 run
8b 06 00 00 0c 00 04 sbin
8c 06 00 00 1c 00 12 sbin.usr-is-merged
8d 06 00 00 0c 00 04 snap
92 06 00 00 0c 00 03 srv
93 06 00 00 0c 00 03 sys
94 06 00 00 0c 00 03 tmp
95 06 00 00 0c 00 03 usr
1c df 00 00 98 0e 03 var
00 00 00 00 0c 00 00 ÞõWO9
第76回シェル芸勉強会
39
ファイルの構造 エクステントツリーという構造で収められている 雑解説 iノードテーブルエントリに根がある木構造 根: エクステントヘッダ その下はすべてデータブロックに構造とデータが入る 葉の部分: エクステント データを入れるブロックの範囲が書いてある 枝の部分: エクステントインデックス 下の層がどのデータブロックにあるか書いてある ヘッダ、エクステント、インデックスは全部12バイト レイアウト(kernel.org) 第76回シェル芸勉強会 40
Q7 パーティションに大きなファイルを置いて解析してみましょう もちろんワンライナーでなくてかまいません 例 ### loop41p2をマウントしましょう ### ueda@x1gen13:/run/media/ueda/writable/home/ubuntu$ seq 500000000 > a ueda@x1gen13:/run/media/ueda/writable/home/ubuntu$ ls -altrhi a 131025 -rw-rw-r-- 1 ueda ueda 4.6G Aug 16 14:38 a #このiノード番号からiノードテーブルエントリを解析 第76回シェル芸勉強会 41
解答例 ### ブロックグループ ### $ echo $(( (131025-1) /8016)) 16 ### ブロックグループで何個目か ### $ echo $(( 131025 - 16*8016 - 1)) 2768 #0から数えて2768番目 ### グループ16のiノードテーブルのうち何番目のブロックにあるか ### $ echo $((2768 / 16 )) 173 ### そのブロックの何個目にあるか ### $ echo $((2768 % 16 )) 0 #先頭 ### 検算 ### $ echo $(( 8016*16 + 173*16 + 1)) 131025 第76回シェル芸勉強会 42
解答例(続き) ブロックグループ16のiノードテーブル グループ 16: (ブロック 524288-557055) csum 0x0dc0 [ITABLE_ZEROED] Block bitmap at 524288 (+0), csum 0x9dff1dbe Inode bitmap at 524298 (+10), csum 0x403fe007 Inode table at 524308-524808 (+20) #524308から開始 iノードテーブルエントリの抽出 $ sudo dd if=/dev/loop41p2 bs=4096 skip=$((524308+173)) count=1 2> /dev/null | head -c 128 | xxd 00000000: b481 e803 4286 6623 c84c 816a ee4c 816a ....B.f#.L.j.L.j #4286 6623がサイズの下の桁 00000010: ee4c 816a 0000 0000 e803 0100 88b4 9100 .L.j............ 00000020: 0000 0800 b729 0000 0af3 0100 0400 0200 .....).......... 00000030: 0000 0000 0000 0000 4882 1800 0000 0200 ........H....... 00000040: 4d3c 0000 f100 3400 0000 0200 1463 0000 M<....4......c.. 00000050: 3d82 1800 0000 0200 cb7e 0000 3e82 1800 =........~..>... 00000060: 0000 0200 6735 50c1 0000 0000 0100 0000 ....g5P......... #0100 0000がサイズの上の桁 00000070: 0000 0000 0000 0000 0000 0000 314f 0000 ............1O.. ### ファイルのサイズを確認しましょう ### $ echo $((0x0123668642)) 4888888898 ueda@x1gen13:/run/media/ueda/writable/home/ubuntu ls -l a -rw-rw-r-- 1 ueda ueda 4888888898 Aug 16 14:38 a 第76回シェル芸勉強会 43
解答例(続き) エクステントヘッダ: 0af3 0100 0400 0200 0000 0000 深さ2、エントリ1つ エクステントインデックス(木構造の枝): 1つめ: 0000 0000 4882 1800 0000 0200 ファイルの中で0番目からのブロックを担当 0x188248 番が下の層 第76回シェル芸勉強会 44
解答例(続き) $ sudo dd if=/dev/loop41p2 bs=4096 skip=$((0x188248)) count=1 2> /dev/null | xxd -ps | tr -d \\n | fold -w 24 | sed 's/..../& /g' 0af3 2700 5401 0100 0000 0000 #エクステントヘッダー 0000 0000 3882 1800 0000 0200 #以下エクステントインデックス(この行: 0x188238が下の層) 4d3c 0000 f100 3400 0000 0200 1463 0000 3d82 1800 0000 0200 cb7e 0000 3e82 1800 0000 0200 ・・・ $ sudo dd if=/dev/loop41p2 bs=4096 skip=$((0x188238)) count=1 2> /dev/null | xxd -ps | tr -d \\n | fold -w 24 | sed 's/..../& /g' 0af3 5401 5401 0000 0000 0000 #エクステントヘッダー 0000 0000 3f00 0000 c1e3 0200 #以下エクステント(この行: 0x2e3c1から0x3f個がデータのブロック) 3f00 0000 3e00 0000 c28f 0200 7d00 0000 3c00 0000 c48a 0200 ・・・ ### seqの書き出しを発見!### $ sudo dd if=/dev/loop41p2 bs=4096 skip=$((0x2e3c1)) count=1 2> /dev/null | head 1 2 3 4 5 6 7 8 9 10 ・・・ 第76回シェル芸勉強会 45
まとめ ストレージの構造、ext4の構造を調べた 3時間じゃ無理 今日のテクニックと今日使ってないコマンドでいろいろ解析してみましょう 消したファイルが復活できるようになるかも 第76回シェル芸勉強会 46