Skip to content
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

glob: Scan current directory for first component pattern #162

Merged
merged 3 commits into from
Jun 25, 2022
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
12 changes: 6 additions & 6 deletions yash-env/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ pub trait System: Debug {
fn write(&mut self, fd: Fd, buffer: &[u8]) -> nix::Result<usize>;

/// Opens a directory for enumerating entries.
fn fdopendir(&self, fd: Fd) -> nix::Result<Box<dyn Dir>>;
fn fdopendir(&mut self, fd: Fd) -> nix::Result<Box<dyn Dir>>;

/// Opens a directory for enumerating entries.
fn opendir(&self, path: &CStr) -> nix::Result<Box<dyn Dir>>;
fn opendir(&mut self, path: &CStr) -> nix::Result<Box<dyn Dir>>;

/// Returns the current time.
#[must_use]
Expand Down Expand Up @@ -626,11 +626,11 @@ impl System for SharedSystem {
fn write(&mut self, fd: Fd, buffer: &[u8]) -> nix::Result<usize> {
self.0.borrow_mut().write(fd, buffer)
}
fn fdopendir(&self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
self.0.borrow().fdopendir(fd)
fn fdopendir(&mut self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
self.0.borrow_mut().fdopendir(fd)
}
fn opendir(&self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
self.0.borrow().opendir(path)
fn opendir(&mut self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
self.0.borrow_mut().opendir(path)
}
fn now(&self) -> Instant {
self.0.borrow().now()
Expand Down
6 changes: 3 additions & 3 deletions yash-env/src/system/real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,13 @@ impl System for RealSystem {
}
}

fn fdopendir(&self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
fn fdopendir(&mut self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
let dir = unsafe { nix::libc::fdopendir(fd.0) };
let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
Ok(Box::new(RealDir(dir)))
}

fn opendir(&self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
fn opendir(&mut self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
let dir = unsafe { nix::libc::opendir(path.as_ptr()) };
let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
Ok(Box::new(RealDir(dir)))
Expand Down Expand Up @@ -379,7 +379,7 @@ mod tests {

#[test]
fn real_system_directory_entries() {
let system = unsafe { RealSystem::new() };
let mut system = unsafe { RealSystem::new() };
let path = CString::new(".").unwrap();
let mut dir = system.opendir(&path).unwrap();
let mut count = 0;
Expand Down
86 changes: 77 additions & 9 deletions yash-env/src/system/virtual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ use crate::System;
use async_trait::async_trait;
use nix::errno::Errno;
use nix::fcntl::OFlag;
use std::borrow::Cow;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
Expand All @@ -76,6 +77,8 @@ use std::fmt::Debug;
use std::future::Future;
use std::os::raw::c_int;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use std::time::Duration;
Expand Down Expand Up @@ -194,6 +197,17 @@ impl VirtualSystem {
let mut ofd = body.open_file_description.borrow_mut();
f(&mut *ofd)
}

fn resolve_relative_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
if path.is_absolute() {
Cow::Borrowed(path)
} else {
// TODO Support changing the current working directory
let mut rebased = PathBuf::from("/");
rebased.push(path);
Cow::Owned(rebased)
}
}
}

impl Default for VirtualSystem {
Expand Down Expand Up @@ -273,9 +287,9 @@ impl System for VirtualSystem {
}

fn open(&mut self, path: &CStr, option: OFlag, mode: nix::sys::stat::Mode) -> nix::Result<Fd> {
let path = OsStr::from_bytes(path.to_bytes());
let path = self.resolve_relative_path(Path::new(OsStr::from_bytes(path.to_bytes())));
let mut state = self.state.borrow_mut();
let file = match state.file_system.get(path) {
let file = match state.file_system.get(&path) {
Ok(inode) => {
if option.contains(OFlag::O_EXCL) {
return Err(Errno::EEXIST);
Expand All @@ -297,7 +311,7 @@ impl System for VirtualSystem {
// TODO Apply umask
inode.permissions = Mode(mode.bits());
let inode = Rc::new(RefCell::new(inode));
state.file_system.save(path, Rc::clone(&inode))?;
state.file_system.save(&path, Rc::clone(&inode))?;
inode
}
Err(errno) => return Err(errno),
Expand Down Expand Up @@ -353,19 +367,22 @@ impl System for VirtualSystem {
self.with_open_file_description_mut(fd, |ofd| ofd.write(buffer))
}

fn fdopendir(&self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
fn fdopendir(&mut self, fd: Fd) -> nix::Result<Box<dyn Dir>> {
self.with_open_file_description(fd, |ofd| {
let inode = ofd.i_node();
let dir = VirtualDir::try_from(&inode.borrow().body)?;
Ok(Box::new(dir) as Box<dyn Dir>)
})
}

fn opendir(&self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
let path = OsStr::from_bytes(path.to_bytes());
let inode = self.state.borrow().file_system.get(path)?;
let dir = VirtualDir::try_from(&inode.borrow().body)?;
Ok(Box::new(dir))
fn opendir(&mut self, path: &CStr) -> nix::Result<Box<dyn Dir>> {
// TODO Should use O_SEARCH, but currently it is only supported on netbsd
let fd = self.open(
path,
OFlag::O_RDONLY | OFlag::O_DIRECTORY,
nix::sys::stat::Mode::empty(),
)?;
self.fdopendir(fd)
}

/// Returns `now` in [`SystemState`].
Expand Down Expand Up @@ -728,6 +745,7 @@ mod tests {
use assert_matches::assert_matches;
use futures_executor::LocalPool;
use std::ffi::CString;
use std::ffi::OsString;

impl Executor for futures_executor::LocalSpawner {
fn spawn(
Expand Down Expand Up @@ -1033,6 +1051,29 @@ mod tests {
assert_eq!(result, Err(Errno::ENOTDIR));
}

#[test]
fn open_default_working_directory() {
// The default working directory is the root directory.
let mut system = VirtualSystem::new();

let writer = system.open(
&CString::new("/dir/file").unwrap(),
OFlag::O_WRONLY | OFlag::O_CREAT,
nix::sys::stat::Mode::all(),
);
system.write(writer.unwrap(), &[1, 2, 3, 42]).unwrap();

let reader = system.open(
&CString::new("./dir/file").unwrap(),
OFlag::O_RDONLY,
nix::sys::stat::Mode::empty(),
);
let mut buffer = [0; 10];
let count = system.read(reader.unwrap(), &mut buffer).unwrap();
assert_eq!(count, 4);
assert_eq!(buffer[0..4], [1, 2, 3, 42]);
}

#[test]
fn close() {
let mut system = VirtualSystem::new();
Expand All @@ -1045,6 +1086,33 @@ mod tests {
assert_eq!(result, Ok(()));
}

#[test]
fn opendir_default_working_directory() {
// The default working directory is the root directory.
let mut system = VirtualSystem::new();

let _ = system.open(
&CString::new("/dir/file").unwrap(),
OFlag::O_WRONLY | OFlag::O_CREAT,
nix::sys::stat::Mode::all(),
);

let mut dir = system.opendir(&CString::new("./dir").unwrap()).unwrap();
let mut files = Vec::new();
while let Some(entry) = dir.next().unwrap() {
files.push(entry.name.to_os_string());
}
files.sort_unstable();
assert_eq!(
files[..],
[
OsString::from("."),
OsString::from(".."),
OsString::from("file")
]
);
}

#[test]
fn select_regular_file_is_always_ready() {
let mut system = VirtualSystem::new();
Expand Down
2 changes: 2 additions & 0 deletions yash-env/src/system/virtual/file_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ where
}
}

// TODO impl Drop for VirtualDir: close backing file descriptor

#[cfg(test)]
mod tests {
use super::*;
Expand Down
3 changes: 1 addition & 2 deletions yash-semantics/src/expansion/glob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ impl SearchEnv<'_> {
}
Some(Err(pattern)) => {
let dir_path = if self.prefix.is_empty() {
// TODO CString::new(".")
CString::new("/").unwrap()
CString::new(".").unwrap()
} else {
// TODO What if prefix contains a nul byte?
CString::new(self.prefix.as_str()).unwrap()
Expand Down