仮想ファイルシステム

Bayside2006-04-08


はりぼてOSには FDドライバーがありません。じゃあ、どうやってフロッピーからファイルを読み込むかというと、起動時に BIOS を使ってまとめて読み込んでいます。FAT12 の情報も含めて単純にごそっと読み込むため、あとでメモリーにアクセスするだけでフロッピーにあったファイルを読み出すことができます。要はメモリー上の仮想ファイルシステムです。これだと FAT 解析ロジックだけを作ればよく、CD-ROM ブート時(El Torito)も無修正で対応できます。

一方、Mona も起動時に BIOS を使ってファイルを読み込みますが、FAT12 を解析して、カーネルだけを読み込むます。Mona のほうが賢いと思うかもしれませんが、この方法だと FD ドライバーを作らなければいけないし、CD-ROM ブートに対応させようと思ったら ISO-9660 解析ロジックも必要です。Monaカーネルが小さそうに見えて、実は必須サーバーをあわせると 400KB 近くあるのはそのせいです。

理想的には、はりぼてOS の IPL → はりぼて OS のセカンドローダ → 自作カーネル というシーケンスにしたいのですが、Mona の IPL → Monaセカンドローダ → 自作カーネル というシーケンスになっています。次善の策として、自作カーネルの後ろに必要なファイルをまとめた tar ボールをつけるという方法を思いつきました。FAT12 上は1つのファイルで、この tar ボールを仮想ファイルシステムに見立てるという方法です。tar は無駄が多いので Waba プロジェクトで使っていた wrp だとなおいい感じです。

さっそく実行してみました。仮想ファイルシステムなので、ディレクトリやロングファイルネームをサポートしても、驚くほど小さいソースコードでできました。

http://www.wabasoft.com/spec_warp.shtml

file.h

#ifndef _FILE_H_
#define _FILE_H_

#include "types.h"

#define MAX_FILES 32
#define get_word(b) (word)( ((b)[0]<<8)|(b)[1] )
#define get_dword(b) (dword)( (dword)((b)[0])<<24 | (dword)((b)[1])<<16 | (dword)((b)[2])<<8 | (dword)((b)[3]) )

class File {
public:
	static dword file_system_start;
	static int number_of_files;
	static dword* file_offset_list;
	static dword* file_size_list;

public:
	static void init();
	static byte* load(char* file_path, dword* read_size);
};

#endif

file.cpp

#include "file.h"
#include "string.h"
#include "stdarg.h"
#include "memory.h"
#include "screen.h"

dword  File::file_system_start = 0;
int File::number_of_files  = 0;
dword* File::file_offset_list   = NULL;
dword* File::file_size_list  = NULL;

void File::init()
{
	File::file_system_start = 0;
	File::file_offset_list = new dword[MAX_FILES];
	File::file_size_list = new dword[MAX_FILES];
	
	/* カーネルの後ろについている Warp ファイルを検索 */
	/* 0x2200 + 20KB 付近からマジック文字列を適当に探す */
	for (int i = 0x2200 + 0x5000; i < 0x80000; i++) {
		char* p = (char *)i;
		if (strncmp(p, "Wrp1", 4) == 0) {
			file_system_start = i;
			break;
		}
	}
	if (file_system_start == 0) return;
	
	/* ファイルデータのオフセットリストを生成する */
	byte* fp = (byte *)file_system_start;
	number_of_files = get_dword(fp + 4);
	
	for (int i = 0 ; i < number_of_files; i++) {
		file_offset_list[i] = get_dword(fp + 8 + 4 * i);
		file_size_list[i] = get_dword(fp + 12 + 4 * i) - file_offset_list[i];
	}
}

byte* File::load(char* file_path, dword* read_size)
{
	if (file_system_start == 0) {
		*read_size = 0;
		return NULL;
	}

	/* Warp ファイルの中からファイルを検索する */
	byte* fp = (byte *)file_system_start;
	for (int i = 0; i < number_of_files; i++) {
		if (strncmp(file_path, (char *)(fp + file_offset_list[i] + 2), strlen(file_path)) == 0) {
			int nameLen = get_word(fp + file_offset_list[i]);
			*read_size = file_size_list[i] - nameLen - 2;
			return (fp + file_offset_list[i] + 2 + nameLen);
		}
	}

	*read_size = 0;
	return NULL;
}

仮想ファイルシステムWARPアーカイブ)の作り方

./warp c test a.txt b.exe c.jpg d/longfilename.mp3
mv test.wrp test.dat