@@ -392,18 +392,24 @@ async fn read_file(host: &HostCtx, path: &str) -> Result<String> {
392392 if entry. is_directory {
393393 bail ! ( "EISDIR is a directory: {}" , entry. path) ;
394394 }
395- // Fetch the content BLOB only here (lookup_entry is metadata-only now).
395+ Ok ( fetch_content ( host, & entry. path ) . await ?. unwrap_or_default ( ) )
396+ }
397+
398+ /// Fetch the content BLOB with a dedicated query. `lookup_entry` is
399+ /// metadata-only (`NULL AS content`), so every consumer that actually needs
400+ /// bytes (`read_file`, `pread`, `truncate`, `link`) must fetch them here —
401+ /// reading `FsEntry::content` off a lookup silently yields empty data.
402+ async fn fetch_content ( host : & HostCtx , path : & str ) -> Result < Option < String > > {
396403 let mut rows = query_rows (
397404 host,
398405 "SELECT content FROM agent_os_fs_entries WHERE path = ?" ,
399- & [ json ! ( entry . path) ] ,
406+ & [ json ! ( path) ] ,
400407 )
401408 . await ?;
402- let content = match rows. first_mut ( ) {
403- Some ( row) => optional_content_col ( row, "content" ) ?,
404- None => None ,
405- } ;
406- Ok ( content. unwrap_or_default ( ) )
409+ match rows. first_mut ( ) {
410+ Some ( row) => optional_content_col ( row, "content" ) ,
411+ None => Ok ( None ) ,
412+ }
407413}
408414
409415// --- ctx-free helpers (copied verbatim from rivetkit-agent-os::persistence) ---
@@ -846,11 +852,12 @@ async fn link_entry(host: &HostCtx, old_path: String, new_path: String) -> Resul
846852 if entry. is_directory {
847853 bail ! ( "EPERM cannot hard-link directory: {old_path}" ) ;
848854 }
855+ let content = fetch_content ( host, & entry. path ) . await ?;
849856 insert_entry (
850857 host,
851858 & new_path,
852859 false ,
853- entry . content ,
860+ content,
854861 entry. mode ,
855862 entry. size ,
856863 entry. symlink_target ,
@@ -896,7 +903,8 @@ async fn truncate_file(host: &HostCtx, path: &str, len: i64) -> Result<()> {
896903 if entry. is_directory {
897904 bail ! ( "EISDIR is a directory: {}" , entry. path) ;
898905 }
899- let mut bytes = decode_content ( entry. content . as_deref ( ) . unwrap_or_default ( ) ) ?;
906+ let content = fetch_content ( host, & entry. path ) . await ?;
907+ let mut bytes = decode_content ( content. as_deref ( ) . unwrap_or_default ( ) ) ?;
900908 bytes. resize ( len as usize , 0 ) ;
901909 let content = BASE64 . encode ( bytes) ;
902910 run_stmt (
@@ -921,7 +929,8 @@ async fn pread_file(host: &HostCtx, path: &str, offset: i64, len: i64) -> Result
921929 if entry. is_directory {
922930 bail ! ( "EISDIR is a directory: {}" , entry. path) ;
923931 }
924- let bytes = decode_content ( entry. content . as_deref ( ) . unwrap_or_default ( ) ) ?;
932+ let content = fetch_content ( host, & entry. path ) . await ?;
933+ let bytes = decode_content ( content. as_deref ( ) . unwrap_or_default ( ) ) ?;
925934 let start = ( offset as usize ) . min ( bytes. len ( ) ) ;
926935 let end = start. saturating_add ( len as usize ) . min ( bytes. len ( ) ) ;
927936 Ok ( BASE64 . encode ( & bytes[ start..end] ) )
0 commit comments