fsck

boot系

以前やった、kernel以前について、どんな舞台裏になってるか探る。cd arch/i386/stand して、そこで make -n なんて言うドライ・ランしても、難読化されたシェルスクリプトが出力されるだけだった。

じゃ、本物のmakeはどうだ?

qm# make
===> mbr
sh /usr/src/sys/arch/i386/stand/mbr/../../../../kern/genassym.sh cc -no-integrat
ed-as   -Oz -Wall -Werror -ffreestanding -fno-stack-protector -DMDRANDOM  -MD -M
P  -I/usr/src/sys/arch/i386/stand/mbr/../../../.. -I/usr/src/sys/arch/i386/stand
/mbr/../libsa -I. -I/usr/src/sys/arch/i386/stand/mbr   < /usr/src/sys/arch/i386/
stand/mbr/../etc/genassym.cf > assym.h.tmp &&  mv -f assym.h.tmp assym.h
cc  -I/usr/src/sys/arch/i386/stand/mbr -I/usr/src/sys/arch/i386/stand/mbr/../../
..  -fno-pie  -I/usr/src/sys/arch/i386/stand/mbr/../../../.. -I/usr/src/sys/arch
/i386/stand/mbr/../libsa -I. -I/usr/src/sys/arch/i386/stand/mbr -c -MD -MF mbr.d
 -o mbr.o mbr.S
ld -nostdlib -Ttext 0 -x -N -s -Bstatic -e start -nopie -znorelro -o mbr mbr.o
text    data    bss     dec     hex
512     0       0       512     200
-rwxr-xr-x  1 root  wsrc  512 Oct 20 05:32 /usr/src/sys/arch/i386/stand/mbr/mbr
===> cdbr
  :
===> biosboot
sh /usr/src/sys/arch/i386/stand/biosboot/../../../../kern/genassym.sh cc -no-int
egrated-as   -Oz -Wall -Werror -ffreestanding -fno-stack-protector -DMDRANDOM  -
MD -MP  -DLOADADDR=0x40000 -DLINKADDR=0x40120 -DBOOTMAGIC=0xc001d00d  -I/usr/src
/sys/arch/i386/stand/biosboot/../../../.. -I/usr/src/sys/arch/i386/stand/biosboo
t/../libsa -I. -I/usr/src/sys/arch/i386/stand/biosboot   < /usr/src/sys/arch/i38
6/stand/biosboot/../etc/genassym.cf > assym.h.tmp &&  mv -f assym.h.tmp assym.h
cc  -no-integrated-as -fno-pie  -DLOADADDR=0x40000 -DLINKADDR=0x40120 -DBOOTMAGI
C=0xc001d00d  -I/usr/src/sys/arch/i386/stand/biosboot/../../../.. -I/usr/src/sys
/arch/i386/stand/biosboot/../libsa -I. -I/usr/src/sys/arch/i386/stand/biosboot -
c -MD -MF biosboot.d -o biosboot.o biosboot.S
ld -nostdlib -Ttext 0 -N -x -Bstatic -nopie -znorelro -T /usr/src/sys/arch/i386/
stand/biosboot/ld.script -o biosboot biosboot.o
text    data    bss     dec     hex
512     0       0       512     200
===> boot
sh /usr/src/sys/arch/i386/stand/boot/../../../../kern/genassym.sh cc -no-integra
ted-as   -Oz -Wall -Werror -ffreestanding -fno-stack-protector -DMDRANDOM  -MD -
MP -m32 -D_STANDALONE -nostdinc -fno-builtin -fpack-struct -D__INTERNAL_LIBSA_CR
EAD -fno-pie  -I/usr/src/sys/arch/i386/stand/boot/../../../.. -I/usr/src/sys/arc
h/i386/stand/boot/../libsa -I. -I/usr/src/sys/arch/i386/stand/boot -DSOFTRAID -D
BOOTMAGIC=0xc001d00d  -DLINKADDR=0x40120 -DSLOW -DSMALL -DNOBYFOUR -DNO_GZIP -DD
YNAMIC_CRC_TABLE -DBUILDFIXED -DHIBERNATE -DHEAP_LIMIT=0xA0000 -I/usr/src/sys/ar
ch/i386/stand/boot/../../../../stand/boot    < /usr/src/sys/arch/i386/stand/boot
/../etc/genassym.cf > assym.h.tmp &&  mv -f assym.h.tmp assym.h
cc  -no-integrated-as -m32  -fno-pie  -I/usr/src/sys/arch/i386/stand/boot/../../
../.. -I/usr/src/sys/arch/i386/stand/boot/../libsa -I. -I/usr/src/sys/arch/i386/
stand/boot -DSOFTRAID -DBOOTMAGIC=0xc001d00d  -DLINKADDR=0x40120 -DSLOW -DSMALL
-DNOBYFOUR -DNO_GZIP -DDYNAMIC_CRC_TABLE -DBUILDFIXED -DHIBERNATE -DHEAP_LIMIT=0
xA0000 -I/usr/src/sys/arch/i386/stand/boot/../../../../stand/boot  -c -MD -MF sr
t0.d -o srt0.o srt0.S
  :
ld  -nostdlib -Bstatic -nopie -znorelro -Ttext 0x40120 -N -x -o boot.new srt0.o
conf.o boot.o bootarg.o cmd.o vars.o debug_i386.o gidt.o pslid.o mdrandom.o apmp
robe.o debug.o pciprobe.o ps2probe.o cmd_i386.o dev_i386.o exec_i386.o gateA20.o
 machdep.o bioscons.o biosdev.o diskprobe.o memprobe.o time.o softraid_i386.o al
loc.o ctime.o exit.o getchar.o hexdump.o memcmp.o memcpy.o memmove.o memset.o pr
intf.o putchar.o snprintf.o strcmp.o strerror.o strlen.o strncmp.o strncpy.o str
tol.o strtoll.o close.o closeall.o cons.o cread.o dev.o disklabel.o dkcksum.o fc
hmod.o fstat.o lseek.o open.o read.o readdir.o stat.o elf32.o elf64.o loadfile.o
 arc4.o ufs.o ufs2.o aes_xts.o bcrypt_pbkdf.o blowfish.o explicit_bzero.o hmac_s
ha1.o pkcs5_pbkdf2.o rijndael.o sha1.o sha2.o softraid.o ashldi3.o ashrdi3.o div
di3.o lshrdi3.o moddi3.o qdivrem.o strlcpy.o adler32.o crc32.o inflate.o inftree
s.o
text    data    bss     dec     hex
87357   308     9456    97121   17b61
===> cdboot
sh /usr/src/sys/arch/i386/stand/cdboot/../../../../kern/genassym.sh cc -no-integ
rated-as   -Oz -Wall -Werror -ffreestanding -fno-stack-protector -DMDRANDOM  -MD
 -MP -D_STANDALONE -nostdinc -fno-builtin -fpack-struct -D__INTERNAL_LIBSA_CREAD
 -DOSREV=\"7.1\" -DMACHINE=\"i386\" -DKERNEL=\"/7.1/i386/bsd.rd\" -fno-pie  -I/u
sr/src/sys/arch/i386/stand/cdboot/../../../.. -I/usr/src/sys/arch/i386/stand/cdb
oot/../libsa -I. -I/usr/src/sys/arch/i386/stand/cdboot -DSOFTRAID -DBOOTMAGIC=0x
c001d00d  -DLINKADDR=0x40120 -DSLOW -DSMALL -DNOBYFOUR -DNO_GZIP -DDYNAMIC_CRC_T
ABLE -DBUILDFIXED -I/usr/src/sys/arch/i386/stand/cdboot/../../../../stand/boot
 < /usr/src/sys/arch/i386/stand/cdboot/../etc/genassym.cf > assym.h.tmp &&  mv -
f assym.h.tmp assym.h
  :

のんびり進むので、途中で中断した。bootって、結構大掛かりなアプリなんだね。それからcdbootって、bsd.rdっていうミニカーネルのお世話になってるのかな。後なじっくりオプションを見ていくと参考になるだろう。

たとえばオプチマイザーの -Oz なんて初めて御目にかかった。 かつかつの所に押し込める為に苦労してるのね。perlまで動員して、サイズチェックしてるよ。 これじゃgdbで調べようにも、手が出ないな。

qm# ls -l mbr
total 52
-rw-r--r--  1 root  wsrc    665 Oct 18  2017 Makefile
-rw-r--r--  1 root  wsrc    459 Oct 20 05:32 assym.h
lrwxr-xr-x  1 root  wsrc     62 Oct 20 05:31 machine@ -> /usr/src/sys/arch/i386/stand/mbr/../../../../arch/i386/include
 :

assym.hに大事なdefineが有る。includeファイルもマシンに合わせて用意してるのが特徴か。

boot

bootのソースを見ていたら、多分いけるんじゃないかってのを見付たので試してみる。

machine diskinfo

それは、ddbの machine diskinfo で表示されるDISK名前を無視してもよかろうって穴だ。

[sakae@fb ~/QEMU]$ ./boot -S -E &
[1] 1228
[sakae@fb ~/QEMU]$ char device redirected to /dev/pts/3 (label serial0)
VNC server running on ::1:5900

[sakae@fb ~/QEMU]$ cu -l /dev/pts/3
Connected
>> OpenBSD/i386 BOOT 3.44
boot> help
commands: # boot echo env help hexdump ls machine reboot set stty time
machine: boot comaddr diskinfo memory
boot> machine diskinfo
Disk    BIOS#   Type    Cyls    Heads   Secs    Flags   Checksum
fd0     0x0     *none*  80      2       36      0x4     0x0
hd0     0x80    label   779     128     63      0x2     0xc26c94d7
hd1     0x81    label   811     64      63      0x0     0x0

FreeBSDのqemuでは、bootメニューに入るチャンスが無いので、cu -l して接続しておいてから、emacs側で cont すれば良い。で、こんな風になった。

boot> boot wd0:/bsd.org
cannot open wd0:/etc/random.seed: No such file or directory
booting wd0:/bsd.org: open wd0:/bsd.org: No such file or directory
 failed(2). will try /bsd
boot> boot wd0a:/bsd.org
booting wd0a:/bsd.org: 10349079+2479108+241672+0+1130496 [709328+107+585136+629189]=0xf62cf0
 :
OpenBSD 7.1 (GENERIC) #151: Mon Apr 11 18:57:52 MDT 2022

hd0aを指定しなくても、wd0aの指定でいけた。sd0aとかも候補になるんで、総称してhd0とかって表現してるって事かな。

で、カーネルをオリジナルな奴に切り換え。そうしておいてbsd,gdbをgeneric以外なものにしてして、gdbを起動。この状態でBPはおけるが、ヒットはしない。アドレスが全く違う所にBPがセットされるんで、ヒットしないんだな。当たりまえの事を確認してみたのさ。

(gdb) b sys_gettimeofday
Breakpoint 1 at 0xd03b38b0: file /usr/src/sys/kern/kern_time.c, line 331.

ちょっと確認って事で、現有のものと、違う条件で作ったやつとの比較。

[sakae@fb /sys/arch/i386/compile/SEE]$ nm bsd | grep sys_gettimeofday
d03b38b0 T sys_gettimeofday
[sakae@fb /sys/arch/i386/compile/SEE]$ nm 02P/bsd | grep sys_gettimeofday
d07db9a0 T sys_gettimeofday

関数名前をキーに、値はアドレスって言うDBがカーネルには内蔵されてるんだね。

boot crash

bootにBPを置いて起動したら、ddbに落ちたので、boot crashしてみた。なお、再現性は無い。 たまたまなのか。それから、ddbで、boot sync とか boot dump すると、何故か刺さる(永久ロープに陥る)。

Automatic boot in progress: starting file system checks.
/dev/wd0a (f1bc5dab4c1884ce.a): INCORRECT BLOCK COUNT I=155524 (4 should be 0) )
fd0 at fdc0 drive 1: density unknown
/dev/wd0a (f1bc5dab4c1884ce.a): UNREF FILE I=155524  OWNER=root MODE=100600
/dev/wd0a: SIZE=0 MTIME=Oct 23 08:37 2022  (CLEARED)
/dev/wd0a (f1bc5dab4c1884ce.a): FREE BLK COUNT(S) WRONG IN SUPERBLK (SALVAGED)
/dev/wd0a (f1bc5dab4c1884ce.a): SUMMARY INFORMATION BAD (SALVAGED)
/dev/wd0a (f1bc5dab4c1884ce.a): BLK(S) MISSING IN BIT MAPS (SALVAGED)
/dev/wd0a (f1bc5dab4c1884ce.a): 17891 files, 407480 used, 929855 free (367 frag)
/dev/wd0a (f1bc5dab4c1884ce.a): MARKING FILE SYSTEM CLEAN
  :
savecore: reboot after panic: uvm_fault(0xd1770460, 0x0, 0, 1) -> e
savecore: system went down at Sun Oct 23 08:37:45 2022
savecore: /var/crash/bounds: No such file or directory
savecore: writing core to /var/crash/bsd.0.core
savecore: writing kernel to /var/crash/bsd.0

こんなに綺麗なfsckの魚拓が取れたので、これを肴に一献傾むけよう。日本酒が美味しい秋ですからら。

fsck

/etc/rcから呼び出されるfsckの挙動を追ってみるかな。綺麗なログが出てますからねぇ。 まずは軽くmanします。そしたら、DISK毎に、プロセスを走らせて並列チェックしてますなんて説明。

そして、

SEE ALSO
     fs(5), fstab(5), fsck_ext2fs(8), fsck_ffs(8), fsck_msdos(8), fsdb(8),
     growfs(8), mount(8), newfs(8), rc(8), scan_ffs(8)

ファイルシステムの性格によって、下請けを呼び出しているっぽい。そんな観点でsrc/sbin/fsck/fsck.cを観察すると、

static int
checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg,
    pid_t *pidp)
{
   
        switch (pid = fork()) {
        case 0:                                 /* Child. */
                if (flags & CHECK_DEBUG)
                        _exit(0);

                /* Go find an executable. */
                edir = edirs;
                do {
                        (void)snprintf(execname,
                            sizeof(execname), "%s/fsck_%s", *edir, vfstype);
                        execv(execname, (char * const *)argv);

どうやらNTFSは怖いんで手を出さないようにしてるっぽい。で、今回は、 fsck_ffs が主舞台になりそう。 解説書もそれに併せて読んでおく。

fsck by hand

自動でやらせるのはもったいないので手動でも。ちょっと舞台設定って事で、newfsした2ndDISKを用意。それに併せて、fstabも用意。但し、目的DISKはコメントアウトしとく。

qm# cat /etc/fstab
f1bc5dab4c1884ce.b none swap sw
f1bc5dab4c1884ce.a / ffs rw,wxallowed 1 1
# /dev/wd1c /mnt ffs rw,wxallowed 0 0

その状態でfsckすると、どんな代物か解らないって言ってきた。AIっぽく、自動判定なんて器用な真似はしないのさ。

qm# fsck /dev/wd1c
fsck: /dev/wd1c: unknown special file or file system.

じゃ、次にコメントを外してからfsck。マウントはしていない。

qm# fsck /dev/wd1c
 ** /dev/rwd1c
 ** File system is clean; not checking

次は、マウントしてから実行。

qm# mount -a
qm# fsck /dev/wd1c
 ** /dev/rwd1c (NO WRITE)
 ** Last Mounted on /mnt
 ** Phase 1 - Check Blocks and Sizes
 ** Phase 2 - Check Pathnames
 ** Phase 3 - Check Connectivity
 ** Phase 4 - Check Reference Counts
 ** Phase 5 - Check Cyl groups
1 files, 1 used, 793110 free (14 frags, 99137 blocks, 0.0% fragmentation)

mountの可否で、挙動が違うね。

それから、プチ面白いコマンドを発見。

qm# touch /mnt/hogefuga
qm# echo hello > /mnt/hello
qm# ncheck /dev/wd1c
/dev/rwd1c:
3       /hogefuga
4       /hello
qm# df
Filesystem  512-blocks      Used     Avail Capacity  Mounted on
/dev/wd0a      5349340   1795968   3285908    35%    /
/dev/wd1c      3172444         8   3013816     0%    /mnt

ncheckってのを使うと、inodeとファイル名前の対応が得られるのか。これを発展させると、fsdbとかになるんだな。

qm# fsdb -d -f wd1c
 ** /dev/wd1c (wd1c) (NO WRITE)
clean = 0
Editing file system `wd1c'
Last Mounted on /mnt
current inode: directory
I=2 MODE=40755 SIZE=512
        MTIME=Oct 23 15:11:03 2022 [758500778 nsec]
        CTIME=Oct 23 15:11:03 2022 [758500778 nsec]
        ATIME=Oct 23 15:18:34 2022 [475688605 nsec]
OWNER=root GRP=wheel LINKCNT=2 FLAGS=0 BLKCNT=4 GEN=e8be5db5
No entry for terminal type "vt220";
using dumb terminal settings.
fsdb (inum: 2)> ls
command `'
slot 0 ino 2 reclen 12: directory, `.'
slot 1 ino 2 reclen 12: directory, `..'
slot 2 ino 3 reclen 20: regular, `hogefuga'
slot 3 ino 4 reclen 468: regular, `hello'

fsck-ffs

mainを見ていくと、主は、checkfilesysって関数で実行されてる。指定したファイルシステムがどんな状況だったかに続いて、Phase1-6 の詳細がチェックされる。そして最後にサマリーが報告されるって流れだ。

super blockってのが頻出してるけど、それって、/sys/ufs/ffs/fs.h に定義されてるやつかな。普通の人は、/usr/include/ufs/ffs/fs.h を見るんだな。

まずはスーパー・ブロックの攻略だな。それにはファイルシステムを作るコマンドを実行してみる事だな。

newfs

と言う事で、3番目のDISKを作った。そしてそれを -hdc test.img で組込もうとしたけど、そんな事はqemuが許してくれなかった。qemuがどうやらハングしてしまうんだ。

ならば別の手段を考える。思い出したのは、vnconfigで、普通のファイルをDISKと見做してしまうスペシャルな奴。

qm# dd if=/dev/zero of=test.img bs=1M count=500
500+0 records in
500+0 records out
524288000 bytes transferred in 50.847 secs (10310981 bytes/sec)
qm# vnconfig vnd0 /tmp/test.img
qm# disklabel -E vnd0
Label editor (enter '?' for help at any prompt)
vnd0> p
OpenBSD area: 0-1024000; size: 1024000; free: 1024000
#                size           offset  fstype [fsize bsize   cpg]
  c:          1024000                0  unused
vnd0> a a
offset: [0]
size: [1024000]
FS type: [4.2BSD]
vnd0*> w
vnd0> q

ZEROクリアした普通のファイルをddを使って作成。それをvnconfigでDISKに見立てる。後は、disklabelでOpenBSD用にする。これで、vnd0ってDISKを捏ち上げられた。

次は目当てのnewfsを実行。

qm# newfs /dev/vnd0a
newfs: /dev/vnd0a: block device
qm#
qm# newfs /dev/rvnd0a
/dev/rvnd0a: 500.0MB in 1024000 sectors of 512 bytes
4 cylinder groups of 125.00MB, 8000 blocks, 16000 inodes each
super-block backups (for fsck -b #) at:
 160, 256160, 512160, 768160,

が、普通にやるとエラーだ。newfs.cのこんな所で引掛っている。でも、キャラクターデバイスなら、まあまあOKよってヒントを貰ったので、その通りに裸のデバイスを指定。何だか動いているっぽい。

if (!mfs) {
        if (S_ISBLK(st.st_mode))
                fatal("%s: block device", special);
        if (!S_ISCHR(st.st_mode))
                warnx("%s: not a character-special device",
                    special);

ああ、事前に、/etc/fstabに設定しといたよ。勿論、事前に/tst って言うマウントポイントを用意しておく事。

/dev/vnd0a /tst ffs rw,wxallowed 0 0

マウントしてみるぞ。

qm# df
Filesystem  512-blocks      Used     Avail Capacity  Mounted on
/dev/wd0a      5349340   3735056   1346820    73%    /
/dev/wd1a      3172444   1921368   1092456    64%    /mnt
/dev/vnd0a      991580         4    942000     0%    /tst
qm# hexdump -C test.img
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000200  57 45 56 82 0c 00 00 00  76 6e 64 20 64 65 76 69  |WEV.....vnd devi|
00000210  63 65 00 00 00 00 00 00  66 69 63 74 69 74 69 6f  |ce......fictitio|
00000220  75 73 00 00 00 00 00 00  00 02 00 00 64 00 00 00  |us..........d...|
00000230  01 00 00 00 00 28 00 00  64 00 00 00 00 a0 0f 00  |.....(..d.......|
00000240  ba 4f 61 0d 92 52 12 b4  00 00 00 00 00 00 00 00  |.Oa..R..........|
00000250  00 00 00 00 00 a0 0f 00  00 00 00 00 00 00 00 00  |................|
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000270  00 00 01 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000280  00 00 00 00 57 45 56 82  12 f7 10 00 00 20 00 00  |....WEV...... ..|
00000290  00 00 01 00 00 a0 0f 00  00 00 00 00 00 00 00 00  |................|
000002a0  07 14 40 1f 00 00 00 00  00 00 00 00 00 00 00 00  |..@.............|
000002b0  00 00 00 00 00 a0 0f 00  00 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00010000  00 00 00 00 00 00 00 00  28 00 00 00 30 00 00 00  |........(...0...|
00010010  38 00 00 00 08 08 00 00  00 00 00 00 00 00 00 00  |8...............|
00010020  00 00 00 00 00 00 00 00  00 00 00 00 04 00 00 00  |................|
00010030  00 40 00 00 00 08 00 00  08 00 00 00 05 00 00 00  |.@..............|
00010040  00 00 00 00 00 00 00 00  00 c0 ff ff 00 f8 ff ff  |................|
  :

次はnewfsの挙動観察だな。その前に、お隣様の事情を観察。

fsck for debian

なんとminixのファイルシステムの修復出来るのか、Linuxの出身の元だから、リナの世界では、世界遺産に問答無用で登録されてるんだな。そして、それに対して誰も異をとなえない。リナス神には逆らうなですかね。

SEE ALSO
       fstab(5), mkfs(8), fsck.ext2(8) or fsck.ext3(8) or e2fsck(8),
       fsck.cramfs(8), fsck.jfs(8), fsck.nfs(8), fsck.minix(8), fsck.msdos(8),
       fsck.vfat(8), fsck.xfs(8), reiserfsck(8)

それから、山のようにファイルシステムが登録されてる。みんな足掻いているのね。

長くなってきたので次回に持ち越し。


This year's Index

Home