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