abi: merge remove() and remove_directory()

This commit is contained in:
Mark Poliakov 2025-01-06 20:19:32 +02:00
parent f978a6b287
commit f1256e262b
11 changed files with 198 additions and 124 deletions

View File

@ -1,6 +1,6 @@
use yggdrasil_abi::{
error::Error,
io::{AccessMode, FileMode, GroupId, OpenOptions, UserId},
io::{AccessMode, FileMode, GroupId, OpenOptions, RemoveFlags, UserId},
path::{Path, PathBuf},
};
@ -291,32 +291,6 @@ impl IoContext {
Ok(())
}
fn remove_entry<P: AsRef<Path>>(
&mut self,
at: Option<NodeRef>,
path: P,
directory: bool,
) -> Result<(), Error> {
let path = path.as_ref();
let (parent, name) = path.trim_end_separators().split_right();
if name.is_empty() {
log::warn!("Tried to remove weird path: {:?}", path);
return Err(Error::DoesNotExist);
}
let parent = self.find(at, parent, false)?;
let access = self.check_access(&parent, AccessMode::WRITE)?;
if directory {
// parent.remove_directory(name, access)
todo!()
} else {
let filename = Filename::new(name)?;
parent.remove_file(filename, access)
}
}
pub fn rename(
&mut self,
src_at: Option<NodeRef>,
@ -374,12 +348,37 @@ impl IoContext {
}
/// Removes a device or regular file node at given path
pub fn remove_file<P: AsRef<Path>>(
pub fn remove<P: AsRef<Path>>(
&mut self,
at: Option<NodeRef>,
path: P,
flags: RemoveFlags,
) -> Result<(), Error> {
self.remove_entry(at, path, false)
let path = path.as_ref();
let (parent, name) = path.trim_end_separators().split_right();
if name.is_empty() {
log::warn!("Tried to remove weird path: {:?}", path);
return Err(Error::DoesNotExist);
}
let parent = self.find(at, parent, false)?;
let access = self.check_access(&parent, AccessMode::WRITE)?;
let filename = Filename::new(name)?;
let child = parent.lookup_or_load(filename, access.clone())?;
if child.is_directory() {
if !flags.contains_any(RemoveFlags::DIRECTORY | RemoveFlags::DIRECTORY_ONLY) {
return Err(Error::IsADirectory);
}
} else {
if flags.contains_any(RemoveFlags::DIRECTORY_ONLY) {
return Err(Error::NotADirectory);
}
}
parent.remove_file(filename, access)
}
/// Locates a [crate::Node] pointed to by given [Path]

View File

@ -4,7 +4,7 @@ pub(crate) use abi::{
error::Error,
io::{
AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, MountOptions, OpenOptions,
PipeOptions, PollControl, RawFd, TerminalOptions, TerminalSize, TimerOptions,
PipeOptions, PollControl, RawFd, RemoveFlags, TerminalOptions, TerminalSize, TimerOptions,
UnmountOptions,
},
mem::{MappingFlags, MappingSource},

View File

@ -5,8 +5,8 @@ use abi::{
io::{
AccessMode, ChannelPublisherId, DeviceRequest, DirectoryEntry, FileAttr, FileControl,
FileMetadataUpdate, FileMode, FileSync, FilesystemControl, MessageDestination, OpenOptions,
PipeOptions, PollControl, RawFd, ReceivedMessageMetadata, Rename, SeekFrom, SentMessage,
TerminalOptions, TerminalSize, TimerOptions,
PipeOptions, PollControl, RawFd, ReceivedMessageMetadata, RemoveFlags, Rename, SeekFrom,
SentMessage, TerminalOptions, TerminalSize, TimerOptions,
},
process::{ProcessWait, WaitFlags},
};
@ -176,16 +176,12 @@ pub(crate) fn create_symlink(at: Option<RawFd>, target: &str, path: &str) -> Res
})
}
pub(crate) fn remove_directory(_at: Option<RawFd>, _path: &str) -> Result<(), Error> {
todo!()
}
pub(crate) fn remove(at: Option<RawFd>, path: &str) -> Result<(), Error> {
pub(crate) fn remove(at: Option<RawFd>, path: &str, options: RemoveFlags) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process();
run_with_io_at(&process, at, |at, mut io| {
io.ioctx_mut().remove_file(Some(at), path)?;
io.ioctx_mut().remove(Some(at), path, options)?;
Ok(())
})
}

View File

@ -32,6 +32,11 @@ bitfield FileMode(u32) {
USER_READ: 8,
}
bitfield RemoveFlags(u32) {
DIRECTORY: 1,
DIRECTORY_ONLY: 2,
}
bitfield FileSync(u32) {
DATA: 0,
METADATA: 1,

View File

@ -125,11 +125,10 @@ syscall write_at(fd: RawFd, pos: u64, data: &[u8]) -> Result<usize>;
syscall open_directory(at: Option<RawFd>, path: &str) -> Result<RawFd>;
syscall read_directory_entries(fd: RawFd, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize>;
syscall create_directory(at: Option<RawFd>, path: &str, mode: FileMode) -> Result<()>;
syscall remove_directory(at: Option<RawFd>, path: &str) -> Result<()>;
syscall create_symlink(at: Option<RawFd>, target: &str, path: &str) -> Result<()>;
syscall read_link(at: Option<RawFd>, path: &str, buf: &mut [u8]) -> Result<usize>;
syscall remove(at: Option<RawFd>, path: &str) -> Result<()>;
syscall remove(at: Option<RawFd>, path: &str, flags: RemoveFlags) -> Result<()>;
syscall rename(rename: &Rename<'_>) -> Result<()>;
syscall clone_fd(source: RawFd, target: Option<RawFd>) -> Result<RawFd>;
syscall update_metadata(at: Option<RawFd>, path: &str, update: &FileMetadataUpdate) -> Result<()>;

View File

@ -9,7 +9,8 @@ mod terminal;
pub use crate::generated::{
AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, FileType, GroupId, MountOptions,
OpenOptions, PipeOptions, PollControl, RawFd, TimerOptions, UnmountOptions, UserId,
OpenOptions, PipeOptions, PollControl, RawFd, RemoveFlags, TimerOptions, UnmountOptions,
UserId,
};
pub use channel::{ChannelPublisherId, MessageDestination, ReceivedMessageMetadata, SentMessage};
pub use device::{DeviceRequest, Framebuffer};

View File

@ -1,71 +0,0 @@
#![allow(missing_docs)]
pub mod device {
pub use abi::io::{DeviceRequest, Framebuffer, MountOptions, UnmountOptions};
}
pub mod terminal {
pub use abi::io::{
TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions,
TerminalSize,
};
}
pub mod message_channel {
pub use abi::io::{
ChannelPublisherId, MessageDestination, ReceivedMessageMetadata, SentMessage,
};
}
pub mod poll {
pub use abi::io::PollControl;
}
use core::str::FromStr;
pub use abi::io::{
AccessMode, DirectoryEntry, FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode,
FileSync, FileTimesUpdate, FileType, OpenOptions, PipeOptions, RawFd, Rename, SeekFrom,
TimerOptions,
};
use abi::{error::Error, io::DeviceRequest, process::ProcessOption, util::FixedString};
use alloc::string::String;
pub fn current_exe<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
todo!()
}
pub fn home_directory<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
todo!()
}
pub fn current_directory<T, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> {
let mut option = ProcessOption::Directory(FixedString::empty());
unsafe { crate::sys::get_process_option(&mut option) }?;
let ProcessOption::Directory(path) = &option else {
unreachable!()
};
Ok(mapper(path.as_ref()))
}
pub fn current_directory_string() -> Result<String, Error> {
current_directory(|s| s.into())
}
pub fn set_current_directory(path: &str) -> Result<(), Error> {
let mut option = ProcessOption::Directory(FixedString::from_str(path)?);
unsafe { crate::sys::set_process_option(&mut option) }?;
Ok(())
}
pub fn make_temp_directory(_template: &mut [u8]) -> Result<(), Error> {
todo!()
}
pub fn is_terminal(f: RawFd) -> bool {
let mut option = DeviceRequest::IsTerminal(false);
let res = unsafe { crate::sys::device_request(f, &mut option) };
matches!((res, option), (Ok(()), DeviceRequest::IsTerminal(true)))
}

53
lib/runtime/src/io/mod.rs Normal file
View File

@ -0,0 +1,53 @@
#![allow(missing_docs)]
pub mod device {
pub use abi::io::{DeviceRequest, Framebuffer, MountOptions, UnmountOptions};
}
pub mod terminal {
pub use abi::io::{
TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions,
TerminalSize,
};
}
pub mod message_channel {
pub use abi::io::{
ChannelPublisherId, MessageDestination, ReceivedMessageMetadata, SentMessage,
};
}
pub mod poll {
pub use abi::io::PollControl;
}
pub use abi::io::{
AccessMode, DirectoryEntry, FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode,
FileSync, FileTimesUpdate, FileType, OpenOptions, PipeOptions, RawFd, RemoveFlags, Rename,
SeekFrom, TimerOptions,
};
pub mod paths;
pub use paths::*;
use abi::error::Error;
use device::DeviceRequest;
pub fn is_terminal(f: RawFd) -> bool {
let mut option = DeviceRequest::IsTerminal(false);
let res = unsafe { crate::sys::device_request(f, &mut option) };
matches!((res, option), (Ok(()), DeviceRequest::IsTerminal(true)))
}
pub fn remove_file(at: Option<RawFd>, path: &str) -> Result<(), Error> {
unsafe { crate::sys::remove(at, path, RemoveFlags::empty()) }
}
pub fn remove_directory(at: Option<RawFd>, path: &str) -> Result<(), Error> {
unsafe { crate::sys::remove(at, path, RemoveFlags::DIRECTORY_ONLY) }
}
pub fn remove_directory_recursive(_at: Option<RawFd>, _path: &str) -> Result<(), Error> {
todo!()
}

View File

@ -0,0 +1,40 @@
use core::str::FromStr;
use abi::{error::Error, process::ProcessOption, util::FixedString};
use alloc::string::String;
pub fn current_exe<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
todo!()
}
pub fn home_directory<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
todo!()
}
pub fn current_directory<T, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> {
let mut option = ProcessOption::Directory(FixedString::empty());
unsafe { crate::sys::get_process_option(&mut option) }?;
let ProcessOption::Directory(path) = &option else {
unreachable!()
};
Ok(mapper(path.as_ref()))
}
pub fn current_directory_string() -> Result<String, Error> {
current_directory(|s| s.into())
}
pub fn set_current_directory(path: &str) -> Result<(), Error> {
let mut option = ProcessOption::Directory(FixedString::from_str(path)?);
unsafe { crate::sys::set_process_option(&mut option) }?;
Ok(())
}
pub fn make_temp_directory(_template: &mut [u8]) -> Result<(), Error> {
todo!()
}
pub fn canonicalize<T, F: FnOnce(&str) -> T>(_path: &str, _mapper: F) -> Result<T, Error> {
todo!()
}

View File

@ -17,8 +17,8 @@ mod generated {
error::Error,
io::{
AccessMode, ChannelPublisherId, DirectoryEntry, FileAttr, FileMode, FileSync,
MountOptions, OpenOptions, PipeOptions, PollControl, RawFd, TerminalOptions,
TerminalSize, TimerOptions, UnmountOptions,
MountOptions, OpenOptions, PipeOptions, PollControl, RawFd, RemoveFlags,
TerminalOptions, TerminalSize, TimerOptions, UnmountOptions,
},
mem::{MappingFlags, MappingSource},
net::SocketType,

View File

@ -1,5 +1,10 @@
#![feature(io_error_more)]
use std::{io, fs, path::{Path, PathBuf}, process::ExitCode};
use std::{
fs, io::{self, stdin, stdout, Write},
path::{Path, PathBuf},
process::ExitCode,
};
use clap::Parser;
@ -7,16 +12,59 @@ use clap::Parser;
struct Args {
#[arg(short)]
recurse: bool,
#[arg(short)]
verbose: bool,
#[arg(short)]
force: bool,
#[clap(required = true)]
paths: Vec<PathBuf>
paths: Vec<PathBuf>,
}
fn rm<P: AsRef<Path>>(path: P, recurse: bool) -> bool {
fn read_answer() -> Result<bool, io::Error> {
let stdin = stdin();
for _ in 0..3 {
let mut buffer = String::new();
let len = stdin.read_line(&mut buffer)?;
if len == 0 {
return Ok(false);
}
match buffer.as_str().trim() {
"y" | "Y" | "" => return Ok(true),
"n" | "N" => return Ok(false),
_ => {
eprintln!("Please answer `y` (default) or `n`");
}
}
}
Ok(false)
}
fn rm<P: AsRef<Path>>(path: P, args: &Args) -> bool {
let path = path.as_ref();
println!("remove {}", path.display());
if !args.force {
print!("Remove {} [Y/n]? ", path.display());
stdout().flush().ok();
match read_answer() {
Ok(true) => (),
Ok(false) => return true,
Err(err) => {
eprintln!("stdin: {err}");
return false;
}
}
}
if args.verbose {
println!("remove {}", path.display());
}
match fs::remove_file(path) {
Err(e) if recurse && e.kind() == io::ErrorKind::IsADirectory => {
Err(e) if args.recurse && e.kind() == io::ErrorKind::IsADirectory => {
let readdir = match fs::read_dir(path) {
Ok(dir) => dir,
Err(error) => {
@ -37,7 +85,11 @@ fn rm<P: AsRef<Path>>(path: P, recurse: bool) -> bool {
}
};
result &= rm(entry.path(), recurse);
if entry.file_name() == ".." || entry.file_name() == "." {
continue;
}
result &= rm(entry.path(), args);
}
if result {
@ -48,12 +100,12 @@ fn rm<P: AsRef<Path>>(path: P, recurse: bool) -> bool {
}
result
},
}
Err(e) => {
eprintln!("{}: {}", path.display(), e);
false
}
Ok(_) => true
Ok(_) => true,
}
}
@ -61,8 +113,8 @@ fn main() -> ExitCode {
let args = Args::parse();
let mut result = ExitCode::SUCCESS;
for arg in args.paths {
if !rm(arg, args.recurse) {
for arg in args.paths.iter() {
if !rm(arg, &args) {
result = ExitCode::FAILURE;
}
}