# 帳票設計書 38-ffsinfo

## 概要

本ドキュメントは、FreeBSDのffsinfoコマンドが生成するFFSメタデータダンプレポートの設計を記述する。ffsinfoはFFS（Fast File System）の全メタデータを詳細にダンプするツールで、dumpfs(8)よりも詳細な情報を提供し、ファイルシステムの障害診断やfsck(8)前後の比較に特化している。

### 本帳票の処理概要

ffsinfoコマンドは、libufs(3)ライブラリを使用してUFS/UFS2ファイルシステムのスーパーブロック、シリンダーグループ、inode、間接ブロックなどの全メタデータを読み取り、デバッグ出力マクロを通じて詳細なダンプを生成する。出力レベルはビットマスクで制御され、必要な情報のみを選択的に出力できる。

**業務上の目的・背景**：fsck(8)前後のファイルシステム状態を比較し、障害箇所を特定するためには、dumpfs(8)よりも詳細なメタデータダンプが必要である。ffsinfoは全inodeのブロックアロケーション情報まで含む完全なダンプを提供し、diffコマンドとの組み合わせで効率的な障害診断を可能にする。

**帳票の利用シーン**：fsck前後のファイルシステム状態比較、ファイルシステム障害の詳細診断、inode・間接ブロックの調査、特定シリンダーグループのメタデータ確認、ファイルシステム成長余地の確認に利用される。

**主要な出力内容**：
1. スーパーブロック（プライマリ、各CGのコピー）の詳細ダンプ
2. シリンダーグループサマリー（csum配列）
3. シリンダーグループ詳細（ヘッダー、inodeマップ、フラグメントマップ、クラスターマップ）
4. inode詳細（UFS1/UFS2対応、直接/間接/二重間接/三重間接ブロック一覧）

**帳票の出力タイミング**：ユーザーがffsinfoコマンドを実行した際に出力される（ファイルまたは標準出力）。

**帳票の利用者**：ファイルシステムエンジニア、障害対応担当者

## 帳票種別

詳細メタデータダンプレポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | CLIターミナル | N/A | `ffsinfo [-g cg] [-i inode] [-l level] [-o outfile] special` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（標準出力またはファイル） |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | -oで指定（デフォルト: 標準出力("-")） |
| 出力方法 | stdout または -oで指定したファイル |
| 文字コード | ASCII |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

出力はデバッグマクロ（DBG_DUMP_*）によって生成され、以下の階層構造を持つ。

```
┌──────────────────────────────────────────────────────────────────┐
│  === PRIMARY SUPERBLOCK ===                                       │
│  [スーパーブロック全フィールドダンプ]                                 │
├──────────────────────────────────────────────────────────────────┤
│  === CYLINDER GROUP SUMMARY ===                                   │
│  0. csum in fscs: nbfree=N, ndir=N, nifree=N, nffree=N           │
│  1. csum in fscs: ...                                             │
│  ...                                                              │
├──────────────────────────────────────────────────────────────────┤
│  === CYLINDER GROUP N ===                                         │
│  [スーパーブロックコピー] (level & 0x002)                            │
│  [CGヘッダー] (level & 0x008)                                      │
│  [inodeマップ] (level & 0x010)                                     │
│  [フラグメントマップ] (level & 0x020)                                │
│  [クラスターマップ+サマリー] (level & 0x040)                          │
│  ...                                                              │
├──────────────────────────────────────────────────────────────────┤
│  === INODE N ===                                                  │
│  [inode全フィールドダンプ] (level & 0x100)                           │
│  [直接/間接/二重間接/三重間接ブロック一覧] (level & 0x200)            │
└──────────────────────────────────────────────────────────────────┘
```

### ヘッダー部

該当なし（各セクションのタイトル行がセパレータとして機能）

### 明細部

#### 出力レベル制御（ビットマスク）

| ビット | 値 | 出力内容 | 対応コード |
|-------|---|---------|----------|
| 0 | 0x001 | プライマリスーパーブロック | 行211-212 |
| 1 | 0x002 | 各CGのスーパーブロックコピー | 行260-267 |
| 2 | 0x004 | シリンダーグループサマリー（csum配列） | 行229-253 |
| 3 | 0x008 | CGヘッダー | 行278-279 |
| 4 | 0x010 | inodeマップ（使用中inodeビットマップ） | 行280-281 |
| 5 | 0x020 | フラグメントマップ（空きフラグメントビットマップ） | 行282-283 |
| 6 | 0x040 | クラスターマップ+クラスターサマリー | 行284-287 |
| 8 | 0x100 | inode構造体ダンプ | 行303/505 |
| 9 | 0x200 | ブロック割り当て一覧（直接+間接） | 行356-466/508-607 |

デフォルトレベルは0xff（行134）で、0x001〜0x040の全出力が有効。

#### inode ブロック割り当て出力（dump_whole_ufs1_inode/dump_whole_ufs2_inode）

各inodeについて以下を出力：
1. **inode構造体ダンプ**（level & 0x100）: 全フィールド
2. **直接ブロック一覧**（level & 0x200）: di_db[0..UFS_NDADDR-1]
3. **単一間接ブロック一覧**: di_ib[0]が指すブロックの内容
4. **二重間接ブロック一覧**: di_ib[1]が指す間接ブロックツリー
5. **三重間接ブロック一覧**: di_ib[2]が指す間接ブロックツリー

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| special | デバイスまたはファイルシステム | Yes |
| -g cylinder_group | 特定CGのみ出力（-1で最後のCG） | No |
| -i inode | 特定inodeのみ出力 | No |
| -l level | 出力レベルビットマスク（0x001〜0x3ff） | No |
| -o outfile | 出力先ファイル（"-"で標準出力） | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | シリンダーグループ番号 | 昇順 |
| 2 | inode番号 | 昇順 |

### 改ページ条件

改ページなし

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| UFS スーパーブロック | ファイルシステム構造情報 | sbfind() |
| シリンダーグループ | CG情報 | bread()による直接読取 |
| inode | ファイルメタデータ | getinode()による読取 |
| 間接ブロック | ブロック割り当て情報 | bread()による直接読取 |

### テーブル別参照項目詳細

#### struct fs（スーパーブロック）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| 全フィールド | DBG_DUMP_FS出力 | sbfind() | デバッグマクロで全項目ダンプ |
| fs_ncg | CGループ範囲 | sbfind() | シリンダーグループ数 |
| fs_ipg | inodeループ範囲 | sbfind() | CG当たりinode数 |
| fs_cssize | csumバッファサイズ | sbfind() | |
| fs_csaddr | csumディスク位置 | sbfind() | |

#### union dinodep（inode）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| di_nlink | リンク数（0ならスキップ） | getinode() | 未使用inode判定 |
| di_size | ファイルサイズ | getinode() | ブロック数計算に使用 |
| di_ib[0..2] | 間接ブロックポインタ | getinode() | 1段/2段/3段間接 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| rb（残りブロック数） | howmany(di_size, fs_bsize) - UFS_NDADDR | N/A | 行364/516 |
| CGスーパーブロック位置 | fsbtodb(cgsblock(fs, cylno)) | N/A | 行262 |
| CG位置 | fsbtodb(cgtod(fs, cylno)) | N/A | 行273 |
| cg_start | cfg_cg==-2: 0, cfg_cg==-1: fs_ncg-1, otherwise: cfg_cg | N/A | 行215-227 |
| cg_stop | cfg_cg==-2: fs_ncg, cfg_cg==-1: fs_ncg, otherwise: cfg_cg+1 | N/A | 行215-227 |
| inodeループ範囲 | cg_start*fs_ipg ... cg_stop*fs_ipg | N/A | 行305-308 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コマンド実行] --> B[getoptによるオプション解析]
    B --> C[デバイス名解決]
    C --> D[ufs_disk_fillout_blank + sbfind]
    D --> E[DBG_OPEN: 出力先オープン]
    E --> F{level & 0x001?}
    F -->|Yes| G[DBG_DUMP_FS: プライマリSBダンプ]
    F -->|No| H{level & 0x004?}
    G --> H
    H -->|Yes| I[csumサマリー読取・ダンプ]
    H -->|No| J{level & 0xf8?}
    I --> J
    J -->|Yes| K[CGループ]
    K --> L{level & 0x002?}
    L -->|Yes| M[SBコピーダンプ]
    L -->|No| N[CG bread]
    M --> N
    N --> O{level & 0x008?}
    O -->|Yes| P[DBG_DUMP_CG]
    O -->|No| Q{level & 0x010?}
    P --> Q
    Q -->|Yes| R[DBG_DUMP_INMAP]
    Q -->|No| S{level & 0x020?}
    R --> S
    S -->|Yes| T[DBG_DUMP_FRMAP]
    S -->|No| U{level & 0x040?}
    T --> U
    U -->|Yes| V[DBG_DUMP_CLMAP + CLSUM]
    U -->|No| K
    V --> K
    K -->|完了| W{level & 0x300?}
    J -->|No| W
    W -->|Yes| X[inodeループ]
    X --> Y[DUMP_WHOLE_INODE]
    Y --> X
    X -->|完了| Z[DBG_CLOSE]
    W -->|No| Z
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| スーパーブロック取得失敗 | sbfind失敗 | "superblock fetch failed: disk.d_error" | デバイス名確認 |
| 不正CG番号 | -gに-1未満の値 | usage表示 | -1以上を指定 |
| 不正inode番号 | -iに負の値 | usage表示 | 0以上を指定 |
| 不正レベル値 | -lに0x1未満または0x3ff超 | usage表示 | 0x1-0x3ff範囲を指定 |
| bread失敗 | ディスク読取失敗 | "bread: disk.d_error" | デバイスアクセス確認 |
| getinode失敗 | inode読取失敗 | "getinode: disk.d_error" | ファイルシステム修復 |
| メモリ不足 | calloc/malloc失敗 | "calloc/malloc failed" | システムリソース確認 |
| strdup失敗 | 出力ファイル名コピー失敗 | "strdup failed" | メモリ確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1ファイルシステム（CG数 x inode数に依存、大規模FSでは大量出力） |
| 目標出力時間 | ファイルシステムサイズに依存（大規模FSでは数分〜） |
| 同時出力数上限 | 制限なし |

## セキュリティ考慮事項

- デバイスへの直接アクセスにはroot権限が通常必要
- 全inodeのメタデータ（ファイルサイズ、所有者、タイムスタンプ）が公開される
- -oオプションで出力ファイルを指定する場合、ファイルのパーミッションに注意

## 備考

- dumpfs(8)よりも詳細な情報を提供（inode個別ダンプ、間接ブロック展開）
- デバッグマクロ（DBG_DUMP_FS, DBG_DUMP_CG, DBG_DUMP_INO等）を使用しており、これらは`sbin/growfs/debug.c`で定義される
- UFS1とUFS2の両方に対応（dump_whole_ufs1_inode/dump_whole_ufs2_inode）
- cfg_cg=-2（デフォルト）で全CG、-1で最後のCG、0以上で特定CGを出力
- cfg_in=-2（デフォルト）で-g範囲の全inode、0以上で特定inodeを出力
- デフォルトの出力先は"-"（標準出力）

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行75-91: グローバル変数（disk, fsun, i1blk/i2blk/i3blk, fscs） |
| 1-2 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行77-85: sblock/acg/osblocマクロ |
| 1-3 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行98-100: DUMP_WHOLE_INODEマクロ（UFS1/UFS2分岐） |

**読解のコツ**: sblock は disk.d_fs、acg は disk.d_cg へのマクロ。i1blk/i2blk/i3blk は単一/二重/三重間接ブロック読取用バッファ。

#### Step 2: エントリーポイント（main）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行115-316: main関数 |

**主要処理フロー**:
1. **行134-137**: デフォルト値設定（cfg_lv=0xff, cfg_in=-2, cfg_cg=-2, out_file="-"）
2. **行139-173**: getoptによるオプション解析（g/i/l/o）
3. **行184-206**: デバイス名解決（/dev/r%s, /dev/%s試行）
4. **行205-207**: ufs_disk_fillout_blank + sbfind
5. **行211-212**: level & 0x001 → プライマリSBダンプ
6. **行215-227**: CG範囲決定（cg_start, cg_stop）
7. **行229-253**: level & 0x004 → csumサマリーダンプ
8. **行256-297**: level & 0xf8 → CGループ（SBコピー/CGヘッダー/各種マップ）
9. **行300-309**: level & 0x300 → inodeダンプ

#### Step 3: inode全体ダンプ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行323-470: dump_whole_ufs1_inode関数 |
| 3-2 | ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | 行477-611: dump_whole_ufs2_inode関数 |

**主要処理フロー**:
- **行338-343**: getinode()でinode読取、di_nlink==0ならスキップ
- **行349-354**: level & 0x100 → DBG_DUMP_INO（inode構造体ダンプ）
- **行364-380**: 単一間接ブロック読取・ダンプ
- **行381-411**: 二重間接ブロック展開
- **行413-466**: 三重間接ブロック展開

### プログラム呼び出し階層図

```
main() [ffsinfo.c:115]
    |
    +-- getopt() [オプション解析]
    +-- ufs_disk_fillout_blank() [libufs]
    +-- sbfind() [libufs: スーパーブロック検出]
    +-- DBG_OPEN() [出力先オープン]
    |
    +-- DBG_DUMP_FS() [level & 0x001: プライマリSB]
    |
    +-- bread() + DBG_DUMP_CSUM() [level & 0x004: csumサマリー]
    |
    +-- [CGループ] [level & 0xf8]
    |       +-- bread() + DBG_DUMP_FS() [level & 0x002: SBコピー]
    |       +-- bread() [CG読取]
    |       +-- DBG_DUMP_CG() [level & 0x008]
    |       +-- DBG_DUMP_INMAP() [level & 0x010]
    |       +-- DBG_DUMP_FRMAP() [level & 0x020]
    |       +-- DBG_DUMP_CLMAP() + DBG_DUMP_CLSUM() [level & 0x040]
    |
    +-- [inodeループ] [level & 0x300]
            +-- DUMP_WHOLE_INODE()
                   +-- dump_whole_ufs1_inode() or dump_whole_ufs2_inode()
                          +-- getinode() [libufs: inode読取]
                          +-- DBG_DUMP_INO() [level & 0x100]
                          +-- bread() [間接ブロック読取]
                          +-- DBG_DUMP_IBLK() [level & 0x200]
```

### データフロー図

```
[入力]                         [処理]                       [出力]

デバイス名 ──────────> ufs_disk_fillout_blank()       ──> diskハンドル
                            |                                |
ディスク ─────────> sbfind() [SB読取]                ──> struct fs (sblock)
                            |                                |
ディスク ─────────> bread() [csum/CG/SBコピー/inode]  ──> 各構造体
                            |                                |
各構造体 ─────────> DBG_DUMP_*() [デバッグマクロ]     ──> テキスト出力
                            |                                |
inode ────────────> getinode() + bread()             ──> 間接ブロック展開
                            |                                |
                      DBG_DUMP_IBLK()                 ──> ブロック一覧出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ffsinfo.c | `sbin/ffsinfo/ffsinfo.c` | ソース | メインプログラム |
| debug.h | `sbin/ffsinfo/debug.h` | ヘッダ | デバッグマクロ宣言 |
| debug.c | `sbin/growfs/debug.c` | ソース | DBG_DUMP_*マクロの実装 |
| Makefile | `sbin/ffsinfo/Makefile` | 設定 | ビルド設定 |
| fs.h | `sys/ufs/ffs/fs.h` | ヘッダ | struct fs, struct cg定義 |
| dinode.h | `sys/ufs/ufs/dinode.h` | ヘッダ | struct ufs1_dinode, struct ufs2_dinode定義 |
| libufs.h | `/usr/include/libufs.h` | ヘッダ | libufs API |
