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