/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];
}





