Skip to content

fs: implement remove_dir_all #799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 4 additions & 1 deletion uefi-test-runner/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ pub fn test(sfs: ScopedProtocol<SimpleFileSystem>) -> 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(())
}
10 changes: 9 additions & 1 deletion uefi/src/fs/dir_entry_iter.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
//! 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`].
///
/// 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);

impl UefiDirectoryIter {
/// Constructor.
#[must_use]
pub fn new(handle: UefiDirectoryHandle) -> Self {
Self(handle)
}
Expand Down
3 changes: 1 addition & 2 deletions uefi/src/fs/file_system/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
35 changes: 29 additions & 6 deletions uefi/src/fs/file_system/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,34 @@ 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<Path>) -> FileSystemResult<()> {
let path = path.as_ref();
}*/
for file_info in self
.read_dir(path)?
.filter_map(|file_info_result| file_info_result.ok())
{
if COMMON_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<Path>) -> FileSystemResult<()> {
Expand Down Expand Up @@ -282,17 +305,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<UefiFileHandle> {
validate_path(path)?;

let attr = if mode == UefiFileMode::CreateReadWrite && is_dir {
let attr = if mode == UefiFileMode::CreateReadWrite && create_dir {
UefiFileAttribute::DIRECTORY
} else {
UefiFileAttribute::empty()
Expand Down
10 changes: 4 additions & 6 deletions uefi/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -35,8 +33,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::*;
2 changes: 1 addition & 1 deletion uefi/src/fs/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://en.wikipedia.org/wiki/Comparison_of_file_systems>,
/// paths should not contain the following symbols.
/// paths should not contain these symbols.
pub const CHARACTER_DENY_LIST: [Char16; 10] = unsafe {
[
NUL_16,
Expand Down
12 changes: 12 additions & 0 deletions uefi/src/proto/media/file/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down