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