Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Eio.File.{seek,sync,truncate} #626

Merged
merged 2 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib_eio/file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ module Pi = struct

val pread : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
val stat : t -> Stat.t
val seek : t -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
val close : t -> unit
end

Expand All @@ -80,6 +81,8 @@ module Pi = struct
include READ with type t := t

val pwrite : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
val sync : t -> unit
val truncate : t -> Optint.Int63.t -> unit
end

type (_, _, _) Resource.pi +=
Expand Down Expand Up @@ -140,3 +143,15 @@ let pwrite_all (Resource.T (t, ops)) ~file_offset bufs =
)
in
aux ~file_offset bufs

let seek (Resource.T (t, ops)) off cmd =
let module X = (val (Resource.get ops Pi.Read)) in
X.seek t off cmd

let sync (Resource.T (t, ops)) =
let module X = (val (Resource.get ops Pi.Write)) in
X.sync t

let truncate (Resource.T (t, ops)) len =
let module X = (val (Resource.get ops Pi.Write)) in
X.truncate t len
74 changes: 50 additions & 24 deletions lib_eio/file.mli
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
open Std

(** {2 Types} *)

(** Traditional Unix permissions. *)
module Unix_perm : sig
type t = int
Expand Down Expand Up @@ -54,37 +56,16 @@ type rw_ty = [ro_ty | Flow.sink_ty]
type 'a rw = ([> rw_ty] as 'a) r
(** A file opened for reading and writing. *)

module Pi : sig
module type READ = sig
include Flow.Pi.SOURCE

val pread : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
val stat : t -> Stat.t
val close : t -> unit
end

module type WRITE = sig
include Flow.Pi.SINK
include READ with type t := t

val pwrite : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
end

type (_, _, _) Resource.pi +=
| Read : ('t, (module READ with type t = 't), [> ro_ty]) Resource.pi
| Write : ('t, (module WRITE with type t = 't), [> rw_ty]) Resource.pi

val ro : (module READ with type t = 't) -> ('t, ro_ty) Resource.handler

val rw : (module WRITE with type t = 't) -> ('t, rw_ty) Resource.handler
end
(** {2 Metadata} *)

val stat : _ ro -> Stat.t
(** [stat t] returns the {!Stat.t} record associated with [t]. *)

val size : _ ro -> Optint.Int63.t
(** [size t] returns the size of [t]. *)

(** {2 Reading and writing} *)

val pread : _ ro -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
(** [pread t ~file_offset bufs] performs a single read of [t] at [file_offset] into [bufs].

Expand All @@ -108,3 +89,48 @@ val pwrite_single : _ rw -> file_offset:Optint.Int63.t -> Cstruct.t list -> int

val pwrite_all : _ rw -> file_offset:Optint.Int63.t -> Cstruct.t list -> unit
(** [pwrite_all t ~file_offset bufs] writes all the data in [bufs] to location [file_offset] in [t]. *)

val seek : _ ro -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
(** Set and/or get the current file position.

Like {!Unix.lseek}. *)

val sync : _ rw -> unit
(** Flush file buffers to disk.

Like {!Unix.fsync}. *)

val truncate : _ rw -> Optint.Int63.t -> unit
(** Set the length of a file.

Like {!Unix.ftruncate}. *)

(** {2 Provider Interface} *)

module Pi : sig
module type READ = sig
include Flow.Pi.SOURCE

val pread : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
val stat : t -> Stat.t
val seek : t -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
val close : t -> unit
end

module type WRITE = sig
include Flow.Pi.SINK
include READ with type t := t

val pwrite : t -> file_offset:Optint.Int63.t -> Cstruct.t list -> int
val sync : t -> unit
val truncate : t -> Optint.Int63.t -> unit
end

type (_, _, _) Resource.pi +=
| Read : ('t, (module READ with type t = 't), [> ro_ty]) Resource.pi
| Write : ('t, (module WRITE with type t = 't), [> rw_ty]) Resource.pi

val ro : (module READ with type t = 't) -> ('t, ro_ty) Resource.handler

val rw : (module WRITE with type t = 't) -> ('t, rw_ty) Resource.handler
end
4 changes: 4 additions & 0 deletions lib_eio_linux/eio_linux.ml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ module Flow = struct
let recv_msg_with_fds t ~sw ~max_fds data =
let _addr, n, fds = Low_level.recv_msg_with_fds t ~sw ~max_fds data in
n, fds

let seek = Low_level.lseek
let sync = Low_level.fsync
let truncate = Low_level.ftruncate
end

let flow_handler = Eio_unix.Pi.flow_handler (module Flow)
Expand Down
15 changes: 15 additions & 0 deletions lib_eio_linux/eio_linux.mli
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ module Low_level : sig
The entries are not returned in any particular order
(not even necessarily the order in which Linux returns them). *)

val lseek : fd -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
(** Set and/or get the current file position.

Like {!Unix.lseek}. *)

val fsync : fd -> unit
(** Flush file buffers to disk.

Like {!Unix.fsync}. *)

val ftruncate : fd -> Optint.Int63.t -> unit
(** Set the length of a file.

Like {!Unix.ftruncate}. *)

(** {1 Sockets} *)

val accept : sw:Switch.t -> fd -> (fd * Unix.sockaddr)
Expand Down
21 changes: 21 additions & 0 deletions lib_eio_linux/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,27 @@ external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrand

external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents"

let lseek fd off cmd =
Fd.use_exn "lseek" fd @@ fun fd ->
let cmd =
match cmd with
| `Set -> Unix.SEEK_SET
| `Cur -> Unix.SEEK_CUR
| `End -> Unix.SEEK_END
in
Unix.LargeFile.lseek fd (Optint.Int63.to_int64 off) cmd
|> Optint.Int63.of_int64

let fsync fd =
(* todo: https://github.com/ocaml-multicore/ocaml-uring/pull/103 *)
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "fsync" fd Unix.fsync

let ftruncate fd len =
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "ftruncate" fd @@ fun fd ->
Unix.LargeFile.ftruncate fd (Optint.Int63.to_int64 len)

let getrandom { Cstruct.buffer; off; len } =
let rec loop n =
if n = len then
Expand Down
4 changes: 4 additions & 0 deletions lib_eio_posix/flow.ml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ module Impl = struct
let _addr, n, fds = Low_level.recv_msg_with_fds t ~sw ~max_fds (Array.of_list data) in
n, fds

let seek = Low_level.lseek
let sync = Low_level.fsync
let truncate = Low_level.ftruncate

let fd t = t

let close = Eio_unix.Fd.close
Expand Down
20 changes: 20 additions & 0 deletions lib_eio_posix/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,26 @@ 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 lseek fd off cmd =
Fd.use_exn "lseek" fd @@ fun fd ->
let cmd =
match cmd with
| `Set -> Unix.SEEK_SET
| `Cur -> Unix.SEEK_CUR
| `End -> Unix.SEEK_END
in
Unix.LargeFile.lseek fd (Optint.Int63.to_int64 off) cmd
|> Optint.Int63.of_int64

let fsync fd =
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "fsync" fd Unix.fsync

let ftruncate fd len =
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "ftruncate" fd @@ fun fd ->
Unix.LargeFile.ftruncate fd (Optint.Int63.to_int64 len)

let pipe ~sw =
let unix_r, unix_w = Unix.pipe ~cloexec:true () in
let r = Fd.of_unix ~sw ~blocking:false ~close_unix:true unix_r in
Expand Down
4 changes: 4 additions & 0 deletions lib_eio_posix/low_level.mli
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ val send_msg : fd -> ?fds:fd list -> ?dst:Unix.sockaddr -> Cstruct.t array -> in

val getrandom : Cstruct.t -> unit

val lseek : fd -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
val fsync : fd -> unit
val ftruncate : fd -> Optint.Int63.t -> unit

type stat

val create_stat : unit -> stat
Expand Down
4 changes: 4 additions & 0 deletions lib_eio_windows/flow.ml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ module Impl = struct

let recv_msg_with_fds _t ~sw:_ ~max_fds:_ _data = failwith "Not implemented on Windows"

let seek = Low_level.lseek
let sync = Low_level.fsync
let truncate = Low_level.ftruncate

let fd t = t

let close = Eio_unix.Fd.close
Expand Down
20 changes: 20 additions & 0 deletions lib_eio_windows/low_level.ml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,26 @@ let rename ?old_dir old_path ?new_dir new_path =
in_worker_thread @@ fun () ->
eio_renameat old_dir old_path new_dir new_path

let lseek fd off cmd =
Fd.use_exn "lseek" fd @@ fun fd ->
let cmd =
match cmd with
| `Set -> Unix.SEEK_SET
| `Cur -> Unix.SEEK_CUR
| `End -> Unix.SEEK_END
in
Unix.LargeFile.lseek fd (Optint.Int63.to_int64 off) cmd
|> Optint.Int63.of_int64

let fsync fd =
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "fsync" fd Unix.fsync

let ftruncate fd len =
Eio_unix.run_in_systhread @@ fun () ->
Fd.use_exn "ftruncate" fd @@ fun fd ->
Unix.LargeFile.ftruncate fd (Optint.Int63.to_int64 len)

let pipe ~sw =
let unix_r, unix_w = Unix.pipe ~cloexec:true () in
let r = Fd.of_unix ~sw ~blocking:false ~close_unix:true unix_r in
Expand Down
4 changes: 4 additions & 0 deletions lib_eio_windows/low_level.mli
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ val send_msg : fd -> ?dst:Unix.sockaddr -> bytes -> int

val getrandom : Cstruct.t -> unit

val lseek : fd -> Optint.Int63.t -> [`Set | `Cur | `End] -> Optint.Int63.t
val fsync : fd -> unit
val ftruncate : fd -> Optint.Int63.t -> unit

val fstat : fd -> Unix.LargeFile.stats
val lstat : string -> Unix.LargeFile.stats

Expand Down
20 changes: 20 additions & 0 deletions tests/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -675,3 +675,23 @@ Exception: Failure "Simulated error".
+<native-sub:/etc/passwd> -> Some /etc/passwd
- : unit = ()
```

# Seek, truncate and sync

```ocaml
# Eio_main.run @@ fun env ->
Eio.Path.with_open_out (env#cwd / "seek-test") ~create:(`If_missing 0o700) @@ fun file ->
Eio.File.truncate file (Int63.of_int 10);
assert ((Eio.File.stat file).size = (Int63.of_int 10));
let pos = Eio.File.seek file (Int63.of_int 3) `Set in
traceln "seek from start: %a" Int63.pp pos;
let pos = Eio.File.seek file (Int63.of_int 2) `Cur in
traceln "relative seek: %a" Int63.pp pos;
let pos = Eio.File.seek file (Int63.of_int (-1)) `End in
traceln "seek from end: %a" Int63.pp pos;
Eio.File.sync file; (* (no way to check if this actually worked, but ensure it runs) *)
+seek from start: 3
+relative seek: 5
+seek from end: 9
- : unit = ()
```
Loading