Skip to main content

abi_definitions/file_system/
functions.rs

1/// This module implements the POSIX like file system C ABI.
2use core::{
3    cmp::min,
4    ffi::{CStr, c_char},
5    num::NonZeroU32,
6    ptr::copy_nonoverlapping,
7};
8use file_system::{AccessFlags, CreateFlags, Flags, StateFlags, character_device};
9use task::block_on;
10use virtual_file_system::{
11    Error, SynchronousDirectory, SynchronousFile, get_instance as get_file_system_instance,
12};
13
14use crate::{XilaFileSystemPollEvent, XilaFileSystemState, XilaTime, file_system::into_position};
15
16use super::{
17    XilaFileIdentifier, XilaFileSystemMode, XilaFileSystemOpen, XilaFileSystemResult,
18    XilaFileSystemSize, XilaFileSystemStatistics, XilaFileSystemWhence,
19};
20
21use abi_context::{self as context, FileIdentifier};
22
23/// This function is used to convert a function returning a Result into a u32.
24pub fn into_u32<F>(function: F) -> XilaFileSystemResult
25where
26    F: FnOnce() -> Result<(), virtual_file_system::Error>,
27{
28    match function() {
29        Ok(()) => 0,
30        Err(error) => {
31            let non_zero: NonZeroU32 = error.into();
32
33            log::error!("File system error: {:?} ({})", error, non_zero);
34            log::error!("Context debug info: {:?}", context::get_instance());
35
36            non_zero.get()
37        }
38    }
39}
40
41/// This function is used to open a file.
42///
43/// # Safety
44///
45/// This function is unsafe because it dereferences raw pointers.
46///
47/// # Errors
48///
49/// This function may return an error if the file system fails to open the file.
50#[unsafe(no_mangle)]
51pub unsafe extern "C" fn xila_file_system_get_statistics(
52    file: XilaFileIdentifier,
53    statistics: *mut XilaFileSystemStatistics,
54) -> XilaFileSystemResult {
55    unsafe {
56        into_u32(move || {
57            let statistics = XilaFileSystemStatistics::from_mutable_pointer(statistics)
58                .ok_or(Error::InvalidParameter)?;
59
60            let context = context::get_instance();
61
62            let s = if let Some(result) = context.perform_operation_on_directory(
63                file.try_into()?,
64                SynchronousDirectory::get_statistics,
65            ) {
66                result.inspect_err(|&e| {
67                    log::error!(
68                        "Performing operation on directory to get statistics: {:?}",
69                        e
70                    );
71                })?
72            } else {
73                context
74                    .perform_operation_on_file(file.try_into()?, SynchronousFile::get_statistics)
75                    .ok_or(Error::InvalidParameter)
76                    .inspect_err(|&e| {
77                        log::error!("Performing operation on file to get statistics: {:?}", e);
78                    })??
79            };
80
81            *statistics = XilaFileSystemStatistics::from_statistics(s);
82
83            Ok(())
84        })
85    }
86}
87
88/// This function is used to get the statistics of a file from its path.
89///
90/// # Safety
91///
92/// This function is unsafe because it dereferences raw pointers.
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn xila_file_system_get_statistics_from_path_at(
95    directory: XilaFileIdentifier,
96    path: *const c_char,
97    statistics: *mut XilaFileSystemStatistics,
98    _: bool,
99) -> XilaFileSystemResult {
100    unsafe {
101        into_u32(move || {
102            let path = CStr::from_ptr(path)
103                .to_str()
104                .map_err(|_| Error::InvalidParameter)?;
105
106            let context = context::get_instance();
107
108            let task = context.get_current_task_identifier();
109
110            let path = context::get_instance()
111                .resolve_path(task, directory.try_into()?, path)
112                .ok_or(Error::InvalidParameter)?;
113
114            let statistics = XilaFileSystemStatistics::from_mutable_pointer(statistics)
115                .ok_or(Error::InvalidParameter)?;
116
117            *statistics = XilaFileSystemStatistics::from_statistics(block_on(
118                get_file_system_instance().get_statistics(&path),
119            )?);
120
121            Ok(())
122        })
123    }
124}
125
126/// This function is used to get the access mode of a file.
127///
128/// # Safety
129///
130/// This function is unsafe because it dereferences raw pointers.
131///
132/// # Errors
133///
134/// This function may return an error if the file system fails to get the access mode of the file.
135#[unsafe(no_mangle)]
136pub unsafe extern "C" fn xila_file_system_get_access_flags(
137    file: XilaFileIdentifier,
138    mode: *mut XilaFileSystemMode,
139) -> XilaFileSystemResult {
140    unsafe {
141        // Debug: Getting file access mode
142
143        into_u32(move || {
144            if mode.is_null() {
145                Err(Error::InvalidParameter)?;
146            }
147
148            let m = context::get_instance()
149                .perform_operation_on_file_or_directory(
150                    file.try_into()?,
151                    |f| SynchronousFile::get_access(f),
152                    |d| SynchronousDirectory::get_access(d),
153                )
154                .ok_or(Error::InvalidIdentifier)??;
155
156            mode.write(m.bits());
157
158            Ok(())
159        })
160    }
161}
162
163/// This function is used to close a file.
164///
165/// # Errors
166///
167/// This function may return an error if the file system fails to close the file.
168///
169#[unsafe(no_mangle)]
170pub extern "C" fn xila_file_system_close(file: XilaFileIdentifier) -> XilaFileSystemResult {
171    into_u32(move || {
172        let file: FileIdentifier = file.try_into()?;
173
174        if !file.is_directory() {
175            context::get_instance()
176                .remove_file(file)
177                .ok_or(Error::InvalidIdentifier)?
178                .close(get_file_system_instance())?;
179        } else {
180            log::warning!("Attempted to close a directory with identifier: {:?}", file);
181        }
182
183        Ok(())
184    })
185}
186
187/// This function is used perform a vectored write operation on a file.
188///
189/// # Safety
190///
191/// This function is unsafe because it dereferences raw pointers.
192///
193/// # Errors
194///
195/// This function may return an error if the file system fails to open the file.
196///
197/// # Example
198#[unsafe(no_mangle)]
199pub unsafe extern "C" fn xila_file_system_write_vectored(
200    file: XilaFileIdentifier,
201    buffers: *const *const u8,
202    buffers_length: *const usize,
203    buffer_count: usize,
204    written: *mut usize,
205) -> XilaFileSystemResult {
206    unsafe {
207        into_u32(move || {
208            let buffers = core::slice::from_raw_parts(buffers, buffer_count);
209            let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
210
211            let size = context::get_instance()
212                .perform_operation_on_file(file.try_into()?, |file| {
213                    let mut current_written = 0;
214
215                    for (buffer, length) in buffers.iter().zip(buffers_length.iter()) {
216                        let buffer_slice = core::slice::from_raw_parts(*buffer, *length);
217
218                        current_written += file.write(buffer_slice)?;
219                    }
220
221                    Ok::<_, virtual_file_system::Error>(current_written)
222                })
223                .ok_or(Error::InvalidIdentifier)??;
224
225            if !written.is_null() {
226                *written = size;
227            }
228
229            Ok(())
230        })
231    }
232}
233
234/// This function is used to perform a write operation on a file.
235///
236/// # Safety
237///
238/// This function is unsafe because it dereferences raw pointers.
239///
240/// # Errors
241///
242/// This function may return an error if the file system fails to open the file.
243#[unsafe(no_mangle)]
244pub unsafe extern "C" fn xila_file_system_read_vectored(
245    file: XilaFileIdentifier,
246    buffers: *mut *mut u8,
247    buffers_length: *mut usize,
248    buffer_count: usize,
249    read: *mut usize,
250) -> XilaFileSystemResult {
251    unsafe {
252        into_u32(move || {
253            let buffers = core::slice::from_raw_parts_mut(buffers, buffer_count);
254            let buffers_length = core::slice::from_raw_parts_mut(buffers_length, buffer_count);
255
256            let current_read = context::get_instance()
257                .perform_operation_on_file(file.try_into()?, |file| {
258                    let mut current_read = 0;
259
260                    for (buffer_pointer, buffer_length) in
261                        buffers.iter_mut().zip(buffers_length.iter_mut())
262                    {
263                        let buffer =
264                            core::slice::from_raw_parts_mut(*buffer_pointer, *buffer_length);
265
266                        let read_count = file.read(buffer)?;
267
268                        current_read += read_count;
269                    }
270
271                    Ok::<_, virtual_file_system::Error>(current_read)
272                })
273                .ok_or(Error::InvalidIdentifier)??;
274
275            if !read.is_null() {
276                *read = current_read;
277            }
278
279            Ok(())
280        })
281    }
282}
283
284/// This function is used to perform a read operation on a file at a specific position.
285///
286/// # Safety
287///
288/// This function is unsafe because it dereferences raw pointers.
289#[unsafe(no_mangle)]
290pub unsafe extern "C" fn xila_file_system_read_at_position_vectored(
291    file: XilaFileIdentifier,
292    buffers: *mut *mut u8,
293    buffers_length: *mut usize,
294    buffer_count: usize,
295    position: u64,
296    read: *mut usize,
297) -> XilaFileSystemResult {
298    unsafe {
299        into_u32(move || {
300            let buffers = core::slice::from_raw_parts_mut(buffers, buffer_count);
301            let buffers_length = core::slice::from_raw_parts_mut(buffers_length, buffer_count);
302
303            let current_read = context::get_instance()
304                .perform_operation_on_file(file.try_into()?, |file| {
305                    file.set_position(&file_system::Position::Start(position))?;
306
307                    let mut current_read = 0;
308
309                    for (buffer_pointer, buffer_length) in
310                        buffers.iter_mut().zip(buffers_length.iter_mut())
311                    {
312                        let buffer =
313                            core::slice::from_raw_parts_mut(*buffer_pointer, *buffer_length);
314
315                        let read_count = file.read(buffer)?;
316
317                        current_read += read_count;
318                    }
319
320                    Ok::<_, virtual_file_system::Error>(current_read)
321                })
322                .ok_or(Error::InvalidIdentifier)??;
323
324            if !read.is_null() {
325                *read = current_read;
326            }
327
328            Ok(())
329        })
330    }
331}
332
333/// This function is used to perform a write operation on a file at a specific position.
334///
335/// # Safety
336///
337/// This function is unsafe because it dereferences raw pointers.
338#[unsafe(no_mangle)]
339pub unsafe extern "C" fn xila_file_system_write_at_position_vectored(
340    file: XilaFileIdentifier,
341    buffers: *const *const u8,
342    buffers_length: *const usize,
343    buffer_count: usize,
344    position: u64,
345    written: *mut usize,
346) -> XilaFileSystemResult {
347    unsafe {
348        into_u32(move || {
349            let buffers = core::slice::from_raw_parts(buffers, buffer_count);
350            let buffers_length = core::slice::from_raw_parts(buffers_length, buffer_count);
351
352            let current_written = context::get_instance()
353                .perform_operation_on_file(file.try_into()?, |file| {
354                    file.set_position(&file_system::Position::Start(position))?;
355
356                    let mut current_written = 0;
357
358                    for (buffer, length) in buffers.iter().zip(buffers_length.iter()) {
359                        let buffer_slice = core::slice::from_raw_parts(*buffer, *length);
360
361                        current_written += file.write(buffer_slice)?;
362                    }
363
364                    Ok::<_, virtual_file_system::Error>(current_written)
365                })
366                .ok_or(Error::InvalidIdentifier)??;
367
368            if !written.is_null() {
369                *written = current_written;
370            }
371
372            Ok(())
373        })
374    }
375}
376
377/// This function is used to check if a file is a terminal.
378///
379/// # Safety
380///
381/// This function is unsafe because it dereferences raw pointers.
382///
383/// # Errors
384///
385/// This function may return an error if the file system fails to open the file.
386#[unsafe(no_mangle)]
387pub unsafe extern "C" fn xila_file_system_is_a_terminal(
388    file: XilaFileIdentifier,
389    is_a_terminal: *mut bool,
390) -> XilaFileSystemResult {
391    into_u32(move || {
392        if is_a_terminal.is_null() {
393            Err(Error::InvalidParameter)?;
394        }
395
396        unsafe {
397            *is_a_terminal = context::get_instance()
398                .perform_operation_on_file(file.try_into()?, |file| {
399                    file.control(character_device::IS_A_TERMINAL, &())
400                })
401                .ok_or(Error::InvalidIdentifier)??;
402        }
403
404        Ok(())
405    })
406}
407
408#[unsafe(no_mangle)]
409pub extern "C" fn xila_file_system_is_stdin(file: XilaFileIdentifier) -> bool {
410    if let Ok(file) = file.try_into() {
411        // Debug: Checking if file is stdin
412        FileIdentifier::STANDARD_IN == file
413    } else {
414        false
415    }
416}
417
418#[unsafe(no_mangle)]
419pub extern "C" fn xila_file_system_is_stderr(file: XilaFileIdentifier) -> bool {
420    if let Ok(file) = file.try_into() {
421        FileIdentifier::STANDARD_ERROR == file
422    } else {
423        false
424    }
425}
426
427#[unsafe(no_mangle)]
428pub extern "C" fn xila_file_system_is_stdout(file: XilaFileIdentifier) -> bool {
429    if let Ok(file) = file.try_into() {
430        FileIdentifier::STANDARD_OUT == file
431    } else {
432        false
433    }
434}
435
436/// This function is used to open a file.
437///
438/// # Safety
439///
440/// This function is unsafe because it dereferences raw pointers.
441#[unsafe(no_mangle)]
442pub unsafe extern "C" fn xila_file_system_open(
443    path: *const c_char,
444    mode: XilaFileSystemMode,
445    open: XilaFileSystemOpen,
446    status: XilaFileSystemState,
447    file: *mut XilaFileIdentifier,
448) -> XilaFileSystemResult {
449    unsafe {
450        into_u32(move || {
451            let path = core::ffi::CStr::from_ptr(path)
452                .to_str()
453                .map_err(|_| Error::InvalidParameter)?;
454
455            let mode = AccessFlags::from_bits_truncate(mode);
456            let open = CreateFlags::from_bits_truncate(open);
457            let status = StateFlags::from_bits_truncate(status);
458
459            let flags = Flags::new(mode, Some(open), Some(status));
460
461            // Debug: Opening file
462
463            let task = context::get_instance().get_current_task_identifier();
464
465            let f = SynchronousFile::open(get_file_system_instance(), task, path, flags)?;
466
467            *file = context::get_instance()
468                .insert_file(task, f, None)
469                .ok_or(Error::InvalidIdentifier)?
470                .into();
471
472            Ok(())
473        })
474    }
475}
476
477#[unsafe(no_mangle)]
478pub extern "C" fn xila_file_system_set_flags(
479    _file: XilaFileIdentifier,
480    _status: XilaFileSystemState,
481) -> XilaFileSystemResult {
482    todo!()
483}
484
485/// This function is used to get the flags of a file.
486///
487/// # Safety
488///
489/// This function is unsafe because it dereferences raw pointers.
490#[unsafe(no_mangle)]
491pub unsafe extern "C" fn xila_file_system_get_state_flags(
492    _file: XilaFileIdentifier,
493    _status: *mut XilaFileSystemState,
494) -> XilaFileSystemResult {
495    unsafe {
496        into_u32(move || {
497            if _status.is_null() {
498                Err(Error::InvalidParameter)?;
499            }
500
501            let state = context::get_instance()
502                .perform_operation_on_file_or_directory(
503                    _file.try_into()?,
504                    |f| SynchronousFile::get_state(f),
505                    |d| SynchronousDirectory::get_state(d),
506                )
507                .ok_or(Error::InvalidIdentifier)??;
508
509            _status.write(state.bits());
510
511            Ok(())
512        })
513    }
514}
515
516/// This function is used to convert a path to a resolved path (i.e. a path without symbolic links or relative paths).
517///
518/// # Safety
519///
520/// This function is unsafe because it dereferences raw pointers.
521#[unsafe(no_mangle)]
522pub unsafe extern "C" fn xila_file_system_resolve_path(
523    path: *const i8,
524    resolved_path: *mut u8,
525    resolved_path_size: usize,
526) -> XilaFileSystemResult {
527    unsafe {
528        into_u32(move || {
529            let path = core::ffi::CStr::from_ptr(path)
530                .to_str()
531                .map_err(|_| Error::InvalidParameter)?;
532
533            // Debug: Resolving path
534
535            // Copy path to resolved path.
536            copy_nonoverlapping(
537                path.as_ptr(),
538                resolved_path,
539                min(resolved_path_size, path.len()),
540            );
541
542            Ok(())
543        })
544    }
545}
546
547#[unsafe(no_mangle)]
548pub extern "C" fn xila_file_system_flush(
549    file: XilaFileIdentifier,
550    _: bool,
551) -> XilaFileSystemResult {
552    into_u32(move || {
553        context::get_instance()
554            .perform_operation_on_file(file.try_into()?, |file| file.flush())
555            .ok_or(Error::InvalidIdentifier)??;
556
557        Ok(())
558    })
559}
560
561#[unsafe(no_mangle)]
562pub extern "C" fn xila_file_system_create_symbolic_link_at(
563    _: XilaFileIdentifier,
564    _: *const c_char,
565    _: *const c_char,
566) -> XilaFileSystemResult {
567    todo!()
568}
569
570#[unsafe(no_mangle)]
571pub extern "C" fn xila_file_system_read_link_at(
572    _directory: XilaFileIdentifier,
573    _path: *mut i8,
574    _size: usize,
575    _used: *mut usize,
576) -> XilaFileSystemResult {
577    todo!()
578}
579
580/// This function is used to set the position in a file.
581///
582/// # Safety
583///
584/// This function is unsafe because it dereferences raw pointers.
585#[unsafe(no_mangle)]
586pub unsafe extern "C" fn xila_file_system_set_position(
587    file: XilaFileIdentifier,
588    offset: i64,
589    whence: XilaFileSystemWhence,
590    position: *mut XilaFileSystemSize,
591) -> XilaFileSystemResult {
592    unsafe {
593        into_u32(move || {
594            let current_position = into_position(whence, offset);
595
596            // Debug: Setting position
597
598            let result = context::get_instance()
599                .perform_operation_on_file(file.try_into()?, |file| {
600                    file.set_position(&current_position)
601                })
602                .ok_or(Error::InvalidIdentifier)??;
603
604            *position = result;
605
606            Ok(())
607        })
608    }
609}
610
611/// This function is used to get the position in a file.
612///
613/// # Safety
614///
615/// This function is unsafe because it dereferences raw pointers.
616#[unsafe(no_mangle)]
617pub unsafe extern "C" fn xila_file_system_create_directory_at(
618    directory: XilaFileIdentifier,
619    path: *const c_char,
620) -> XilaFileSystemResult {
621    unsafe {
622        into_u32(move || {
623            let path = CStr::from_ptr(path)
624                .to_str()
625                .map_err(|_| Error::InvalidParameter)?;
626
627            let context = context::get_instance();
628
629            let task = context.get_current_task_identifier();
630
631            let path = context
632                .resolve_path(task, directory.try_into()?, path)
633                .ok_or(Error::InvalidParameter)?;
634
635            let task = context::get_instance().get_current_task_identifier();
636            block_on(get_file_system_instance().create_directory(task, &path))?;
637
638            Ok(())
639        })
640    }
641}
642
643/// This function is used to rename (move) a file.
644///
645/// # Safety
646///
647/// This function is unsafe because it dereferences raw pointers.
648#[unsafe(no_mangle)]
649pub unsafe extern "C" fn xila_file_system_rename(
650    old_path: *const c_char,
651    new_path: *const c_char,
652) -> XilaFileSystemResult {
653    unsafe {
654        into_u32(move || {
655            let old_path = CStr::from_ptr(old_path)
656                .to_str()
657                .map_err(|_| Error::InvalidParameter)?;
658
659            let new_path = CStr::from_ptr(new_path)
660                .to_str()
661                .map_err(|_| Error::InvalidParameter)?;
662
663            // Debug: Renaming files
664
665            block_on(get_file_system_instance().rename(&old_path, &new_path))?;
666
667            Ok(())
668        })
669    }
670}
671
672#[unsafe(no_mangle)]
673pub extern "C" fn xila_file_system_set_times(
674    _: XilaFileIdentifier,
675    _: XilaTime,
676    _: XilaTime,
677    _: u8,
678) -> XilaFileSystemResult {
679    todo!()
680}
681
682/// This function is used to set access and modification times of a file.
683///
684/// # Safety
685///
686/// This function is unsafe because it dereferences raw pointers.
687#[unsafe(no_mangle)]
688pub unsafe extern "C" fn xila_file_system_set_times_from_path(
689    _path: *const c_char,
690    _access: XilaTime,
691    _modification: XilaTime,
692    _flags: u8,
693    _follow: bool,
694) -> XilaFileSystemResult {
695    todo!()
696}
697
698/// This function is used to remove a file.
699///
700/// # Safety
701///
702/// This function is unsafe because it dereferences raw pointers.
703#[unsafe(no_mangle)]
704pub unsafe extern "C" fn xila_file_system_remove(_path: *const c_char) -> XilaFileSystemResult {
705    unsafe {
706        into_u32(|| {
707            let path = CStr::from_ptr(_path)
708                .to_str()
709                .map_err(|_| Error::InvalidParameter)?;
710
711            let task = context::get_instance().get_current_task_identifier();
712
713            block_on(get_file_system_instance().remove(task, path))?;
714
715            Ok(())
716        })
717    }
718}
719
720/// This function is used to truncate a file.
721#[unsafe(no_mangle)]
722pub extern "C" fn xila_file_system_truncate(
723    _file: XilaFileIdentifier,
724    _length: XilaFileSystemSize,
725) -> XilaFileSystemResult {
726    into_u32(move || {
727        let _task = context::get_instance().get_current_task_identifier();
728
729        let _file: FileIdentifier = _file.try_into()?;
730
731        todo!();
732    })
733}
734
735/// This function is used to create a symbolic link.
736///
737/// # Safety
738///
739/// This function is unsafe because it dereferences raw pointers.
740#[unsafe(no_mangle)]
741pub unsafe extern "C" fn xila_file_system_link(
742    _path: *const c_char,
743    _link: *const c_char,
744) -> XilaFileSystemResult {
745    todo!()
746}
747
748/// This function is used to advice the file system about the access pattern of a file.
749#[unsafe(no_mangle)]
750pub extern "C" fn xila_file_system_advise(
751    _file: XilaFileIdentifier,
752    _offset: XilaFileSystemSize,
753    _length: XilaFileSystemSize,
754    _advice: u8,
755) -> XilaFileSystemResult {
756    todo!()
757}
758
759#[unsafe(no_mangle)]
760pub extern "C" fn xila_file_system_allocate(
761    _file: XilaFileIdentifier,
762    _offset: XilaFileSystemSize,
763    _length: XilaFileSystemSize,
764) -> XilaFileSystemResult {
765    todo!()
766}
767
768#[unsafe(no_mangle)]
769pub extern "C" fn xila_file_system_dummy(_: XilaFileSystemPollEvent) -> XilaFileSystemResult {
770    into_u32(move || Ok(()))
771}