Skip to content

Commit

Permalink
eio_linux: Expose more functions in Low_level module
Browse files Browse the repository at this point in the history
Add all the functions used by other parts of eio_linux (`openat`,
`mkdir`, `read_link`, `unlink`, `rename` and `pipe`).

Tidied the API up a bit too:
- `mkdir_beneath` is now just `mkdir`.
- `statx_confined` is now just `statx`.
- `open_dir` is gone; the single user now calls `openat` directly.
  • Loading branch information
talex5 committed Feb 29, 2024
1 parent 7b58999 commit e48f611
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 26 deletions.
13 changes: 10 additions & 3 deletions lib_eio_linux/eio_linux.ml
Original file line number Diff line number Diff line change
Expand Up @@ -532,11 +532,18 @@ end = struct
let d = v ~label ~path:(native_internal t path) (Low_level.FD fd) in
Eio.Resource.T (d, Dir_handler.v)

let mkdir t ~perm path = Low_level.mkdir_beneath ~perm t.fd path
let mkdir t ~perm path = Low_level.mkdir ~perm t.fd path

let read_dir t path =
Switch.run ~name:"read_dir" @@ fun sw ->
let fd = Low_level.open_dir ~sw t.fd (if path = "" then "." else path) in
let path = if path = "" then "." else path in
let fd =
Low_level.openat ~sw t.fd path
~seekable:false
~access:`R
~flags:Uring.Open_flags.(cloexec + directory)
~perm:0
in
Low_level.read_dir fd

let read_link t path = Low_level.read_link t.fd path
Expand All @@ -562,7 +569,7 @@ end = struct
if !Sched.statx_works then (
let module X = Uring.Statx in
let x = X.create () in
Low_level.statx_confined ~follow ~mask:X.Mask.basic_stats t.fd path x;
Low_level.statx ~follow ~mask:X.Mask.basic_stats t.fd path x;
{ Eio.File.Stat.
dev = X.dev x;
ino = X.ino x;
Expand Down
50 changes: 45 additions & 5 deletions lib_eio_linux/eio_linux.mli
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ val run :

(** Low-level API for using uring directly. *)
module Low_level : sig
type dir_fd =
| FD of fd
| Cwd (** Confined to "." *)
| Fs (** Unconfined "."; also allows absolute paths *)

val noop : unit -> unit
(** [noop ()] performs a uring noop. This is only useful for benchmarking. *)

Expand Down Expand Up @@ -88,6 +93,16 @@ module Low_level : sig

(** {1 File manipulation functions} *)

val openat :
sw:Switch.t ->
?seekable:bool ->
access:[`R|`W|`RW] ->
flags:Uring.Open_flags.t ->
perm:Unix.file_perm ->
dir_fd -> string ->
fd
(** [openat ~sw ~access ~flags ~perm dir path] opens [dir/path]. *)

val openat2 :
sw:Switch.t ->
?seekable:bool ->
Expand All @@ -96,8 +111,9 @@ module Low_level : sig
perm:Unix.file_perm ->
resolve:Uring.Resolve.t ->
?dir:fd -> string -> fd
(** [openat2 ~sw ~flags ~perm ~resolve ~dir path] opens [dir/path].
(** [openat2 ~sw ~access ~flags ~perm ~resolve ~dir path] opens [dir/path].
It provides full access to the resolve flags.
See {!Uring.openat2} for details. *)

val read_upto : ?file_offset:Optint.Int63.t -> fd -> Uring.Region.chunk -> int -> int
Expand Down Expand Up @@ -156,11 +172,35 @@ module Low_level : sig
val fstat : fd -> Eio.File.Stat.t
(** Like {!Unix.LargeFile.fstat}. *)

val statx : ?fd:fd -> mask:Uring.Statx.Mask.t -> string -> Uring.Statx.t -> Uring.Statx.Flags.t -> unit
(** [statx t ?fd ~mask path buf flags] stats [path], which is resolved relative to [fd]
(or the current directory if [fd] is not given).
val statx :
mask:Uring.Statx.Mask.t ->
follow:bool ->
dir_fd -> string ->
Uring.Statx.t ->
unit
(** [statx ~mask ~follow dir path buf] stats [dir / path].
The results are written to [buf].
If [follow = true] and the item is a symlink, information is reported about the target of the link.
Otherwise, information about the symlink itself is returned. *)

val mkdir : perm:int -> dir_fd -> string -> unit
(** [mkdir ~perm dir path] creates directory [dir / path]. *)

val read_link : dir_fd -> string -> string
(** [read_link dir path] reads the target of symlink [dir / path]. *)

val unlink : rmdir:bool -> dir_fd -> string -> unit
(** [unlink ~rmdir dir path] removes directory entry [dir / path].
If [rmdir = true] then the target must be a directory.
Otherwise, it must not be a directory. *)

val rename : dir_fd -> string -> dir_fd -> string -> unit
(** [rename old_dir old_path new_dir new_path] renames [old_dir / old_path] as [new_dir / new_path]. *)

The results are written to [buf]. *)
val pipe : sw:Switch.t -> fd * fd
(** [pipe ~sw] returns a pair [r, w] with the readable and writeable ends of a new pipe. *)

val read_dir : fd -> string list
(** [read_dir dir] reads all directory entries from [dir].
Expand Down
21 changes: 8 additions & 13 deletions lib_eio_linux/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ let with_parent_dir op dir path fn =
Fd.use_exn op parent @@ fun parent ->
fn parent leaf

let statx ?fd ~mask path buf flags =
let statx_raw ?fd ~mask path buf flags =
let res =
match fd with
| None -> Sched.enter "statx" (enqueue_statx (None, path, buf, flags, mask))
Expand All @@ -411,24 +411,25 @@ let statx ?fd ~mask path buf flags =
in
if res <> 0 then raise @@ Err.wrap_fs (Uring.error_of_errno res) "statx" path

let statx_confined ~mask ~follow fd path buf =
let statx ~mask ~follow fd path buf =
let module X = Uring.Statx in
let flags = if follow then X.Flags.empty else X.Flags.symlink_nofollow in
let flags = if follow then X.Flags.empty_path else X.Flags.(empty_path + symlink_nofollow) in
match fd with
| Fs -> statx ~mask path buf flags
| Fs -> statx_raw ~mask path buf flags
| FD fd when path = "" -> statx_raw ~fd ~mask "" buf flags
| Cwd | FD _ when not follow ->
with_parent_dir_fd fd path @@ fun parent leaf ->
statx ~mask ~fd:parent leaf buf flags
statx_raw ~mask ~fd:parent leaf buf flags
| Cwd | FD _ ->
Switch.run ~name:"statx" @@ fun sw ->
let fd = openat ~sw ~seekable:false fd (if path = "" then "." else path)
~access:`R
~flags:Uring.Open_flags.(cloexec + path)
~perm:0
in
statx ~fd ~mask "" buf Uring.Statx.Flags.(flags + empty_path)
statx_raw ~fd ~mask "" buf flags

let mkdir_beneath ~perm dir path =
let mkdir ~perm dir path =
(* [mkdir] is really an operation on [path]'s parent. Get a reference to that first: *)
with_parent_dir "mkdir" dir path @@ fun parent leaf ->
try eio_mkdirat parent leaf perm
Expand Down Expand Up @@ -470,12 +471,6 @@ let accept ~sw fd =
client, client_addr
)

let open_dir ~sw dir path =
openat ~sw ~seekable:false dir path
~access:`R
~flags:Uring.Open_flags.(cloexec + directory)
~perm:0

let read_dir fd =
Fd.use_exn "read_dir" fd @@ fun fd ->
let rec read_all acc fd =
Expand Down
10 changes: 5 additions & 5 deletions lib_eio_linux/tests/test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ let test_statx () =
Eio.Path.with_open_out path ~create:(`Exclusive 0o600) @@ fun file ->
Eio.Flow.copy_string "hello" file;
let buf = Uring.Statx.create () in
let test expected_len ~flags ?fd path =
Eio_linux.Low_level.statx ~mask:X.Mask.(type' + size) ?fd path buf flags;
let test expected_len ~follow dir path =
Eio_linux.Low_level.statx ~follow ~mask:X.Mask.(type' + size) dir path buf;
Alcotest.check kind_t "kind" `Regular_file (Uring.Statx.kind buf);
Alcotest.(check int64) "size" expected_len (Uring.Statx.size buf)
in
(* Lookup via cwd *)
test 5L ~flags:X.Flags.empty ?fd:None "test2.data";
test 5L ~follow:false Cwd "test2.data";
Eio.Flow.copy_string "+" file;
(* Lookup via file FD *)
Switch.run (fun sw ->
Expand All @@ -182,7 +182,7 @@ let test_statx () =
~resolve:Uring.Resolve.empty
"test2.data"
in
test 6L ~flags:X.Flags.empty_path ~fd ""
test 6L ~follow:false (FD fd) ""
);
(* Lookup via directory FD *)
Eio.Flow.copy_string "+" file;
Expand All @@ -194,7 +194,7 @@ let test_statx () =
~resolve:Uring.Resolve.empty
"."
in
test 7L ~flags:X.Flags.empty_path ~fd "test2.data"
test 7L ~follow:false (FD fd) "test2.data"
);
()

Expand Down

0 comments on commit e48f611

Please sign in to comment.