Skip to main content

abi_definitions/file_system/
file.rs

1/// This module implements the POSIX like file system C ABI.
2use core::ffi::c_char;
3use file_system::{
4    AccessFlags, CreateFlags, Flags, Kind, Permissions, StateFlags, Statistics, Time,
5    character_device,
6};
7use shared::generate_shadow_type;
8use users::{GroupIdentifier, UserIdentifier};
9use virtual_file_system::{Error, SynchronousFile, get_instance as get_file_system_instance};
10
11use crate::{
12    XilaFileSystemPollEvent, XilaFileSystemState, XilaTaskIdentifier, XilaTime,
13    abi_unsafe_function, file_system::into_position, parse_c_str,
14};
15
16use super::{
17    XilaFileSystemAccess, XilaFileSystemOpen, XilaFileSystemSize, XilaFileSystemStatistics,
18    XilaFileSystemWhence,
19};
20
21generate_shadow_type!(XilaFileSystemFile, SynchronousFile);
22
23abi_unsafe_function! {
24    /// This function is used to get the statistics of a file.
25    ///
26    /// # Safety
27    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
28    ///
29    /// # Errors
30    /// This function may return an error if the file system fails to get the statistics of the file.
31    fn xila_file_system_file_get_statistics(
32        file: *mut XilaFileSystemFile,
33        statistics: *mut XilaFileSystemStatistics,
34    ) -> XilaFileSystemResult {
35        log::debug!("Getting statistics for file {file:?}");
36
37        let result = match (*file).get_statistics() {
38            Ok(statistics) => statistics,
39            // Some character devices don't expose attribute operations.
40            // Return minimal synthetic metadata so POSIX callers (e.g. WASI libc)
41            // can proceed after open/fstat.
42            Err(Error::UnsupportedOperation)
43            | Err(Error::FileSystem(file_system::Error::UnsupportedOperation)) => {
44                log::warning!(
45                    "File system does not support getting statistics for file {file:?}, returning synthetic character device metadata"
46                );
47                Statistics::new(
48                    0,
49                    1,
50                    0,
51                    Time::new(0),
52                    Time::new(0),
53                    Time::new(0),
54                    Time::new(0),
55                    Kind::CharacterDevice,
56                    Permissions::DEVICE_DEFAULT,
57                    UserIdentifier::ROOT,
58                    GroupIdentifier::ROOT,
59                )
60            }
61            Err(error) => return Err(error),
62        };
63
64        *statistics = XilaFileSystemStatistics::from_statistics(result);
65        Ok(())
66    }
67}
68
69abi_unsafe_function! {
70    /// This function is used to get the access mode of a file.
71    ///
72    /// # Safety
73    ///
74    /// This function is unsafe because it dereferences raw pointers.
75    ///
76    /// # Errors
77    ///
78    /// This function may return an error if the file system fails to get the access mode of the file.
79    fn xila_file_system_file_get_access_flags(
80        file: *mut XilaFileSystemFile,
81        mode: *mut XilaFileSystemAccess,
82    ) -> XilaFileSystemResult {
83        let m = (*file).get_access()?;
84        mode.write(m.bits());
85        Ok(())
86    }
87}
88
89abi_unsafe_function! {
90    /// This function is used to close a file.
91    ///
92    /// # Safety
93    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
94    ///
95    /// # Errors
96    ///
97    /// This function may return an error if the file system fails to close the file.
98    fn xila_file_system_file_close(
99        file: *mut XilaFileSystemFile,
100    ) -> XilaFileSystemResult {
101        log::information!("Closing file {file:?}");
102        (*file).close_internal(get_file_system_instance())
103    }
104}
105
106abi_unsafe_function! {
107    /// This function is used perform a vectored write operation on a file.
108    ///
109    /// # Safety
110    ///
111    /// This function is unsafe because it dereferences raw pointers.
112    ///
113    /// # Errors
114    ///
115    /// This function may return an error if the file system fails to open the file.
116    fn xila_file_system_file_write(
117        file: *mut XilaFileSystemFile,
118        buffers: *const *const u8,
119        buffers_length: *const usize,
120        buffer_count: usize,
121        written: *mut usize,
122    ) -> XilaFileSystemResult {
123        let buffers = core::slice::from_raw_parts(buffers, buffer_count);
124        let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
125
126        let mut current_written = 0;
127
128        for (buffer, length) in buffers.iter().zip(buffers_length.iter()) {
129            let buffer_slice = core::slice::from_raw_parts(*buffer, *length);
130            current_written += (*file).write(buffer_slice)?;
131        }
132
133        if !written.is_null() {
134            *written = current_written;
135        }
136        Ok(())
137    }
138}
139
140abi_unsafe_function! {
141    /// This function is used to perform a write operation on a file.
142    ///
143    /// # Safety
144    ///
145    /// This function is unsafe because it dereferences raw pointers.
146    ///
147    /// # Errors
148    ///
149    /// This function may return an error if the file system fails to open the file.
150    fn xila_file_system_file_read(
151        file: *mut XilaFileSystemFile,
152        buffers: *const *mut u8,
153        buffers_length: *const usize,
154        buffer_count: usize,
155        read: *mut usize,
156    ) -> XilaFileSystemResult {
157        let buffers = core::slice::from_raw_parts(buffers, buffer_count);
158        let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
159
160        let mut current_read = 0;
161
162        for (buffer_pointer, buffer_length) in buffers.iter().zip(buffers_length.iter()) {
163            let buffer = core::slice::from_raw_parts_mut(*buffer_pointer, *buffer_length);
164            let read_count = (*file).read(buffer)?;
165            current_read += read_count;
166        }
167
168        if !read.is_null() {
169            *read = current_read;
170        }
171        Ok(())
172    }
173}
174
175abi_unsafe_function! {
176    /// This function is used to perform a read operation on a file at a specific position.
177    ///
178    /// # Safety
179    ///
180    /// This function is unsafe because it dereferences raw pointers.
181    fn xila_file_system_file_read_at(
182        file: *mut XilaFileSystemFile,
183        position: u64,
184        buffers: *const *mut u8,
185        buffers_length: *const usize,
186        buffer_count: usize,
187        read: *mut usize,
188    ) -> XilaFileSystemResult {
189        let buffers = core::slice::from_raw_parts(buffers, buffer_count);
190        let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
191
192        (*file).set_position(&file_system::Position::Start(position))?;
193
194        let mut current_read = 0;
195
196        for (buffer_pointer, buffer_length) in buffers.iter().zip(buffers_length.iter()) {
197            let buffer = core::slice::from_raw_parts_mut(*buffer_pointer, *buffer_length);
198            let read_count = (*file).read(buffer)?;
199            current_read += read_count;
200        }
201
202        if !read.is_null() {
203            *read = current_read;
204        }
205        Ok(())
206    }
207}
208
209abi_unsafe_function! {
210    /// This function is used to perform a write operation on a file at a specific position.
211    ///
212    /// # Safety
213    ///
214    /// This function is unsafe because it dereferences raw pointers.
215    fn xila_file_system_file_write_at(
216        file: *mut XilaFileSystemFile,
217        position: u64,
218        buffers: *const *const u8,
219        buffers_length: *const usize,
220        buffer_count: usize,
221        written: *mut usize,
222    ) -> XilaFileSystemResult {
223        let buffers = core::slice::from_raw_parts(buffers, buffer_count);
224        let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
225
226        (*file).set_position(&file_system::Position::Start(position))?;
227
228        let mut current_written = 0;
229
230        for (buffer, length) in buffers.iter().zip(buffers_length.iter()) {
231            let buffer_slice = core::slice::from_raw_parts(*buffer, *length);
232            current_written += (*file).write(buffer_slice)?;
233        }
234
235        if !written.is_null() {
236            *written = current_written;
237        }
238        Ok(())
239    }
240}
241
242abi_unsafe_function! {
243    /// This function is used to check if a file is a terminal.
244    ///
245    /// # Safety
246    ///
247    /// This function is unsafe because it dereferences raw pointers.
248    ///
249    /// # Errors
250    ///
251    /// This function may return an error if the file system fails to open the file.
252    fn xila_file_system_file_is_a_terminal(
253        file: *mut XilaFileSystemFile,
254        is_a_terminal: *mut bool,
255    ) -> XilaFileSystemResult {
256        *is_a_terminal = match (*file).control(character_device::IS_A_TERMINAL, &()) {
257            Ok(result) => result,
258            Err(Error::UnsupportedOperation) => false,
259            Err(_) => false,
260        };
261        Ok(())
262    }
263}
264
265abi_unsafe_function! {
266    /// This function is used to open a file.
267    ///
268    /// # Safety
269    ///
270    /// This function is unsafe because it dereferences raw pointers.
271    fn xila_file_system_file_open(
272        task: XilaTaskIdentifier,
273        path: *const c_char,
274        mode: XilaFileSystemAccess,
275        open: XilaFileSystemOpen,
276        status: XilaFileSystemState,
277        file: *mut XilaFileSystemFile,
278    ) -> XilaFileSystemResult {
279        log::information!("Opening file at path {:?} with mode {:?}, open {:?} and status {:?}",
280            unsafe { parse_c_str(path) },
281            mode,
282            open,
283            status
284        );
285
286        let path = parse_c_str(path)?;
287
288        let mode = AccessFlags::from_bits_truncate(mode);
289        let open = CreateFlags::from_bits_truncate(open);
290        let status = StateFlags::from_bits_truncate(status);
291
292        let flags = Flags::new(mode, Some(open), Some(status));
293
294        log::information!("Resolved flags: {:?}", flags);
295
296        let f = SynchronousFile::open(get_file_system_instance(), task.into(), path, flags)?;
297
298        log::information!("Successfully opened file at path {:?} with mode {:?}, open {:?} and status {:?}",
299            path,
300            mode,
301            open,
302            status
303         );
304
305        unsafe {
306            let target_slot = file as *mut SynchronousFile;
307            ::core::ptr::write(target_slot, f);
308        }
309
310        Ok(())
311    }
312}
313
314abi_unsafe_function! {
315    /// This function is used to set the flags of a file.
316    ///
317    /// # Safety
318    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
319    fn xila_file_system_file_set_flags(
320        _file: *mut XilaFileSystemFile,
321        _state: XilaFileSystemState,
322    ) -> XilaFileSystemResult {
323        todo!()
324    }
325}
326
327abi_unsafe_function! {
328    /// This function is used to get the flags of a file.
329    ///
330    /// # Safety
331    ///
332    /// This function is unsafe because it dereferences raw pointers.
333    fn xila_file_system_file_get_state(
334        file: *mut XilaFileSystemFile,
335        state: *mut XilaFileSystemState,
336    ) -> XilaFileSystemResult {
337        let s = (*file).get_state()?;
338        state.write(s.bits());
339        Ok(())
340    }
341}
342
343abi_unsafe_function! {
344    /// This function is used to flush a file.
345    ///
346    /// # Safety
347    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFile
348    fn xila_file_system_file_flush(
349        file: *mut XilaFileSystemFile,
350        _t: bool,
351    ) -> XilaFileSystemResult {
352        (*file).flush()
353    }
354}
355
356abi_unsafe_function! {
357    /// This function is used to set the position in a file.
358    ///
359    /// # Safety
360    ///
361    /// This function is unsafe because it dereferences raw pointers.
362    fn xila_file_system_file_set_position(
363        file: *mut XilaFileSystemFile,
364        offset: i64,
365        whence: XilaFileSystemWhence,
366        position: *mut XilaFileSystemSize,
367    ) -> XilaFileSystemResult {
368        let current_position = into_position(whence, offset);
369
370        // Debug: Setting position
371        let result = (*file).set_position(&current_position)?;
372        *position = result;
373        Ok(())
374    }
375}
376
377abi_unsafe_function! {
378    /// This function is used to advice the file system about the access pattern of a file.
379    ///
380    /// # Safety
381    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
382    fn xila_file_system_set_times(
383        _file: *mut XilaFileSystemFile,
384        _access: XilaTime,
385        _modification: XilaTime,
386        _flags: u8,
387    ) -> XilaFileSystemResult {
388        todo!()
389    }
390}
391
392abi_unsafe_function! {
393    /// This function is used to set access and modification times of a file.
394    ///
395    /// # Safety
396    ///
397    /// This function is unsafe because it dereferences raw pointers.
398    fn xila_file_system_set_times_from_path(
399        _path: *const c_char,
400        _access: XilaTime,
401        _modification: XilaTime,
402        _flags: u8,
403        _follow: bool,
404    ) -> XilaFileSystemResult {
405        todo!()
406    }
407}
408
409abi_unsafe_function! {
410    /// This function is used to truncate a file.
411    ///
412    /// # Safety
413    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
414    fn xila_file_system_truncate(
415        _file:  *mut XilaFileSystemFile,
416        _length: XilaFileSystemSize,
417    ) -> XilaFileSystemResult {
418        todo!()
419    }
420}
421
422abi_unsafe_function! {
423    /// This function is used to create a symbolic link.
424    ///
425    /// # Safety
426    ///
427    /// This function is unsafe because it dereferences raw pointers.
428    fn xila_file_system_link(
429        _path: *const c_char,
430        _link: *const c_char,
431    ) -> XilaFileSystemResult {
432        todo!()
433    }
434}
435
436abi_unsafe_function! {
437    /// This function is used to advice the file system about the access pattern of a file.
438    ///
439    /// # Safety
440    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
441    fn xila_file_system_file_advise(
442        _file:  *mut XilaFileSystemFile,
443        _offset: XilaFileSystemSize,
444        _length: XilaFileSystemSize,
445        _advice: u8,
446    ) -> XilaFileSystemResult {
447        todo!()
448    }
449}
450
451abi_unsafe_function! {
452    /// This function is used to allocate space for a file.
453    ///
454    /// # Safety
455    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
456    fn xila_file_system_file_allocate(
457        _file:  *mut XilaFileSystemFile,
458        _offset: XilaFileSystemSize,
459        _length: XilaFileSystemSize,
460    ) -> XilaFileSystemResult {
461        todo!()
462    }
463}
464
465abi_unsafe_function! {
466    /// This function is used to set the position in a file.
467    ///
468    /// # Safety
469    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
470    fn xila_file_system_file_truncate(
471        _file:  *mut XilaFileSystemFile,
472        _length: XilaFileSystemSize,
473    ) -> XilaFileSystemResult {
474        todo!()
475    }
476}
477
478abi_unsafe_function! {
479    /// This function is used to perform a poll operation on a file.
480    ///
481    /// # Safety
482    /// The caller must ensure that the provided pointer is valid and points to a properly initialized `XilaFileSystemItem` that is a file.
483    fn xila_file_system_dummy(_event: XilaFileSystemPollEvent) -> XilaFileSystemResult {
484        Ok(())
485    }
486}