1919//! # Directory sync
2020//!
2121//! WASI preview1 does not have a dedicated directory-sync syscall. `sync_dir`
22- //! opens the directory as a file descriptor and calls `fd_sync`. Some sandboxed
23- //! runtimes stub `fd_sync` out with `ENOTSUP`; that error is treated as a
24- //! no-op (best-effort durability, with a `tracing::debug!` trace emitted so the
25- //! caller has observability).
22+ //! opens the directory as a file descriptor and calls `sync_all` on it. Some
23+ //! sandboxed runtimes do not support syncing a directory fd and return an
24+ //! error; that is treated as a no-op (best-effort durability, with a
25+ //! `tracing::debug!` trace emitted so the caller has observability).
26+ //!
27+ //! # Positioned I/O
28+ //!
29+ //! Positioned reads and writes use the stable `Seek` + `Read` / `Write` traits
30+ //! under the file's `Mutex`: each operation seeks to the target offset and then
31+ //! transfers. The mutex serialises the seek/transfer pair, so the shared file
32+ //! cursor is never observed in a torn state. This keeps the backend on stable
33+ //! Rust (no `wasi_ext` / preview1 raw-syscall dependency).
2634
2735// ── Real WASI implementation ──────────────────────────────────────────────────
2836
@@ -139,8 +147,9 @@ mod real {
139147 /// Handle to an open file on a WASI target.
140148 ///
141149 /// Uses `std::fs::File`, which maps directly onto WASI preview1 `fd_*`
142- /// syscalls. Positional reads and writes loop over `std::os::wasi::fs::FileExt`
143- /// `read_at` / `write_at` which correspond to `fd_pread` / `fd_pwrite`.
150+ /// syscalls. Positional reads and writes seek the shared cursor under the
151+ /// `Mutex` and then transfer, which corresponds to `fd_seek` + `fd_read` /
152+ /// `fd_write`.
144153 pub struct WasiFile {
145154 /// Mutex so that `read_at(&self, …)` can seek without requiring `&mut`.
146155 /// On WASI every seek+read pair is already synchronous so this adds no
@@ -247,39 +256,26 @@ mod real {
247256 ///
248257 /// WASI preview1 has no dedicated directory-sync syscall. This
249258 /// implementation opens the directory as a file descriptor and calls
250- /// `wasi::fd_sync ` on it. Runtimes that stub out `fd_sync` with
251- /// `ENOTSUP` / `ENOSYS` are treated as a no-op so that pagedb still
259+ /// `sync_all ` on it. Runtimes that do not support syncing a directory
260+ /// fd return an error, which is treated as a no-op so that pagedb still
252261 /// operates in those environments; a `tracing::debug!` message is
253262 /// emitted for observability.
254263 async fn sync_dir ( & self , path : & str ) -> Result < ( ) > {
255- use std:: os:: wasi:: io:: AsRawFd ;
256-
257264 let p = self . resolve ( path) ;
258265 let dir = match std:: fs:: File :: open ( & p) {
259266 Ok ( d) => d,
260267 Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: NotFound => return Ok ( ( ) ) ,
261268 Err ( e) => return Err ( PagedbError :: Io ( e) ) ,
262269 } ;
263270
264- // SAFETY: `dir.as_raw_fd()` is a valid, open file descriptor for
265- // the duration of this call. `wasi::fd_sync` is a pure I/O syscall
266- // that does not move or alias memory.
267- let rc = unsafe { wasi:: fd_sync ( dir. as_raw_fd ( ) ) } ;
268- match rc {
269- Ok ( ( ) ) => Ok ( ( ) ) ,
270- Err ( e) if e == wasi:: ERRNO_NOTSUP || e == wasi:: ERRNO_NOSYS => {
271- tracing:: debug!(
272- path = %p. display( ) ,
273- "sync_dir: fd_sync returned {:?}; treating as no-op \
274- (runtime does not support directory sync)",
275- e
276- ) ;
277- Ok ( ( ) )
278- }
279- Err ( e) => Err ( PagedbError :: Io ( std:: io:: Error :: from_raw_os_error (
280- e. raw ( ) as i32
281- ) ) ) ,
271+ if let Err ( e) = dir. sync_all ( ) {
272+ tracing:: debug!(
273+ path = %p. display( ) ,
274+ error = %e,
275+ "sync_dir: directory sync unsupported; treating as no-op"
276+ ) ;
282277 }
278+ Ok ( ( ) )
283279 }
284280
285281 async fn lock_exclusive ( & self , path : & str ) -> Result < Self :: LockHandle > {
@@ -321,11 +317,11 @@ mod real {
321317
322318 impl VfsFile for WasiFile {
323319 async fn read_at ( & self , offset : u64 , buf : & mut [ u8 ] ) -> Result < usize > {
324- use std :: os :: wasi :: fs :: FileExt ;
325- let f = self . inner . lock ( ) ;
320+ let mut f = self . inner . lock ( ) ;
321+ f . seek ( SeekFrom :: Start ( offset ) ) . map_err ( PagedbError :: Io ) ? ;
326322 let mut total = 0usize ;
327323 while total < buf. len ( ) {
328- match f. read_at ( & mut buf[ total..] , offset + total as u64 ) {
324+ match f. read ( & mut buf[ total..] ) {
329325 Ok ( 0 ) => break ,
330326 Ok ( n) => total += n,
331327 Err ( e) => return Err ( PagedbError :: Io ( e) ) ,
@@ -335,15 +331,13 @@ mod real {
335331 }
336332
337333 async fn read_at_vectored ( & self , reqs : & mut [ ReadReq < ' _ > ] ) -> Result < ( ) > {
338- use std:: os:: wasi:: fs:: FileExt ;
339- let f = self . inner . lock ( ) ;
334+ let mut f = self . inner . lock ( ) ;
340335 for req in reqs. iter_mut ( ) {
336+ f. seek ( SeekFrom :: Start ( req. offset ) )
337+ . map_err ( PagedbError :: Io ) ?;
341338 let mut total = 0usize ;
342- loop {
343- if total == req. buf . len ( ) {
344- break ;
345- }
346- match f. read_at ( & mut req. buf [ total..] , req. offset + total as u64 ) {
339+ while total < req. buf . len ( ) {
340+ match f. read ( & mut req. buf [ total..] ) {
347341 Ok ( 0 ) => break ,
348342 Ok ( n) => total += n,
349343 Err ( e) => return Err ( PagedbError :: Io ( e) ) ,
@@ -362,11 +356,11 @@ mod real {
362356 if !self . writable {
363357 return Err ( PagedbError :: ReadOnly ) ;
364358 }
365- use std :: os :: wasi :: fs :: FileExt ;
366- let f = self . inner . lock ( ) ;
359+ let mut f = self . inner . lock ( ) ;
360+ f . seek ( SeekFrom :: Start ( offset ) ) . map_err ( PagedbError :: Io ) ? ;
367361 let mut total = 0usize ;
368362 while total < buf. len ( ) {
369- match f. write_at ( & buf[ total..] , offset + total as u64 ) {
363+ match f. write ( & buf[ total..] ) {
370364 Ok ( 0 ) => {
371365 return Err ( PagedbError :: Io ( std:: io:: Error :: from (
372366 std:: io:: ErrorKind :: WriteZero ,
@@ -383,12 +377,13 @@ mod real {
383377 if !self . writable {
384378 return Err ( PagedbError :: ReadOnly ) ;
385379 }
386- use std:: os:: wasi:: fs:: FileExt ;
387- let f = self . inner . lock ( ) ;
380+ let mut f = self . inner . lock ( ) ;
388381 for req in reqs {
382+ f. seek ( SeekFrom :: Start ( req. offset ) )
383+ . map_err ( PagedbError :: Io ) ?;
389384 let mut total = 0usize ;
390385 while total < req. buf . len ( ) {
391- match f. write_at ( & req. buf [ total..] , req . offset + total as u64 ) {
386+ match f. write ( & req. buf [ total..] ) {
392387 Ok ( 0 ) => {
393388 return Err ( PagedbError :: Io ( std:: io:: Error :: from (
394389 std:: io:: ErrorKind :: WriteZero ,
0 commit comments