diff --git a/dune-project b/dune-project index c4b0b1e..4728a8d 100644 --- a/dune-project +++ b/dune-project @@ -11,6 +11,6 @@ (synopsis "In-memory resources for Eio") (description "Eio_mem provides in-memory resources for Eio") (depends - eio + (eio (>= 1.4.0)) (eio_main :with-test) (mdx :with-test))) diff --git a/eio_mem.opam b/eio_mem.opam index 715d2bc..2827219 100644 --- a/eio_mem.opam +++ b/eio_mem.opam @@ -4,7 +4,7 @@ synopsis: "In-memory resources for Eio" description: "Eio_mem provides in-memory resources for Eio" depends: [ "dune" {>= "3.21"} - "eio" + "eio" {>= "1.4.0"} "eio_main" {with-test} "mdx" {with-test} "odoc" {with-doc} diff --git a/example/main.ml b/example/main.ml index 4aaece0..6999ece 100644 --- a/example/main.ml +++ b/example/main.ml @@ -1,97 +1,8 @@ let ( / ) = Eio.Path.( / ) -open Eio - -let run ?clear:(paths = []) fn = - Eio_mock.Backend.run @@ fun () -> - Eio_mem.run ~cwd:"/home/patrick" @@ fun env -> - Eio.Path.mkdirs ~perm:0o755 (env#fs / "/home/patrick"); +let () = + Eio_main.run @@ fun _ -> + Eio_mem.run @@ fun env -> let cwd = Eio.Stdenv.cwd env in - List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p)) paths; - fn env - -let try_read_file path = - match Path.load path with - | s -> traceln "read %a -> %S" Path.pp path s - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_read_dir path = - match Path.read_dir path with - | names -> - traceln "read_dir %a -> %a" Path.pp path Fmt.Dump.(list string) names - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_write_file ~create ?append path content = - match Path.save ~create ?append path content with - | () -> traceln "write %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_mkdir path = - match Path.mkdir path ~perm:0o700 with - | () -> traceln "mkdir %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_mkdirs ?exists_ok path = - match Path.mkdirs ?exists_ok path ~perm:0o700 with - | () -> traceln "mkdirs %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_rename p1 p2 = - match Path.rename p1 p2 with - | () -> traceln "rename %a to %a -> ok" Path.pp p1 Path.pp p2 - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let dir_entry f (k, name) = - Fmt.pf f "%S%s" name - (match k with - | `Regular_file -> "" - | `Directory -> "(dir)" - | _ -> "(special)") - -let try_read_link path = - match Path.read_link path with - | target -> traceln "read_link %a -> %S" Path.pp path target - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_unlink path = - match Path.unlink path with - | () -> traceln "unlink %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_rmdir path = - match Path.rmdir path with - | () -> traceln "rmdir %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_rmtree ?missing_ok path = - match Path.rmtree ?missing_ok path with - | () -> traceln "rmtree %a -> ok" Path.pp path - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_stat path = - let stat ~follow = - match Eio.Path.stat ~follow path with - | info -> Fmt.str "@[%a@]" Eio.File.Stat.pp_kind info.kind - | exception Eio.Io (e, _) -> Fmt.str "@[%a@]" Eio.Exn.pp_err e - in - let a = stat ~follow:false in - let b = stat ~follow:true in - if a = b then traceln "%a -> %s" Eio.Path.pp path a - else traceln "%a -> %s / %s" Eio.Path.pp path a b - -let try_symlink ~link_to path = - match Path.symlink ~link_to path with - | s -> traceln "symlink %a -> %S" Path.pp path link_to - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - -let try_rename t = - try_mkdir (t / "tmp"); - try_rename (t / "tmp") (t / "dir"); - try_write_file (t / "foo") "FOO" ~create:(`Exclusive 0o600); - try_rename (t / "foo") (t / "dir/bar"); - try_read_file (t / "dir/bar"); - Path.with_open_dir (t / "dir") @@ fun dir -> - try_rename (dir / "bar") (t / "foo"); - try_read_file (t / "foo") - -let () = run ~clear:[ "tmp" ] @@ fun env -> try_rename env#cwd + Eio.Path.save ~create:(`Exclusive 0o666) (cwd / "test-file") "my-data"; + Eio.traceln "Got %S" @@ Eio.Path.load (cwd / "test-file") diff --git a/src/eio_mem.ml b/src/eio_mem.ml index 995afe2..06f5c8a 100644 --- a/src/eio_mem.ml +++ b/src/eio_mem.ml @@ -72,6 +72,7 @@ module Fs = struct let rmdir t path = Low_level.rmdir t.fd path let stat t ~follow path = Low_level.statat ~follow t.fd path let read_dir t path = Low_level.read_dir t.fd path + let with_dir_entries t path = Low_level.with_dir_entries t.fd path let read_link t path = Low_level.read_link t.fd path let rename t old_path (new_dir : [> Eio.Fs.dir_ty ] Eio.Std.r) new_path = @@ -81,7 +82,7 @@ module Fs = struct let symlink ~link_to t path = Low_level.symlinkat ~link_to t.fd path - let open_dir t ~sw path = + let open_subtree t ~sw path = let flags = Low_level.Open_flags.(o_rdonly + o_directory) in let fd = Low_level.openat ~sw ~mode:0 t.fd path flags in let label = Filename.basename path in @@ -92,6 +93,8 @@ module Fs = struct let d = v ~label ~path:full_path (Low_level.Dir.Dir_fd fd) in Eio.Resource.T (d, Handler.v) + let chown ~follow:_ ?uid:_ ?gid:_ _t _p = () + let chmod _t ~follow:_ ~perm:_ _user = () let pp f t = Fmt.string f (String.escaped t.label) let native_internal t path = diff --git a/src/file.ml b/src/file.ml index 0bdf754..9b33878 100644 --- a/src/file.ml +++ b/src/file.ml @@ -7,3 +7,23 @@ type t = { let make ~stat name = { stat; name; contents = Cstruct.empty; pos = Optint.Int63.zero } + +let with_stat ?dev ?ino ?kind ?perm ?nlink ?uid ?gid ?rdev ?size ?blksize + ?blocks ?atime ?mtime ?ctime s = + Eio.File.Stat. + { + dev = Option.value ~default:s.dev dev; + ino = Option.value ~default:s.ino ino; + kind = Option.value ~default:s.kind kind; + perm = Option.value ~default:s.perm perm; + nlink = Option.value ~default:s.nlink nlink; + uid = Option.value ~default:s.uid uid; + gid = Option.value ~default:s.gid gid; + rdev = Option.value ~default:s.rdev rdev; + size = Option.value ~default:s.size size; + blksize = Option.value ~default:s.blksize blksize; + blocks = Option.value ~default:s.blocks blocks; + atime = Option.value ~default:s.atime atime; + mtime = Option.value ~default:s.mtime mtime; + ctime = Option.value ~default:s.ctime ctime; + } diff --git a/src/flow.ml b/src/flow.ml index 94a41d0..47aa4ce 100644 --- a/src/flow.ml +++ b/src/flow.ml @@ -34,6 +34,13 @@ module Impl = struct let seek _ _ = Fmt.failwith "TODO: seek" let sync _ = Fmt.failwith "TODO: sync" let truncate _ = Fmt.failwith "TODO: truncate" + + let setsockopt _ _ = + raise @@ Eio.Exn.(create (Not_available (Low_level.Eio_mem "setsockopt"))) + + let getsockopt _ _ = + raise @@ Eio.Exn.(create (Not_available (Low_level.Eio_mem "getsockopt"))) + let fd t = t let close = Low_level.close end diff --git a/src/low_level.ml b/src/low_level.ml index 1e4b4f4..d07e56a 100644 --- a/src/low_level.ml +++ b/src/low_level.ml @@ -221,6 +221,8 @@ let default_stat = atime = 0.; mtime = 0.; ctime = 0.; + blksize = 0L; + blocks = 0L; } let make_new_stat ?kind ?perm () = @@ -533,15 +535,33 @@ let read_link dir_fd path = | Symlink s -> s.link_to | r -> Error.err (Fmt.str "not a symlink: %a" Resource.pp r) -let read_dir dir_fd path = +let chown ~follow ?uid ?gid dir_fd path = + let path = check_path path in + let r = Filesystem.lookup ~follow dir_fd path in + match !r with + | File f -> r := File { f with stat = File.with_stat ?uid ?gid f.stat } + | Symlink s -> r := Symlink { s with stat = File.with_stat ?uid ?gid s.stat } + | Dir d -> r := Dir { d with stat = File.with_stat ?uid ?gid d.stat } + | r -> Error.err (Fmt.str "resource cannot be chowned: %a" Resource.pp r) + +let read_dir_all dir_fd path = let path = check_path path in let r = Filesystem.lookup ~follow:true dir_fd path in match !r with | Resource.Dir dir -> - List.map snd dir.entries - |> List.filter (function "." | ".." -> false | _ -> true) + List.filter (function _, "." | _, ".." -> false | _ -> true) dir.entries + |> List.map (fun (ino, e) -> + let r = Filesystem.read_filesystem (Filesystem.Entries.find ino) in + (Resource.stat !r, e)) | _ -> Error.err "Not a directory" +let read_dir dir_fd path = List.map snd (read_dir_all dir_fd path) + +let with_dir_entries dir_fd path fn = + read_dir_all dir_fd path + |> List.map (fun (s, v) -> (s.Eio.File.Stat.kind, v)) + |> List.to_seq |> fn + let rmdir dir_fd path = let path = check_path path in let r = Filesystem.lookup ~follow:true dir_fd path in @@ -658,10 +678,14 @@ let readv fd bufs = let len = Cstruct.length buf in let posi = Optint.Int63.to_int !pos in let actual_len = if posi + len <= size then len else size - posi in - Cstruct.blit e.contents (Optint.Int63.to_int !pos) buf 0 actual_len; - pos := Optint.Int63.add !pos (Optint.Int63.of_int len); - read := !read + actual_len; - Eio.Fiber.yield ()) + if actual_len < 0 then () + else begin + Cstruct.blit e.contents (Optint.Int63.to_int !pos) buf 0 + actual_len; + pos := Optint.Int63.add !pos (Optint.Int63.of_int len); + read := !read + actual_len; + Eio.Fiber.yield () + end) bufs; r := File { e with pos = !pos } | r -> Fmt.failwith "TODO: readv %a" Resource.pp r