···11+Copyright (C) 2024 Patrick Ferris
22+33+Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
44+55+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
66+77+Much of the codebase is borrowed directly from Eio, with the following license:
88+99+Copyright (C) 2021 Anil Madhavapeddy
1010+Copyright (C) 2022 Thomas Leonard
1111+1212+Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
1313+1414+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+5
vendor/void/README.md
···11+Void
22+----
33+44+Void processes for Eio.
55+
+29
vendor/void/dune-project
···11+(lang dune 3.15)
22+33+(name void)
44+55+(generate_opam_files true)
66+77+(source
88+ (github patricoferris/void))
99+1010+(authors "Patrick Ferris")
1111+1212+(maintainers "Patrick Ferris")
1313+1414+(license MIT)
1515+1616+(documentation https://ocaml.org/p/void)
1717+1818+(package
1919+ (name void)
2020+ (synopsis "Void Processes in Eio_linux")
2121+ (description "A longer description")
2222+ (depends
2323+ ocaml
2424+ dune
2525+ eio_posix)
2626+ (tags
2727+ (linux process spawn)))
2828+2929+; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
+69
vendor/void/examples/alpine.ml
···11+open Eio.Std
22+33+let ( / ) = Eio.Path.( / )
44+55+let test_data fs data =
66+ let tempdir = Filename.temp_dir "void-" "-alpine" in
77+ Eio.Path.(save ~create:(`If_missing 0o644) (fs / tempdir / "data.txt") data);
88+ Eio.traceln "Test data in %s" tempdir;
99+ tempdir
1010+1111+let get_alpine_image ~fs ~proc =
1212+ let tmpdir = Filename.temp_dir "void" "alpine" in
1313+ Eio.traceln "Extracting alpine to %s..." tmpdir;
1414+ let container_id =
1515+ Eio.Process.parse_out proc Eio.Buf_read.take_all
1616+ [ "docker"; "run"; "-d"; "alpine" ]
1717+ |> String.trim
1818+ in
1919+ Eio.traceln "Container %s" container_id;
2020+ let () =
2121+ Eio.Process.run proc
2222+ [
2323+ "docker";
2424+ "export";
2525+ container_id;
2626+ "-o";
2727+ Filename.concat tmpdir "alpine.tar.gz";
2828+ ]
2929+ in
3030+ Eio.traceln "Untarring...";
3131+ Eio.Path.mkdir ~perm:0o777 (fs / tmpdir / "rootfs");
3232+ let () =
3333+ Eio.Process.run proc
3434+ [
3535+ "tar";
3636+ "-xf";
3737+ Filename.concat tmpdir "alpine.tar.gz";
3838+ "-C";
3939+ Filename.concat tmpdir "rootfs";
4040+ ]
4141+ in
4242+ Filename.concat tmpdir "rootfs"
4343+4444+(* This example read-only mounts a copy of busybox
4545+ into the root-filesystem of the process. *)
4646+let () =
4747+ Eio_posix.run @@ fun env ->
4848+ Switch.run @@ fun sw ->
4949+ let fs = env#fs in
5050+ let proc = env#process_mgr in
5151+ let alpine_img = get_alpine_image ~fs ~proc in
5252+ let mount_src = test_data fs "Hello, World!" in
5353+ let open Void in
5454+ let args =
5555+ let l = Array.length Sys.argv in
5656+ if l <= 1 then
5757+ [
5858+ "/bin/ash"; "-c"; "/bin/echo hello > /hello.txt && /bin/cat /hello.txt";
5959+ ]
6060+ else Array.sub Sys.argv 1 (l - 1) |> Array.to_list
6161+ in
6262+ let void =
6363+ empty |> rootfs ~mode:RW alpine_img
6464+ |> mount ~mode:R ~src:mount_src ~tgt:"data"
6565+ |> exec args
6666+ in
6767+ let t = Void.spawn ~sw void in
6868+ let status = Promise.await (Void.exit_status t) in
6969+ Eio.traceln "Status: %s" (Void.exit_status_to_string status)
+55
vendor/void/examples/curl.ml
···11+open Eio.Std
22+33+let ( / ) = Eio.Path.( / )
44+55+let get_alpine_image ~fs ~proc =
66+ let tmpdir = Filename.temp_dir "void-" "-alpine-curl" in
77+ Eio.traceln "Extracting alpine/curl to %s..." tmpdir;
88+ let container_id =
99+ Eio.Process.parse_out proc Eio.Buf_read.take_all
1010+ [ "docker"; "run"; "-d"; "alpine/curl" ]
1111+ |> String.trim
1212+ in
1313+ Eio.traceln "Container %s" container_id;
1414+ let () =
1515+ Eio.Process.run proc
1616+ [
1717+ "docker";
1818+ "export";
1919+ container_id;
2020+ "-o";
2121+ Filename.concat tmpdir "alpine-curl.tar.gz";
2222+ ]
2323+ in
2424+ Eio.traceln "Untarring...";
2525+ Eio.Path.mkdir ~perm:0o777 (fs / tmpdir / "rootfs");
2626+ let () =
2727+ Eio.Process.run proc
2828+ [
2929+ "tar";
3030+ "-xf";
3131+ Filename.concat tmpdir "alpine-curl.tar.gz";
3232+ "-C";
3333+ Filename.concat tmpdir "rootfs";
3434+ ]
3535+ in
3636+ Filename.concat tmpdir "rootfs"
3737+3838+(* This example read-only mounts a copy of busybox
3939+ into the root-filesystem of the process. *)
4040+let () =
4141+ Eio_posix.run @@ fun env ->
4242+ Switch.run @@ fun sw ->
4343+ let fs = env#fs in
4444+ let proc = env#process_mgr in
4545+ let alpine_img = get_alpine_image ~fs ~proc in
4646+ let open Void in
4747+ let args =
4848+ let l = Array.length Sys.argv in
4949+ if l <= 1 then [ "/bin/ls"; "-l" ]
5050+ else Array.sub Sys.argv 1 (l - 1) |> Array.to_list
5151+ in
5252+ let void = empty |> rootfs ~mode:R alpine_img |> exec args in
5353+ let t = Void.spawn ~sw void in
5454+ let status = Promise.await (Void.exit_status t) in
5555+ Eio.traceln "Status: %s" (Void.exit_status_to_string status)
···11+open Eio.Std
22+33+let () =
44+ Eio_linux.run @@ fun _env ->
55+ Switch.run @@ fun sw ->
66+ let open Void in
77+ let void = empty |> exec [] in
88+ Eio.traceln "Spawning the empty void...";
99+ let t = Void.spawn ~sw void in
1010+ let status = Promise.await (Void.exit_status t) in
1111+ Eio.traceln "Status: %s" (Void.exit_status_to_string status)
vendor/void/examples/hey
This is a binary file and will not be displayed.
+11
vendor/void/examples/hey.c
···11+// Based off of Docker's hello-world
22+// This is standalone program
33+#include <sys/syscall.h>
44+#include <unistd.h>
55+66+const char message[] = "Hello from the Void!\n";
77+88+int main() {
99+ syscall(SYS_write, STDOUT_FILENO, message, sizeof(message) - 1);
1010+ return 0;
1111+}
+22
vendor/void/examples/hey.ml
···11+open Eio.Std
22+33+let ( / ) = Eio.Path.( / )
44+55+let copy_hey fs =
66+ let temp_dir = Filename.temp_dir "void-" "-world" in
77+ let hey = Eio.Path.load (fs / "./examples/hey") in
88+ Eio.Path.save ~create:(`If_missing 0o755) (fs / temp_dir / "hey") hey;
99+ temp_dir
1010+1111+(* This mounts the hello-world into the void process. *)
1212+let () =
1313+ Eio_posix.run @@ fun env ->
1414+ Switch.run @@ fun sw ->
1515+ let hey_dir = copy_hey env#fs in
1616+ let void =
1717+ let open Void in
1818+ empty |> mount ~mode:R ~src:hey_dir ~tgt:"say" |> exec [ "/say/hey" ]
1919+ in
2020+ let t = Void.spawn ~sw void in
2121+ let status = Promise.await (Void.exit_status t) in
2222+ Eio.traceln "Void process: %s" (Void.exit_status_to_string status)
···11+open Eio.Std
22+module Process = Eio_posix.Low_level.Process
33+module Trace = Eio.Private.Trace
44+module Fd = Eio_unix.Fd
55+module Rcfd = Eio_unix.Private.Rcfd
66+module Fork_action = Eio_unix.Private.Fork_action
77+88+type mode = R | RW
99+1010+type void = {
1111+ args : string list;
1212+ rootfs : (string * mode) option;
1313+ mounts : mount list;
1414+}
1515+1616+and mount = { src : string; tgt : string; mode : int [@warning "-69"] }
1717+1818+(* Actions for namespacing *)
1919+module Mount = struct
2020+ module Flags = struct
2121+ include Config.Mount_flags
2222+2323+ let empty : t = 0
2424+ let ( + ) = ( lor )
2525+ end
2626+2727+ module Types = struct
2828+ type t = string
2929+3030+ let btrfs = "btrfs"
3131+ let ext4 = "ext4"
3232+ let auto = "auto"
3333+ end
3434+end
3535+3636+external action_mount : unit -> Fork_action.fork_fn = "void_fork_mount"
3737+3838+let action_mount = action_mount ()
3939+4040+let _mount ~(src : string) ~(target : string) (type_ : Mount.Types.t)
4141+ (flags : Mount.Flags.t) =
4242+ Fork_action.
4343+ { run = (fun k -> k (Obj.repr (action_mount, src, target, type_, flags))) }
4444+4545+external action_pivot_root : unit -> Fork_action.fork_fn
4646+ = "void_fork_pivot_root"
4747+4848+let action_pivot_root = action_pivot_root ()
4949+5050+let pivot_root (new_root : string) (new_root_flags : Mount.Flags.t)
5151+ (tmpfs : bool) (mounts : mount list) =
5252+ Fork_action.
5353+ {
5454+ run =
5555+ (fun k ->
5656+ k
5757+ (Obj.repr
5858+ (action_pivot_root, new_root, new_root_flags, tmpfs, mounts)));
5959+ }
6060+6161+external action_map_uid_gid : unit -> Fork_action.fork_fn
6262+ = "void_fork_map_uid_gid"
6363+6464+let action_map_uid_gid = action_map_uid_gid ()
6565+6666+let map_uid_gid ~uid ~gid =
6767+ Fork_action.{ run = (fun k -> k (Obj.repr (action_map_uid_gid, uid, gid))) }
6868+6969+module Flags = struct
7070+ include Config.Clone_flags
7171+7272+ let ( + ) = ( lor )
7373+end
7474+7575+external eio_spawn :
7676+ Unix.file_descr ->
7777+ Flags.t ->
7878+ Eio_unix.Private.Fork_action.c_action list ->
7979+ int * Unix.file_descr = "caml_void_clone3"
8080+8181+type t = {
8282+ pid : int;
8383+ pid_fd : Fd.t;
8484+ exit_status : Unix.process_status Promise.t;
8585+}
8686+8787+let exit_status t = t.exit_status
8888+let pid t = t.pid
8989+9090+(* Read a (typically short) error message from a child process. *)
9191+let rec read_response fd =
9292+ let buf = Cstruct.create 256 in
9393+ match Eio_posix.Low_level.readv fd [| buf |] with
9494+ | 0 | (exception End_of_file) -> ""
9595+ | len -> Cstruct.to_string buf ~len ^ read_response fd
9696+9797+let void_flags = List.fold_left Flags.( + ) 0 Flags.all
9898+9999+type path = string
100100+101101+let empty = { args = []; rootfs = None; mounts = [] }
102102+103103+let actions v : Fork_action.t list =
104104+ let root, tmpfs, root_mode =
105105+ match v.rootfs with
106106+ | None -> (Filename.temp_dir "void-" "-tmpdir", true, R)
107107+ | Some (s, m) -> (s, false, m)
108108+ in
109109+ let args = match v.args with [] -> failwith "No exec" | args -> args in
110110+ let e =
111111+ Process.Fork_action.execve (List.hd args) ~env:[||]
112112+ ~argv:(Array.of_list args)
113113+ in
114114+ (* Process mount point points *)
115115+ let mounts =
116116+ List.map
117117+ (fun mnt ->
118118+ let src = Filename.concat "/.old_root" mnt.src in
119119+ let tgt = Filename.concat "/" mnt.tgt in
120120+ { mnt with tgt; src })
121121+ v.mounts
122122+ in
123123+ let root_flags =
124124+ if root_mode = R then Mount.Flags.ms_rdonly else Mount.Flags.empty
125125+ in
126126+ let mounts = pivot_root root root_flags tmpfs mounts in
127127+ let uid, gid = Unix.(getuid (), getgid ()) in
128128+ let user_namespace = map_uid_gid ~uid ~gid in
129129+ [ user_namespace; mounts; e ]
130130+131131+let rootfs ~mode path v = { v with rootfs = Some (path, mode) }
132132+let exec args v = { v with args }
133133+134134+let mount ~mode ~src ~tgt v =
135135+ let mode = if mode = R then Mount.Flags.ms_rdonly else Mount.Flags.empty in
136136+ { v with mounts = { src; tgt; mode } :: v.mounts }
137137+138138+(* From eio_linux/eio_posix *)
139139+let with_pipe fn =
140140+ Switch.run @@ fun sw ->
141141+ let r, w = Eio_posix.Low_level.pipe ~sw in
142142+ fn r w
143143+144144+external pidfd_send_signal : Unix.file_descr -> int -> unit
145145+ = "caml_void_pidfd_send_signal"
146146+147147+let signal t signum =
148148+ Fd.use t.pid_fd ~if_closed:Fun.id @@ fun pid_fd ->
149149+ pidfd_send_signal pid_fd signum
150150+151151+let rec waitpid pid =
152152+ match Unix.waitpid [] pid with
153153+ | p, status ->
154154+ assert (p = pid);
155155+ status
156156+ | exception Unix.Unix_error (EINTR, _, _) -> waitpid pid
157157+158158+let spawn ~sw v =
159159+ with_pipe @@ fun errors_r errors_w ->
160160+ Eio_unix.Private.Fork_action.with_actions (actions v) @@ fun c_actions ->
161161+ Switch.check sw;
162162+ let exit_status, set_exit_status = Promise.create () in
163163+ let t =
164164+ let pid, pid_fd =
165165+ Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
166166+ Eio.Private.Trace.with_span "spawn" @@ fun () ->
167167+ let flags = Flags.(clone_pidfd + void_flags) in
168168+ eio_spawn errors_w flags c_actions
169169+ in
170170+ let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in
171171+ { pid; pid_fd; exit_status }
172172+ in
173173+ Fd.close errors_w;
174174+ Fiber.fork_daemon ~sw (fun () ->
175175+ let cleanup () =
176176+ Fd.close t.pid_fd;
177177+ Promise.resolve set_exit_status (waitpid t.pid);
178178+ `Stop_daemon
179179+ in
180180+ match Eio_posix.Low_level.await_readable "void_spawn" t.pid_fd with
181181+ | () -> Eio.Cancel.protect cleanup
182182+ | exception Eio.Cancel.Cancelled _ ->
183183+ Eio.Cancel.protect (fun () ->
184184+ Printf.eprintf "Cancelled?";
185185+ signal t Sys.sigkill;
186186+ Eio_posix.Low_level.await_readable "void_spawn" t.pid_fd;
187187+ cleanup ()));
188188+ (* Check for errors starting the process. *)
189189+ match read_response errors_r with
190190+ | "" -> t (* Success! Execing the child closed [errors_w] and we got EOF. *)
191191+ | err -> failwith err
192192+193193+let to_eio_status t =
194194+ match t with
195195+ | Unix.WEXITED i -> `Exited i
196196+ | Unix.WSIGNALED i -> `Signaled i
197197+ | Unix.WSTOPPED _ -> assert false
198198+199199+let exit_status_to_string = function
200200+ | Unix.WEXITED n -> Printf.sprintf "Exited with %i" n
201201+ | Unix.WSTOPPED n -> Printf.sprintf "Stopped with %i" n
202202+ | Unix.WSIGNALED n -> Printf.sprintf "Signalled with %i" n
+61
vendor/void/src/void.mli
···11+(** {1 Void}
22+33+ Void is a library to provide {e void processes}. A void process is an
44+ {e empty} process, one in which most global resources have been removed. As
55+ a user, you can add back in precisely those pieces you need for your
66+ process.
77+88+ Void uses Eio's [fork_action]s to provide this mechanism, thus it is only
99+ available with Eio. *)
1010+1111+module Mount : sig
1212+ module Flags : sig
1313+ type t = private int
1414+1515+ val ms_remount : t
1616+ val ms_bind : t
1717+ val ms_shared : t
1818+ val ( + ) : t -> t -> t
1919+ end
2020+2121+ module Types : sig
2222+ type t = private string
2323+2424+ val btrfs : t
2525+ val ext4 : t
2626+ val auto : t
2727+ end
2828+end
2929+3030+type t
3131+(** A void process *)
3232+3333+type path = string
3434+(** File paths *)
3535+3636+type mode = R | RW
3737+(* Mounting modes *)
3838+3939+type void
4040+(** A configuration for a void process *)
4141+4242+val empty : void
4343+(** The empty void *)
4444+4545+val rootfs : mode:mode -> path -> void -> void
4646+(** Add a new root filesystem *)
4747+4848+val mount : mode:mode -> src:path -> tgt:path -> void -> void
4949+5050+val exec : string list -> void -> void
5151+(** Make a void configuration ready to be spawned *)
5252+5353+val spawn : sw:Eio.Switch.t -> void -> t
5454+(** Spawn a void process *)
5555+5656+val pid : t -> int
5757+(** The pid of a running void process *)
5858+5959+val exit_status : t -> Unix.process_status Eio.Promise.t
6060+val exit_status_to_string : Unix.process_status -> string
6161+val to_eio_status : Unix.process_status -> Eio.Process.exit_status
+399
vendor/void/src/void_action.c
···11+#define _GNU_SOURCE
22+#define _FILE_OFFSET_BITS 64
33+#include <linux/sched.h>
44+55+#include <sys/stat.h>
66+#include <sys/types.h>
77+#include <sys/eventfd.h>
88+#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 24
99+#include <sys/random.h>
1010+#endif
1111+#include <sys/syscall.h>
1212+#include <sys/wait.h>
1313+#include <sys/mount.h>
1414+#include <limits.h>
1515+#include <errno.h>
1616+#include <dirent.h>
1717+#include <fcntl.h>
1818+#include <signal.h>
1919+#include <unistd.h>
2020+#include <string.h>
2121+2222+2323+#include <caml/mlvalues.h>
2424+#include <caml/unixsupport.h>
2525+#include <caml/memory.h>
2626+#include <caml/custom.h>
2727+#include <caml/fail.h>
2828+2929+// From Eio
3030+#include <include/fork_action.h>
3131+3232+#ifndef SYS_pidfd_send_signal
3333+#define SYS_pidfd_send_signal 424
3434+#endif
3535+3636+// struct clone_args isn't defined in linux-lts headers, so define it here
3737+// Note that this struct is versioned by size. See linux/sched.h for details
3838+struct caml_void_clone_args
3939+{
4040+ uint64_t flags;
4141+ uint64_t pidfd;
4242+ uint64_t child_tid;
4343+ uint64_t parent_tid;
4444+ uint64_t exit_signal;
4545+ uint64_t stack;
4646+ uint64_t stack_size;
4747+ uint64_t tls;
4848+};
4949+5050+static int
5151+pidfd_send_signal (int pidfd, int sig, siginfo_t *info, unsigned int flags)
5252+{
5353+ return syscall (SYS_pidfd_send_signal, pidfd, sig, info, flags);
5454+}
5555+5656+CAMLprim value
5757+caml_void_pidfd_send_signal (value v_pidfd, value v_signal)
5858+{
5959+ CAMLparam0 ();
6060+ int res;
6161+6262+ res =
6363+ pidfd_send_signal (Int_val (v_pidfd),
6464+ caml_convert_signal_number (Int_val (v_signal)), NULL,
6565+ 0);
6666+ if (res == -1)
6767+ uerror ("pidfd_send_signal", Nothing);
6868+ CAMLreturn (Val_unit);
6969+}
7070+7171+static pid_t
7272+clone3_no_fallback (struct caml_void_clone_args *cl_args)
7373+{
7474+ int *pidfd = (int *) (uintptr_t) cl_args->pidfd;
7575+ pid_t child_pid =
7676+ syscall (SYS_clone3, cl_args, sizeof (struct caml_void_clone_args));
7777+7878+ if (child_pid >= 0)
7979+ return child_pid; /* Success! */
8080+8181+ if (errno != ENOSYS && errno != EPERM)
8282+ {
8383+ uerror ("clone3", Nothing); /* Unknown error */
8484+ }
8585+8686+ uerror ("clone3", Nothing);
8787+}
8888+8989+CAMLprim value
9090+caml_void_clone3 (value v_errors, value v_flags, value v_actions)
9191+{
9292+ CAMLparam1 (v_actions);
9393+ CAMLlocal1 (v_result);
9494+ pid_t child_pid;
9595+ int pidfd = -1; /* Is automatically close-on-exec */
9696+9797+ struct caml_void_clone_args cl_args = {
9898+ .flags = Int_val (v_flags),
9999+ .pidfd = (uintptr_t) & pidfd,
100100+ .exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */
101101+ .stack = (uintptr_t) NULL, /* Use copy-on-write parent stack */
102102+ .stack_size = 0,
103103+ };
104104+105105+ child_pid = clone3_no_fallback (&cl_args);
106106+ if (child_pid == 0)
107107+ {
108108+ /* Run child actions (doesn't return) */
109109+ eio_unix_run_fork_actions (Int_val (v_errors), v_actions);
110110+ }
111111+112112+ v_result = caml_alloc_tuple (2);
113113+ Store_field (v_result, 0, Val_long (child_pid));
114114+ Store_field (v_result, 1, Val_int (pidfd));
115115+116116+ CAMLreturn (v_result);
117117+}
118118+119119+120120+// Actions
121121+122122+// MOUNT/UNMOUNT
123123+static void
124124+action_mount (int errors, value v_config)
125125+{
126126+ value v_src = Field (v_config, 1);
127127+ value v_tgt = Field (v_config, 2);
128128+ value v_type = Field (v_config, 3);
129129+ value v_flags = Field (v_config, 4);
130130+131131+ int r;
132132+133133+ r =
134134+ mount (String_val (v_src), String_val (v_tgt), String_val (v_type),
135135+ Int_val (v_flags), NULL);
136136+137137+ if (r != 0)
138138+ {
139139+ eio_unix_fork_error (errors, "mount", strerror (errno));
140140+ _exit (1);
141141+ }
142142+}
143143+144144+CAMLprim value
145145+void_fork_mount (value v_unit)
146146+{
147147+ return Val_fork_fn (action_mount);
148148+}
149149+150150+// Writes a single line to a file
151151+static int
152152+put_line (const char *filename, const char *line)
153153+{
154154+ int fd;
155155+ int written;
156156+157157+ fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644);
158158+159159+ if (fd < 0)
160160+ {
161161+ return fd;
162162+ }
163163+164164+ written = write (fd, line, strlen (line));
165165+166166+ close (fd);
167167+168168+ if (written != strlen (line))
169169+ {
170170+ return -1;
171171+ }
172172+173173+ return 0;
174174+}
175175+176176+// MAP UID/GID to root
177177+static void
178178+action_map_uid_gid (int errors, value v_config)
179179+{
180180+ value v_uid = Field (v_config, 1);
181181+ value v_gid = Field (v_config, 2);
182182+ int result;
183183+ char uid_line[30];
184184+ char gid_line[30];
185185+186186+ // We map root onto the calling UID
187187+ snprintf (uid_line, sizeof (uid_line), "0 %i 1\n", Int_val (v_uid));
188188+ result = put_line ("/proc/self/uid_map", uid_line);
189189+190190+ if (result < 0)
191191+ {
192192+ eio_unix_fork_error (errors, "map_uid_gid-uid", strerror (errno));
193193+ _exit (1);
194194+ }
195195+196196+ /* From user_namespaces(7)
197197+ *
198198+ * Writing "deny" to the /proc/pid/setgroups file before writing to
199199+ * /proc/pid/gid_map will permanently disable setgroups(2) in a user
200200+ * namespace and allow writing to /proc/pid/gid_map without having
201201+ * the CAP_SETGID capability in the parent user namespace.
202202+ *
203203+ * See also: https://lwn.net/Articles/626665/ */
204204+205205+ put_line ("/proc/self/setgroups", "deny\n");
206206+207207+ if (result < 0)
208208+ {
209209+ eio_unix_fork_error (errors, "map_uid_gid-setgroups", strerror (errno));
210210+ _exit (1);
211211+ }
212212+213213+ result =
214214+ snprintf (gid_line, sizeof (gid_line), "0 %i 1\n", Int_val (v_gid));
215215+ put_line ("/proc/self/gid_map", gid_line);
216216+217217+ if (result < 0)
218218+ {
219219+ eio_unix_fork_error (errors, "map_uid_gid-gid", strerror (errno));
220220+ _exit (1);
221221+ }
222222+}
223223+224224+225225+CAMLprim value
226226+void_fork_map_uid_gid (value v_unit)
227227+{
228228+ return Val_fork_fn (action_map_uid_gid);
229229+}
230230+231231+// PIVOT ROOT
232232+//
233233+static int
234234+pivot_root (const char *new_root, const char *put_old)
235235+{
236236+ return syscall (SYS_pivot_root, new_root, put_old);
237237+}
238238+239239+// Is there too much OCaml stuff going on here for a fork_action ?
240240+static void
241241+action_pivot_root (int errors, value v_config)
242242+{
243243+ value v_new_root = Field (v_config, 1);
244244+ value v_root_flags = Field (v_config, 2);
245245+ value v_no_root = Field (v_config, 3);
246246+ value v_mounts = Field (v_config, 4);
247247+ char path[PATH_MAX];
248248+ char old_root_path[PATH_MAX];
249249+ char *new_root = String_val (v_new_root);
250250+ const char *put_old = ".old_root";
251251+252252+ // From pivot_root example: We want to change the propagation type
253253+ // of root to be private so we can pivot it.
254254+ if (mount (NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1)
255255+ {
256256+ eio_unix_fork_error (errors, "pivot_root-private", strerror (errno));
257257+ _exit (1);
258258+ }
259259+260260+ // If no pivot_root was given, then we tmpfs the tmpdir we assume was passed.
261261+ if (Bool_val (v_no_root))
262262+ {
263263+ // Make a temporary directory... can't because it allocates ?
264264+ //if (mkdtemp(new_root) != NULL) {
265265+ // eio_unix_fork_error(errors, new_root, strerror(errno));
266266+ // _exit(1);
267267+ //}
268268+269269+ if (mount ("tmpfs", new_root, "tmpfs", 0, NULL) <= -1)
270270+ {
271271+ eio_unix_fork_error (errors, "pivot_root-tmpfs", strerror (errno));
272272+ _exit (1);
273273+ }
274274+ }
275275+ else
276276+ {
277277+ // From pivot_root example: we check that new_root is indeed a mountpoint
278278+ if (mount (new_root, new_root, NULL, MS_BIND, NULL) <= -1)
279279+ {
280280+ eio_unix_fork_error (errors, "pivot_root-new_root",
281281+ strerror (errno));
282282+ _exit (1);
283283+ }
284284+ }
285285+286286+ // Make the place to pivot the old root too, under the new root
287287+ snprintf (old_root_path, sizeof (path), "%s/%s", new_root, put_old);
288288+289289+ if (mkdir (old_root_path, 0777) == -1)
290290+ {
291291+ eio_unix_fork_error (errors, "pivot_root-mkdir-put_old",
292292+ strerror (errno));
293293+ _exit (1);
294294+ }
295295+296296+ // Pivot the root
297297+ if (pivot_root (new_root, old_root_path))
298298+ {
299299+ eio_unix_fork_error (errors, "pivot_root", strerror (errno));
300300+ _exit (1);
301301+ }
302302+303303+ // Add mounts
304304+ value current_mount = v_mounts;
305305+ int mount_result;
306306+ int mode;
307307+ while (current_mount != Val_emptylist)
308308+ {
309309+ // TODO: Mode for mounting
310310+ mode = Int_val (Field (Field (current_mount, 0), 2));
311311+312312+ // A mount is a record {src; tgt; mode}, we first create the mount point
313313+ // directory target
314314+ if (mkdir (String_val (Field (Field (current_mount, 0), 1)), 0777) ==
315315+ -1)
316316+ {
317317+ eio_unix_fork_error (errors, "pivot_root-mkdir-mount",
318318+ strerror (errno));
319319+ _exit (1);
320320+ }
321321+322322+ mount_result = mount (String_val (Field (Field (current_mount, 0), 0)),
323323+ String_val (Field (Field (current_mount, 0), 1)),
324324+ NULL, MS_REC | MS_BIND, NULL);
325325+326326+ // Fail early if a mount fails...
327327+ if (mount_result < 0)
328328+ {
329329+ char error[PATH_MAX];
330330+ snprintf (error, sizeof (error), "mount failed: (%s->%s)",
331331+ String_val (Field (Field (current_mount, 0), 0)),
332332+ String_val (Field (Field (current_mount, 0), 1)));
333333+ eio_unix_fork_error (errors, error, strerror (errno));
334334+ _exit (1);
335335+ }
336336+337337+ // After mounting for the first time, we can come back and add any
338338+ // extra modes that may have been specified, for example RDONLY.
339339+ if (mode != 0)
340340+ {
341341+ mount_result =
342342+ mount (String_val (Field (Field (current_mount, 0), 0)),
343343+ String_val (Field (Field (current_mount, 0), 1)), NULL,
344344+ MS_REMOUNT | MS_BIND | mode, NULL);
345345+346346+ if (mount_result < 0)
347347+ {
348348+ eio_unix_fork_error (errors, "remount for mode",
349349+ strerror (errno));
350350+ _exit (1);
351351+ }
352352+ }
353353+354354+ // Next mount in the list
355355+ current_mount = Field (current_mount, 1);
356356+ }
357357+358358+359359+ // Change to the 'new' root
360360+ if (chdir ("/") == -1)
361361+ {
362362+ eio_unix_fork_error (errors, "pivot_root-chdir", strerror (errno));
363363+ _exit (1);
364364+ }
365365+366366+ // Unmount the old root and remove it
367367+ if (umount2 (put_old, MNT_DETACH) == -1)
368368+ {
369369+ eio_unix_fork_error (errors, put_old, strerror (errno));
370370+ _exit (1);
371371+ }
372372+373373+ // Remove the old root
374374+ if (rmdir (put_old) == -1)
375375+ {
376376+ eio_unix_fork_error (errors, put_old, strerror (errno));
377377+ _exit (1);
378378+ }
379379+380380+381381+ // Apply any flags to the new root, e.g. RDONLY
382382+ if (Int_val (v_root_flags))
383383+ {
384384+ if (mount
385385+ ("/", "/", NULL, (MS_REMOUNT | MS_BIND | Int_val (v_root_flags)),
386386+ NULL) <= -1)
387387+ {
388388+ eio_unix_fork_error (errors, "pivot_root-rootflags",
389389+ strerror (errno));
390390+ _exit (1);
391391+ }
392392+ }
393393+}
394394+395395+CAMLprim value
396396+void_fork_pivot_root (value v_unit)
397397+{
398398+ return Val_fork_fn (action_pivot_root);
399399+}
···11+open Eio.Std
22+33+let _root_filesystem =
44+ "/obuilder-zfs/result/fe532e693c6a86db16b50547aae1345b3515c727b8ed668b3e0c33c1e9a895f9/rootfs"
55+66+let () =
77+ Eio_posix.run @@ fun _ ->
88+ Switch.run @@ fun sw ->
99+ let open Void in
1010+ let void =
1111+ empty
1212+ |> mount ~mode:R ~src:"/tmp/test" ~tgt:"bin"
1313+ |> exec [ "/bin/busybox"; "ls" ]
1414+ in
1515+ let t = Void.spawn ~sw void in
1616+ match Promise.await (Void.exit_status t) with
1717+ | Unix.WEXITED 0 -> print_endline "done"
1818+ | Unix.WEXITED n -> Printf.printf "Exited with %i\n%!" n
1919+ | Unix.WSTOPPED n -> Printf.printf "Stopped with %i\n%!" n
2020+ | Unix.WSIGNALED n -> Printf.printf "Signalled with %i\n%!" n
···11+module Types (F : Ctypes.TYPE) = struct
22+ open F
33+44+ type libzfs_handle_t
55+66+ let libzfs_handle_t :
77+ libzfs_handle_t Ctypes_static.structure Ctypes_static.ptr typ =
88+ ptr @@ structure "libzfs_handle"
99+1010+ type zpool_handle_t
1111+1212+ let zpool_handle_t :
1313+ zpool_handle_t Ctypes_static.structure Ctypes_static.ptr typ =
1414+ ptr @@ structure "zpool_handle"
1515+1616+ type zfs_handle_t
1717+1818+ let zfs_handle_t : zfs_handle_t Ctypes_static.structure Ctypes_static.ptr typ
1919+ =
2020+ ptr @@ structure "zfs_handle"
2121+2222+ type nvlist_t
2323+2424+ let nvlist_t : nvlist_t Ctypes_static.structure typ = structure "nvlist"
2525+ let nvl_version = field nvlist_t "nvl_version" int32_t
2626+ let nvl_nvflag = field nvlist_t "nvl_nvflag" uint32_t
2727+ let nvl_priv = field nvlist_t "nvl_priv" uint64_t
2828+ let nvl_flag = field nvlist_t "nvl_flag" uint32_t
2929+ let nvl_pad = field nvlist_t "nvl_pad" int32_t
3030+ let () = seal nvlist_t
3131+end
+142
vendor/zfs/src/zfs.ml
···11+module Error = struct
22+ include Config.Error
33+end
44+55+module Flags = struct
66+ type t = int
77+88+ let empty = 0
99+ let of_int x = x
1010+ let ( + ) = ( lor )
1111+ let mem a b = a land b = a
1212+end
1313+1414+module Types = struct
1515+ include Flags
1616+1717+ let vdev = Config.Types.vdev
1818+ let pool = Config.Types.pool
1919+ let volume = Config.Types.volume
2020+ let invalid = Config.Types.invalid
2121+ let bookmark = Config.Types.bookmark
2222+ let snapshot = Config.Types.snapshot
2323+ let filesystem = Config.Types.filesystem
2424+ let dataset = Config.Types.dataset
2525+end
2626+2727+module Handle = struct
2828+ type t = C.Types.libzfs_handle_t Ctypes_static.structure Ctypes_static.ptr
2929+end
3030+3131+let init : unit -> Handle.t = C.Functions.init
3232+let debug : Handle.t -> bool -> unit = C.Functions.debug
3333+let errno : Handle.t -> int = C.Functions.errno
3434+3535+module Zpool = struct
3636+ type t = C.Types.zpool_handle_t Ctypes_static.structure Ctypes_static.ptr
3737+3838+ let open_ = C.Functions.Zpool.open_
3939+ let close = C.Functions.Zpool.close
4040+ let get_name = C.Functions.Zpool.get_name
4141+end
4242+4343+module Nvlist = struct
4444+ type t = C.Types.nvlist_t Ctypes_static.structure Ctypes_static.ptr
4545+4646+ type nvlist =
4747+ (string
4848+ * [ `Bool of bool
4949+ | `String of string
5050+ | `Byte of Unsigned.uchar
5151+ | `Int64 of int64 ])
5252+ list
5353+5454+ let check_return i =
5555+ if i = 22 then invalid_arg "Nvlist.v: add bool" else assert (i = 0)
5656+5757+ let v (schema : nvlist) : t =
5858+ let open Ctypes in
5959+ let finalise v = C.Functions.Nvlist.free !@v in
6060+ let nv_pp =
6161+ allocate ~finalise (ptr C.Types.nvlist_t)
6262+ (from_voidp C.Types.nvlist_t null)
6363+ in
6464+ (* TODO: Unique names or not... *)
6565+ C.Functions.Nvlist.alloc nv_pp 0x1 0 |> check_return;
6666+ let rec aux = function
6767+ | [] -> !@nv_pp
6868+ | (k, `Bool b) :: rest ->
6969+ C.Functions.Nvlist.add_bool !@nv_pp k b |> check_return;
7070+ aux rest
7171+ | (k, `String s) :: rest ->
7272+ C.Functions.Nvlist.add_string !@nv_pp k s |> check_return;
7373+ aux rest
7474+ | (k, `Int64 i64) :: rest ->
7575+ C.Functions.Nvlist.add_int64 !@nv_pp k i64 |> check_return;
7676+ aux rest
7777+ | (k, `Byte u) :: rest ->
7878+ C.Functions.Nvlist.add_byte !@nv_pp k u |> check_return;
7979+ aux rest
8080+ | _ -> assert false
8181+ in
8282+ aux schema
8383+8484+ let empty = Ctypes.(coerce (ptr void) (ptr C.Types.nvlist_t) null)
8585+end
8686+8787+type t = C.Types.zfs_handle_t Ctypes_static.structure Ctypes_static.ptr
8888+8989+let create_ancestors handle path =
9090+ let i = C.Functions.create_ancestors handle path in
9191+ if i != 0 then failwith "Failed to create ancestors" else ()
9292+9393+let create ?(props = []) handle path (type_ : Types.t) =
9494+ let i = C.Functions.create handle path type_ (Nvlist.v props) in
9595+ if i != 0 then failwith "Failed to create" else ()
9696+9797+let open_ handle path (type_ : Types.t) = C.Functions.open_ handle path type_
9898+let close : t -> unit = C.Functions.close
9999+let get_type : t -> Types.t = C.Functions.get_type
100100+101101+let clone ?(options = Nvlist.empty) handle path =
102102+ let res = C.Functions.clone handle path options in
103103+ if res = 0 then () else invalid_arg "clone"
104104+105105+let snapshot ?(options = Nvlist.empty) handle path b =
106106+ let res = C.Functions.snapshot handle path b options in
107107+ if res = 0 then () else invalid_arg "snapshot"
108108+109109+let exists handle path (type_ : Types.t) = C.Functions.exists handle path type_
110110+111111+let is_mounted handle path =
112112+ let where = Ctypes.allocate Ctypes.string "" in
113113+ let v = C.Functions.is_mounted handle path where in
114114+ if not v then None else Some (Ctypes.( !@ ) where)
115115+116116+let null_string = Ctypes.(coerce (ptr void) (ptr char) null)
117117+118118+let mount ?mount_opts ?(mount_flags = 0) dataset =
119119+ let opts =
120120+ Option.value
121121+ ~default:(Ctypes.string_from_ptr null_string ~length:0)
122122+ mount_opts
123123+ in
124124+ let res = C.Functions.mount dataset opts mount_flags in
125125+ if res <> 0 then invalid_arg "mounting dataset"
126126+127127+let unmount ?mount_opts ?(mount_flags = 0) dataset =
128128+ let opts =
129129+ Option.value
130130+ ~default:(Ctypes.string_from_ptr null_string ~length:0)
131131+ mount_opts
132132+ in
133133+ let res = C.Functions.unmount dataset opts mount_flags in
134134+ if res <> 0 then invalid_arg "unmounting dataset"
135135+136136+let show_diff ?to_ handle ~from_ (fd : Unix.file_descr) =
137137+ (* TODO: Other Diff Flags https://github.com/openzfs/zfs/blob/5b0c27cd14bbc07d50304c97735cc105d0258673/include/libzfs.h#L917? *)
138138+ let res = C.Functions.diff handle (Obj.magic fd : int) from_ to_ 1 in
139139+ if res = 0 then () else begin
140140+ Format.printf "Diff got %i\n%!" res;
141141+ invalid_arg "show_diff"
142142+ end
+206
vendor/zfs/src/zfs.mli
···11+module Types : sig
22+ type t = private int
33+44+ val empty : int
55+ val of_int : 'a -> 'a
66+ val ( + ) : int -> int -> int
77+ val mem : int -> int -> bool
88+ val vdev : t
99+ val pool : t
1010+ val volume : t
1111+ val invalid : t
1212+ val bookmark : t
1313+ val snapshot : t
1414+ val filesystem : t
1515+ val dataset : t
1616+end
1717+1818+module Handle : sig
1919+ type t
2020+ (** An instance handle for the ZFS library *)
2121+end
2222+2323+val init : unit -> Handle.t
2424+(** Initialise the library *)
2525+2626+val debug : Handle.t -> bool -> unit
2727+(** Enable/disable printing on error from ZFS *)
2828+2929+val errno : Handle.t -> int
3030+(** Check for errors on the handle *)
3131+3232+module Zpool : sig
3333+ type t
3434+ (** A Zpool handle *)
3535+3636+ val open_ : Handle.t -> string -> t
3737+ (** Open a Zpool *)
3838+3939+ val close : t -> unit
4040+ (** Close an open Zpool *)
4141+4242+ val get_name : t -> string
4343+ (** The name of an open Zpool *)
4444+end
4545+4646+module Nvlist : sig
4747+ type t
4848+ (** Generic name-value lists used by ZFS *)
4949+5050+ type nvlist =
5151+ (string
5252+ * [ `Bool of bool
5353+ | `Byte of Unsigned.uchar
5454+ | `String of string
5555+ | `Int64 of int64 ])
5656+ list
5757+ (** A partial OCaml representation of an NV list *)
5858+5959+ val v : nvlist -> t
6060+ (** Convert the OCaml representation to the C representation *)
6161+end
6262+6363+type t
6464+(** A ZFS Dataset *)
6565+6666+val create_ancestors : Handle.t -> string -> unit
6767+(** Often called before {! create} *)
6868+6969+val create : ?props:Nvlist.nvlist -> Handle.t -> string -> Types.t -> unit
7070+(** Create a new ZFS dataset *)
7171+7272+val open_ : Handle.t -> string -> Types.t -> t
7373+(** Open an existing ZFS dataset *)
7474+7575+val close : t -> unit
7676+(** Close a dataset *)
7777+7878+val exists : Handle.t -> string -> Types.t -> bool
7979+(** Check if a dataset of a specific type exists *)
8080+8181+val is_mounted : Handle.t -> string -> string option
8282+(** [is_mounted h d = None] if [d] is not mounted, otherwise
8383+ [is_mounted h d = Some mountpoint]. *)
8484+8585+val mount : ?mount_opts:string -> ?mount_flags:int -> t -> unit
8686+(** Mount a dataset *)
8787+8888+val unmount : ?mount_opts:string -> ?mount_flags:int -> t -> unit
8989+(** Unmount a dataset *)
9090+9191+val get_type : t -> Types.t
9292+(** Get the type of the dataset *)
9393+9494+val clone : ?options:Nvlist.t -> t -> string -> unit
9595+(** Clone an open dataset *)
9696+9797+val snapshot : ?options:Nvlist.t -> Handle.t -> string -> bool -> unit
9898+(** Snapshot a dataset *)
9999+100100+val show_diff : ?to_:string -> t -> from_:string -> Unix.file_descr -> unit
101101+(** Output diff to the file descriptor *)
102102+103103+module Error : sig
104104+ type t = int
105105+106106+ val unknown : t
107107+ val sharefailed : t
108108+ val resume_exists : t
109109+ val cksum : t
110110+ val not_user_namespace : t
111111+ val vdev_notsup : t
112112+ val rebuilding : t
113113+ val export_in_progress : t
114114+ val no_resilver_defer : t
115115+ val trim_notsup : t
116116+ val no_trim : t
117117+ val trimming : t
118118+ val wrong_parent : t
119119+ val no_initialize : t
120120+ val initializing : t
121121+ val toomany : t
122122+ val ioc_notsupported : t
123123+ val vdev_too_big : t
124124+ val devrm_in_progress : t
125125+ val no_checkpoint : t
126126+ val discarding_checkpoint : t
127127+ val checkpoint_exists : t
128128+ val no_pending : t
129129+ val cryptofailed : t
130130+ val active_pool : t
131131+ val scrub_paused_to_cancel : t
132132+ val scrub_paused : t
133133+ val poolreadonly : t
134134+ val diffdata : t
135135+ val diff : t
136136+ val no_scrub : t
137137+ val errorscrub_paused : t
138138+ val errorscrubbing : t
139139+ val scrubbing : t
140140+ val postsplit_online : t
141141+ val threadcreatefailed : t
142142+ val pipefailed : t
143143+ val tagtoolong : t
144144+ val reftag_hold : t
145145+ val reftag_rele : t
146146+ val unplayed_logs : t
147147+ val active_spare : t
148148+ val notsup : t
149149+ val vdevnotsup : t
150150+ val isl2cache : t
151151+ val badcache : t
152152+ val sharesmbfailed : t
153153+ val unsharesmbfailed : t
154154+ val nodelegation : t
155155+ val badpermset : t
156156+ val badperm : t
157157+ val badwho : t
158158+ val labelfailed : t
159159+ val nocap : t
160160+ val openfailed : t
161161+ val nametoolong : t
162162+ val pool_invalarg : t
163163+ val pool_notsup : t
164164+ val poolprops : t
165165+ val nohistory : t
166166+ val recursive : t
167167+ val invalconfig : t
168168+ val isspare : t
169169+ val intr : t
170170+ val io : t
171171+ val fault : t
172172+ val nospc : t
173173+ val perm : t
174174+ val sharenfsfailed : t
175175+ val unsharenfsfailed : t
176176+ val umountfailed : t
177177+ val mountfailed : t
178178+ val zoned : t
179179+ val crosstarget : t
180180+ val badpath : t
181181+ val devoverflow : t
182182+ val poolunavail : t
183183+ val badversion : t
184184+ val resilvering : t
185185+ val noreplicas : t
186186+ val baddev : t
187187+ val nodevice : t
188188+ val badtarget : t
189189+ val badbackup : t
190190+ val badrestore : t
191191+ val invalidname : t
192192+ val voltoobig : t
193193+ val dsreadonly : t
194194+ val badstream : t
195195+ val noent : t
196196+ val exists : t
197197+ val busy : t
198198+ val badtype : t
199199+ val propspace : t
200200+ val propnoninherit : t
201201+ val proptype : t
202202+ val propreadonly : t
203203+ val badprop : t
204204+ val nomem : t
205205+ val success : t
206206+end
+150
vendor/zfs/src/zfs_stubs.c
···11+/*
22+ * Copyright (C) 2020-2021 Anil Madhavapeddy
33+ * Copyright (C) 2020-2021 Sadiq Jaffer
44+ * Copyright (C) 2022 Christiano Haesbaert
55+ *
66+ * Permission to use, copy, modify, and distribute this software for any
77+ * purpose with or without fee is hereby granted, provided that the above
88+ * copyright notice and this permission notice appear in all copies.
99+ *
1010+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1313+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1717+ */
1818+1919+#include <unistd.h>
2020+#include <stdio.h>
2121+#include <stdint.h>
2222+#include <stdbool.h>
2323+2424+#include <libzfs_core.h>
2525+#include <libzfs.h>
2626+#include <caml/alloc.h>
2727+#include <caml/bigarray.h>
2828+#include <caml/callback.h>
2929+#include <caml/custom.h>
3030+#include <caml/fail.h>
3131+#include <caml/memory.h>
3232+#include <caml/mlvalues.h>
3333+#include <caml/signals.h>
3434+#include <caml/unixsupport.h>
3535+#include <caml/socketaddr.h>
3636+3737+#undef ZFS_DEBUG
3838+#ifdef ZFS_DEBUG
3939+#define dprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
4040+#else
4141+#define dprintf(fmt, ...) ((void)0)
4242+#endif
4343+4444+value ocaml_zfs_prop_is_string(value v_prop){
4545+ int res;
4646+ res = zfs_prop_is_string(Int_val(v_prop));
4747+ if (res < 0) {
4848+ caml_failwith("Error occurred!");
4949+ }
5050+ return Val_bool(res);
5151+}
5252+5353+#define Zfs_list_val(v) (*((struct nv_list **) Data_custom_val(v)))
5454+#define Zfs_handle_val(v) (*((libzfs_handle_t **) Data_custom_val(v)))
5555+#define Zfs_pool_val(v) (*((zpool_handle_t **) Data_custom_val(v)))
5656+5757+static void finalize_zfs_list(value v) {
5858+ caml_stat_free(Zfs_list_val(v));
5959+ Zfs_list_val(v) = NULL;
6060+}
6161+6262+static struct custom_operations zfs_list_ops = {
6363+ "zfs.zfs_list_ops",
6464+ finalize_zfs_list,
6565+ custom_compare_default,
6666+ custom_hash_default,
6767+ custom_serialize_default,
6868+ custom_deserialize_default,
6969+ custom_compare_ext_default,
7070+ custom_fixed_length_default
7171+};
7272+7373+static void finalize_zfs_handle(value v) {
7474+ caml_stat_free(Zfs_handle_val(v));
7575+ Zfs_handle_val(v) = NULL;
7676+}
7777+7878+static struct custom_operations zfs_handle_ops = {
7979+ "zfs.zfs_handle",
8080+ finalize_zfs_handle,
8181+ custom_compare_default,
8282+ custom_hash_default,
8383+ custom_serialize_default,
8484+ custom_deserialize_default,
8585+ custom_compare_ext_default,
8686+ custom_fixed_length_default
8787+};
8888+8989+static void finalize_zfs_pool(value v) {
9090+ caml_stat_free(Zfs_pool_val(v));
9191+ Zfs_pool_val(v) = NULL;
9292+}
9393+9494+static struct custom_operations zfs_pool_ops = {
9595+ "zfs.zfs_pool",
9696+ finalize_zfs_pool,
9797+ custom_compare_default,
9898+ custom_hash_default,
9999+ custom_serialize_default,
100100+ custom_deserialize_default,
101101+ custom_compare_ext_default,
102102+ custom_fixed_length_default
103103+};
104104+105105+// ZFS Initialisation
106106+107107+value
108108+ocaml_zfs_init(value v_unit) {
109109+ CAMLparam0();
110110+ libzfs_handle_t* res;
111111+ CAMLlocal1(v_handle);
112112+113113+ v_handle = caml_alloc_custom_mem(&zfs_handle_ops, sizeof(libzfs_handle_t*), 64);
114114+ res = libzfs_init();
115115+ Zfs_handle_val(v_handle) = res;
116116+117117+ CAMLreturn(v_handle);
118118+}
119119+120120+// ZFS Pools
121121+122122+value
123123+ocaml_zfs_pool_open(value v_handle, value v_path) {
124124+ CAMLparam2(v_handle, v_path);
125125+ zpool_handle_t* res;
126126+ CAMLlocal1(v_pool);
127127+128128+ if (!caml_string_is_c_safe(v_path))
129129+ caml_invalid_argument("ocaml_zfs_pool_open: path is not C-safe");
130130+131131+ v_pool = caml_alloc_custom_mem(&zfs_pool_ops, sizeof(zpool_handle_t*), 64);
132132+ res = zpool_open(Zfs_handle_val(v_handle), String_val(v_path));
133133+ Zfs_pool_val(v_handle) = res;
134134+135135+ CAMLreturn(v_handle);
136136+}
137137+138138+value
139139+ocaml_zfs_pool_get_name(value v_pool) {
140140+ CAMLparam1(v_pool);
141141+ CAMLlocal1(v_path);
142142+ const char* result;
143143+144144+ result = zpool_get_name(Zfs_pool_val(v_pool));
145145+ v_path = caml_copy_string(result);
146146+147147+ CAMLreturn(v_path);
148148+}
149149+150150+