LoginSignup
3
1

More than 5 years have passed since last update.

cramfs を読む

cramfs は読み取り専用のファイルシステムで、linux 界によって創造された。

小規模なファイルを扱うことに特化しており、さらにデータは圧縮される。ブータブル CD や組み込み機器向けによく利用されているようだ。

一般的なファイルシステムの持つ機能をばっさり省き、最低限の機能しかもっていないが、目的に一致するならばとても有用な選択となる。

当文書は、引用されたものを除いて CC0 とする。

cramfs の特徴

  • ボリュームは最大 256 MiB + α。
  • 1ファイルあたり最大 16 MiB 未満。
  • GID が0 から 255 まで。
    通常 GID は16ビットなので、上位8ビットが欠落する。このことはセキュリティリスクとなるとの注意書きかある。
  • ファイル名は最大 252 文字。
  • シンボリックリンクはサポートされるが、ハードリンクはサポートされない。
    でぃれくとりえんとりちゅうに inode がかくのうされるため。
    ただし、実データが同じものは同じ場所を指すことが出来る。
  • inode は作成日時や更新日時を持たない。
  • ファイルデータは『ページ』に分割して格納される。ページサイズは 4096 バイトで固定?

cramfs のバイトオーダー

cramfs の バイトオーダーは定義されていない (!)。「作成と利用は同じアーキテクチャでやってね!」的な発想のようだ。

もっともスーパーブロックのマジックナンバーで判断可能である。

内部構造

+========================================+
|  スーパーブロック                      |
+========================================+
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
|  ファイルデータやディレクトリエントリ  |
+========================================+

データはブロック単位で扱われるものと思いきや、バイト単位であった。

ひとつのディレクトリエントリやファイルデータは必ず連続したものであり、断片化は発生のしようがない。

スーパーブロック

スーパーブロックは多くのファイルシステムと同じく、ファイルシステムの識別情報がまとめてある部分である。

(cramfs-1.1/linux/cramfs_fs.h から引用)

cramfs-1.1/linux/cramfs_fs.h
struct cramfs_super {
        u32 magic;                      /* 0x28cd3d45 - random number */
        u32 size;                       /* length in bytes */
        u32 flags;                      /* feature flags */
        u32 future;                     /* reserved for future use */
        u8 signature[16];               /* "Compressed ROMFS" */
        struct cramfs_info fsid;        /* unique filesystem info */
        u8 name[16];                    /* user-defined name */
        struct cramfs_inode root;       /* root inode data */
};

先に述べたように、バイトオーダーの判断を magic で確認するべきである。

inode

(cramfs-1.1/linux/cramfs_fs.h より引用)

cramfs-1.1/linux/cramfs_fs.h
struct cramfs_inode {
        u32 mode:CRAMFS_MODE_WIDTH, uid:CRAMFS_UID_WIDTH;
        /* SIZE for device files is i_rdev */
        u32 size:CRAMFS_SIZE_WIDTH, gid:CRAMFS_GID_WIDTH;
        /* NAMELEN is the length of the file name, divided by 4 and
           rounded up.  (cramfs doesn't support hard links.) */
        /* OFFSET: For symlinks and non-empty regular files, this
           contains the offset (divided by 4) of the file data in
           compressed form (starting with an array of block pointers;
           see README).  For non-empty directories it is the offset
           (divided by 4) of the inode of the first file in that
           directory.  For anything else, offset is zero. */
        u32 namelen:CRAMFS_NAMELEN_WIDTH, offset:CRAMFS_OFFSET_WIDTH;
};

見てわかるように 12 バイトしか使わない。

ビット割り当てがどうなっているのかを見るために、定数を見てみる。

(cramfs-1.1/linux/cramfs_fs.h より引用)

cramfs-1.1/linux/cramfs_fs.h
#define CRAMFS_MODE_WIDTH 16
#define CRAMFS_UID_WIDTH 16
#define CRAMFS_SIZE_WIDTH 24
#define CRAMFS_GID_WIDTH 8
#define CRAMFS_NAMELEN_WIDTH 6
#define CRAMFS_OFFSET_WIDTH 26

構造体の気をつけたいところはビットフィールドを用いていることと、namelenoffset が 4 で割られ端数は切り上げられている点だ。

ディレクトリエントリ

ディレクトリエントリには inode と名前が格納される。このディレクトリエントリは 圧縮されない

名前は inode の直後に4バイト単位に NUL 拡張されて格納される。

ただし、自身と親ディレクトリエントリを示す『.』『..』は格納されない。

+============================
| ディレクトリエントリ
|
|   +=======+======+
|   | inode | 名前 |
|   +=======+======+
|   | inode | 名前 |
|   +=======+======+
|   / ............ /
|   +=======+======+
|
+============================

ファイルデータ

ファイルデータは『ページ』という単位にして連続して格納される。このページのデータは zlib ヘッダ付きで deflate によって つねに 圧縮されている。

各ページの開始位置はひとまとめにされて第1ページの前、つまりはファイルデータブロックの最初に配置される。このページ位置は32ビット整数値で、そのインデックスに対するページはどこまで読めばよいのかを示している。

ページデータの各終端位置はひとつうしろのページ位置に等しい。第1ページの開始位置はページインデックスの直後となるので、すでにわかっている。

これでページデータを読むのに必要な情報がそろったので、ファイルデータを読むことができる。

ファイルデータ配置

+=======================+ ページインデックスの開始位置 = ファイルデータの開始位置
| ページインデックス    |  <-- 4バイト×ページ数
+=======================+ ページ[0]の開始位置 = ページインデックスの終端位置
| ページデータ (任意長) |
+=======================+ ページ[1]の開始位置 = ページ[0]の終端位置
| ページデータ (任意長) |
+=======================+ ページ[2]の開始位置 = ページ[1]の終端位置
| ページデータ (任意長) |
+=======================+ ページ[3]の開始位置 = ページ[2]の終端位置 ≦ (ファイルデータ開始位置 + ファイルデータサイズ)

参考文献


CC0
To the extent possible under law, dearblue has waived all copyright and related or neighboring rights to this work.

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1