virtual_file_system/
synchronous_file.rs

1use core::{fmt::Debug, mem::forget};
2
3use crate::{Error, ItemStatic, Result, VirtualFileSystem};
4use alloc::{vec, vec::Vec};
5use exported_file_system::{ControlCommand, Permissions};
6use file_system::{
7    AccessFlags, AttributeFlags, Attributes, Context, Flags, Path, Position, Size, Statistics,
8};
9use shared::AnyByLayout;
10use task::TaskIdentifier;
11use task::block_on;
12use users::{GroupIdentifier, UserIdentifier};
13
14pub struct SynchronousFile {
15    pub(crate) item: ItemStatic,
16    pub(crate) position: Size,
17    pub(crate) flags: Flags,
18    pub(crate) context: Context,
19}
20
21impl Debug for SynchronousFile {
22    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23        f.debug_struct("SynchronousFile")
24            .field("item", &self.item)
25            .field("position", &self.position)
26            .field("flags", &self.flags)
27            .finish()
28    }
29}
30
31impl SynchronousFile {
32    pub(crate) const fn new(
33        item: ItemStatic,
34        position: Size,
35        flags: Flags,
36        context: Context,
37    ) -> Self {
38        Self {
39            item,
40            position,
41            flags,
42            context,
43        }
44    }
45
46    pub fn open(
47        virtual_file_system: &VirtualFileSystem,
48        task: TaskIdentifier,
49        path: impl AsRef<Path>,
50        flags: Flags,
51    ) -> Result<Self> {
52        let file_identifier = block_on(virtual_file_system.open(&path, flags, task))?;
53
54        Ok(file_identifier.into_synchronous_file())
55    }
56
57    pub fn set_position(&mut self, position: &Position) -> Result<Size> {
58        self.position = self
59            .item
60            .as_base_operations()
61            .ok_or(Error::UnsupportedOperation)?
62            .set_position(&mut self.context, self.position, position)?;
63
64        Ok(self.position)
65    }
66
67    // - Operations
68
69    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
70        if !self.flags.get_access().contains(AccessFlags::Write) {
71            return Err(Error::InvalidMode);
72        }
73
74        let size = self
75            .item
76            .as_base_operations()
77            .ok_or(Error::UnsupportedOperation)?
78            .write(&mut self.context, buffer, self.position)?;
79
80        self.position += size as Size;
81
82        Ok(size)
83    }
84
85    pub fn write_vectored(&mut self, buffers: &[&[u8]]) -> Result<usize> {
86        if !self.flags.get_access().contains(AccessFlags::Write) {
87            return Err(Error::InvalidMode);
88        }
89
90        let size = self
91            .item
92            .as_base_operations()
93            .ok_or(Error::UnsupportedOperation)?
94            .write_vectored(&mut self.context, buffers, self.position)?;
95
96        self.position += size as Size;
97
98        Ok(size)
99    }
100
101    pub fn write_line(&mut self, buffer: &[u8]) -> Result<usize> {
102        let size = self.write(buffer)? + self.write(b"\n")?;
103
104        Ok(size)
105    }
106
107    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
108        let size = self
109            .item
110            .as_base_operations()
111            .ok_or(Error::UnsupportedOperation)?
112            .read(&mut self.context, buffer, self.position)?;
113
114        self.position += size as Size;
115
116        Ok(size)
117    }
118
119    pub fn read_until(&mut self, buffer: &mut [u8], delimiter: &[u8]) -> Result<usize> {
120        let size = self
121            .item
122            .as_base_operations()
123            .ok_or(Error::UnsupportedOperation)?
124            .read_until(&mut self.context, buffer, self.position, delimiter)?;
125
126        self.position += size as Size;
127
128        Ok(size)
129    }
130
131    pub fn read_to_end(&mut self, buffer: &mut Vec<u8>, chunk_size: usize) -> Result<usize> {
132        let mut total_read_size = 0;
133
134        let mut chunk = vec![0u8; chunk_size];
135        loop {
136            let read_size = self
137                .item
138                .as_base_operations()
139                .ok_or(Error::UnsupportedOperation)?
140                .read(&mut self.context, &mut chunk, self.position)?;
141
142            if read_size == 0 {
143                break;
144            }
145
146            self.position += read_size as Size;
147
148            total_read_size += read_size;
149
150            buffer.extend_from_slice(&chunk[..read_size]);
151
152            // Yield to allow other tasks to run.
153            //yield_now().await;
154        }
155
156        Ok(total_read_size)
157    }
158
159    pub fn flush(&mut self) -> Result<()> {
160        self.item
161            .as_base_operations()
162            .ok_or(Error::UnsupportedOperation)?
163            .flush(&mut self.context)?;
164
165        Ok(())
166    }
167
168    pub fn duplicate(&self) -> Result<Self> {
169        let context = self
170            .item
171            .as_base_operations()
172            .ok_or(Error::UnsupportedOperation)?
173            .clone_context(&self.context)?;
174
175        Ok(Self {
176            item: self.item.clone(),
177            position: self.position,
178            flags: self.flags,
179            context,
180        })
181    }
182
183    pub fn get_statistics(&mut self) -> Result<Statistics> {
184        let mut attributes = Attributes::new().set_mask(AttributeFlags::All);
185
186        self.item
187            .as_attributes_operations()
188            .ok_or(Error::UnsupportedOperation)?
189            .get_attributes(&mut self.context, &mut attributes)?;
190
191        Statistics::from_attributes(&attributes).ok_or(Error::MissingAttribute)
192    }
193
194    pub fn set_owner(
195        &mut self,
196        user: Option<UserIdentifier>,
197        group: Option<GroupIdentifier>,
198    ) -> Result<()> {
199        let mut attributes = Attributes::new();
200
201        if let Some(user) = user {
202            attributes = attributes.set_user(user);
203        }
204
205        if let Some(group) = group {
206            attributes = attributes.set_group(group);
207        }
208
209        self.item
210            .as_attributes_operations()
211            .ok_or(Error::UnsupportedOperation)?
212            .set_attributes(&mut self.context, &attributes)?;
213
214        Ok(())
215    }
216
217    pub fn set_permissions(&mut self, permissions: Permissions) -> Result<()> {
218        let attributes = Attributes::new().set_permissions(permissions);
219
220        self.item
221            .as_attributes_operations()
222            .ok_or(Error::UnsupportedOperation)?
223            .set_attributes(&mut self.context, &attributes)?;
224
225        Ok(())
226    }
227
228    pub fn control<C>(&mut self, _command: C, argument: &C::Input) -> Result<C::Output>
229    where
230        C: ControlCommand,
231        C::Output: Default,
232    {
233        let mut output = C::Output::default();
234
235        self.item
236            .as_base_operations()
237            .ok_or(Error::UnsupportedOperation)?
238            .control(
239                &mut self.context,
240                C::IDENTIFIER,
241                AnyByLayout::from(argument),
242                AnyByLayout::from_mutable(&mut output),
243            )?;
244
245        Ok(output)
246    }
247
248    pub fn get_access(&self) -> Result<AccessFlags> {
249        Ok(self.flags.get_access())
250    }
251
252    pub fn close_internal(&mut self, virtual_file_system: &VirtualFileSystem) -> crate::Result<()> {
253        block_on(virtual_file_system.close(&self.item, &mut self.context))
254    }
255
256    pub fn close(mut self, virtual_file_system: &VirtualFileSystem) -> crate::Result<()> {
257        let result = self.close_internal(virtual_file_system);
258        forget(self);
259
260        result
261    }
262}
263
264impl Drop for SynchronousFile {
265    fn drop(&mut self) {
266        // Note: We cannot use async in Drop, so we just ignore errors here.
267        let _ = self.close_internal(crate::get_instance()).map_err(|e| {
268            log::warning!("Failed to close SynchronousFile in Drop: {e}");
269        });
270    }
271}