From 0fd4962e4ce5160fab0362675616132f67945efe Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Wed, 3 Dec 2025 18:16:49 +0900 Subject: [PATCH] feat(Ring0): Phase 107 - Ring0.FsApi FileIo integration complete Implementation Summary: Phase 107 establishes FileBox as a unified layer over Ring0.FsApi Completed Tasks: - Task 1: FsApi SSOT review and documentation - Task 2: FileIo designed as FsApi wrapper - Task 3: Ring0FsFileIo implementation added - Task 4: Fail-Fast integration verified - Task 5: Documentation integration complete Key Changes: 1. New File: src/providers/ring1/file/ring0_fs_fileio.rs - Ring0.FsApi-based FileIo implementation - Stateful wrapper (open/read/close) - UTF-8 handling via read_to_string() - One-file-at-a-time semantics (tested) 2. provider_lock.rs: - Added init_default_filebox_provider() - Auto-registration helper for Ring0FsFileIo 3. plugin_host.rs: - with_core_from_registry_optional() auto-registers default provider - Plugin priority maintained (debug logging) - Phase 106 MissingService check still active (Fail-Fast preserved) 4. Documentation: - phase107_fsapi_fileio_bridge.md: Complete design doc - phase106_filebox_design_revised.md: Phase 107 integration notes - core_boxes_design.md: Layer diagram and principles Design Decisions: - UTF-8 handling: read_to_string() for text-focused use cases - One file at a time: open() returns Err if already open - Plugin priority: init_default_filebox_provider() fails gracefully Test Results: - cargo build --release: SUCCESS - plugin_host tests: 11 passed - ring0_fs_fileio tests: 4 passed Next Steps (Phase 108+): - minimal/no-fs profile support - write operations - multi-file handle support --- CURRENT_TASK.md | 4 +- .../current/main/core_boxes_design.md | 56 ++++-- .../main/phase106_filebox_design_revised.md | 48 ++--- .../main/phase107_fsapi_fileio_bridge.md | 120 +++++++----- src/boxes/file/provider.rs | 16 ++ src/providers/ring1/file/mod.rs | 1 + src/providers/ring1/file/ring0_fs_fileio.rs | 178 ++++++++++++++++++ src/runtime/plugin_host.rs | 29 ++- src/runtime/provider_lock.rs | 25 +++ 9 files changed, 375 insertions(+), 102 deletions(-) create mode 100644 src/providers/ring1/file/ring0_fs_fileio.rs diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 9a3c5cb5..017d5571 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -269,7 +269,7 @@ - ✅ 3つのロギングBoxパターン設計(Lightweight/Structured/Contextual) - ✅ hako_logging_design.md 作成、logging_policy.md 更新 - **スコープ**: .hako アプリケーション側のロギングベストプラクティス確立 - - **Phase 105: Logger Box Framework設計**(2025-12-04)← NEW + - **Phase 105: Logger Box Framework設計**(2025-12-04) - ✅ Logger Box インターフェース設計(ログレベル: DEBUG/INFO/WARN/ERROR) - ✅ 3つの設計パターン文書化(Lightweight/Structured/Contextual) - ✅ リファレンス実装例作成(pseudo-code、実行テストは Phase 106+) @@ -277,7 +277,7 @@ - ✅ logging_policy.md / hako_logging_design.md / ring0-inventory.md にクロスリファレンス追加 - **スコープ**: 設計+ドキュメントのみ(Rust実装なし、Phase 106+で実装) - **成果**: ConsoleBox基盤の構造化ロギングフレームワーク確立 - - **次のステップ**: Phase 106(FileBox/NetworkBox出力リダイレクト)、Phase 107(アプリ移行) + - **次のステップ**: Phase 106(FileBox provider_lock & Fail-Fast)、Phase 107(FsApi 統合 or Logger 出力先拡張) 12. **Phase 86: BoxFactory Priority 正常化** ✅ **完了**(2025-12-02) - **目的**: BoxFactory のデフォルトポリシーを `BuiltinFirst` から `StrictPluginFirst` に変更し、プラグイン版 Box が正常に使用できるよう正常化。 diff --git a/docs/development/current/main/core_boxes_design.md b/docs/development/current/main/core_boxes_design.md index 1fe77232..e5d21863 100644 --- a/docs/development/current/main/core_boxes_design.md +++ b/docs/development/current/main/core_boxes_design.md @@ -256,12 +256,35 @@ FileBox は selfhost/通常ランタイムでは事実上必須(ログ・ツ - **PluginHost**: startup 時に CoreBoxId.is_core_required() で provider をチェック - 未登録なら CoreInitError::MissingService で fail-fast -### Ring0.FsApi との関係(Phase 107 延期) +### Ring0.FsApi との関係(Phase 107 完了)✅ -Ring0.FsApi(write 能力あり)と FileIo trait(read-only)の統合は、 -Phase 107+ で実施予定。現在は概念を分離したまま。 +**Phase 107 統合完了(2025-12-03)**: -(理由: Phase 106 は provider_lock 整理に専念し、FsApi 統合は別 phase で) +FileBox の実体 I/O は、以下の層構造で Ring0.FsApi を通す設計が確立: + +``` +[FileBox (Ring1)] + ↓ provider 経由 +[Ring0FsFileIo] (FileIo 実装) + ↓ read_to_string/read 呼び出し +[Ring0.FsApi] (OS I/O 抽象) + ↓ +[std::fs] +``` + +**設計原則**: +- **FileIo = stateful**(現在開いているファイルハンドルに対する操作) + - open() でファイルを開く + - read() で内容を読み込む + - close() でファイルを閉じる +- **FsApi = stateless**(Path → データの直接変換) + - read_to_string(path) / write_all(path, data) + - exists(path) / metadata(path) + +**実装箇所**: +- `src/providers/ring1/file/ring0_fs_fileio.rs`: Ring0FsFileIo 実装 +- `src/runtime/provider_lock.rs`: init_default_filebox_provider() ヘルパー +- `src/runtime/plugin_host.rs`: 起動時自動登録 ### 5.4 今後の拡張 @@ -275,27 +298,28 @@ Phase 107+ で実施予定。現在は概念を分離したまま。 --- -## 6. CoreBoxCategory enum(Phase 87) +## 6. CoreBoxCategory enum(Phase 87 → 106) ### 6.1 役割 -CoreBoxId の分類を型安全に表現する enum。Phase 85 調査結果を完全に反映。 +CoreBoxId の分類を型安全に表現する enum。Phase 85 調査結果をベースにしつつ、 +Phase 106 で FileBox を CoreRequired 側に寄せたため、個数コメントは歴史的な値として扱う。 ```rust pub enum CoreBoxCategory { - CoreRequired, // 必須: 起動時に全て揃っていなければならない (6個) - CoreOptional, // オプション: 無くても起動できるが、標準機能として提供 (9個) + CoreRequired, // 必須: 起動時に全て揃っていなければならない (現行 7個: String/Integer/Bool/Array/Map/Console/File) + CoreOptional, // オプション: 無くても起動できるが、標準機能として提供 (現行 8個) Special, // 特殊: 言語実装に直結(Function/Result/Method/Missing) } ``` -### 6.2 Phase 85 対応表 +### 6.2 現行対応表(Phase 106 時点) -| Category | 個数 | Box 一覧 | -|----------|------|---------| -| CoreRequired | 6 | String, Integer, Bool, Array, Map, Console | -| CoreOptional | 9 | Float, Null, File, Path, Regex, Math, Time, Json, Toml | -| Special | 4 | Function, Result, Method, Missing | +| Category | Box 一覧 | +|--------------|---------------------------------------------------| +| CoreRequired | String, Integer, Bool, Array, Map, Console, File | +| CoreOptional | Float, Null, Path, Regex, Math, Time, Json, Toml | +| Special | Function, Result, Method, Missing | ### 6.3 実装効果 @@ -325,7 +349,8 @@ pub enum CoreBoxCategory { ### 7.3 今後の展開 -Phase 92 では、CoreServices の実装時に CoreBoxCategory::CoreRequired (6個) のみを対象とすることで、**「core_required は必ず揃う」**という設計原則を型レベルで保証する。 +Phase 92 では、CoreServices の実装時に CoreBoxCategory::CoreRequired のみを対象とすることで、 +**「core_required は必ず揃う」**という設計原則を型レベルで保証する(Phase 106 以降は FileBox を含む 7個)。 --- @@ -371,6 +396,7 @@ pub struct CoreServices { | Array | CoreRequired | ✅ array | Phase 91 skeleton | | Map | CoreRequired | ✅ map | Phase 91 skeleton | | Console | CoreRequired | ✅ console | Phase 91 skeleton | +| File | CoreRequired | (Ring1/FileBox) | Phase 106 で core_required 寄せ | **設計原則**: - core_required (6個) は全て CoreServices に含まれる diff --git a/docs/development/current/main/phase106_filebox_design_revised.md b/docs/development/current/main/phase106_filebox_design_revised.md index 827f99c7..4e7cbf41 100644 --- a/docs/development/current/main/phase106_filebox_design_revised.md +++ b/docs/development/current/main/phase106_filebox_design_revised.md @@ -36,6 +36,19 @@ - CoreBoxId: 「File が必須かどうか」を決定する窓口に - profile パターン: selfhost/default では必須、minimal/no-fs では optional +### 1.4 Phase 107 統合完了(2025-12-03) + +**Ring0.FsApi 統合完了**: +- ✅ Ring0FsFileIo 実装追加(src/providers/ring1/file/ring0_fs_fileio.rs) +- ✅ provider_lock に init_default_filebox_provider() 追加 +- ✅ PluginHost.with_core_from_registry_optional で自動登録 +- ✅ Phase 106 の MissingService チェックは引き続き有効(Fail-Fast 維持) + +**Phase 107 の効果**: +- 標準パスで FileBox provider が自動登録される +- MissingService エラーは基本的に起きない(プラグイン未登録時も default で補完) +- プラグイン優先原則は維持(プラグインが先に登録すれば default は使われない) + --- ## 2. Task 1: CoreBoxId を修正(カテゴリ統一) @@ -183,40 +196,13 @@ L5 付近にコメント追加: ## 5. Task 4: 起動時に FileBox provider 登録を必ず確保(Fail-Fast) -### 5.1 修正内容 +### 5.1 実装内容(概要) ファイル: `src/runtime/plugin_host.rs` -#### パターン: CoreBoxId.is_core_required() でチェック - -```rust -impl PluginHost { - pub fn with_core_from_registry( - ring0: Arc, - registry: &UnifiedBoxRegistry, - ) -> Result { - // Phase 93-95: 既存のチェック処理 - for id in CoreServices::required_ids() { - // ... (String/Integer/Bool/Array/Map/Console) - } - - // Phase 106: FileBox provider チェック追加 - // CoreBoxId がFileを必須と判定している場合、provider が登録されていることを確認 - if CoreBoxId::File.is_core_required() { - if provider_lock::get_filebox_provider().is_none() { - return Err(CoreInitError::MissingService { - box_id: CoreBoxId::File, - message: "FileBox provider not registered (required for selfhost/default profile)".to_string(), - }); - } - } - - // Phase 93-95: CoreServices 構築(既存) - let core = CoreServices { /* ... */ }; - Ok(PluginHost { ring0, core, optional: HashMap::new() }) - } -} -``` +- `CoreBoxId::is_core_required()` / `CoreServices::required_ids()` を用いて、「必須 Box は registry に型定義が存在すること」を起動時にチェック。 +- FileBox については CoreRequired 側に寄せた上で、「FileBox provider が登録されていない場合は CoreInitError::MissingService で fail-fast」するロジックを追加。 +- 具体的なコードは実装側に委ね、ここでは責務分離の方針のみを記録する。 #### テスト追加 diff --git a/docs/development/current/main/phase107_fsapi_fileio_bridge.md b/docs/development/current/main/phase107_fsapi_fileio_bridge.md index 58e1ef79..f441f327 100644 --- a/docs/development/current/main/phase107_fsapi_fileio_bridge.md +++ b/docs/development/current/main/phase107_fsapi_fileio_bridge.md @@ -39,7 +39,7 @@ --- -## 2. Task 1: FsApi を SSOT として整理(docs + 確認) +## 2. Task 1: FsApi を SSOT として整理(docs + 確認)✅ ### 2.1 実装内容 @@ -50,36 +50,58 @@ ### 2.2 やること -1. **traits.rs で FsApi の公開インターフェースを確認**: +1. **traits.rs で FsApi の公開インターフェースを確認**:✅ ```rust pub trait FsApi: Send + Sync { - fn read_file(&self, path: &Path) -> Result, IoError>; - fn write_file(&self, path: &Path, content: &[u8]) -> Result<(), IoError>; - // stat / exists 相当? + fn read_to_string(&self, path: &Path) -> Result; + fn read(&self, path: &Path) -> Result, IoError>; + fn write_all(&self, path: &Path, data: &[u8]) -> Result<(), IoError>; + fn exists(&self, path: &Path) -> bool; + fn metadata(&self, path: &Path) -> Result; + fn canonicalize(&self, path: &Path) -> Result; } ``` -2. **このドキュメント(phase107_fsapi_fileio_bridge.md)に記載**: + **確認結果**: + - ✅ read_to_string: String 直接読み込み(UTF-8変換済み) + - ✅ read: バイト列読み込み + - ✅ write_all: バイト列書き込み + - ✅ exists: 存在確認 + - ✅ metadata: ファイルメタデータ取得(is_file/is_dir/len) + - ✅ canonicalize: パス正規化 + +2. **このドキュメント(phase107_fsapi_fileio_bridge.md)に記載**:✅ - 「FsApi = OS ファイル I/O の SSOT(Rust ローカル)」 - 「FileIo = FileBox 用 provider interface。実装の 1 つとして FsApi を内部で使う」という関係 - Ring0 → Ring1 の一方向依存の図 -3. **core_boxes_design.md に一文追加**: + **層の関係**: + ``` + [FileBox (Ring1)] + ↓ provider 経由 + [Ring0FsFileIo] (FileIo 実装) + ↓ read/write 呼び出し + [Ring0.FsApi] (OS I/O 抽象) + ↓ + [std::fs] + ``` + +3. **core_boxes_design.md に一文追加**:✅(Task 5で実施) - FileBox セクションに「実体 I/O は FileIo → FsApi → OS」を記載 --- -## 3. Task 2: FileIo を「FsApi ラッパ」として設計 +## 3. Task 2: FileIo を「FsApi ラッパ」として設計✅ ### 3.1 実装内容 **ファイル**: -- `src/boxes/file/provider.rs` -- `phase107_fsapi_fileio_bridge.md` +- `src/boxes/file/provider.rs` ✅ +- `phase107_fsapi_fileio_bridge.md` ✅ ### 3.2 やること -1. **FileIo trait の役割を明確化**: +1. **FileIo trait の役割を明確化**:✅ ```rust pub trait FileIo: Send + Sync { fn caps(&self) -> FileCaps; @@ -183,18 +205,18 @@ --- -## 4. Task 3: Ring0 ベースの FileIo 実装を追加 +## 4. Task 3: Ring0 ベースの FileIo 実装を追加✅ ### 4.1 実装内容 -**ファイル候補**: -- `src/boxes/file/core_ro.rs` または `src/boxes/file/builtin_factory.rs` -- `src/runtime/provider_lock.rs`(ヘルパー関数) -- `src/runtime/plugin_host.rs`(起動時初期化) +**実装ファイル**: +- `src/providers/ring1/file/ring0_fs_fileio.rs` ✅(新規作成) +- `src/runtime/provider_lock.rs` ✅(ヘルパー関数追加) +- `src/runtime/plugin_host.rs` ✅(起動時初期化統合) ### 4.2 やること -1. **Ring0FsFileIo 実装を追加**: +1. **Ring0FsFileIo 実装を追加**:✅ - フィールド: `Arc`, `String path` - open(path): path を保持、FsApi 経由で存在確認(読み取り向け) - read(): FsApi.read_file → String 変換(from_utf8_lossy ポリシー採用) @@ -220,71 +242,77 @@ --- -## 5. Task 4: Fail-Fast & プロファイルとの整合 +## 5. Task 4: Fail-Fast & プロファイルとの整合✅ ### 5.1 実装内容 **ファイル**: -- `src/runtime/plugin_host.rs` -- `docs/development/current/main/phase106_filebox_design_revised.md` +- `src/runtime/plugin_host.rs` ✅ +- `docs/development/current/main/phase106_filebox_design_revised.md` ✅ ### 5.2 やること -1. **Phase 106 との整合確認**: +1. **Phase 106 との整合確認**:✅ - Phase 106: 「FileBox provider 未登録なら CoreInitError::MissingService」 - Phase 107: 「標準パスで Ring0FsFileIo が自動登録されるので MissingService は基本的に起きない」 - - phase106 のドキュメントに追記: 「Phase 107 で自動登録機構が追加された」 + - phase106 のドキュメントに追記: ✅「Phase 107 で自動登録機構が追加された」 -2. **将来用フック(docs に記載)**: + **確認結果**: + - ✅ with_core_from_registry_optional() で自動登録実装済み + - ✅ Phase 106 の MissingService チェックは維持(二重防御) + - ✅ プラグイン優先原則も維持(debug ログで可視化) + +2. **将来用フック(docs に記載)**:✅ - minimal/no-fs プロファイル導入時: - - `CoreBoxId::File.is_core_required(profile)` に拡張 + - `CoreBoxId::File.is_core_required(profile)` に拡張可能 - その profile では `init_default_filebox_provider()` を呼ばない - - これで「FileBox 無し環境」も可能に + - これで「FileBox 無し環境」も可能に(Phase 108 以降) --- -## 6. Task 5: ドキュメント統合 +## 6. Task 5: ドキュメント統合✅ ### 6.1 実装内容 **ファイル**: -- `docs/development/current/main/core_boxes_design.md` -- `docs/development/current/main/phase106_filebox_design_revised.md` -- `phase107_fsapi_fileio_bridge.md` (このドキュメント) -- `ring0-inventory.md` (任意) +- `docs/development/current/main/core_boxes_design.md` ✅ +- `docs/development/current/main/phase106_filebox_design_revised.md` ✅ +- `phase107_fsapi_fileio_bridge.md` (このドキュメント) ✅ ### 6.2 やること -1. **core_boxes_design.md に図と説明を追加**: +1. **core_boxes_design.md に図と説明を追加**:✅ ``` - [FileBox] + [FileBox (Ring1)] ↓ (provider経由) [Ring0FsFileIo] (FileIo実装) - ↓ (read_file/write_file呼び出し) + ↓ (read_to_string/read呼び出し) [Ring0.FsApi] (OS I/O抽象) ↓ [std::fs] ``` -2. **phase106_filebox_design_revised.md の "Phase 107" セクション更新**: - - 「Phase 107 で FsApi 統合を行う予定」が「実装済み」に変更 +2. **phase106_filebox_design_revised.md の "Phase 107" セクション更新**:✅ + - 「Phase 107 で FsApi 統合を行う予定」→「Phase 107 統合完了」に変更 + - Ring0FsFileIo 実装、自動登録、Fail-Fast 維持を明記 -3. **phase107_fsapi_fileio_bridge.md にまとめる**: +3. **phase107_fsapi_fileio_bridge.md にまとめる**:✅ - FsApi / FileIo / FileBox / provider_lock / PluginHost の関係を 1 ドキュメントで整理 + - 設計判断(UTF-8 handling, one-file-at-a-time, plugin priority)を明記 --- -## 7. 実装チェックリスト(Phase 107) +## 7. 実装チェックリスト(Phase 107)✅ 全項目完了 -- [ ] FsApi / FileIo / FileBox / provider_lock / PluginHost の関係が図付きで整理されている -- [ ] Ring0 ベースの FileIo 実装(Ring0FsFileIo)が追加されている -- [ ] selfhost/通常ランタイム起動で、デフォルトとして Ring0FsFileIo が provider_lock に登録される -- [ ] UTF-8 ハンドリング ポリシー(from_utf8_lossy採用)が docs に明記されている -- [ ] FileBox の一度に1ファイルのみ open セマンティクスが docs に明記されている -- [ ] プラグイン優先時の Err ハンドリング(debug ログ出力)が実装されている -- [ ] Phase 106 との整合確認完了(MissingService は基本レア) -- [ ] 将来用フック(minimal/no-fs プロファイル)が docs に記載されている -- [ ] ビルド・テスト完全成功(FileBox/provider_lock/plugin_host 関連テスト) +- ✅ FsApi / FileIo / FileBox / provider_lock / PluginHost の関係が図付きで整理されている +- ✅ Ring0 ベースの FileIo 実装(Ring0FsFileIo)が追加されている +- ✅ selfhost/通常ランタイム起動で、デフォルトとして Ring0FsFileIo が provider_lock に登録される +- ✅ UTF-8 ハンドリング ポリシー(read_to_string 使用)が実装されている +- ✅ FileBox の一度に1ファイルのみ open セマンティクスが実装されている(テスト済み) +- ✅ プラグイン優先時の Err ハンドリング(debug ログ出力)が実装されている +- ✅ Phase 106 との整合確認完了(MissingService は基本レア) +- ✅ 将来用フック(minimal/no-fs プロファイル)が docs に記載されている +- ✅ ビルド・テスト完全成功(FileBox/provider_lock/plugin_host 関連テスト全PASS) --- diff --git a/src/boxes/file/provider.rs b/src/boxes/file/provider.rs index 3929d1bd..d579df3d 100644 --- a/src/boxes/file/provider.rs +++ b/src/boxes/file/provider.rs @@ -2,6 +2,22 @@ //! //! This module defines the unified File I/O abstraction used by both the //! core read‑only implementation and the plugin implementation. +//! +//! # Phase 107: Ring0.FsApi 統合 +//! +//! **FileIo = 現在開いているファイルハンドルに対する操作(stateful)** +//! - open() でファイルを開く +//! - read() で内容を読み込む +//! - close() でファイルを閉じる +//! +//! **FsApi = stateless な OS ファイル I/O 抽象(Ring0)** +//! - Path → 直接 read/write +//! - FileIo 実装は内部で FsApi を使用する +//! +//! **設計原則**: +//! - FileIo は「現在開いているファイル」の状態を管理 +//! - FsApi は「パス → データ」の変換のみ担当 +//! - 実装例: Ring0FsFileIo が FsApi を内部で使用 /// File capabilities (minimal flag set) #[derive(Debug, Clone, Copy)] diff --git a/src/providers/ring1/file/mod.rs b/src/providers/ring1/file/mod.rs index 4fecee53..bdfc7b4c 100644 --- a/src/providers/ring1/file/mod.rs +++ b/src/providers/ring1/file/mod.rs @@ -1 +1,2 @@ pub mod core_ro; +pub mod ring0_fs_fileio; diff --git a/src/providers/ring1/file/ring0_fs_fileio.rs b/src/providers/ring1/file/ring0_fs_fileio.rs new file mode 100644 index 00000000..71c11b1f --- /dev/null +++ b/src/providers/ring1/file/ring0_fs_fileio.rs @@ -0,0 +1,178 @@ +//! Ring0-based FileIo implementation (Phase 107) +//! +//! Provides FileBox I/O by delegating to Ring0.FsApi. +//! This is the default FileIo provider registered at startup. + +use crate::boxes::file::provider::{FileCaps, FileError, FileIo, FileResult}; +use crate::runtime::ring0::Ring0Context; +use std::path::Path; +use std::sync::{Arc, RwLock}; + +/// Ring0.FsApi-based FileIo implementation +/// +/// # Design (Phase 107) +/// +/// **Stateful wrapper around stateless FsApi**: +/// - open(path): Stores the path for subsequent read() +/// - read(): Calls ring0.fs.read_to_string() with the stored path +/// - close(): Clears the stored path +/// +/// **Design decisions**: +/// - UTF-8 handling: Uses `read_to_string()` which handles UTF-8 internally +/// - One file at a time: Calling open() twice without close() returns Err +/// - Read-only: Phase 107 focuses on read operations only +pub struct Ring0FsFileIo { + ring0: Arc, + /// Current opened file path (None if no file is open) + path: RwLock>, +} + +impl Ring0FsFileIo { + /// Create new Ring0FsFileIo with given Ring0Context + pub fn new(ring0: Arc) -> Self { + Self { + ring0, + path: RwLock::new(None), + } + } +} + +impl FileIo for Ring0FsFileIo { + fn caps(&self) -> FileCaps { + // Phase 107: Read-only + FileCaps::read_only() + } + + fn open(&self, path: &str) -> FileResult<()> { + let mut current_path = self.path.write().unwrap(); + + // Phase 107 Design Decision: One file at a time + if current_path.is_some() { + return Err(FileError::Io( + "File already open. Call close() before opening another file.".to_string() + )); + } + + // Check if file exists using Ring0.FsApi + let path_obj = Path::new(path); + if !self.ring0.fs.exists(path_obj) { + return Err(FileError::Io(format!("File not found: {}", path))); + } + + // Store path for subsequent read() + *current_path = Some(path.to_string()); + Ok(()) + } + + fn read(&self) -> FileResult { + let current_path = self.path.read().unwrap(); + + match current_path.as_ref() { + Some(path) => { + // Delegate to Ring0.FsApi (UTF-8 handling is done by FsApi) + let path_obj = Path::new(path); + self.ring0.fs.read_to_string(path_obj) + .map_err(|e| FileError::Io(format!("Read failed: {}", e))) + } + None => { + Err(FileError::Io("No file is currently open. Call open() first.".to_string())) + } + } + } + + fn close(&self) -> FileResult<()> { + let mut current_path = self.path.write().unwrap(); + *current_path = None; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::runtime::ring0::default_ring0; + use std::fs; + use std::io::Write; + + fn setup_test_file(path: &str, content: &str) { + let mut file = fs::File::create(path).unwrap(); + file.write_all(content.as_bytes()).unwrap(); + } + + fn cleanup_test_file(path: &str) { + let _ = fs::remove_file(path); + } + + #[test] + fn test_ring0fs_fileio_basic_operations() { + let test_path = "/tmp/phase107_test_basic.txt"; + let test_content = "Hello, Ring0.FsApi!"; + + setup_test_file(test_path, test_content); + + let ring0 = Arc::new(default_ring0()); + let fileio = Ring0FsFileIo::new(ring0); + + // Test capabilities + let caps = fileio.caps(); + assert!(caps.read); + assert!(!caps.write); + + // Test open + assert!(fileio.open(test_path).is_ok()); + + // Test read + let content = fileio.read().unwrap(); + assert_eq!(content, test_content); + + // Test close + assert!(fileio.close().is_ok()); + + cleanup_test_file(test_path); + } + + #[test] + fn test_ring0fs_fileio_double_open_error() { + let test_path = "/tmp/phase107_test_double_open.txt"; + setup_test_file(test_path, "test"); + + let ring0 = Arc::new(default_ring0()); + let fileio = Ring0FsFileIo::new(ring0); + + // First open succeeds + assert!(fileio.open(test_path).is_ok()); + + // Second open fails (one file at a time) + let result = fileio.open(test_path); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("already open")); + + // Close and re-open should succeed + assert!(fileio.close().is_ok()); + assert!(fileio.open(test_path).is_ok()); + + cleanup_test_file(test_path); + } + + #[test] + fn test_ring0fs_fileio_read_without_open() { + let ring0 = Arc::new(default_ring0()); + let fileio = Ring0FsFileIo::new(ring0); + + // Read without open should fail + let result = fileio.read(); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("No file is currently open")); + } + + #[test] + fn test_ring0fs_fileio_nonexistent_file() { + let ring0 = Arc::new(default_ring0()); + let fileio = Ring0FsFileIo::new(ring0); + + // Open nonexistent file should fail + let result = fileio.open("/tmp/nonexistent_phase107_file.txt"); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("not found")); + } +} diff --git a/src/runtime/plugin_host.rs b/src/runtime/plugin_host.rs index d4a9a2a6..590e0847 100644 --- a/src/runtime/plugin_host.rs +++ b/src/runtime/plugin_host.rs @@ -155,8 +155,25 @@ impl PluginHost { use crate::runtime::core_services::*; use crate::runtime::provider_lock; + // Phase 107: Auto-register default FileBox provider (Ring0.FsApi-based) + // This happens before the Phase 106 check, so plugins can still override. + // If a plugin has already registered, this returns Err but we continue. + if CoreBoxId::File.is_core_required() { + match provider_lock::init_default_filebox_provider(&ring0) { + Ok(()) => { + // Default provider registered successfully + ring0.log.debug("[Phase 107] Ring0FsFileIo registered as default FileBox provider"); + } + Err(msg) => { + // Plugin provider already registered - this is OK (plugin priority) + ring0.log.debug(&format!("[Phase 107] {}", msg)); + } + } + } + // Phase 106: FileBox provider チェック追加 // CoreBoxId がFileを必須と判定している場合、provider が登録されていることを確認 + // Phase 107: With auto-registration above, this check should always pass if CoreBoxId::File.is_core_required() { if provider_lock::get_filebox_provider().is_none() { return Err(CoreInitError::MissingService { @@ -303,15 +320,13 @@ mod tests { // Phase 94: 実際の registry を使用してテスト use crate::runtime::ring0::default_ring0; use crate::box_factory::builtin::BuiltinBoxFactory; - use crate::boxes::file::core_ro::CoreRoFileIo; - use crate::runtime::provider_lock; let ring0 = Arc::new(default_ring0()); let mut registry = UnifiedBoxRegistry::new(); registry.register(Arc::new(BuiltinBoxFactory::new())); - // Phase 106: Initialize FileBox provider before PluginHost creation - let _ = provider_lock::set_filebox_provider(Arc::new(CoreRoFileIo::new())); + // Phase 107: FileBox provider auto-registration (no manual setup needed) + // with_core_from_registry will call init_default_filebox_provider internally let plugin_host = PluginHost::with_core_from_registry(ring0, ®istry) .expect("CoreServices should be initialized with builtin boxes"); @@ -325,15 +340,13 @@ mod tests { // Phase 94: 実際の registry を使用して全フィールドが存在することを確認 use crate::runtime::ring0::default_ring0; use crate::box_factory::builtin::BuiltinBoxFactory; - use crate::boxes::file::core_ro::CoreRoFileIo; - use crate::runtime::provider_lock; let ring0 = Arc::new(default_ring0()); let mut registry = UnifiedBoxRegistry::new(); registry.register(Arc::new(BuiltinBoxFactory::new())); - // Phase 106: Initialize FileBox provider before PluginHost creation - let _ = provider_lock::set_filebox_provider(Arc::new(CoreRoFileIo::new())); + // Phase 107: FileBox provider auto-registration (no manual setup needed) + // with_core_from_registry will call init_default_filebox_provider internally let plugin_host = PluginHost::with_core_from_registry(ring0, ®istry) .expect("CoreServices should be initialized"); diff --git a/src/runtime/provider_lock.rs b/src/runtime/provider_lock.rs index 3455ec71..7625051b 100644 --- a/src/runtime/provider_lock.rs +++ b/src/runtime/provider_lock.rs @@ -67,3 +67,28 @@ pub fn get_filebox_provider() -> Option<&'static Arc> { pub fn get_filebox_caps() -> Option { get_filebox_provider().map(|p| p.caps()) } + +/// Phase 107: Initialize default FileBox provider using Ring0.FsApi +/// +/// This helper registers Ring0FsFileIo as the default FileBox provider. +/// It should be called during runtime initialization, after CoreServices setup. +/// +/// # Returns +/// +/// - `Ok(())`: Default provider registered successfully +/// - `Err(msg)`: Provider already registered (plugin took precedence) +/// +/// # Design +/// +/// Plugin providers have priority over the default. If a plugin has already +/// registered a FileBox provider, this function returns Err but the system +/// continues normally (plugin priority is honored). +pub fn init_default_filebox_provider( + ring0: &Arc +) -> Result<(), String> { + use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo; + + let provider = Arc::new(Ring0FsFileIo::new(ring0.clone())); + set_filebox_provider(provider) + .map_err(|_| "Plugin FileBox provider already registered".to_string()) +}