# 機能設計書 111-ファイル転送

## 概要

本ドキュメントは、libuvのファイル転送機能 `uv_fs_sendfile()` の設計仕様を記述する。ファイルディスクリプタ間でデータを効率的に転送するための非同期API設計について定義する。

### 本機能の処理概要

`uv_fs_sendfile()` は、入力ファイルディスクリプタから出力ファイルディスクリプタへデータを転送する機能である。OSのsendfile()システムコールやcopy_file_range()を活用し、カーネル空間内でのゼロコピー転送を試みることで、ユーザー空間でのバッファリングを最小化し高効率なデータ転送を実現する。

**業務上の目的・背景**：大容量ファイルのコピーやネットワーク越しのファイル送信において、ユーザー空間バッファを経由せずカーネル空間内で直接データ転送を行うことで、CPUとメモリの使用効率を大幅に改善する。libuvのファイルコピー機能（uv_fs_copyfile）の内部でも利用される基盤的な転送機能である。

**機能の利用シーン**：ファイルサーバーからクライアントへの大容量ファイル配信、ファイルバックアップ処理、ファイルコピー処理の内部実装、パイプを介したデータストリーミングなどで使用される。

**主要な処理内容**：
1. uv_fs_tリクエストの初期化（INIT(SENDFILE)マクロ）
2. 入力FD、出力FD、オフセット、転送サイズの設定
3. コールバック指定時はスレッドプールへの非同期ディスパッチ（POST マクロ）
4. コールバックNULL時は同期実行（uv__fs_sendfile直接呼び出し）
5. プラットフォーム別の最適転送メカニズム選択（copy_file_range / sendfile / エミュレーション）
6. 転送完了後のコールバック呼び出し

**関連システム・外部連携**：OSカーネルのsendfile(2)システムコール（Linux/macOS/FreeBSD/SunOS）、Linux固有のcopy_file_range(2)システムコール、uv_fs_copyfile()からの内部呼び出し。

**権限による制御**：ファイルディスクリプタの読み取り・書き込み権限が必要。出力先が読み取り専用の場合はエラーとなる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 24 | ファイルシステム操作 | 主機能 | uv_fs_sendfile()によるファイル間データ転送のAPI定義 |

## 機能種別

データ転送処理（ファイルディスクリプタ間のカーネルレベルデータコピー）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| loop | uv_loop_t* | Yes | イベントループハンドル（同期実行時はNULL可） | NULLの場合は同期実行 |
| req | uv_fs_t* | Yes | ファイルシステムリクエスト | NULLの場合UV_EINVALを返す |
| out_fd | uv_file | Yes | 出力先ファイルディスクリプタ | 有効なFDであること |
| in_fd | uv_file | Yes | 入力元ファイルディスクリプタ | 有効なFDであること |
| in_offset | int64_t | Yes | 入力ファイルの読み取り開始オフセット | 非負整数 |
| length | size_t | Yes | 転送するバイト数 | 0以上 |
| cb | uv_fs_cb | No | 完了コールバック（NULLで同期実行） | - |

### 入力データソース

ユーザーが指定するファイルディスクリプタ（事前にuv_fs_open等で取得）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時は0、エラー時は負のエラーコード |
| req->result | ssize_t | 転送されたバイト数。エラー時は負のエラーコード |
| req->off | int64_t | 更新されたオフセット位置（転送後） |

### 出力先

コールバック関数への通知（非同期時）、または戻り値としての直接返却（同期時）。

## 処理フロー

### 処理シーケンス

```
1. リクエスト初期化（INIT(SENDFILE)マクロ）
   └─ req->fs_type = UV_FS_SENDFILE、result/ptr/path等を初期化
2. パラメータ設定
   └─ req->flags = in_fd, req->file = out_fd, req->off = in_offset, req->bufsml[0].len = length
3. 実行モード分岐（POST マクロ）
   ├─ cb != NULL: スレッドプールへ非同期ディスパッチ → uv__fs_work → uv__fs_sendfile
   └─ cb == NULL: 直接 uv__fs_work を呼び出し → 結果をreq->resultに設定
4. プラットフォーム別転送処理（uv__fs_sendfile）
   ├─ Linux: copy_file_range() → 失敗時 sendfile() → 失敗時エミュレーション
   ├─ macOS/FreeBSD: sendfile() → 失敗時エミュレーション
   └─ その他: read/writeによるエミュレーション（uv__fs_sendfile_emul）
5. 完了処理
   └─ 非同期時: uv__fs_done → ユーザーコールバック呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[uv_fs_sendfile 呼び出し] --> B[INIT: リクエスト初期化]
    B --> C[パラメータ設定: in_fd, out_fd, offset, length]
    C --> D{cb != NULL?}
    D -->|Yes| E[スレッドプールへ非同期ディスパッチ]
    D -->|No| F[同期実行: uv__fs_work直接呼び出し]
    E --> G[uv__fs_sendfile]
    F --> G
    G --> H{プラットフォーム判定}
    H -->|Linux| I[copy_file_range試行]
    H -->|macOS/FreeBSD| K[sendfile試行]
    H -->|その他| M[エミュレーション]
    I -->|成功| N[転送完了]
    I -->|ENOSYS| J[sendfile試行]
    J -->|成功| N
    J -->|EINVAL/EIO| M
    K -->|成功| N
    K -->|EINVAL/EIO| M
    M --> N
    N --> O{非同期?}
    O -->|Yes| P[uv__fs_done コールバック呼び出し]
    O -->|No| Q[結果を返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-111-1 | ゼロコピー優先 | 可能な場合はカーネルのゼロコピー機構を使用する | Linux/macOS/FreeBSD |
| BR-111-2 | フォールバック転送 | ゼロコピーが失敗した場合はread/writeエミュレーションにフォールバック | sendfileがEINVAL/EIO/ENOTSOCK/EXDEVを返した場合 |
| BR-111-3 | 書き込みエラー優先報告 | 読み取りエラーはnsent==0の場合のみ報告、書き込みエラーは常に報告 | エミュレーションモード時 |
| BR-111-4 | pread/readフォールバック | preadが失敗した場合はreadにフォールバック | EIO/ESPIPEエラー時 |

### 計算ロジック

エミュレーション時のバッファサイズは8192バイト固定。転送は8KBチャンク単位で行われ、書き込み先がEAGAINを返した場合はpoll()で書き込み可能になるまで待機する。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（ファイルシステム操作のみ）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | パラメータ不正 | reqがNULL | 有効なリクエストを渡す |
| UV_EBADF | FD不正 | 無効なファイルディスクリプタ | 有効なFDを指定する |
| UV_EIO | I/Oエラー | ディスクI/Oエラー発生 | リトライまたはエラー通知 |
| UV_EAGAIN | 一時的エラー | 非ブロッキングFDで送信不可 | poll()で待機後リトライ（エミュレーション時は自動） |
| UV_ECANCELED | キャンセル | uv_cancel()で操作がキャンセルされた | 必要に応じて再実行 |

### リトライ仕様

エミュレーションモードでは、書き込み先がEAGAIN/EWOULDBLOCKを返した場合、poll()で書き込み可能になるまで自動的に待機してリトライする。読み取り時のEINTRは自動リトライされる。

## トランザクション仕様

ファイルシステム操作のためトランザクション管理は行わない。部分的な転送が発生した場合、req->resultに実際に転送されたバイト数が設定される。

## パフォーマンス要件

- Linux環境ではcopy_file_range(2)を最優先で使用し、カーネル空間内でのゼロコピー転送を実現
- sendfile(2)はソケット出力時に最適化されたカーネルパスを使用
- エミュレーションモードでも8KBバッファで効率的な転送を実行
- 非同期モードではスレッドプール（UV__WORK_FAST_IO）で実行され、イベントループをブロックしない

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

- ファイルディスクリプタの権限はOS側で管理される
- 入力元FDの読み取り権限と出力先FDの書き込み権限が必要
- 転送データはユーザー空間を経由しない場合がある（ゼロコピー時）

## 備考

- req->flagsフィールドにin_fdを格納する実装は"hack"とコメントされている（src/unix/fs.c L2149）
- uv_fs_copyfile()の内部で本関数が使用されている（src/unix/fs.c L1386）
- SunOS環境ではsendfile()がEINVALを返してもデータ書き込みが行われる場合があり、オフセット更新で検出する

---

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

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

### 推奨読解順序

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

まず、ファイルシステムリクエストの基本構造体を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **L1449-1460**: uv_fs_s構造体の定義。fs_type, loop, cb, result, ptr, path, statbufフィールド |
| 1-2 | uv.h | `include/uv.h` | **L1401-1440**: uv_fs_type列挙型。UV_FS_SENDFILEが定義されている |

**読解のコツ**: uv_fs_tはuv_req_tのサブクラス（UV_REQ_FIELDSマクロ展開）。UV_FS_PRIVATE_FIELDSにはプラットフォーム固有のフィールド（file, flags, off, bufsml等）が含まれる。sendfileではbufsml[0].lenに転送サイズ、flagsにin_fdを格納するという特殊な使い方をしている。

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

公開APIであるuv_fs_sendfile()から処理が始まる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | uv.h | `include/uv.h` | **L1584-1590**: uv_fs_sendfile()のAPI宣言 |
| 2-2 | fs.c | `src/unix/fs.c` | **L2141-2154**: uv_fs_sendfile()の実装 |

**主要処理フロー**:
1. **L2148**: INIT(SENDFILE)マクロでリクエスト初期化
2. **L2149**: req->flags = in_fd（入力FDをflagsフィールドに格納、hackコメント付き）
3. **L2150**: req->file = out_fd（出力FDをfileフィールドに格納）
4. **L2151**: req->off = off（オフセット設定）
5. **L2152**: req->bufsml[0].len = len（転送サイズ設定）
6. **L2153**: POSTマクロで非同期/同期実行を分岐

#### Step 3: マクロによる共通処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fs.c | `src/unix/fs.c` | **L90-104**: INITマクロ。reqのNULLチェック、UV_FS_SENDFILEの設定、各フィールド初期化 |
| 3-2 | fs.c | `src/unix/fs.c` | **L139-149**: POSTマクロ。cb!=NULLの場合はuv__work_submitでスレッドプールへディスパッチ |

#### Step 4: ワーカースレッドでの実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fs.c | `src/unix/fs.c` | **L1700-1750**: uv__fs_work関数。fs_typeに応じたディスパッチテーブル |
| 4-2 | fs.c | `src/unix/fs.c` | **L1728**: X(SENDFILE, uv__fs_sendfile(req))の呼び出し |
| 4-3 | fs.c | `src/unix/fs.c` | **L1753-1765**: uv__fs_done関数。完了時のコールバック呼び出し |

#### Step 5: プラットフォーム別の転送実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | fs.c | `src/unix/fs.c` | **L1029-1138**: uv__fs_sendfile関数。プラットフォーム別分岐 |
| 5-2 | fs.c | `src/unix/fs.c` | **L1047-1050**: Linux: copy_file_range試行とsendfileフォールバック |
| 5-3 | fs.c | `src/unix/fs.c` | **L1076-1130**: macOS/FreeBSD: sendfile()実装（引数順序が逆転） |
| 5-4 | fs.c | `src/unix/fs.c` | **L831-942**: uv__fs_sendfile_emul関数。read/writeによるエミュレーション |

**読解のコツ**: sendfile(2)のインターフェースはOS間で異なる。Linux/SunOSではsendfile(out_fd, in_fd, ...)だが、macOS/FreeBSDではsendfile(in_fd, out_fd, ...)と引数順序が逆転する。エミュレーション関数ではpread()を優先使用し、EIO/ESPIPE時にread()にフォールバックする設計になっている。

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

```
uv_fs_sendfile()                         [src/unix/fs.c L2141]
    |
    +-- INIT(SENDFILE)                    [src/unix/fs.c L90-104]
    |
    +-- POST                              [src/unix/fs.c L139-149]
         |
         +-- uv__work_submit()            [スレッドプールへ投入]
              |
              +-- uv__fs_work()           [src/unix/fs.c L1700]
              |    |
              |    +-- uv__fs_sendfile()  [src/unix/fs.c L1029]
              |         |
              |         +-- copy_file_range()      [Linux固有]
              |         +-- sendfile()             [Linux/macOS/FreeBSD/SunOS]
              |         +-- uv__fs_sendfile_emul() [src/unix/fs.c L831]
              |              |
              |              +-- pread() / read()
              |              +-- write()
              |              +-- poll()             [EAGAIN時]
              |
              +-- uv__fs_done()           [src/unix/fs.c L1753]
                   |
                   +-- req->cb(req)       [ユーザーコールバック]
```

### データフロー図

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

in_fd (入力FD)  ────> uv__fs_sendfile()                ────> out_fd (出力FD)
in_offset       ────>   |                               ────> req->result (転送バイト数)
length          ────>   +-> copy_file_range() [Linux]   ────> req->off (更新後オフセット)
                        +-> sendfile() [OS固有]
                        +-> read()/write() [エミュレーション]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | uv_fs_sendfile()のAPI宣言、uv_fs_t構造体定義 |
| fs.c (Unix) | `src/unix/fs.c` | ソース | Unix系のsendfile実装（API、ワーカー、プラットフォーム別処理） |
| fs.c (Win) | `src/win/fs.c` | ソース | Windows版のsendfile実装 |
| uv-common.h | `src/uv-common.h` | ヘッダー | 共通マクロ・内部関数宣言 |
| internal.h | `src/unix/internal.h` | ヘッダー | Unix内部関数宣言 |
| threadpool.c | `src/threadpool.c` | ソース | スレッドプール実装（uv__work_submit） |
