1use std::{
2 fs::{File, OpenOptions},
3 io::{ErrorKind, Read, Seek, SeekFrom, Write},
4};
5
6use anyhow::{Context as _, Error};
7use daicon::protocol::{FileAction, FileMessage, ReadResult, WriteLocation, WriteResult};
8use stewart::{Actor, Context, Options, Sender, State};
9use tracing::{event, instrument, Level};
10
11#[instrument("SystemFile", skip_all)]
12pub fn open_system_file(
13 ctx: &mut Context,
14 path: String,
15 truncate: bool,
16) -> Result<Sender<FileMessage>, Error> {
17 event!(Level::INFO, "opening");
18
19 let (mut ctx, sender) = ctx.create(Options::default())?;
20
21 let file = OpenOptions::new()
22 .read(true)
23 .write(true)
24 .truncate(truncate)
25 .create(true)
26 .open(path)
27 .context("failed to open system file for writing")?;
28
29 let actor = SystemFile { file };
30 ctx.start(actor)?;
31
32 Ok(sender)
33}
34
35struct SystemFile {
36 file: File,
37}
38
39impl Actor for SystemFile {
40 type Message = FileMessage;
41
42 #[instrument("SystemFile", skip_all)]
43 fn process(&mut self, ctx: &mut Context, state: &mut State<Self>) -> Result<(), Error> {
44 while let Some(message) = state.next() {
45 match message.action {
46 FileAction::Read(action) => {
47 let mut data = vec![0u8; action.size as usize];
51
52 self.file.seek(SeekFrom::Start(action.offset))?;
53 read_exact_eof(&mut self.file, &mut data)?;
54
55 let result = ReadResult {
57 id: message.id,
58 offset: action.offset,
59 data,
60 };
61 action.on_result.send(ctx, result);
62 }
63 FileAction::Write(action) => {
64 let from = match action.location {
66 WriteLocation::Offset(offset) => SeekFrom::Start(offset),
67 WriteLocation::Append => SeekFrom::End(0),
68 };
69 self.file.seek(from)?;
70 let offset = self.file.stream_position()?;
71
72 self.file.write_all(&action.data)?;
74
75 let result = WriteResult {
77 id: message.id,
78 offset,
79 };
80 action.on_result.send(ctx, result);
81 }
82 }
83 }
84
85 Ok(())
86 }
87}
88
89fn read_exact_eof(file: &mut File, mut buf: &mut [u8]) -> Result<(), Error> {
91 while !buf.is_empty() {
92 match file.read(buf) {
93 Ok(0) => break,
94 Ok(n) => {
95 let tmp = buf;
96 buf = &mut tmp[n..];
97 }
98 Err(error) => match error.kind() {
99 ErrorKind::Interrupted => {}
100 ErrorKind::UnexpectedEof => break,
101 _ => return Err(error.into()),
102 },
103 }
104 }
105
106 Ok(())
107}