cramfs を読む

# cramfs を読む

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

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

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

当文書は、引用されたものを除いて [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/) とする。


## 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 から引用)

> ```c: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 より引用)

> ```c: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 より引用)

> ```c: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
> ```

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


## ディレクトリエントリ

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

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

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

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

## ファイルデータ

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

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

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

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

### ファイルデータ配置

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

## 参考文献

- cramfs tools (公式プロジェクト):  
  \<http://sourceforge&#x2e;net/projects/cramfs/\>  
  \<http://sourceforge&#x2e;net/projects/cramfs/files/cramfs/1.1/cramfs-1.1.tar.gz\>
- CRAMFS ファイルシステム解説  
  \<http://archive&#x2e;linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/cramfs.txt.html\>
- Cramfs  
  \<http://ja&#x2e;wikipedia.org/wiki/Cramfs\>

---

<p xmlns:dct="http://purl.org/dc/terms/">
  <a rel="license"
     href="http://creativecommons.org/publicdomain/zero/1.0/">
    <img src="http://i.creativecommons.org/p/zero/1.0/88x31.png" style="border-style: none;" alt="CC0" />
  </a>
  <br />
  To the extent possible under law,
  <span resource="[_:publisher]" rel="dct:publisher">
    <span property="dct:title">dearblue</span></span>
  has waived all copyright and related or neighboring rights to
  this work.
</p>