# 通知設計書 42-VFS FSマウントイベント通知

## 概要

本ドキュメントは、FreeBSD カーネルの VFS（仮想ファイルシステム）サブシステムが、ファイルシステムのマウント・リマウント・アンマウント操作時に発行する devctl 通知「VFS / FS / {type}」の設計を記述する。

### 本通知の処理概要

VFS サブシステムにおいて、ファイルシステムのマウント状態が変更された際に、ユーザーランドのデーモンやスクリプトへ状態変更を通知する。MOUNT、REMOUNT、UNMOUNT の 3 種類のイベントタイプが存在する。

**業務上の目的・背景**：ファイルシステムのマウント状態変更はシステム管理上の重要なイベントである。自動バックアップの開始、ネットワーク共有の構成変更、ログ記録、セキュリティ監査など、さまざまなユースケースでマウント状態の監視が必要となる。この通知により、devd(8) 経由でこれらの自動化処理をトリガーできる。

**通知の送信タイミング**：ファイルシステムの mount(2) 成功時（MOUNT）、remount 操作成功時（REMOUNT）、unmount(2) 成功時（UNMOUNT）に送信される。

**通知の受信者**：devd(8) デーモンを経由して、ユーザーランドのファイルシステム管理スクリプトやモニタリングツールが受信する。

**通知内容の概要**：マウントポイント、マウントデバイス、ファイルシステムタイプ、FSID、オーナーUID、マウントフラグが含まれる。

**期待されるアクション**：受信者はイベントタイプに応じて、バックアップ開始・停止、NFS共有設定の更新、監査ログの記録などのアクションを実行する。

## 通知種別

devctl カーネル通知（devd(8) 経由のシステムイベント通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（カーネル内 devctl_notify） |
| 優先度 | 中 |
| リトライ | 無 |

### 送信先決定ロジック

devctl_notify() によりカーネルの devctl キューに投入される。devd(8) が /dev/devctl を監視し、イベントを受信する。

## 通知テンプレート

### devctl 通知フォーマット

| 項目 | 内容 |
|-----|------|
| system | VFS |
| subsystem | FS |
| type | MOUNT / REMOUNT / UNMOUNT |
| data | mount-point, mount-dev, mount-type, fsid, owner, flags |

### 本文テンプレート

```
!system=VFS subsystem=FS type=MOUNT
mount-point="/mnt/data" mount-dev="/dev/da0p1" mount-type="ufs" fsid=0x... owner=0 flags="local;noatime;"
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| mount-point | マウントポイントのパス | sfp->f_mntonname | Yes |
| mount-dev | マウント元デバイス | sfp->f_mntfromname | Yes |
| mount-type | ファイルシステムタイプ | sfp->f_fstypename | Yes |
| fsid | ファイルシステムID | sfp->f_fsid | Yes |
| owner | オーナーUID | sfp->f_owner | Yes |
| flags | マウントフラグ | mp->mnt_flag | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システムコール | mount(2) 成功 | sbuf_error(&sb) == 0 | MOUNT イベント発行 |
| システムコール | remount 操作成功 | sbuf_error(&sb) == 0 | REMOUNT イベント発行 |
| システムコール | unmount(2) 成功 | sbuf_error(&sb) == 0 | UNMOUNT イベント発行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| メモリ割り当て失敗 | buf = malloc(DEVCTL_LEN, M_MOUNT, M_NOWAIT) が NULL の場合 |
| sbuf エラー | sbuf_error(&sb) != 0 の場合、通知は発行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[mount/remount/unmount 操作完了] --> B[mount_devctl_event 呼び出し]
    B --> C{メモリ割り当て成功?}
    C -->|No| D[通知スキップ]
    C -->|Yes| E[sbuf にマウント情報を構築]
    E --> F{sbuf_error == 0?}
    F -->|Yes| G[devctl_notify 発行]
    F -->|No| H[通知スキップ]
    G --> I[sbuf/buf 解放]
    H --> I
    D --> J[終了]
    I --> J
```

## データベース参照・更新仕様

### 参照テーブル一覧

カーネルの mount 構造体および statfs 構造体から情報を取得する。

| 構造体フィールド | 用途 | 備考 |
|-----------|------|------|
| mp->mnt_stat.f_mntonname | マウントポイントパス | devctl_safe_quote_sb でエスケープ |
| mp->mnt_stat.f_mntfromname | マウント元デバイス名 | devctl_safe_quote_sb でエスケープ |
| mp->mnt_stat.f_fstypename | ファイルシステムタイプ名 | devctl_safe_quote_sb でエスケープ |
| mp->mnt_stat.f_fsid | ファイルシステムID | 16進数で出力 |
| mp->mnt_stat.f_owner | オーナーUID | 10進数で出力 |
| mp->mnt_flag | マウントフラグ | optnames テーブルで変換 |

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| メモリ不足 | malloc が M_NOWAIT で NULL を返した場合 | 通知をスキップ（サイレントに失敗） |
| バッファ溢れ | sbuf が SBUF_FIXEDLEN の 1024 バイトを超えた場合 | sbuf_error が非ゼロとなり通知をスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 明示的な制限なし |
| 1日あたり上限 | 明示的な制限なし |

### 配信時間帯

制限なし

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

- マウントポイントとデバイス名は devctl_safe_quote_sb() でエスケープされ、インジェクション攻撃を防止
- マウント操作のオプションは意図的に通知に含まれない（バイナリデータを含む可能性があるため）
- /dev/devctl の読み取りには root 権限が必要

## 備考

- DEVCTL_LEN は 1024 バイトに設定されており、長いパス名の場合にバッファ溢れが発生する可能性がある
- マウントフラグは optnames テーブルに基づいてセミコロン区切りの文字列として出力される

---

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

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

### 推奨読解順序

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

マウント情報を保持する構造体を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mount.h | `sys/sys/mount.h` | struct mount, struct statfs の定義 |

**読解のコツ**: mnt_flag のビットフラグと optnames テーブルの対応関係を理解すること。

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

マウント操作の完了時に通知が発行される箇所を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vfs_mount.c | `sys/kern/vfs_mount.c` | mount_devctl_event() 関数（行2969）が通知の生成・発行を行う |

**主要処理フロー**:
1. **行1290**: mount 成功時に `mount_devctl_event("MOUNT", mp, false)` を呼び出し
2. **行1553**: remount 成功時に `mount_devctl_event("REMOUNT", mp, true)` を呼び出し
3. **行2403**: unmount 成功時に `mount_devctl_event("UNMOUNT", mp, false)` を呼び出し

#### Step 3: 通知構築処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vfs_mount.c | `sys/kern/vfs_mount.c` | mount_devctl_event()（行2969-3012）で sbuf を使って通知データを構築 |

**主要処理フロー**:
- **行2977-2980**: バッファ割り当てと sbuf 初期化
- **行2981-2998**: マウント情報の sbuf への書き込み
- **行3008-3009**: sbuf エラーチェック後に devctl_notify() 発行

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

```
vfs_domount() [マウント処理]
    |
    +-- mount_devctl_event("MOUNT", mp, false)    [行1290]
            |
            +-- devctl_notify("VFS", "FS", "MOUNT", ...)

vfs_remount() [リマウント処理]
    |
    +-- mount_devctl_event("REMOUNT", mp, true)   [行1553]
            |
            +-- devctl_notify("VFS", "FS", "REMOUNT", ...)

dounmount() [アンマウント処理]
    |
    +-- mount_devctl_event("UNMOUNT", mp, false)  [行2403]
            |
            +-- devctl_notify("VFS", "FS", "UNMOUNT", ...)
```

### データフロー図

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

mount(2) / unmount(2) --> vfs_domount() / dounmount() --> mount_devctl_event()
                                                              |
struct mount ---------> sbuf 構築（パス、デバイス、        --> devctl キュー
  mnt_stat               タイプ、FSID、フラグ）                |
  mnt_flag                                                devd(8) 受信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vfs_mount.c | `sys/kern/vfs_mount.c` | ソース | VFS マウント処理と通知発行 |
| mount.h | `sys/sys/mount.h` | ヘッダー | mount, statfs 構造体定義 |
| subr_bus.c | `sys/kern/subr_bus.c` | ソース | devctl_notify() 実装 |
