Windows互換じゃなくて独自のファイルシステムを作ろう!

  • (by K, 2006.03.24)
  • 発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)
  • ファイルシステムの説明がないみたいだけどいいのかな、という意見があったので書いてみる。

独自ファイルシステムのつらいところ

  • 間違ってWindowsからアクセスしたりするとたぶんWindowsによってディスクを壊される。
  • tolsetに付属する imgtol.com ではディスクイメージをうまく書き込めないかもしれない。
    • 厳密に言うと、書き込めないことはないはず。まずディスクをフォーマットして、それから書き込めば多分書き込める。書き込んでしまったディスクにもう一度書き込みたいときは、そのディスクをフォーマットからやり直す。
    • rawriteやddなどのツールではこんな面倒なことをしなくてもいいはず。
  • 専用のディスクイメージツールも作らないといけない。

3時間くらいで適当にやってみた例

  • 結論「かんたんだね」(註:まともなものを作るのはもちろんそれなりに大変ですよ)
  • とりあえずharib27fをベースにしている。

フォーマットを決めてIPLを書く

  • どんなファイルシステムにしようかなー?
  • Windowsとの互換性なんか全く考えずにとにかくシンプルに行ってみる。たしかブートセクタは最初の16バイトを使わないようにすればどうでもよかったような気がする(最初の16バイトと最後の2バイトはBIOSが見るので勝手なことはしないほうがいい)。
    ; haribote-ipl
    ; TAB=4
    
    CYLS    EQU     9
    
            ORG     0x7c00
            JMP     entry
            DB      0x90, "HARIBOTE"
            DW      512
            DB      1
            DW      1
    entry:
            以下ipl09.nasと同じ(最後のJMP命令の番地は変更)
    
  • 4バイトのファイルサイズ、4バイトのファイル位置、ファイル名は24バイト。これでいいや。わーい、ちょっとだけロングファイルネームだ。ファイル属性とか考えてないけど、ファイルサイズやファイル位置の上位8bitやくらいは使わないから、そこを使ってもいいかな(24bitあれば16MBまでは管理できる)。
    struct MYFS_FILEINFO {
        int size, addr;
        unsigned char name[24];
    };
  • この構造が シリンダ0-ヘッド0-セクタ2(=0x000200) から並ぶ。FATなんていうややこしいものはとりあえずなし。これが272個続く。そうすると 0x0023ff までは埋まる。これでセクタ18まで埋まるので、次はシリンダ0-ヘッド1-セクタ1から。
  • ということでそこからはファイル領域。最初はharibote.sysが入る。だからIPLの最後のジャンプ命令は、0xc200→0xa400に変更。asmhead.nasのORGも変更。
  • このディスクフォーマットのいいところ:FATがないので簡単でわかりやすい、ファイル領域の番地の管理を512バイト単位にしていないのでディスクのムダになる領域が少ない、ファイル名が24文字までOK、ファイル名の中にピリオドを自由に混ぜられる、ファイル名に大文字小文字の区別がある。
    • 実際にテストしてみたところ、harib27fと同じファイルを全部詰め込んでも、8シリンダで収まりました。つまり1シリンダだけ得をしていることになります。
  • このディスクフォーマットのよくないところ:ファイルサイズを大きくするときのことを考えてない、小さくするときのことも考えてない、削除したときのことも考えてない(ファイルサイズの変更や削除などによって生じたスペースをうまくつなげて活用するための仕組みがない)。Windowsの方法なら、FATでつなぎかえれば跳び跳びになった空きもつなげて使える。

ディスクイメージツールを自作

  • 次に必要なのがディスクイメージツールだ。とりあえず、IPLとファイルを書き込む機能さえあればいいだろう。
    #include <stdio.h>
    #include <string.h>
    
    struct MYFS_FILEINFO {
        int size, addr;
        unsigned char name[24];
    };
    
    int main(int argc, char **argv)
    /* argv[1] : 出力ファイル名, argv[2] : IPLファイル名, argv[3〜] : ファイル名 */
    {
        FILE *fp;
        unsigned char img[1440 * 1024];
        int i, j;
        struct MYFS_FILEINFO *myfs = (struct MYFS_FILEINFO *) (img + 0x000200);
    
        if (argc < 4) { return 1; /* エラー終了 */ }
    
        /* IPL読み込み */
        fp = fopen(argv[2], "rb");
        if (fp == 0) { return 1; }
        fread(img, 1, 512, fp);
        fclose(fp);
        for (i = 512; i < 1440 * 1024; i++) {
            img[i] = 0;
        }
    
        /* ファイルコピー */
        j = 0x002400;
        for (i = 3; i < argc; i++) {
            myfs[i - 3].addr = j;
            strncpy(myfs[i - 3].name, argv[i], 24); /* 文字列のコピー */
            fp = fopen(argv[i], "rb");
            if (fp == 0) { return 1; }
            myfs[i - 3].size = fread(img + j, 1, 1440 * 1024 - j, fp);
            fclose(fp);
            j = (j + myfs[i - 3].size + 0xf) & ~0xf; /* なんとなく16バイト単位に切り上げ */
            if (j == 1440 * 1024) { break; }
        }
        myfs[argc - 3].size = -1; /* 終端マーク */
        myfs[argc - 3].addr = j; 
    
        /* ディスクイメージ出力 */
        fp = fopen(argv[1], "wb");
        fwrite(img, 1, 1440 * 1024, fp);
        fclose(fp);
        return 0;
    }
  • これをコンパイルしたものを myfs_img.exe とすると、
    prompt>myfs_img.exe haribote.img my_ipl09.bin haribote.sys
  • とするだけで、とりあえずディスクイメージはできあがる。しかもここまでで起動はできるようになるはず(dirとかすると変なファイルが出てくるけどね)。
    • tolsetで作ると 4,096バイト の myfs_img.exe ができる(z_new_wを使う)。
  • こんなツール作るの面倒だよ、っていう場合は、バイナリエディタでディスクイメージを作ってもOK。ファイルの数が少ないなら、適当にコピー&ペーストでも十分に作れる。

きちんとdirできるように「はりぼてOS」を改造

  • bootpack.hのstruct FILEINFOを書き直し
    struct FILEINFO {
        int size, clustno; /* clustnoはaddrのこと */
        unsigned char name[24];
    };
  • console.cのcmd_dir()を書き換え
    void cmd_dir(struct CONSOLE *cons)
    {
        struct MYFS_FILEINFO *finfo = (struct MYFS_FILEINFO *) (ADR_DISKIMG + 0x000200);
        int i, j;
        char s[31];
        for (i = 0; i < 272; i++) {
            if (finfo[i].size == -1) {
                break;
            }
            sprintf(s, "                      %7d\n", finfo[i].size);
            for (j = 0; j < 24; j++) {
                if (finfo[i].name[j] == 0) {
                    break;
                }
                s[j] = finfo[i].name[j];
            }
            cons_putstr0(cons, s);
        }
        cons_newline(cons);
        return;
    }

ファイルも読み込めるように「はりぼてOS」を改造

  • file_readfat()は不要なので、file.cとbootpack.hとbootpack.cから削除
  • その他のFATに関する記述は削除
  • file.cの一部
    #include <string.h>
    
    void file_loadfile(int clustno, int size, char *buf, char *img)
    {
        int i;
        for (i = 0; i < size; i++) {
            buf[i] = img[clustno + i];
        }
        return;
    }
    
    struct FILEINFO *file_search(char *name, struct FILEINFO *finfo, int max)
    {
        int i;
        for (i = 0; i < max; i++) {
            if (finfo->size == -1) {
                break;
            }
            if (strncmp(name, finfo[i].name, 24) == 0) {
                return finfo + i; /* ファイルが見つかった */
            }
        }
        return 0; /* 見つからなかった */
    }
  • file.cのfile_loadfile2()の一部
        file_loadfile(clustno, size, buf, (char *) ADR_DISKIMG);
  • console.cのcmd_app()
    int cmd_app(struct CONSOLE *cons, char *cmdline)
    {
        (中略)
        char name[30], *p, *q;
        (中略)
    
        /* コマンドラインからファイル名を生成 */
        for (i = 0; i < 24; i++) {
             if (cmdline[i] <= ' ') {
                 break;
             }
             name[i] = cmdline[i];
        }
        name[i] = 0; /* とりあえずファイル名の後ろを0にする */
    
        /* ファイルを探す */
        finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x000200), 272);
        if (finfo == 0 && name[i - 1] != '.') {
             /* 見つからなかったので後ろに".HRB"をつけてもう一度探してみる */
             name[i    ] = '.';
             name[i + 1] = 'H';
             name[i + 2] = 'R';
             name[i + 3] = 'B';
             name[i + 4] = 0;
             finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x000200), 272);
             if (finfo == 0) {
                 /* 見つからなかったので後ろに".hrb"をつけてさらにもう一度探してみる */
                 name[i + 1] = 'h';
                 name[i + 2] = 'r';
                 name[i + 3] = 'b';
                 finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x000200), 272);
            }
        }
        (中略)
    }
  • 他にfile_search()を使っているところが何ヶ所かあるけれど、同様に改造すればOK。
  • これですべてのアプリが普通に使えます。信じられない人はダウンロードして「make run」してみましょう。

ダウンロードコーナー

感想

  • 「ファイルシステムって難しい」→×
  • ファイルシステムそのものは難しくないです。難しいファイルシステムを使おうとするから苦労するだけです。もちろん実用性を考えたらそれなりに難しいファイルシステムを使わないといけないかもしれないけど、とにかくファイルシステムそのものが難しいなんて思ったら負けです。先入観を持たないで自分の能力に応じて好きなファイルシステムを作ったら楽しいと思います。そしてどうせ作るのなら楽しめたほうがいいに決まっています。そうやって遊んでいろいろ作っているうちに力がつきます。
  • きっと30日目までを読破したみなさんなら、このページの内容を難しいと思うことはないはずです。・・・そうであってほしい!

こめんと欄

  • 私の好きなファイルシステムはrawシステムです。<- おいおい、それはファイルシステムじゃ無いだろ!! -- kuni 2006-04-21 (金) 11:50:54
  • 独自とWin互換のってどっちがいいの?っていうか、システムが安全>? -- 名無しさん 2006-05-06 (土) 09:23:04
  • どっちがいいかなんて、それはまさにあなたが決めることですよ。他人にこちらがいいといわれてそれに従うだけなら、自分でOSなんか作らないで他人の作ったOSを使えばいいような気が僕にはします。 -- K 2006-05-06 (土) 09:52:01
  • ファイルシステムそのものを取り替えるのではなく、FAT内にイメージファイルを保持させる と言う方法もありますね。ブートローダ周りでやることが増えるのとディスクの使える容量が少し減るのとが主な問題点でしょうか。 -- Source 2008-08-24 (日) 03:56:02
  • 「最初の16バイトと最後の2バイトはBIOSが見るので」とありますが、最初の16バイトには何があるべきなのでしょうか? -- 名無しさん 2010-02-21 (日) 14:11:02
  • 名無しさんが示された文の直後にあるソースコードがまさにそれです。JMP命令から始まってDW 1で終わる5行分のことです。 -- uchan 2010-02-21 (日) 15:43:36
  • 最後の2バイトはBIOSで見ていません。ブートローダが見ているだけ。 -- kaoaru 2019-11-18 (月) 21:47:07

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2019-11-18 (月) 21:47:07 (20d)