Explorar o código

Implement EXDEV symlink resolution.

4lDO2 %!s(int64=2) %!d(string=hai) anos
pai
achega
36775eeb1b
Modificáronse 2 ficheiros con 46 adicións e 10 borrados
  1. 14 7
      src/platform/redox/mod.rs
  2. 32 3
      src/platform/redox/path.rs

+ 14 - 7
src/platform/redox/mod.rs

@@ -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 {

+ 32 - 3
src/platform/redox/path.rs

@@ -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))
+}