CCSDS File Delivery Protocol (CCSDS 727.0-B-5) for space file transfer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Eio wrappers for transport, SDLS, CFDP, and LTP

- transport-eio: CLTU/ASM over Eio flows, COP-1 service layer
- sdls-eio: file-backed keystore, SA store, OTAR, security log,
KEK management, encrypted KV store, SDLS server
- cfdp: Eio filesystem-backed filestore for segmented transfers
- ltp-eio: length-prefixed segment send/recv over TCP

Ported from borealis eio/ wrappers with dependency updates
(crypto instead of mirage-crypto, bare int for Spi/Vcid/Scid).

+202
+148
lib/eio/filestore_eio.ml
··· 1 + (** Eio filesystem-backed Filestore for CFDP. 2 + 3 + Provides random-access file operations needed by CFDP for segmented file 4 + transfer. *) 5 + 6 + let ( / ) = Eio.Path.( / ) 7 + 8 + (* {1 Types} *) 9 + 10 + type error = 11 + | Not_found of string 12 + | Already_exists of string 13 + | Permission_denied of string 14 + | Io_error of string 15 + 16 + let pp_error ppf = function 17 + | Not_found path -> Fmt.pf ppf "not found: %s" path 18 + | Already_exists path -> Fmt.pf ppf "already exists: %s" path 19 + | Permission_denied path -> Fmt.pf ppf "permission denied: %s" path 20 + | Io_error msg -> Fmt.pf ppf "I/O error: %s" msg 21 + 22 + (* Use GADT to hide path type *) 23 + type t = T : { root : 'a Eio.Path.t } -> t 24 + 25 + let of_path root = T { root } 26 + 27 + (* {1 File Operations} *) 28 + 29 + let file_size (T { root }) path = 30 + let p = root / path in 31 + try 32 + match Eio.Path.kind ~follow:true p with 33 + | `Regular_file -> 34 + Eio.Path.with_open_in p @@ fun file -> 35 + Ok (Eio.File.size file |> Optint.Int63.to_int64) 36 + | _ -> Error (Io_error (Printf.sprintf "%s: not a regular file" path)) 37 + with 38 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found path) 39 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 40 + Error (Permission_denied path) 41 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 42 + 43 + let read_at (T { root }) path ~offset ~len = 44 + let p = root / path in 45 + try 46 + Eio.Path.with_open_in p @@ fun file -> 47 + let buf = Cstruct.create len in 48 + let file_off = Optint.Int63.of_int64 offset in 49 + let n = Eio.File.pread file ~file_offset:file_off [ buf ] in 50 + Ok (Cstruct.to_bytes (Cstruct.sub buf 0 n)) 51 + with 52 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found path) 53 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 54 + Error (Permission_denied path) 55 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 56 + 57 + let exists (T { root }) path = 58 + let p = root / path in 59 + match Eio.Path.kind ~follow:true p with 60 + | `Regular_file -> true 61 + | _ -> false 62 + | exception Eio.Io _ -> false 63 + 64 + let create (T { root }) path = 65 + let p = root / path in 66 + try 67 + if 68 + match Eio.Path.kind ~follow:true p with 69 + | `Regular_file -> true 70 + | _ -> false 71 + | exception Eio.Io _ -> false 72 + then Error (Already_exists path) 73 + else begin 74 + Eio.Path.save ~create:(`Exclusive 0o644) p ""; 75 + Ok () 76 + end 77 + with 78 + | Eio.Io (Eio.Fs.E (Eio.Fs.Already_exists _), _) -> 79 + Error (Already_exists path) 80 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 81 + Error (Permission_denied path) 82 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 83 + 84 + let write_at (T { root }) path ~offset data = 85 + let p = root / path in 86 + try 87 + Eio.Path.with_open_out ~create:`Never p @@ fun file -> 88 + let file_off = Optint.Int63.of_int64 offset in 89 + let buf = Cstruct.of_bytes data in 90 + Eio.File.pwrite_all file ~file_offset:file_off [ buf ]; 91 + Ok () 92 + with 93 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found path) 94 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 95 + Error (Permission_denied path) 96 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 97 + 98 + let delete (T { root }) path = 99 + let p = root / path in 100 + try 101 + Eio.Path.unlink p; 102 + Ok () 103 + with 104 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found path) 105 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 106 + Error (Permission_denied path) 107 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 108 + 109 + let rename (T { root }) ~src ~dst = 110 + let src_p = root / src in 111 + let dst_p = root / dst in 112 + try 113 + if exists (T { root }) dst then Error (Already_exists dst) 114 + else begin 115 + Eio.Path.rename src_p dst_p; 116 + Ok () 117 + end 118 + with 119 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found src) 120 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 121 + Error (Permission_denied src) 122 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 123 + 124 + let truncate (T { root }) path size = 125 + let p = root / path in 126 + try 127 + Eio.Path.with_open_out ~create:`Never p @@ fun file -> 128 + Eio.File.truncate file (Optint.Int63.of_int64 size); 129 + Ok () 130 + with 131 + | Eio.Io (Eio.Fs.E (Eio.Fs.Not_found _), _) -> Error (Not_found path) 132 + | Eio.Io (Eio.Fs.E (Eio.Fs.Permission_denied _), _) -> 133 + Error (Permission_denied path) 134 + | Eio.Io _ as e -> Error (Io_error (Printexc.to_string e)) 135 + 136 + let list (T { root }) = 137 + try 138 + Eio.Path.read_dir root 139 + |> List.filter (fun name -> 140 + match Eio.Path.kind ~follow:true (root / name) with 141 + | `Regular_file -> true 142 + | _ -> false 143 + | exception _ -> false) 144 + with _ -> [] 145 + 146 + let get_contents (T { root }) path = 147 + let p = root / path in 148 + try Some (Eio.Path.load p |> Bytes.of_string) with _ -> None
+54
lib/eio/filestore_eio.mli
··· 1 + (** Eio filesystem-backed Filestore for CFDP. 2 + 3 + Provides random-access file I/O operations needed by CFDP for segmented file 4 + transfer using Eio. 5 + 6 + {2 Usage} 7 + 8 + {[ 9 + Eio_main.run @@ fun env -> 10 + let fs = Filestore_eio.of_path Eio.Path.(Eio.Stdenv.cwd env / "data") in 11 + let () = Filestore_eio.create fs "test.txt" |> Result.get_ok in 12 + let () = 13 + Filestore_eio.write_at fs "test.txt" ~offset:0L 14 + (Bytes.of_string "hello") 15 + |> Result.get_ok 16 + in 17 + ... 18 + ]} *) 19 + 20 + (** {1 Errors} *) 21 + 22 + type error = 23 + | Not_found of string 24 + | Already_exists of string 25 + | Permission_denied of string 26 + | Io_error of string 27 + 28 + val pp_error : error Fmt.t 29 + 30 + (** {1 Filestore} *) 31 + 32 + type t 33 + (** A filesystem-backed filestore. *) 34 + 35 + val of_path : _ Eio.Path.t -> t 36 + (** [of_path dir] creates a filestore rooted at directory [dir]. 37 + 38 + Files are stored relative to [dir]. The directory must exist. 39 + 40 + Uses Eio's [pread]/[pwrite_all] for random-access operations required by 41 + CFDP segmented file transfer. *) 42 + 43 + (** {1 Operations} *) 44 + 45 + val file_size : t -> string -> (int64, error) result 46 + val read_at : t -> string -> offset:int64 -> len:int -> (bytes, error) result 47 + val exists : t -> string -> bool 48 + val create : t -> string -> (unit, error) result 49 + val write_at : t -> string -> offset:int64 -> bytes -> (unit, error) result 50 + val delete : t -> string -> (unit, error) result 51 + val rename : t -> src:string -> dst:string -> (unit, error) result 52 + val truncate : t -> string -> int64 -> (unit, error) result 53 + val list : t -> string list 54 + val get_contents : t -> string -> bytes option