/sbin/initの疑問(2)?
unpack_to_rootfs()からwrite_buffer()をコールすることで、cpioフォーマットをrootfs下に展開していくのですが、cpioはtarと違って、それぞれのファイル毎に管理情報が配置しているのだそうです。其れゆえの実装なのだろうと、思いますが、我々のレベルから見ると、でも何でこんなマドロッコシイ実装しての? ってちょっと疑問に思ってしまう程です。
actions配列にコールバックを設定し、state変数に設定したインデックス()で、順次actions[]()をコールする事で展開します。unpack_to_rootfs()からstate=Startでwrite_buffer()がコールされ、スタティックのcountにサイズ、victimに読み込みファイル位置が設定されます。(で、なんぜvictimなの?) で、actions[state]()がnot 0まで、コールバックをコールし続けます。なお各コールバック関数において、次にコールバック関数として、stateにenum stateのどれかが設定されていくという按配です。
actions配列にコールバックを設定し、state変数に設定したインデックス()で、順次actions[]()をコールする事で展開します。unpack_to_rootfs()からstate=Startでwrite_buffer()がコールされ、スタティックのcountにサイズ、victimに読み込みファイル位置が設定されます。(で、なんぜvictimなの?) で、actions[state]()がnot 0まで、コールバックをコールし続けます。なお各コールバック関数において、次にコールバック関数として、stateにenum stateのどれかが設定されていくという按配です。
static __initdata int (*actions[])(void) = { [Start] = do_start, [Collect] = do_collect, [GotHeader] = do_header, [SkipIt] = do_skip, [GotName] = do_name, [CopyFile] = do_copy, [GotSymlink] = do_symlink, [Reset] = do_reset, }; static __initdata enum state { Start, Collect, GotHeader, SkipIt, GotName, CopyFile, GotSymlink, Reset } state, next_state; static int __init write_buffer(char *buf, unsigned len) { count = len; victim = buf; while (!actions[state]()) ; return len - count; }最初にコールされる関数が、do_start()です。read_into()でファイルサイズが110以上なら、引数のnextのstate = GotHeaderで、そうでないならstate = Collectとして、次のコールバック関数が設定されます。なお、変数next_stateはCollectコールバック関数の次にコールされるコールバック関数となります。こんな感じで、(*actions[])(void)を順繰りに呼び出すことで、rootfsにイメージを複写しています。
static int __init do_start(void) { read_into(header_buf, 110, GotHeader); return 0; } static void __init read_into(char *buf, unsigned size, enum state next) { if (count >= size) { collected = victim; eat(size); state = next; } else { collect = collected = buf; remains = size; next_state = next; state = Collect; } }で、/initが/sbin/initにリンクされているかな。との思いで、do_symlink()は以下の通りで、collectedの名前のリンクをsys_symlink()で作成し、uid/gidを設定しているだけです。
static int __init do_symlink(void) { collected[N_ALIGN(name_len) + body_len] = '\0'; sys_symlink(collected + N_ALIGN(name_len), collected); sys_lchown(collected, uid, gid); state = SkipIt; next_state = Start; return 0; }なお、stateにGotSymlinkを設定しているのは、do_header()内で、cpioのヘッダー情報がリンクの場合だけで、他で設定している箇所はありませんでした。ここでは、parse_header()で、collectedのヘッダー情報か各情報を取得し、そのmodeがリンクなら、state = Collectで次にデータを読み込んで、next_state = GotSymlinkで、それでもってdo_symlink()がコールされるということです。従って、cpio内にリンクとして/sbin/initが無いと、rootfs内にも/sbin/initは作成されないということなのですが・・・?
static int __init do_header(void) { if (memcmp(collected, "070701", 6)) { error("no cpio magic"); return 1; } parse_header(collected); next_header = this_header + N_ALIGN(name_len) + body_len; next_header = (next_header + 3) & ~3; if (dry_run) { read_into(name_buf, N_ALIGN(name_len), GotName); return 0; } state = SkipIt; if (name_len <= 0 || name_len > PATH_MAX) return 0; if (S_ISLNK(mode)) { if (body_len > PATH_MAX) return 0; collect = collected = symlink_buf; remains = N_ALIGN(name_len) + body_len; next_state = GotSymlink; state = Collect; return 0; } if (S_ISREG(mode) || !body_len) read_into(name_buf, N_ALIGN(name_len), GotName); return 0; } static void __init parse_header(char *s) { unsigned long parsed[12]; char buf[9]; int i; buf[8] = '\0'; for (i = 0, s += 6; i < 12; i++, s += 8) { memcpy(buf, s, 8); parsed[i] = simple_strtoul(buf, NULL, 16); } ino = parsed[0]; mode = parsed[1]; uid = parsed[2]; gid = parsed[3]; nlink = parsed[4]; body_len = parsed[6]; major = parsed[7]; minor = parsed[8]; rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); name_len = parsed[11]; }