1use super::{
3 Attributes, AttrsOptions, Error, ReadOp, ReadOptions, Result, Target, Vfsi, WriteOp,
4 WriteOptions,
5};
6use std::io::{self, Read, Seek, SeekFrom, Write};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum SyncMode {
11 Data,
13 All,
15}
16
17pub(super) struct StdIo<'a, C: Vfsi + ?Sized> {
26 client: &'a C,
27 file: &'a C::File,
28 position: u64,
29}
30impl<'a, C: Vfsi + ?Sized> StdIo<'a, C> {
31 pub(super) fn new(client: &'a C, file: &'a C::File) -> Self {
32 Self {
33 client,
34 file,
35 position: 0,
36 }
37 }
38
39 fn read_at(&mut self, buffer: &mut [u8], budget: usize) -> Result<(usize, bool)> {
40 let length = buffer.len().min(budget);
41 if !buffer.is_empty() && length == 0 {
42 return Err(Error::client(0, libc::EFBIG as u32));
43 }
44 self.position
45 .checked_add(length as u64)
46 .ok_or_else(|| Error::client(0, libc::EOVERFLOW as u32))?;
47 let values = self.client.vread(
48 [ReadOp::into(
49 self.file,
50 self.position,
51 &mut buffer[..length],
52 )],
53 ReadOptions::new().max_total_bytes(std::num::NonZeroUsize::new(budget)),
54 )?;
55 let [value] = values.as_slice() else {
56 return Err(invalid("invalid read count"));
57 };
58 if value.offset() != self.position
59 || value.data().is_some()
60 || value.read() > length
61 || (length != 0 && value.read() == 0 && !value.eof())
62 {
63 return Err(invalid("invalid read progress"));
64 }
65 self.position += value.read() as u64;
66 Ok((value.read(), value.eof()))
67 }
68 fn read_uninterrupted(&mut self, buffer: &mut [u8], budget: usize) -> Result<(usize, bool)> {
69 loop {
70 match self.read_at(buffer, budget) {
71 Err(error) if error.kind() == io::ErrorKind::Interrupted => continue,
72 result => return result,
73 }
74 }
75 }
76 fn write_at(&mut self, buffer: &[u8]) -> Result<usize> {
77 self.position
78 .checked_add(buffer.len() as u64)
79 .ok_or_else(|| Error::client(0, libc::EOVERFLOW as u32))?;
80 let values = self.client.vwrite(
81 &[WriteOp::at(self.file, self.position, buffer)],
82 WriteOptions::new(),
83 )?;
84 let [value] = values.as_slice() else {
85 return Err(invalid("invalid write count"));
86 };
87 if value.written > buffer.len() {
88 return Err(invalid("invalid write progress"));
89 }
90 self.position = value
92 .offset
93 .checked_add(value.written as u64)
94 .ok_or_else(|| invalid("write offset overflow"))?;
95 Ok(value.written)
96 }
97}
98fn invalid(message: &'static str) -> Error {
99 Error::transport_with_kind(None, crate::TransportKind::InvalidReply, message)
100}
101impl<C: Vfsi + ?Sized> Read for StdIo<'_, C> {
102 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
103 if buffer.is_empty() {
104 return Ok(0);
105 }
106 self.read_at(buffer, self.client.limits().read_byte_limit())
107 .map(|(count, _)| count)
108 .map_err(Into::into)
109 }
110 fn read_to_end(&mut self, output: &mut Vec<u8>) -> io::Result<usize> {
111 let budget = self.client.limits().read_byte_limit();
112 let mut added = 0;
113 loop {
114 let remaining = budget - added;
115 let length = remaining.min(super::DEFAULT_READ_STREAM_CHUNK_BYTES);
116 if length == 0 {
117 let (count, _) = self.read_uninterrupted(&mut [0; 1], 1)?;
120 return if count == 0 {
121 Ok(added)
122 } else {
123 Err(Error::client(0, libc::EFBIG as u32).into())
124 };
125 }
126 output
129 .try_reserve(length)
130 .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?;
131 let start = output.len();
132 output.resize(start + length, 0);
133 let result = self.read_uninterrupted(&mut output[start..], remaining);
134 output.truncate(start + result.as_ref().map_or(0, |(count, _)| *count));
135 let (count, eof) = result?;
136 added += count;
137 if eof {
138 return Ok(added);
139 }
140 }
141 }
142 fn read_to_string(&mut self, output: &mut String) -> io::Result<usize> {
143 let mut bytes = Vec::new();
144 let count = self.read_to_end(&mut bytes)?;
145 let text =
146 std::str::from_utf8(&bytes).map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?;
147 output.push_str(text);
148 Ok(count)
149 }
150}
151impl<C: Vfsi + ?Sized> Write for StdIo<'_, C> {
152 fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
153 self.write_at(buffer).map_err(Into::into)
154 }
155 fn flush(&mut self) -> io::Result<()> {
156 self.client
157 .vfsync(&[self.file], SyncMode::Data)
158 .map_err(Into::into)
159 }
160}
161impl<C: Vfsi + ?Sized> Seek for StdIo<'_, C> {
162 fn seek(&mut self, position: SeekFrom) -> io::Result<u64> {
163 let (base, offset) = match position {
164 SeekFrom::Start(position) => {
165 self.position = position;
166 return Ok(position);
167 }
168 SeekFrom::Current(offset) => (self.position, offset),
169 SeekFrom::End(offset) => {
170 let values = self.client.vgetattrs(
171 &[Target::file(self.file)],
172 AttrsOptions::new().fields(Attributes::SIZE),
173 )?;
174 let [value] = values.as_slice() else {
175 return Err(invalid("invalid metadata count").into());
176 };
177 (value.len().ok_or_else(|| Error::unsupported(0))?, offset)
178 }
179 };
180 let position = i128::from(base) + i128::from(offset);
181 self.position =
182 u64::try_from(position).map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
183 Ok(self.position)
184 }
185}