fsdb
2010 ワールドカップの総括をしよう。
たこのパウル君である。8回予想をして全部的中させちゃったそうだ。確率的には何ら 問題は無く、1/256である。4年に1回のワールドカップだから、まさに、1000年に1回の 快挙。数学的には納得出来るんだけど、やはりホェーである。
今回優勝したスペインは、7戦全勝だったとか。勝ち抜き戦で、葉っぱから根に至る訳だが、 この軌跡を見ると、2分木と思っちゃうおいらは、コンピュータサイエンスに染まっているんだ なあ。つくづくそう思いますよ。
たこと言えば昔、『たこは食べるものじゃなくて、育てるものだ』と、おごちゃんが言ってたな。 そのせい(かどうかは知らないけど)で、Linuxな人が増殖しちゃって、今じゃ、うじゃうじゃ いるね。
fsdb その前に
FreeBSDのブートプロセスをみる を、みるしてる。(我ながら、変な日本語だわい!)勿論 ただ見るだけじゃつまらないので、解説されてる所以外も読みながらね。
で、boot2の中で、いよいよカーネルをロードするくだりがあるんだけど、そのためには、指定された ファイル名(/boot/kernel/kernel)から、そのiノード番号を検出する事が必要になる。 lookupは、boot/common/ufsread.cに有った。
これを理解するには、fsを理解せんとな。そこで、fsdbですよ。man fsdbすると
FSDB(8) FreeBSD システム管理者マニュアル FSDB(8) 名称 fsdb - FFS デバッグ/編集ツール : 警告 このツールは最大限に注意を払って使って下さい。 fsck(8) を使っても修復でき ないほど FFS ファイルシステムを壊す恐れがあります。
なんて言う脅し文句が書いてある。まあ、-rを付けて、読み取り専用で起動すれば問題は無い と思うけど。。。 折角なので、自前のファイルシステムを作って、その上で思う存分暴れてみる事にする。
[sakae@cdr ~]$ dd if=/dev/zero of=mydisk bs=10k count=1000 1000+0 records in 1000+0 records out 10240000 bytes transferred in 0.800050 secs (12799200 bytes/sec) [root@cdr /usr/home/sakae]# mdconfig -a -t vnode -f mydisk -u 0 [root@cdr /usr/home/sakae]# bsdlabel -w md0 auto [root@cdr /usr/home/sakae]# newfs md0a /dev/md0a: 9.8MB (19984 sectors) block size 16384, fragment size 2048 using 4 cylinder groups of 2.45MB, 157 blks, 320 inodes. super-block backups (for fsck -b #) at: 160, 5184, 10208, 15232 [root@cdr /usr/home/sakae]# bsdlabel md0 # /dev/md0: 8 partitions: # size offset fstype [fsize bsize bps/cpg] a: 19984 16 unused 0 0 c: 20000 0 unused 0 0 # "raw" part, don't edit [root@cdr /usr/home/sakae]# ffsinfo -l 1 -o md0a.log /dev/md0a
まずdiskの元を作ります。気分はVMWarePlayerみたいだな。そしてそのdiskをデバイスに割付ます。 ラベルを書いてから、地均ししてあげました。最後に、スーパーブロックのログを取っておきます。
[root@cdr /usr/home/sakae]# mkdir /a [root@cdr /usr/home/sakae]# mount /dev/md0a /a [root@cdr /usr/home/sakae]# chmod 777 /a [root@cdr /usr/home/sakae]# df -hi Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/ad0s1a 496M 310M 146M 68% 3.1k 63k 5% / : /dev/md0a 9.2M 4.0K 8.5M 0% 2 1.3k 0% /a
某OSのように、マウントポイントを用意して(昔、この非常用マウントポイントに大いに助け られましたよ。)マウントし、誰でも書き込めるようにしておきます。どんな具合になってるか 確認しました。まだ、空ですね。
[sakae@cdr ~]$ cd /a [sakae@cdr /a]$ cp /sys/boot/common/ufsread.c . [sakae@cdr /a]$ mkdir hoge [sakae@cdr /a]$ cd hoge [sakae@cdr /a/hoge]$ ln ../ufsread.c backup-ufsread.c [sakae@cdr /a/hoge]$ ln -s ../ufsread.c link-ufsread.c [sakae@cdr /a/hoge]$ dd if=/dev/zero of=big bs=1k count=5000 5000+0 records in 5000+0 records out 5120000 bytes transferred in 0.269849 secs (18973570 bytes/sec) [sakae@cdr /a/hoge]$ df -hi /a Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/md0a 9.2M 4.9M 3.6M 58% 6 1.3k 0% /a
適当にファイルやディレクトリィーを作ってみました。そして、使われ具合を確認。さあ、これで 準備完了。
fsdb してみる。
[sakae@cdr /a/hoge]$ fsdb /dev/md0a ** /dev/md0a (NO WRITE) Editing file system `/dev/md0a' Last Mounted on /a current inode: directory I=2 MODE=40777 SIZE=512 BTIME=Jul 14 08:32:31 2010 [0 nsec] MTIME=Jul 14 08:53:46 2010 [0 nsec] CTIME=Jul 14 08:53:46 2010 [0 nsec] ATIME=Jul 14 09:07:42 2010 [0 nsec] OWNER=root GRP=wheel LINKCNT=4 FLAGS=0 BLKCNT=4 GEN=3cba0563 fsdb (inum: 2)> help Commands are: command min args max args what help 0 0 Print out help ? 0 0 Print out help inode 1 1 Set active inode to INUM clri 1 1 Clear inode INUM lookup 1 1 Set active inode by looking up NAME cd 1 1 Set active inode by looking up NAME back 0 0 Go to previous active inode active 0 0 Print active inode print 0 0 Print active inode blocks 0 0 Print block numbers of active inode uplink 0 0 Increment link count downlink 0 0 Decrement link count linkcount 1 1 Set link count to COUNT findblk 1 32 Find inode owning disk block(s) ls 0 0 List current inode as directory rm 1 1 Remove NAME from current inode directory del 1 1 Remove NAME from current inode directory ln 2 2 Hardlink INO into current inode directory as NAME chinum 2 2 Change dir entry number INDEX to INUM chname 2 2 Change dir entry number INDEX to NAME chtype 1 1 Change type of current inode to TYPE chmod 1 1 Change mode of current inode to MODE chlen 1 1 Change length of current inode to LENGTH chown 1 1 Change owner of current inode to OWNER chgrp 1 1 Change group of current inode to GROUP chflags 1 1 Change flags of current inode to FLAGS chgen 1 1 Change generation number of current inode to GEN btime 1 1 Change btime of current inode to BTIME mtime 1 1 Change mtime of current inode to MTIME ctime 1 1 Change ctime of current inode to CTIME atime 1 1 Change atime of current inode to ATIME quit 0 0 Exit q 0 0 Exit
fsdbを起動してHelpを出してみました。いろんなコマンドが用意されてて、とても全部は 使いきれそうにありません。まずは、簡単に
fsdb (inum: 2)> ls slot 0 ino 2 reclen 12: directory, `.' slot 1 ino 2 reclen 12: directory, `..' slot 2 ino 3 reclen 16: directory, `.snap' slot 3 ino 4 reclen 20: regular, `ufsread.c' slot 4 ino 320 reclen 452: directory, `hoge' fsdb (inum: 2)> cd hoge component `hoge': current inode: directory I=320 MODE=40755 SIZE=512 BTIME=Jul 14 08:53:40 2010 [0 nsec] MTIME=Jul 14 08:57:00 2010 [0 nsec] CTIME=Jul 14 08:57:00 2010 [0 nsec] ATIME=Jul 14 09:08:46 2010 [0 nsec] OWNER=sakae GRP=wheel LINKCNT=2 FLAGS=0 BLKCNT=4 GEN=6c0e1acc fsdb (inum: 320)> ls slot 0 ino 320 reclen 12: directory, `.' slot 1 ino 2 reclen 12: directory, `..' slot 2 ino 4 reclen 28: regular, `backup-ufsread.c' slot 3 ino 321 reclen 24: symlink, `link-ufsread.c' slot 4 ino 322 reclen 436: regular, `big'
lsとcdを使ってみました。プロンプトに出てる(inum: 2)と言うのが、現在の対象iノードの ようです。現在、iノードは、2,3,4,320,321,322と使われていて、これは先にdfした時に出てた iノードの消費数と一致してます。(当たり前か)
fsdb (inum: 320)> inode 4 current inode: regular file I=4 MODE=100644 SIZE=7997 BTIME=Jul 14 08:52:47 2010 [0 nsec] MTIME=Jul 14 08:52:47 2010 [0 nsec] CTIME=Jul 14 08:55:07 2010 [0 nsec] ATIME=Jul 14 08:52:47 2010 [0 nsec] OWNER=sakae GRP=wheel LINKCNT=2 FLAGS=0 BLKCNT=10 GEN=ffffffffaa01e68e fsdb (inum: 4)> blocks Blocks for inode 4: Direct blocks: 97 (4 frags)
iノード番号4は、ufsread.cとbackup-ufsread.cで共有化されてます。LINKCNT=2 ってのでも 確認出来ますね。
fsdb (inum: 4)> inode 322 current inode: regular file I=322 MODE=100644 SIZE=5120000 BTIME=Jul 14 08:56:51 2010 [0 nsec] MTIME=Jul 14 08:56:51 2010 [0 nsec] CTIME=Jul 14 08:56:51 2010 [0 nsec] ATIME=Jul 14 08:56:51 2010 [0 nsec] OWNER=sakae GRP=wheel LINKCNT=1 FLAGS=0 BLKCNT=2740 GEN=ffffffff8428f68e fsdb (inum: 322)> blocks Blocks for inode 322: Direct blocks: 1360, 1368, 1376, 1384, 1392, 1400, 1408, 1416, 1456, 1464, 1472, 1480 Indirect blocks: 1488, 1496, 1504, 1512, 1520, 1528, 1536, 1544, 1552, 1560, 1568, 1576, 1584, : 3952, 3960, 3968, 3976, 3984, 3992, 4000, 4008, 4016, 4024, 4032, 4040, 4048, 4056, 4064,
今度は大きなファイルがどうなっているか調べてみました。iノードに直接格納出来るブロック数は 最大12個までなので、それ以上の場合は間接参照になります。一つのブロックが今回は2kに なってますから(newfsする時に変更出来る)それが、2740個も使われてファイルbigの 内容が格納されている事になります。
fsdb (inum: 320)> lookup link-ufsread.c component `link-ufsread.c': current inode: symlink to `../ufsread.c' I=321 MODE=120755 SIZE=12 BTIME=Jul 14 08:55:50 2010 [0 nsec] MTIME=Jul 14 08:55:50 2010 [0 nsec] CTIME=Jul 14 08:55:50 2010 [0 nsec] ATIME=Jul 14 08:55:50 2010 [0 nsec] OWNER=sakae GRP=wheel LINKCNT=1 FLAGS=0 BLKCNT=0 GEN=ffffffffc80466d1 fsdb (inum: 321)> blocks Blocks for inode 321: Direct blocks: 7310032029065621038 (1 frag), 1663984737
今度は、hoge(dir)の中から、名前でiノードを探してみました。リンクファイルですので ブロックを消費していません。
fsdb (inum: 321)> q SETTING DIRTY FLAG IN READ_ONLY MODE *** FILE SYSTEM MARKED DIRTY *** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE *** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload
終了した時、文句を言われてしまいましたので、一応下記を実行しておきました。
[sakae@cdr ~]$ sudo fsck -t ufs /dev/md0a ** /dev/md0a ** Last Mounted on /a ** Phase 1 - Check Blocks and Sizes ** Phase 2 - Check Pathnames ** Phase 3 - Check Connectivity ** Phase 4 - Check Reference Counts ** Phase 5 - Check Cyl groups 6 files, 2519 used, 2212 free (28 frags, 273 blocks, 0.6% fragmentation) ***** FILE SYSTEM IS CLEAN ***** [sakae@cdr ~]$ sudo mount /dev/md0a /a
dirはどうなってるん?
dirは、iノード番号とファイル名を書いたファイルになってます。
[sakae@cdr /a]$ cat hoge >dir-hoge [sakae@cdr /a]$ emacs dir-hoge
こんな事をすると、emacsは必死になっ、豆腐文字ならぬ、"^@" を健気にも表示してくれます。 そう、バイナリーで0x00なんですねぇ。emacsを、hex-editorに変身させましょう。それには、 M-x hexl-modeを指定します。
87654321 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789abcdef 00000000: 4001 0000 0c00 0401 2e00 0000 0200 0000 @............... 00000010: 0c00 0402 2e2e 0000 0400 0000 1c00 0810 ................ 00000020: 6261 636b 7570 2d75 6673 7265 6164 2e63 backup-ufsread.c 00000030: 006b c2d1 4101 0000 1800 0a0e 6c69 6e6b .k..A.......link 00000040: 2d75 6673 7265 6164 2e63 0000 4201 0000 -ufsread.c..B... 00000050: b401 0803 6269 6700 0000 0000 0000 0000 ....big......... 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ :
まあ、こんな無駄をしなくても、FreeBSDなら、hd hoge で済んじゃいますが、ばいなりあんを 目指すおいらは、こういう事も知っておこうと思いまして。。。(バイリンガルもベジタリアンも 駄目そうなので、ちょっとラベル付けしたい為)
ものの本によると、iノードとファイル名の対応は、ディレクトリィエントリと言うそうで 、1エントリーに付き
iノード番号 (4バイト) エントリー長 (2バイト) ファイルタイプ(1バイト) ファイル名長 (1バイト) ファイル名 (4の倍数で余った所は0x00で埋められる)
エントリー長が入っているのは、検索を早く行うためだそうだ。(要するに、リンクリスト なんだな。だから、一つのdirの中に数万と言うファイルを作るなと。) また、ファイルタイプの主なものは、
4 ディレクトリィー 8 普通のファイル 10 シンボリック・ファイル
と、なっている。もっと正確には、/usr/include/ufs/ufs/dir.hを参照知る。
/* * File types */ #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 #define DT_WHT 14
又は、sys/dirent.hにも、説明があるかな。
悪戯してしっぺ返しを喰らう
ufsread.cの中で、lookupが定義されてたので、こやつを使って
main(){ printf("kernel inode = %d\n", (int)lookup("/boot/kernel/kernel")); }
こんな事が出来るかなと、エラーと格闘しながら切り貼りしてたんだ。いい所まで 行ったんだが、最後でこんなエラーが。。
[sakae@cdr /a]$ cc ufsread.c /var/tmp//ccYqYst7.o(.text+0x41): In function `dskread': : undefined reference to `drvread' /var/tmp//ccYqYst7.o(.text+0x14b): In function `dskread': : undefined reference to `drvread' /var/tmp//ccYqYst7.o(.text+0x280): In function `dskread': : undefined reference to `drvread'
問題は、drvreadが足りないよと言うあれである。何処に有るかと探したら、boot2.cに 収録されたんだが、それを見ると、あの忌まわしい v86が出てきたよ。
drvread(void *buf, unsigned lba, unsigned nblk) { static unsigned c = 0x2d5c7c2f; if (!OPT_CHECK(RBX_QUIET)) printf("%c\b", c = c << 8 | c >> 24); v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; v86.addr = XREADORG; /* call to xread in boot1 */ v86.es = VTOPSEG(buf); v86.eax = lba; v86.ebx = VTOPOFF(buf); v86.ecx = lba >> 16; v86.edx = nblk << 8 | dsk.drive; v86int(); v86.ctl = V86_FLAGS; if (V86_CY(v86.efl)) { printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); return -1; } return 0; }
ローレベルな所だと、電卓な石が出てくるのね。もう、やめた。
[sakae@cdr /a]$ stat /boot/kernel/kernel 88 16485 -r-xr-xr-x 1 root wheel 68128 12486242 "Jun 18 19:28:11 2010" "Jun 18 19:20:29 2010" "Jun 18 19:26:40 2010" "Jun 18 19:20:29 2010" 4096 24448 0 /boot/kernel/kernel
そんじゃ、iノードを知るお手軽な方法は無いかな?と思って、探してみた。 何が何だか、よう分からんなあ。いちいちformat設定するのもめんどいし。こういう時は、 ドコモのXperiaじゃないけど、こっちね。matzさんのオモテナシ、なかなかいいぞ。
[sakae@cdr /a]$ ruby -rpp -e 'pp File.stat("/boot/kernel/kernel")' #<File::Stat dev=0x58, ino=16485, mode=0100555 (file r-xr-xr-x), nlink=1, uid=0 (root), gid=0 (wheel), rdev=0x10a20 (10, 65568), size=12486242, blksize=4096, blocks=24448, atime=Fri Jun 18 19:28:11 +0900 2010 (1276856891), mtime=Fri Jun 18 19:20:29 +0900 2010 (1276856429), ctime=Fri Jun 18 19:26:40 +0900 2010 (1276856800)>
最近、FPGAにrubyを焼いた人がいて話題になってたけど、あの人の仮想マシンをその まま入れたのだろうか? だったら、CPU実験だな。秋葉原の某ビル13Fで、夏休み電子工作(やさしい、Ruby-CPUの作り方)を やってくれないかなあ。嗚呼、あの人は、夏休みの期間中、キャンプで忙しいんだっけかな?