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の作り方)を やってくれないかなあ。嗚呼、あの人は、夏休みの期間中、キャンプで忙しいんだっけかな?