///|
pub struct FileExport {
priv file : @fs.File
priv export_size : UInt64
priv read_only : Bool
priv name : Bytes
priv mut closed : Bool
priv mut busy : Bool
}
///|
/// Open an existing regular file without truncation. The owner must keep it stable.
pub async fn FileExport::open(
path : String,
read_only~ : Bool = true,
name~ : Bytes = b"",
) -> FileExport {
if name.length() > 4096 {
raise InvalidArgument("export name is too long")
}
let file = @fs.open(
path[:],
mode=if read_only { ReadOnly } else { ReadWrite },
)
errdefer file.close()
if file.kind() != Regular {
raise InvalidArgument("expected a regular file")
}
let size = file.size()
if size < 0L {
raise InvalidArgument("invalid file size")
}
{
file,
export_size: size.reinterpret_as_uint64(),
read_only,
name,
closed: false,
busy: false,
}
}
///|
pub fn FileExport::close(self : FileExport) -> Unit {
if !self.closed {
self.closed = true
self.file.close()
}
}
///|
pub fn FileExport::size(self : FileExport) -> UInt64 {
self.export_size
}
///|
/// Serves one connection using an existing file; caller closes both resources.
pub async fn FileExport::serve_connection(
self : FileExport,
tcp : @socket.Tcp,
) -> Unit {
if self.closed || self.busy {
raise InvalidArgument("file export is closed or busy")
}
self.busy = true
defer {
self.busy = false
}
ExportBackend::serve_export(File(self), tcp)
}
///|
priv enum ExportBackend {
Memory(MemoryExport)
File(FileExport)
}
///|
fn ExportBackend::size(self : ExportBackend) -> UInt64 {
match self {
Memory(disk) => disk.size()
File(disk) => disk.size()
}
}
///|
fn ExportBackend::name(self : ExportBackend) -> Bytes {
match self {
Memory(disk) => disk.name
File(disk) => disk.name
}
}
///|
fn ExportBackend::matches_name(self : ExportBackend, name : Bytes) -> Bool {
name.length() == 0 || name == self.name()
}
///|
fn ExportBackend::flags(self : ExportBackend) -> UInt32 {
match self {
Memory(disk) => disk.flags()
File(disk) => if disk.read_only { 1027U } else { 1029U }
}
}
///|
async fn execute_request(
disk : ExportBackend,
command : UInt32,
offset : UInt64,
length : Int32,
payload : Bytes,
) -> Result[Bytes, UInt32] {
try {
match disk {
Memory(memory) => {
let start = offset.to_int()
match command {
0U => {
let out : Array[Byte] = []
for i in 0.. {
for i in 0.. {
for i in 0.. Ok(b"")
_ => Err(22U)
}
}
File(file) =>
if file.closed {
Err(5U)
} else {
match command {
0U =>
Ok(
file.file.read_exactly_at(
length,
position=offset.reinterpret_as_int64(),
),
)
1U => {
file.file.write_at(
payload[:],
position=offset.reinterpret_as_int64(),
)
Ok(b"")
}
3U => {
file.file.sync()
Ok(b"")
}
5U => Ok(b"")
_ => Err(22U)
}
}
}
} catch {
_ => Err(5U)
}
}