Add libuv readdir feature

This commit is contained in:
Patrick Ferris 2022-03-16 16:46:57 +00:00
parent 751e2b4e68
commit c64fc76135
6 changed files with 70 additions and 0 deletions

View File

@ -24,6 +24,7 @@ class virtual t = object
path -> <rw; Flow.close>
method virtual mkdir : perm:Unix_perm.t -> path -> unit
method virtual open_dir : sw:Switch.t -> path -> t_with_close
method virtual read_dir : path -> string list
end
and virtual t_with_close = object
(* This dummy class avoids an "Error: The type < .. > is not an object type" error from the compiler. *)
@ -35,6 +36,7 @@ let open_in ~sw (t:#t) = t#open_in ~sw
let open_out ~sw ?(append=false) ~create (t:#t) path = t#open_out ~sw ~append ~create path
let open_dir ~sw (t:#t) = t#open_dir ~sw
let mkdir (t:#t) = t#mkdir
let read_dir (t:#t) path = t#read_dir path
let with_open_in (t:#t) path fn =
Switch.run @@ fun sw -> fn (open_in ~sw t path)

View File

@ -1118,6 +1118,7 @@ module Dir : sig
path -> <rw; Flow.close>
method virtual mkdir : perm:Unix_perm.t -> path -> unit
method virtual open_dir : sw:Switch.t -> path -> t_with_close
method virtual read_dir : path -> path list
end
and virtual t_with_close : object
inherit t
@ -1183,6 +1184,9 @@ module Dir : sig
val with_open_dir : #t -> path -> (<t; Flow.close> -> 'a) -> 'a
(** [with_open_dir] is like [open_dir], but calls [fn dir] with the new directory and closes
it automatically when [fn] returns (if it hasn't already been closed by then). *)
val read_dir : #t -> path -> string list
(** [read_dir t path] reads directory entries for [t/path].*)
end
(** The standard environment of a process. *)

View File

@ -1077,6 +1077,10 @@ class dir fd = object
method mkdir ~perm path =
Low_level.mkdir_beneath ~perm ?dir:fd path
method read_dir _path =
(* TODO(patricoferris): Once liburing supports getdents64 https://github.com/axboe/liburing/issues/111 *)
assert false
method close =
FD.close (Option.get fd)
end

View File

@ -252,6 +252,31 @@ module Low_level = struct
let request = Luv.File.Request.make () in
await_with_cancel ~request (fun loop -> Luv.File.mkdir ~loop ~request ~mode path)
let opendir path =
let request = Luv.File.Request.make () in
await_with_cancel ~request (fun loop -> Luv.File.opendir ~loop ~request path)
let closedir path =
let request = Luv.File.Request.make () in
await_with_cancel ~request (fun loop -> Luv.File.closedir ~loop ~request path)
let with_dir_to_read path fn =
match opendir path with
| Ok dir ->
Fun.protect ~finally:(fun () -> closedir dir |> or_raise) @@ fun () -> fn dir
| Error _ as e -> e
let readdir path =
let fn dir =
let request = Luv.File.Request.make () in
match await_with_cancel ~request (fun loop -> Luv.File.readdir ~loop ~request dir) with
| Ok dirents ->
let dirs = Array.map (fun v -> v.Luv.File.Dirent.name) dirents |> Array.to_list in
Ok dirs
| Error _ as e -> e
in
with_dir_to_read path fn
let to_unix op t =
let os_fd = Luv.File.get_osfhandle (get "to_unix" t) |> or_raise in
let fd = Luv_unix.Os_fd.Fd.to_unix os_fd in
@ -724,6 +749,10 @@ class dir dir_path = object (self)
let real_path = self#resolve_new path in
File.mkdir ~mode:[`NUMERIC perm] real_path |> or_raise_path path
method read_dir path =
let path = self#resolve path in
File.readdir path |> or_raise_path path
method close = ()
end

View File

@ -67,6 +67,8 @@ module Low_level : sig
val mkdir : mode:Luv.File.Mode.t list -> string -> unit or_error
(** Wraps {!Luv.File.mkdir} *)
val readdir : string -> string list or_error
(** Wraps {!Luv.File.readdir}. [readdir] opens and closes the directory for reading for the user. *)
end
module Random : sig

View File

@ -31,6 +31,11 @@ let try_mkdir dir path =
| () -> traceln "mkdir %S -> ok" path
| exception ex -> traceln "mkdir %S -> %a" path Fmt.exn ex
let try_read_dir dir path =
match Eio.Dir.read_dir dir path with
| names -> traceln "read_dir [ %a ] -> ok" Fmt.(list ~sep:Fmt.comma string) names
| exception ex -> traceln "read_dir %a" Fmt.exn ex
let chdir path =
traceln "chdir %S" path;
Unix.chdir path
@ -221,6 +226,30 @@ Using `cwd` we can't access the parent, but using `fs` we can:
- : unit = ()
```
Reading directory entries under `cwd` and outside of `cwd`.
```ocaml
# run @@ fun env ->
let cwd = Eio.Stdenv.cwd env in
try_mkdir cwd "readdir";
chdir "readdir";
Fun.protect ~finally:(fun () -> chdir "..") (fun () ->
try_mkdir cwd "test-1";
try_mkdir cwd "test-2";
let _entries = try_read_dir cwd "." in
let _perm_denied = try_read_dir cwd ".." in
()
);;
+mkdir "readdir" -> ok
+chdir "readdir"
+mkdir "test-1" -> ok
+mkdir "test-2" -> ok
+read_dir [ test-1, test-2 ] -> ok
+read_dir Eio.Dir.Permission_denied ("..", _)
+chdir ".."
- : unit = ()
```
Can use `fs` to access absolute paths:
```ocaml