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