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

Implement fs::realpath #11734

Closed
wants to merge 1 commit into from
Closed
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
33 changes: 11 additions & 22 deletions src/librustc/metadata/filesearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// except according to those terms.

use std::cell::RefCell;
use std::option;
use std::os;
use std::io;
use std::io::fs;
Expand Down Expand Up @@ -160,32 +159,22 @@ fn make_rustpkg_target_lib_path(dir: &Path,
}

pub fn get_or_default_sysroot() -> Path {
// Follow symlinks. If the resolved path is relative, make it absolute.
fn canonicalize(path: Option<Path>) -> Option<Path> {
path.and_then(|mut path|
match io::io_error::cond.trap(|_| ()).inside(|| fs::readlink(&path)) {
Some(canon) => {
if canon.is_absolute() {
Some(canon)
} else {
path.pop();
Some(path.join(canon))
}
},
None => Some(path),
})
}

match canonicalize(os::self_exe_name()) {
option::Some(p) => { let mut p = p; p.pop(); p.pop(); p }
option::None => fail!("can't determine value for sysroot")
// Use the realpath function order to get the true real path of `rustc`, we
// want to avoid dealing with symlinks.
match os::self_exe_path() {
Some(p) => {
let mut p = fs::realpath(&p);
p.pop();
p
}
None => fail!("can't determine value for sysroot")
}
}

fn get_sysroot(maybe_sysroot: &Option<@Path>) -> @Path {
match *maybe_sysroot {
option::Some(sr) => sr,
option::None => @get_or_default_sysroot()
Some(sr) => sr,
None => @get_or_default_sysroot()
}
}

Expand Down
104 changes: 103 additions & 1 deletion src/libstd/io/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use super::{SeekStyle, Read, Write, Open, IoError, Truncate,
use rt::rtio::{RtioFileStream, IoFactory, LocalIo};
use io;
use option::{Some, None, Option};
use os;
use result::{Ok, Err};
use path;
use path::{Path, GenericPath};
Expand Down Expand Up @@ -437,6 +438,65 @@ pub fn readlink(path: &Path) -> Option<Path> {
LocalIo::maybe_raise(|io| io.fs_readlink(&path.to_c_str()))
}

/// Returns an absolute path in the filesystem that `path` points to. The
/// returned path does not contain any symlinks in its hierarchy.
///
/// # Errors
///
/// If this function encounters errors, it will raise the error on the
/// `io_error` condition and the original path argument is returned.
pub fn realpath(original: &Path) -> Path {
static MAX_LINKS_FOLLOWED: uint = 256;
let mut io = match LocalIo::borrow() {
Some(io) => io,
None => {
io_error::cond.raise(io::standard_error(io::IoUnavailable));
return original.clone()
}
};
let original = os::make_absolute(original);

let result = original.root_path();
let mut result = result.expect("make_absolute has no root_path");
let mut followed = 0;

for part in original.components() {
result.push(part);

loop {
if followed == MAX_LINKS_FOLLOWED {
io_error::cond.raise(io::IoError {
kind: io::OtherIoError,
desc: "too many symlinks",
detail: None,
});
return original.clone();
}

let to_test = result.to_c_str();
match io.get().fs_lstat(&to_test) {
Err(..) => break,
Ok(ref stat) if stat.kind != io::TypeSymlink => break,
Ok(..) => {
followed += 1;
match io.get().fs_readlink(&to_test) {
Ok(path) => {
result.pop();
result.push(path);
}
Err(e) => {
io_error::cond.raise(e);
return original.clone();
}
}
}
}
}
}

return result;
}

/// Create a new, empty directory at the provided path
///
/// # Example
Expand Down Expand Up @@ -704,7 +764,7 @@ mod test {
use str;
use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive,
mkdir_recursive, copy, unlink, stat, symlink, link,
readlink, chmod, lstat, change_file_times};
readlink, chmod, lstat, change_file_times, realpath};
use util;
use path::Path;
use io;
Expand Down Expand Up @@ -1252,4 +1312,46 @@ mod test {
Err(..) => {}
}
}

iotest!(fn realpath_works() {
let tmpdir = tmpdir();
let tmpdir = realpath(tmpdir.path());
let file = tmpdir.join("test");
let dir = tmpdir.join("test2");
let link = dir.join("link");
let linkdir = tmpdir.join("test3");

File::create(&file);
mkdir(&dir, io::UserRWX);
symlink(&file, &link);
symlink(&dir, &linkdir);

assert_eq!(realpath(&tmpdir), tmpdir);
assert_eq!(realpath(&file), file);
assert_eq!(realpath(&link), file);
assert_eq!(realpath(&linkdir), dir);
assert_eq!(realpath(&linkdir.join("link")), file);
} #[ignore(cfg(windows))])

iotest!(fn realpath_works_tricky() {
let tmpdir = tmpdir();
let tmpdir = realpath(tmpdir.path());

let a = tmpdir.join("a");
let b = a.join("b");
let c = b.join("c");
let d = a.join("d");
let e = d.join("e");
let f = a.join("f");

mkdir_recursive(&b, io::UserRWX);
mkdir_recursive(&d, io::UserRWX);
File::create(&f);
symlink(&Path::new("../d/e"), &c);
symlink(&Path::new("../f"), &e);

assert_eq!(realpath(&c), f);
assert_eq!(realpath(&e), f);

} #[ignore(cfg(windows))])
}