From 42e20134643987785c53cbee297aeaf02d4da5b0 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 11 May 2023 11:36:08 +0200 Subject: [PATCH 1/4] fs info: add is_directory() and is_regular_file() --- CHANGELOG.md | 2 ++ uefi/src/proto/media/file/info.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2dfbd5bf..ffd058c89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ - Added `CStr16::as_bytes` - Added `AsRef<[u8]>` and `Borrow<[u8]>` for `Cstr8` and `CStr16`. - Added `LoadedImageDevicePath` protocol. +- Added `FileAttribute::is_directory(&self)` and + `FileAttribute::is_regular_file(&self)` ### Changed diff --git a/uefi/src/proto/media/file/info.rs b/uefi/src/proto/media/file/info.rs index cd923218b..4bb7b8d7f 100644 --- a/uefi/src/proto/media/file/info.rs +++ b/uefi/src/proto/media/file/info.rs @@ -229,6 +229,18 @@ impl FileInfo { pub fn file_name(&self) -> &CStr16 { unsafe { CStr16::from_ptr(self.file_name.as_ptr()) } } + + /// Returns if the file is a directory. + #[must_use] + pub fn is_directory(&self) -> bool { + self.attribute.contains(FileAttribute::DIRECTORY) + } + + /// Returns if the file is a regular file. + #[must_use] + pub fn is_regular_file(&self) -> bool { + !self.is_directory() + } } impl Align for FileInfo { From 49f69a63a8248e989a509da23616bb009a98d1f2 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 11 May 2023 11:27:57 +0200 Subject: [PATCH 2/4] fs: implement remove_dir_all With this PR, the fs module is feature-complete and ready to be shipped. --- uefi-test-runner/src/fs/mod.rs | 5 ++++- uefi/src/fs/dir_entry_iter.rs | 3 +++ uefi/src/fs/file_system/fs.rs | 38 ++++++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/uefi-test-runner/src/fs/mod.rs b/uefi-test-runner/src/fs/mod.rs index cb47146e8..9dae895e4 100644 --- a/uefi-test-runner/src/fs/mod.rs +++ b/uefi-test-runner/src/fs/mod.rs @@ -60,7 +60,10 @@ pub fn test(sfs: ScopedProtocol) -> Result<(), fs::Error> { assert_eq!(boxinfo.file_size(), data_to_write.len() as u64); // test remove dir all - // TODO + fs.remove_dir_all(cstr16!("foo_dir\\1"))?; + // file should not be available after remove all + let err = fs.try_exists(cstr16!("foo_dir\\1")); + assert!(err.is_err()); Ok(()) } diff --git a/uefi/src/fs/dir_entry_iter.rs b/uefi/src/fs/dir_entry_iter.rs index 7118e96d9..26a512c1e 100644 --- a/uefi/src/fs/dir_entry_iter.rs +++ b/uefi/src/fs/dir_entry_iter.rs @@ -6,6 +6,9 @@ use alloc::boxed::Box; /// Iterates over the entries of an UEFI directory. It returns boxed values of /// type [`UefiFileInfo`]. +/// +/// Note that on UEFI/FAT-style file systems, the root dir usually doesn't +/// return the entries `.` and `..`, whereas sub directories do. #[derive(Debug)] pub struct UefiDirectoryIter(UefiDirectoryHandle); diff --git a/uefi/src/fs/file_system/fs.rs b/uefi/src/fs/file_system/fs.rs index c729fd725..99161b90d 100644 --- a/uefi/src/fs/file_system/fs.rs +++ b/uefi/src/fs/file_system/fs.rs @@ -11,6 +11,8 @@ use core::fmt; use core::fmt::{Debug, Formatter}; use core::ops::Deref; use log::debug; +use uefi::CStr16; +use uefi_macros::cstr16; /// Return type for public [`FileSystem`] operations. pub type FileSystemResult = Result; @@ -181,11 +183,35 @@ impl<'a> FileSystem<'a> { } } - /*/// Removes a directory at this path, after removing all its contents. Use + /// Removes a directory at this path, after removing all its contents. Use /// carefully! pub fn remove_dir_all(&mut self, path: impl AsRef) -> FileSystemResult<()> { + const SKIP_DIRS: [&CStr16; 2] = [cstr16!("."), cstr16!("..")]; let path = path.as_ref(); - }*/ + for file_info in self + .read_dir(path)? + .filter_map(|file_info_result| file_info_result.ok()) + { + if SKIP_DIRS.contains(&file_info.file_name()) { + continue; + } + + let mut abs_entry_path = PathBuf::new(); + abs_entry_path.push(path); + abs_entry_path.push(file_info.file_name()); + if file_info.is_directory() { + // delete all inner files + // This recursion is fine as there are no links in UEFI/FAT file + // systems. No cycles possible. + self.remove_dir_all(&abs_entry_path)?; + } else { + self.remove_file(abs_entry_path)?; + } + } + // Now that the dir is empty, we delete it as final step. + self.remove_dir(path)?; + Ok(()) + } /// Removes a file from the filesystem. pub fn remove_file(&mut self, path: impl AsRef) -> FileSystemResult<()> { @@ -282,17 +308,17 @@ impl<'a> FileSystem<'a> { /// absolute path. /// /// May create a file if [`UefiFileMode::CreateReadWrite`] is set. May - /// create a directory if [`UefiFileMode::CreateReadWrite`] and `is_dir` - /// is set. + /// create a directory if [`UefiFileMode::CreateReadWrite`] and `create_dir` + /// is set. The parameter `create_dir` is ignored otherwise. fn open( &mut self, path: &Path, mode: UefiFileMode, - is_dir: bool, + create_dir: bool, ) -> FileSystemResult { validate_path(path)?; - let attr = if mode == UefiFileMode::CreateReadWrite && is_dir { + let attr = if mode == UefiFileMode::CreateReadWrite && create_dir { UefiFileAttribute::DIRECTORY } else { UefiFileAttribute::empty() From 1aab1f0da00822204f25f7a1c6e29e49059c5ce1 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 11 May 2023 11:33:13 +0200 Subject: [PATCH 3/4] fs: add and export constant COMMON_SKIP_DIRS --- uefi/src/fs/dir_entry_iter.rs | 7 ++++++- uefi/src/fs/file_system/fs.rs | 5 +---- uefi/src/fs/mod.rs | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/uefi/src/fs/dir_entry_iter.rs b/uefi/src/fs/dir_entry_iter.rs index 26a512c1e..09ba03a52 100644 --- a/uefi/src/fs/dir_entry_iter.rs +++ b/uefi/src/fs/dir_entry_iter.rs @@ -1,8 +1,12 @@ //! Module for directory iteration. See [`UefiDirectoryIter`]. use super::*; -use crate::Result; +use crate::{CStr16, Result}; use alloc::boxed::Box; +use uefi_macros::cstr16; + +/// Common skip dirs in UEFI/FAT-style file systems. +pub const COMMON_SKIP_DIRS: &[&CStr16] = &[cstr16!("."), cstr16!("..")]; /// Iterates over the entries of an UEFI directory. It returns boxed values of /// type [`UefiFileInfo`]. @@ -14,6 +18,7 @@ pub struct UefiDirectoryIter(UefiDirectoryHandle); impl UefiDirectoryIter { /// Constructor. + #[must_use] pub fn new(handle: UefiDirectoryHandle) -> Self { Self(handle) } diff --git a/uefi/src/fs/file_system/fs.rs b/uefi/src/fs/file_system/fs.rs index 99161b90d..79b66a0ca 100644 --- a/uefi/src/fs/file_system/fs.rs +++ b/uefi/src/fs/file_system/fs.rs @@ -11,8 +11,6 @@ use core::fmt; use core::fmt::{Debug, Formatter}; use core::ops::Deref; use log::debug; -use uefi::CStr16; -use uefi_macros::cstr16; /// Return type for public [`FileSystem`] operations. pub type FileSystemResult = Result; @@ -186,13 +184,12 @@ impl<'a> FileSystem<'a> { /// Removes a directory at this path, after removing all its contents. Use /// carefully! pub fn remove_dir_all(&mut self, path: impl AsRef) -> FileSystemResult<()> { - const SKIP_DIRS: [&CStr16; 2] = [cstr16!("."), cstr16!("..")]; let path = path.as_ref(); for file_info in self .read_dir(path)? .filter_map(|file_info_result| file_info_result.ok()) { - if SKIP_DIRS.contains(&file_info.file_name()) { + if COMMON_SKIP_DIRS.contains(&file_info.file_name()) { continue; } diff --git a/uefi/src/fs/mod.rs b/uefi/src/fs/mod.rs index a896e0d5e..dcdfd0b47 100644 --- a/uefi/src/fs/mod.rs +++ b/uefi/src/fs/mod.rs @@ -35,8 +35,8 @@ mod file_system; mod path; mod uefi_types; +pub use dir_entry_iter::*; pub use file_system::*; pub use path::*; -use dir_entry_iter::*; use uefi_types::*; From bc8d8db5536603a085792ae16d89a99b4741b3ef Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 11 May 2023 11:39:12 +0200 Subject: [PATCH 4/4] fs: doc fixes --- uefi/src/fs/file_system/error.rs | 3 +-- uefi/src/fs/mod.rs | 8 +++----- uefi/src/fs/path/mod.rs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/uefi/src/fs/file_system/error.rs b/uefi/src/fs/file_system/error.rs index f25086539..3a6409632 100644 --- a/uefi/src/fs/file_system/error.rs +++ b/uefi/src/fs/file_system/error.rs @@ -28,8 +28,7 @@ pub struct IoError { pub uefi_error: crate::Error, } -/// Enum that further specifies the context in that a [`Error`] -/// occurred. +/// Enum that further specifies the context in that an [`Error`] occurred. #[derive(Debug, Clone, Display, PartialEq, Eq)] pub enum FileSystemIOErrorContext { /// Can't delete the directory. diff --git a/uefi/src/fs/mod.rs b/uefi/src/fs/mod.rs index dcdfd0b47..aa2b2968d 100644 --- a/uefi/src/fs/mod.rs +++ b/uefi/src/fs/mod.rs @@ -21,11 +21,9 @@ //! [`Path`] and [`PathBuf`]. //! //! # API Hints -//! There are no `File` and `Path` abstractions similar to those from `std` that -//! are publicly exported. Instead, paths to files are provided as `&str`, and -//! will be validated and transformed internally to the correct type. -//! Furthermore, there are no `File` objects that are exposed to users. Instead, -//! it is intended to work with the file system as in `std::fs`. +//! There is no `File` abstraction as in the Rust `std` library. Instead, it is +//! intended to work with the file system via dedicated functions, similar to +//! the public functions of the `std::fs` module. //! //! There is no automatic synchronization of the file system for concurrent //! accesses. This is in the responsibility of the user. diff --git a/uefi/src/fs/path/mod.rs b/uefi/src/fs/path/mod.rs index 35293e0ae..ce943d35a 100644 --- a/uefi/src/fs/path/mod.rs +++ b/uefi/src/fs/path/mod.rs @@ -33,7 +33,7 @@ pub const SEPARATOR_STR: &CStr16 = uefi_macros::cstr16!("\\"); /// Deny list of characters for path components. UEFI supports FAT-like file /// systems. According to , -/// paths should not contain the following symbols. +/// paths should not contain these symbols. pub const CHARACTER_DENY_LIST: [Char16; 10] = unsafe { [ NUL_16,