Skip to content

Commit

Permalink
Merge pull request #626 from talex5/file-ops
Browse files Browse the repository at this point in the history
Add Eio.File.{seek,sync,truncate}
  • Loading branch information
talex5 authored Sep 29, 2023
2 parents 1ef8ad4 + 482d247 commit 082bf00
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 24 deletions.
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 = ()
```

0 comments on commit 082bf00

Please sign in to comment.