Implement EXDEV symlink resolution.
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
use core::{mem, ptr, result::Result as CoreResult, slice, str};
|
||||
use core::convert::TryFrom;
|
||||
use core::arch::asm;
|
||||
|
||||
use syscall::{
|
||||
self,
|
||||
data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
|
||||
PtraceEvent, Result,
|
||||
PtraceEvent, Result, Error, EMFILE,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -705,12 +706,18 @@ impl Pal for Sys {
|
||||
fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int {
|
||||
let path = path_from_c_str!(path);
|
||||
|
||||
e(canonicalize(path).and_then(|canon| {
|
||||
syscall::open(
|
||||
&canon,
|
||||
((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF),
|
||||
)
|
||||
})) as c_int
|
||||
match path::open(path, ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF)) {
|
||||
Ok(fd) => {
|
||||
match c_int::try_from(fd) {
|
||||
Ok(c_fd) => c_fd,
|
||||
Err(_) => {
|
||||
let _ = syscall::close(fd);
|
||||
e(Err(Error::new(EMFILE))) as c_int
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => e(Err(error)) as c_int,
|
||||
}
|
||||
}
|
||||
|
||||
fn pipe2(fds: &mut [c_int], flags: c_int) -> c_int {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use syscall::error::*;
|
||||
use syscall::flag::*;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::borrow::{Cow, ToOwned};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::FdGuard;
|
||||
use crate::sync::Mutex;
|
||||
|
||||
// TODO: Define in syscall
|
||||
@@ -16,7 +17,7 @@ const PATH_MAX: usize = 4096;
|
||||
/// Given a cwd of "scheme:/path", this his function will turn "foo" into "scheme:/path/foo".
|
||||
/// "/foo" will turn into "scheme:/foo". "bar:/foo" will be used directly, as it is already
|
||||
/// absolute
|
||||
pub fn canonicalize_using_cwd(cwd_opt: Option<&str>, path: &str) -> Option<String> {
|
||||
pub fn canonicalize_using_cwd<'a>(cwd_opt: Option<&str>, path: &'a str) -> Option<String> {
|
||||
let mut canon = if path.find(':').is_none() {
|
||||
let cwd = cwd_opt?;
|
||||
let path_start = cwd.find(':')? + 1;
|
||||
@@ -99,7 +100,6 @@ pub fn chdir(path: &str) -> Result<()> {
|
||||
// TODO: Check that the dir exists and is a directory.
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
pub fn clone_cwd() -> Option<Box<str>> {
|
||||
@@ -154,3 +154,32 @@ impl Drop for SignalMask {
|
||||
let _ = syscall::sigprocmask(syscall::SIG_SETMASK, Some(&self.oldset), None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(path: &str, flags: usize) -> Result<usize> {
|
||||
// TODO: SYMLOOP_MAX
|
||||
const MAX_LEVEL: usize = 64;
|
||||
|
||||
let mut resolve_buf = [0_u8; 4096];
|
||||
let mut path = path;
|
||||
|
||||
for _ in 0..MAX_LEVEL {
|
||||
let canon = canonicalize(path)?;
|
||||
match syscall::open(&*canon, flags) {
|
||||
Ok(fd) => return Ok(fd),
|
||||
Err(error) if error == Error::new(EXDEV) => {
|
||||
let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY;
|
||||
let resolve_fd = FdGuard::new(syscall::open(&*canon, resolve_flags)?);
|
||||
|
||||
let bytes_read = syscall::read(*resolve_fd, &mut resolve_buf)?;
|
||||
// TODO: make resolve_buf PATH_MAX + 1 bytes?
|
||||
if bytes_read == resolve_buf.len() { return Err(Error::new(ENAMETOOLONG)); }
|
||||
|
||||
// If the symbolic link path is non-UTF8, it cannot be opened, and is thus
|
||||
// considered a "dangling symbolic link".
|
||||
path = core::str::from_utf8(&resolve_buf[..bytes_read]).map_err(|_| Error::new(ENOENT))?;
|
||||
}
|
||||
Err(other_error) => return Err(other_error),
|
||||
}
|
||||
}
|
||||
Err(Error::new(ELOOP))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user