Skip to content

Commit

Permalink
WIP: fstat
Browse files Browse the repository at this point in the history
  • Loading branch information
talex5 committed Feb 13, 2024
1 parent 4586f12 commit d5359d4
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 40 deletions.
14 changes: 2 additions & 12 deletions lib_eio_posix/fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,6 @@ end = struct

let v ~label ~path:dir_path fd = { fd; dir_path; sandbox = fd <> Fs; label; closed = false }

(* Sandboxes use [O_NOFOLLOW] when opening files ([resolve] already removed any symlinks).
This avoids a race where symlink might be added after [realpath] returns. *)
let opt_nofollow t =
if t.sandbox then Low_level.Open_flags.nofollow else Low_level.Open_flags.empty

let open_in t ~sw path =
let fd = Err.run (Low_level.openat ~mode:0 ~sw t.fd path) Low_level.Open_flags.rdonly in
(Flow.of_fd fd :> Eio.File.ro_ty Eio.Resource.t)
Expand All @@ -99,7 +94,7 @@ end = struct
| `Exclusive perm -> perm, Low_level.Open_flags.(creat + excl)
in
let flags = if append then Low_level.Open_flags.(flags + append) else flags in
let flags = Low_level.Open_flags.(flags + rdwr + opt_nofollow t) in
let flags = Low_level.Open_flags.(flags + rdwr) in
match Low_level.openat ~sw ~mode t.fd path flags with
| fd -> (Flow.of_fd fd :> Eio.File.rw_ty r)
| exception Unix.Unix_error (ELOOP, _, _) ->
Expand Down Expand Up @@ -130,12 +125,7 @@ end = struct

let stat t ~follow path =
let buf = Low_level.create_stat () in
if follow then (
Err.run (Low_level.fstatat ~buf ~follow:true) (resolve t path);
) else (
with_parent_dir t path @@ fun dirfd path ->
Err.run (Low_level.fstatat ~buf ?dirfd ~follow:false) path;
);
Err.run (Low_level.fstatat ~buf ~follow t.fd) path;
Flow.eio_of_stat buf

let read_dir t path =
Expand Down
81 changes: 55 additions & 26 deletions lib_eio_posix/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,21 @@ module Resolve = struct
in
aux state.dir_stack

let open_beneath_fallback ?dirfd:base ~sw:final_sw ~mode path flags =
Switch.run @@ fun sw ->
let path = parse_rel path in
let with_state ~sw base path fn =
let state = { sw; path; dir_stack = Base base; max_follows = 8 } in
Switch.on_release sw (fun () -> close_tmp state);
match fn state with
| x -> close_tmp state; x
| exception ex ->
let bt = Printexc.get_raw_backtrace () in
close_tmp state;
Printexc.raise_with_backtrace ex bt

let open_beneath_fallback ?dirfd:base ~sw ~mode path flags =
let path = parse_rel path in
with_state ~sw base path @@ fun state ->
let rec aux leaf =
let base = current_base state in
match openat_raw ~sw:final_sw base leaf ~mode Open_flags.(flags + nofollow) with
match openat_raw ~sw base leaf ~mode Open_flags.(flags + nofollow) with
| fd -> fd
| exception (Unix.Unix_error ((ELOOP | ENOTDIR), _, _) as e)
when state.max_follows > 0 ->
Expand All @@ -293,15 +300,21 @@ module Resolve = struct
in
aux (resolve state)

let with_parent ?dirfd:base path fn =
Switch.run @@ fun sw ->
let with_parent ?dirfd:base ~sw path fn =
let path = parse_rel path in
let state = { sw; path; dir_stack = Base base; max_follows = 8 } in
Switch.on_release sw (fun () -> close_tmp state);
let leaf = resolve state in
(* todo: could close the stack sooner here *)
let base = current_base state in
fn base leaf
with_state ~sw base path @@ fun state ->
let rec aux leaf =
let base = current_base state in
match fn base leaf with
| Ok x -> x
| Error `Symlink ->
state.max_follows <- state.max_follows - 1;
match Eio_unix.Private.read_link base leaf with
| target ->
state.path <- parse_rel target;
aux (resolve state)
in
aux (resolve state)
end

let open_beneath_fallback = Resolve.open_beneath_fallback (* For tests *)
Expand All @@ -324,11 +337,12 @@ let openat ~sw ~mode fd path flags =
| Cwd -> open_beneath ~sw ~mode ?dirfd:None path flags
| Fd dirfd -> open_beneath ~sw ~mode ~dirfd path flags

let with_parent fd path fn = (* todo: use o_resolve_beneath if available *)
(* Note: called in worker thread *)
let with_parent_worker ~sw fd path fn = (* todo: use o_resolve_beneath if available *)
match fd with
| Fs -> fn None path
| Cwd -> Resolve.with_parent path fn
| Fd dirfd -> Resolve.with_parent ~dirfd path fn
| Cwd -> Resolve.with_parent ~sw path (fun x y -> Ok (fn x y))
| Fd dirfd -> Resolve.with_parent ~sw ~dirfd path (fun x y -> Ok (fn x y))

external eio_mkdirat : Unix.file_descr -> string -> Unix.file_perm -> unit = "caml_eio_posix_mkdirat"

Expand Down Expand Up @@ -357,16 +371,6 @@ external create_stat : unit -> stat = "caml_eio_posix_make_stat"
external eio_fstatat : stat -> Unix.file_descr -> string -> int -> unit = "caml_eio_posix_fstatat"
external eio_fstat : stat -> Unix.file_descr -> unit = "caml_eio_posix_fstat"

let fstat ~buf fd =
Fd.use_exn "fstat" fd @@ fun fd ->
eio_fstat buf fd

let fstatat ~buf ?dirfd ~follow path =
in_worker_thread "fstat" @@ fun () ->
let flags = if follow then 0 else Config.at_symlink_nofollow in
with_dirfd "fstatat" dirfd @@ fun dirfd ->
eio_fstatat buf dirfd path flags

external blksize : stat -> (int64 [@unboxed]) = "ocaml_eio_posix_stat_blksize_bytes" "ocaml_eio_posix_stat_blksize_native" [@@noalloc]
external nlink : stat -> (int64 [@unboxed]) = "ocaml_eio_posix_stat_nlink_bytes" "ocaml_eio_posix_stat_nlink_native" [@@noalloc]
external uid : stat -> (int64 [@unboxed]) = "ocaml_eio_posix_stat_uid_bytes" "ocaml_eio_posix_stat_uid_native" [@@noalloc]
Expand All @@ -387,6 +391,27 @@ external atime_nsec : stat -> int = "ocaml_eio_posix_stat_atime_nsec" [@@noalloc
external ctime_nsec : stat -> int = "ocaml_eio_posix_stat_ctime_nsec" [@@noalloc]
external mtime_nsec : stat -> int = "ocaml_eio_posix_stat_mtime_nsec" [@@noalloc]

let fstat ~buf fd =
Fd.use_exn "fstat" fd @@ fun fd ->
eio_fstat buf fd

let fstatat_confined ~buf ~follow dirfd path =
Switch.run @@ fun sw ->
in_worker_thread "fstat" @@ fun () ->
Resolve.with_parent ~sw ?dirfd path @@ fun dirfd path ->
with_dirfd "fstatat" dirfd @@ fun dirfd ->
eio_fstatat buf dirfd path Config.at_symlink_nofollow;
if follow && kind buf = `Symbolic_link then Error `Symlink else Ok ()

let fstatat ~buf ~follow dirfd path =
match dirfd with
| Fs ->
in_worker_thread "fstat" @@ fun () ->
let flags = if follow then 0 else Config.at_symlink_nofollow in
eio_fstatat buf at_fdcwd path flags
| Cwd -> fstatat_confined ~buf ~follow None path
| Fd dirfd -> fstatat_confined ~buf ~follow (Some dirfd) path

let lseek fd off cmd =
Fd.use_exn "lseek" fd @@ fun fd ->
let cmd =
Expand Down Expand Up @@ -496,3 +521,7 @@ module Process = struct
| "" -> t (* Success! Execing the child closed [errors_w] and we got EOF. *)
| err -> failwith err
end

let with_parent fd path fn =
Switch.run @@ fun sw ->
with_parent_worker ~sw fd path fn
2 changes: 1 addition & 1 deletion lib_eio_posix/low_level.mli
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type stat
val create_stat : unit -> stat

val fstat : buf:stat -> fd -> unit
val fstatat : buf:stat -> ?dirfd:fd -> follow:bool -> string -> unit
val fstatat : buf:stat -> follow:bool -> dir_fd -> string -> unit

external blksize : stat -> (int64 [@unboxed]) = "ocaml_eio_posix_stat_blksize_bytes" "ocaml_eio_posix_stat_blksize_native" [@@noalloc]
external nlink : stat -> (int64 [@unboxed]) = "ocaml_eio_posix_stat_nlink_bytes" "ocaml_eio_posix_stat_nlink_native" [@@noalloc]
Expand Down
2 changes: 1 addition & 1 deletion lib_eio_posix/net.ml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ let listen ~reuse_addr ~reuse_port ~backlog ~sw (listen_addr : Eio.Net.Sockaddr.
| `Unix path ->
if reuse_addr then (
let buf = Low_level.create_stat () in
match Low_level.fstatat ~buf ~follow:false path with
match Low_level.fstatat ~buf ~follow:false Fs path with
| () -> if Low_level.kind buf = `Socket then Unix.unlink path
| exception Unix.Unix_error (Unix.ENOENT, _, _) -> ()
| exception Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap code name arg
Expand Down

0 comments on commit d5359d4

Please sign in to comment.