# 通知設計書 18-Interop.Sys.INotify (IN_MOVED_FROM)

## 概要

本ドキュメントは、Linux の inotify サブシステムが提供する `IN_MOVED_FROM` 通知について、.NET ランタイムでの実装詳細と設計仕様を記述した通知設計書である。

### 本通知の処理概要

IN_MOVED_FROM は、ファイルまたはディレクトリが監視対象ディレクトリから移動された（移動元として検出された）際に Linux カーネルの inotify サブシステムが生成する通知イベントである。.NET の FileSystemWatcher では、IN_MOVED_TO とペアになった場合は Renamed イベントに、単独の場合は Deleted イベントに変換される。

**業務上の目的・背景**：ファイルの移動検知は、ファイル同期システム、バージョン管理、ファイル追跡、監査ログの記録など、多くの業務シナリオで必要とされる。IN_MOVED_FROM は IN_MOVED_TO と組み合わせてファイルリネーム（移動）を検知するために使用され、cookie 値によってペアが関連付けられる。

**通知の送信タイミング**：監視対象ディレクトリ内のファイルまたはディレクトリが rename() システムコールで移動（リネーム）された際に、カーネルが移動元に対して IN_MOVED_FROM イベントを生成する。このイベントは .NET の FileSystemWatcher.Linux 実装で処理される。

**通知の受信者**：FileSystemWatcher インスタンスの Renamed または Deleted イベントにハンドラを登録したアプリケーションコード。NotifyFilters に FileName または DirectoryName が含まれている必要がある。

**通知内容の概要**：inotify イベントとして、ウォッチ記述子（wd）、イベントマスク（IN_MOVED_FROM）、cookie（ペア関連付け用）、ファイル名（name）が通知される。対応する IN_MOVED_TO が存在する場合は Renamed イベント、存在しない場合は Deleted イベントとしてアプリケーションに伝達される。

**期待されるアクション**：受信者は通知を受け取り後、ファイル追跡の更新、同期先でのリネーム処理、監査ログの記録、キャッシュの更新などの処理を実行することが期待される。

## 通知種別

OSカーネル通知（inotify イベント）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（カーネルからの割り込み） |
| 優先度 | 通常（他の inotify イベントと同等） |
| リトライ | なし（カーネルは一度のみ通知） |

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

inotify_add_watch() で登録されたウォッチ記述子に対応する inotify ファイルディスクリプタに対してイベントが送信される。IN_MOVED_FROM は IN_MOVED_TO とペアになる可能性があり、cookie 値で関連付けられる。

## 通知テンプレート

### inotify イベント構造

| 項目 | 内容 |
|-----|------|
| イベント型 | struct inotify_event |
| マスク値 | IN_MOVED_FROM (0x00000040) |
| cookie | ペア関連付け用の一意な値（0以外） |

### イベントデータ構造

```c
struct inotify_event {
    int      wd;       // ウォッチ記述子
    uint32_t mask;     // IN_MOVED_FROM = 0x00000040
    uint32_t cookie;   // IN_MOVED_TO との関連付け用
    uint32_t len;      // name の長さ
    char     name[];   // 移動元のファイル名（NULL終端）
};
```

### .NET での変換後

```csharp
// IN_MOVED_FROM + IN_MOVED_TO ペアの場合
RenamedEventArgs {
    ChangeType = WatcherChangeTypes.Renamed,
    FullPath = "新しいパス",
    Name = "新しい名前",
    OldFullPath = "古いパス",
    OldName = "古い名前"  // IN_MOVED_FROM の name
}

// IN_MOVED_FROM 単独の場合（タイムアウト）
FileSystemEventArgs {
    ChangeType = WatcherChangeTypes.Deleted,
    FullPath = "移動元パス",
    Name = "移動元名前"
}
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| wd | ウォッチ記述子 | inotify_add_watch() の戻り値 | Yes |
| mask | イベントマスク | IN_MOVED_FROM (0x00000040) | Yes |
| cookie | イベント関連付け | カーネル生成の一意な値 | Yes |
| len | 名前長 | ファイル名のバイト長 + パディング | Yes |
| name | 移動元ファイル名 | 移動されたファイルの元の名前 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システムコール | rename() | ウォッチにIN_MOVED_FROMが設定されている | ファイル/ディレクトリの移動時（移動元） |
| システムコール | renameat() | ウォッチにIN_MOVED_FROMが設定されている | ディレクトリ相対でのリネーム時 |
| システムコール | renameat2() | ウォッチにIN_MOVED_FROMが設定されている | 拡張リネーム時 |

### .NET での有効化条件

| 条件 | 説明 |
|-----|------|
| NotifyFilters.FileName | ファイルの移動検知に必要 |
| NotifyFilters.DirectoryName | ディレクトリの移動検知に必要 |
| IncludeSubdirectories | サブディレクトリ追跡のために内部的に追加される |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| NotifyFilters に FileName/DirectoryName なし | 移動イベントを監視しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[rename() システムコール] --> B[カーネル: IN_MOVED_FROM 生成]
    B --> C[inotify キューに追加]
    C --> D[.NET: ProcessEvents スレッド]
    D --> E[TryReadEvent でイベント読み取り]
    E --> F{IN_MOVED_FROM 検出}
    F --> G[cookie と name を保存]
    G --> H{Poll で IN_MOVED_TO 待機}
    H -->|2ms以内に到着| I{cookie 一致?}
    H -->|タイムアウト| J[WatcherEvent.Deleted 生成]
    I -->|一致| K[WatcherEvent.Renamed 生成]
    I -->|不一致| J
    K --> L[EmitEvent → OnRenamed]
    J --> M[EmitEvent → OnDeleted]
    L --> N[終了]
    M --> N
```

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

### 参照テーブル一覧

該当なし（OSカーネル通知のためデータベースを使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| IN_Q_OVERFLOW | inotify イベントキューが溢れた | Error イベント発生、監視再起動 |
| ペア不成立 | IN_MOVED_TO が2ms以内に到着しない | Deleted イベントとして処理 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | カーネル設定依存 |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（24時間監視可能）

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

- 移動元と移動先の両方のパス情報が取得可能
- 監視対象ディレクトリへの読み取り権限が必要
- 監視対象外への移動は Deleted として処理される

## 備考

- IN_MOVED_FROM と IN_MOVED_TO は同じ cookie 値で関連付けられる
- 2ms のタイムアウトで IN_MOVED_TO を待機する
- 監視対象外への移動は Deleted イベントとして処理される
- IncludeSubdirectories が true の場合、サブディレクトリの追跡のために内部的に IN_MOVED_FROM/TO が追加される

---

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

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

### 推奨読解順序

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

まず、IN_MOVED_FROM の定義と関連する定数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Interop.INotify.cs | `src/libraries/Common/src/Interop/Linux/System.Native/Interop.INotify.cs` | IN_MOVED_FROM 定数の定義（0x00000040） |

**読解のコツ**: NotifyEvents enum で IN_MOVED_FROM = 0x00000040 として定義されている点、IN_MOVED_TO = 0x00000080 との関係に注目。

#### Step 2: フィルタマッピングを理解する

NotifyFilters から inotify イベントへの変換ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | FileSystemWatcher.Linux.cs | `src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs` | TranslateFilters メソッドでの IN_MOVED_FROM/TO マッピング |

**主要処理フロー**:
- **行 1290-1298 (Linux.cs)**: filtersForMoved に FileName/DirectoryName が含まれる
- **行 253-256 (Linux.cs)**: IncludeSubdirectories の場合に IN_MOVED_FROM/TO を追加

#### Step 3: ペアイベント処理を理解する

IN_MOVED_FROM と IN_MOVED_TO のペア処理ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | FileSystemWatcher.Linux.cs | `src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs` | ProcessEvent での IN_MOVED_FROM 処理 |

**主要処理フロー**:
- **行 604-631 (Linux.cs)**: IN_MOVED_FROM 検出時の処理（cookie 保存、Poll 待機）
- **行 617 (Linux.cs)**: 2ms タイムアウト定数
- **行 567-593 (Linux.cs)**: 前回の IN_MOVED_FROM との照合

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

```
[Linux カーネル]
inotify イベント (IN_MOVED_FROM)
    │
    ▼
[.NET FileSystemWatcher.Linux]
INotify.ProcessEvents()
    │
    ├─ TryReadEvent() → NotifyEvent 構造体
    │
    └─ ProcessEvent()
           │
           ├─ IN_MOVED_FROM 検出
           │      └─ cookie/name を保存
           │
           ├─ Poll で IN_MOVED_TO 待機 (2ms)
           │
           └─ [結果判定]
                  │
                  ├─ [ペア成立] WatcherEvent.Renamed
                  │
                  └─ [タイムアウト] WatcherEvent.Deleted
```

### データフロー図

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

rename()                カーネル inotify
システムコール  ───▶    IN_MOVED_FROM 生成  ───▶  inotify キュー
                              │
                              ▼
                        .NET ProcessEvent()
                              │
                        [cookie/name 保存]
                              │
                        [Poll 2ms]
                              │
                 ┌────────────┴────────────┐
                 ▼                         ▼
          IN_MOVED_TO 到着           タイムアウト
                 │                         │
                 ▼                         ▼
          Renamed イベント          Deleted イベント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Interop.INotify.cs | `src/libraries/Common/src/Interop/Linux/System.Native/Interop.INotify.cs` | ソース | IN_MOVED_FROM 定数定義 |
| FileSystemWatcher.Linux.cs | `src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs` | ソース | IN_MOVED_FROM イベント処理、ペアマッチング |
| FileSystemWatcher.cs | `src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs` | ソース | NotifyFilters、Renamed/Deleted イベント定義 |
| RenamedEventArgs.cs | `src/libraries/System.IO.FileSystem.Watcher/src/System/IO/RenamedEventArgs.cs` | ソース | リネームイベント引数（OldName/OldFullPath） |
