From 0d42d97e72543a14ccd0843dab9f86497e1de01f Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 22 Dec 2025 15:41:50 +0200 Subject: [PATCH] cli: Fix overlay mount I/O error by unifying whiteout schema in SDK The whiteout table created by `agentfs init --base` was missing the `parent_path` column that `overlayfs.rs` queries during directory listing. This caused `get_child_whiteouts()` to fail with a SQL error, which propagated as an I/O error when running `ls` on the mounted filesystem. The fix moves the schema definition to a single location in the SDK (`OverlayFS::init_schema`) to prevent future drift between CLI and SDK. The CLI now calls this method instead of duplicating the SQL. Also removes dead `base_type` code that was set but never read. Fixes #106 --- cli/src/cmd/init.rs | 46 ++++------------------------ sdk/rust/src/filesystem/overlayfs.rs | 23 ++++++++++---- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 9efc98f..11a1dfc 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; -use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions}; +use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions, OverlayFS}; use anyhow::{Context, Result as AnyhowResult}; pub async fn init_database( @@ -63,52 +63,18 @@ pub async fn init_database( .await .context("Failed to initialize database")?; - // If base is provided, store the overlay configuration + // If base is provided, initialize the overlay schema using the SDK if let Some(base_path) = base { - let conn = agent.get_connection(); - - // Create whiteout table for overlay support - conn.execute( - "CREATE TABLE IF NOT EXISTS fs_whiteout ( - path TEXT PRIMARY KEY, - created_at INTEGER NOT NULL - )", - (), - ) - .await - .context("Failed to create whiteout table")?; - - // Create overlay config table - conn.execute( - "CREATE TABLE IF NOT EXISTS fs_overlay_config ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL - )", - (), - ) - .await - .context("Failed to create overlay config table")?; - - // Store base path configuration let base_path_str = base_path .canonicalize() .context("Failed to canonicalize base path")? .to_string_lossy() .to_string(); - conn.execute( - "INSERT INTO fs_overlay_config (key, value) VALUES ('base_type', 'hostfs')", - (), - ) - .await - .context("Failed to store base type")?; - - conn.execute( - "INSERT INTO fs_overlay_config (key, value) VALUES ('base_path', ?)", - (base_path_str.as_str(),), - ) - .await - .context("Failed to store base path")?; + // Use SDK's OverlayFS::init_schema to ensure schema consistency + OverlayFS::init_schema(&agent.get_connection(), &base_path_str) + .await + .context("Failed to initialize overlay schema")?; eprintln!("Created overlay filesystem: {}", db_path.display()); eprintln!("Agent ID: {}", id); diff --git a/sdk/rust/src/filesystem/overlayfs.rs b/sdk/rust/src/filesystem/overlayfs.rs index aadb7cd..a78b017 100644 --- a/sdk/rust/src/filesystem/overlayfs.rs +++ b/sdk/rust/src/filesystem/overlayfs.rs @@ -5,7 +5,7 @@ use std::{ sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; -use turso::Value; +use turso::{Connection, Value}; use super::{agentfs::AgentFS, DirEntry, FileSystem, FilesystemStats, FsError, Stats}; @@ -38,16 +38,15 @@ impl OverlayFS { Self { base, delta } } - /// Initialize the overlay filesystem schema (creates whiteout table) + /// Initialize the overlay filesystem schema in a database. /// - /// This must be called before using the overlay filesystem to ensure - /// the whiteout tracking table exists in the delta layer. + /// This is a static method that can be called without creating an OverlayFS + /// instance, useful for CLI tools that need to initialize an overlay database. /// /// The `base_path` parameter specifies the actual filesystem path that the /// base layer represents. This is stored in the delta database so that /// tools like `agentfs diff` can determine what files were modified. - pub async fn init(&self, base_path: &str) -> Result<()> { - let conn = self.delta.get_connection(); + pub async fn init_schema(conn: &Connection, base_path: &str) -> Result<()> { conn.execute( "CREATE TABLE IF NOT EXISTS fs_whiteout ( path TEXT PRIMARY KEY, @@ -80,6 +79,18 @@ impl OverlayFS { Ok(()) } + /// Initialize the overlay filesystem schema (creates whiteout table) + /// + /// This must be called before using the overlay filesystem to ensure + /// the whiteout tracking table exists in the delta layer. + /// + /// The `base_path` parameter specifies the actual filesystem path that the + /// base layer represents. This is stored in the delta database so that + /// tools like `agentfs diff` can determine what files were modified. + pub async fn init(&self, base_path: &str) -> Result<()> { + Self::init_schema(&self.delta.get_connection(), base_path).await + } + /// Extract the parent path from a normalized path fn parent_path(path: &str) -> String { if path == "/" {