# 帳票設計書 32-stat

## 概要

本ドキュメントは、FreeBSDのstatコマンドが生成するファイルステータスレポートの設計を記述する。statはファイルのメタデータ（サイズ、パーミッション、タイムスタンプ、所有者など）を柔軟なフォーマットで標準出力にレポートするCLIユーティリティである。

### 本帳票の処理概要

statコマンドは、stat(2)またはlstat(2)システムコールを使用してファイルのメタデータ情報を取得し、指定されたフォーマットに従って人間が読める形式で出力する。readlinkコマンドとしても動作可能である。

**業務上の目的・背景**：ファイルシステム上のファイルやディレクトリの詳細な属性情報を確認することは、システム管理やセキュリティ監査において基本的な作業である。statコマンドは、ls -lよりも詳細かつカスタマイズ可能な形式でファイル情報を提供し、スクリプトからの利用にも適している。

**帳票の利用シーン**：ファイルのパーミッション確認やセキュリティ監査、シェルスクリプトでのファイル属性判定、ファイルのタイムスタンプ（atime/mtime/ctime/birthtime）の詳細確認、NFSハンドルによるファイル情報取得、ファイルのhole（スパースファイル）領域の確認に利用される。

**主要な出力内容**：
1. デバイス番号、inode番号、パーミッション、リンク数
2. 所有者UID/GID（名前/数値）
3. ファイルサイズ、ブロック数、ブロックサイズ
4. アクセス時刻、変更時刻、ステータス変更時刻、作成時刻
5. ファイルフラグ、シンボリックリンク先
6. hole領域の範囲（-hオプション）

**帳票の出力タイミング**：ユーザーがコマンドラインからstatコマンドを実行した際に即時出力される。

**帳票の利用者**：システム管理者、開発者、セキュリティ監査担当者

## 帳票種別

一覧表 / 詳細情報レポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | CLIターミナル | N/A | `stat [options] [file ...]` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（標準出力） |
| 用紙サイズ | N/A（ターミナル出力） |
| 向き | N/A |
| ファイル名 | N/A（標準出力） |
| 出力方法 | 標準出力（stdout） |
| 文字コード | ロケール依存 |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

statは複数の出力形式をサポートする。主要な形式は以下の通り。

```
┌──────────────────────────────────────────────────────────────────┐
│  ファイル属性情報（1行または複数行）                                 │
│  （フォーマットはオプションにより変化）                               │
└──────────────────────────────────────────────────────────────────┘
```

### ヘッダー部

該当なし（各ファイルの情報が直接出力される）

### 明細部

#### DEF_FORMAT（デフォルト形式）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | st_dev | デバイス番号 | stat.st_dev | %d | 可変 |
| 2 | st_ino | inode番号 | stat.st_ino | %i | 可変 |
| 3 | st_mode | パーミッション（文字列） | stat.st_mode + strmode() | %Sp | 10文字 |
| 4 | st_nlink | リンク数 | stat.st_nlink | %l | 可変 |
| 5 | st_uid | 所有者名 | stat.st_uid + user_from_uid() | %Su | 可変 |
| 6 | st_gid | グループ名 | stat.st_gid + group_from_gid() | %Sg | 可変 |
| 7 | st_rdev | rawデバイス番号 | stat.st_rdev | %r | 可変 |
| 8 | st_size | ファイルサイズ | stat.st_size | %z | 可変 |
| 9 | st_atime | アクセス時刻 | stat.st_atimespec | "%Sa" | 可変 |
| 10 | st_mtime | 変更時刻 | stat.st_mtimespec | "%Sm" | 可変 |
| 11 | st_ctime | ステータス変更時刻 | stat.st_ctimespec | "%Sc" | 可変 |
| 12 | st_birthtime | 作成時刻 | stat.st_birthtimespec | "%SB" | 可変 |
| 13 | st_blksize | ブロックサイズ | stat.st_blksize | %k | 可変 |
| 14 | st_blocks | ブロック数 | stat.st_blocks | %b | 可変 |
| 15 | st_flags | ファイルフラグ | stat.st_flags | %#Xf | 可変 |
| 16 | filename | ファイル名 | 引数 | %N | 可変 |

#### LINUX_FORMAT（-x形式）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | File | ファイル名 | 引数 | %N | 可変 |
| 2 | Size | ファイルサイズ | stat.st_size | %-11z | 11文字 |
| 3 | FileType | ファイル種別 | stat.st_mode | %HT | 可変 |
| 4 | Mode | パーミッション（8進/文字列） | stat.st_mode | %OMp%03OLp/%.10Sp | 可変 |
| 5 | Uid | 所有者（番号/名前） | stat.st_uid | %5u/%8Su | 可変 |
| 6 | Gid | グループ（番号/名前） | stat.st_gid | %5g/%8Sg | 可変 |
| 7 | Device | デバイス番号 | stat.st_dev | %Hd,%Ld | 可変 |
| 8 | Inode | inode番号 | stat.st_ino | %i | 可変 |
| 9 | Links | リンク数 | stat.st_nlink | %l | 可変 |
| 10 | Access | アクセス時刻 | stat.st_atimespec | %Sa | 可変 |
| 11 | Modify | 変更時刻 | stat.st_mtimespec | %Sm | 可変 |
| 12 | Change | ステータス変更時刻 | stat.st_ctimespec | %Sc | 可変 |
| 13 | Birth | 作成時刻 | stat.st_birthtimespec | %SB | 可変 |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ファイルパス | 情報を表示するファイル（複数指定可、省略時はstdin） | No |
| フォーマット | 出力形式の選択（-f/-l/-r/-s/-x） | No |
| タイムフォーマット | 時刻表示形式（-t） | No |
| シンボリックリンク追跡 | -Lオプションでリンク先を参照 | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 引数の指定順 | 該当なし |

### 改ページ条件

改ページなし

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| ファイルシステムメタデータ | stat(2)/lstat(2)による取得 | ファイルパス |
| /etc/passwd | UID→ユーザー名変換 | user_from_uid() |
| /etc/group | GID→グループ名変換 | group_from_gid() |

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

#### struct stat（ファイルメタデータ）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| st_dev | デバイス番号 | stat(2)/lstat(2) | major/minor分割可能 |
| st_ino | inode番号 | stat(2)/lstat(2) | |
| st_mode | パーミッション | stat(2)/lstat(2) | strmode()で文字列化 |
| st_nlink | ハードリンク数 | stat(2)/lstat(2) | |
| st_uid | 所有者UID | stat(2)/lstat(2) | |
| st_gid | グループGID | stat(2)/lstat(2) | |
| st_size | ファイルサイズ | stat(2)/lstat(2) | バイト単位 |
| st_blocks | 512バイトブロック数 | stat(2)/lstat(2) | |
| st_blksize | I/Oブロックサイズ | stat(2)/lstat(2) | |
| st_atimespec | アクセス時刻 | stat(2)/lstat(2) | ナノ秒精度 |
| st_mtimespec | 変更時刻 | stat(2)/lstat(2) | ナノ秒精度 |
| st_ctimespec | ステータス変更時刻 | stat(2)/lstat(2) | ナノ秒精度 |
| st_birthtimespec | 作成時刻 | stat(2)/lstat(2) | FreeBSD固有 |
| st_flags | ファイルフラグ | stat(2)/lstat(2) | fflagstostr()で文字列化 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| デバイスmajor | major(st_dev) | N/A | %Hd |
| デバイスminor | minor(st_dev) | N/A | %Ld |
| モードhigh | st_mode >> 12 | N/A | %Hp |
| モードmid | (st_mode >> 9) & 07 | N/A | %Mp |
| モードlow | st_mode & 0777 | N/A | %Lp |
| FLOAT時刻 | tv_sec.tv_nsec | ナノ秒精度 | %F形式 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コマンド実行] --> B[オプション解析]
    B --> C{readlinkモード?}
    C -->|Yes| D[statfmt = "%Y"]
    C -->|No| E{フォーマット選択}
    E -->|デフォルト| F[DEF_FORMAT]
    E -->|-l| G[LS_FORMAT]
    E -->|-r| H[RAW_FORMAT]
    E -->|-s| I[SHELL_FORMAT]
    E -->|-x| J[LINUX_FORMAT]
    D --> K[ファイルループ]
    F --> K
    G --> K
    H --> K
    I --> K
    J --> K
    K --> L{-hオプション?}
    L -->|Yes| M[listholes: hole領域出力]
    L -->|No| N{stat/lstat/fhstat}
    N --> O[output関数]
    O --> P[format1関数でフォーマット]
    P --> Q[stdout出力]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイル不在 | 指定ファイルが存在しない | warn: No such file or directory | ファイルパス確認 |
| フォーマット不正 | -fで不正なフォーマット指定 | "bad format" | フォーマット文字列修正 |
| オプション競合 | 複数の出力形式を同時指定 | "can't use format 'X' with 'Y'" | オプション選択を1つに |
| NFS handle不正 | -Hオプションで不正ハンドル | errno = EINVAL | ハンドル値確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1〜数千ファイル（引数の数） |
| 目標出力時間 | ファイル数に比例（通常ミリ秒単位） |
| 同時出力数上限 | 制限なし |

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

- ファイルのパーミッション情報を表示するため、適切なファイルアクセス権限が必要
- -Hオプション（NFS handle）による情報取得はroot権限が必要な場合がある
- -qオプションでエラー出力を抑制可能

## 備考

- readlinkコマンドとして起動された場合は、シンボリックリンクの読み取り専用モードで動作する
- FreeBSD固有のst_birthtimespec（作成時刻）およびst_flags（ファイルフラグ）をサポート
- FLOAT出力形式（%F）はタイムスタンプのナノ秒精度表示に対応

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | stat.c | `usr.bin/stat/stat.c` | 行97-118: DEF_FORMAT, RAW_FORMAT, LS_FORMAT, SHELL_FORMAT, LINUX_FORMAT定数 |
| 1-2 | stat.c | `usr.bin/stat/stat.c` | 行121-182: フォーマット文字定数（FLAG_*, FMT_*, SHOW_*） |

**読解のコツ**: フォーマット文字は%に続く修飾子で構成される。SHOW_*がデータフィールド、FMT_*が出力形式、HIGH_PIECE/LOW_PIECEがサブフィールド選択を示す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stat.c | `usr.bin/stat/stat.c` | 行209-414: main関数 |

**主要処理フロー**:
1. **行233-239**: readlinkモード判定（getprogname()による）
2. **行246-288**: getoptによるオプション解析
3. **行316-349**: フォーマット文字列の選択（fmtcharに基づく）
4. **行354-411**: ファイルごとのループ処理（stat/lstat/fstat/fhstat呼び出し）

#### Step 3: フォーマット出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | stat.c | `usr.bin/stat/stat.c` | 行445-632: output関数 - フォーマット文字列のパース |
| 3-2 | stat.c | `usr.bin/stat/stat.c` | 行637-1110: format1関数 - 各フィールドのフォーマット処理 |

**主要処理フロー**:
- **行454-463**: 非フォーマット文字の直接出力
- **行475-497**: 簡易フォーマット（%n, %t, %%, %@）
- **行521-609**: フォーマット文字列のパース（flags/size/prec/ofmt/hilo/what）
- **行662-971**: format1内のswitch文による各SHOW_*フィールドの処理

#### Step 4: hole検出処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | stat.c | `usr.bin/stat/stat.c` | 行1121-1185: fdlistholes/listholes関数 |

**主要処理フロー**:
- **行1139-1167**: SEEK_HOLE/SEEK_DATAによるスパースファイル領域の検出

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

```
main()
    |
    +-- getprogname() [readlinkモード判定]
    +-- getopt() [オプション解析]
    |
    +-- [ファイルループ]
    |       +-- stat() / lstat() / fstat() / fhstat()
    |       +-- output()
    |              +-- format1() [各フィールド処理]
    |                     +-- strmode() [パーミッション文字列化]
    |                     +-- user_from_uid() [UID→名前]
    |                     +-- group_from_gid() [GID→名前]
    |                     +-- fflagstostr() [フラグ文字列化]
    |                     +-- readlink() [シンボリックリンク]
    |                     +-- realpath() [実パス解決]
    |                     +-- localtime() / strftime() [時刻フォーマット]
    |
    +-- listholes() [-hオプション]
            +-- fdlistholes()
                   +-- lseek(SEEK_HOLE/SEEK_DATA)
```

### データフロー図

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

ファイルパス引数 ──> stat(2)/lstat(2)               ──> struct stat
                        |                                 |
フォーマット文字列 ──> output()                       ──> フォーマット済み文字列
                        |                                 |
struct stat ──────> format1()                       ──> 各フィールド値
                        |                                 |
/etc/passwd ─────> user_from_uid()                  ──> ユーザー名
/etc/group ──────> group_from_gid()                 ──> グループ名
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stat.c | `usr.bin/stat/stat.c` | ソース | メインプログラム（全機能を単一ファイルに実装） |
| Makefile | `usr.bin/stat/Makefile` | 設定 | ビルド設定（readlinkリンクを含む） |
| stat.h | `/usr/include/sys/stat.h` | ヘッダ | struct stat定義 |
