mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-03 02:23:20 +00:00
Define fs::hard_link
to not follow symlinks.
POSIX leaves it implementation-defined whether `link` follows symlinks. In practice, for example, on Linux it does not and on FreeBSD it does. So, switch to `linkat`, so that we can pick a behavior rather than depending on OS defaults. Pick the option to not follow symlinks. This is somewhat arbitrary, but seems the less surprising choice because hard linking is a very low-level feature which requires the source and destination to be on the same mounted filesystem, and following a symbolic link could end up in a different mounted filesystem.
This commit is contained in:
parent
a78a62fc99
commit
91a9f83dd1
@ -1701,10 +1701,13 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
||||
/// The `dst` path will be a link pointing to the `src` path. Note that systems
|
||||
/// often require these two paths to both be located on the same filesystem.
|
||||
///
|
||||
/// If `src` names a symbolic link, it is not followed. The created hard link
|
||||
/// points to the symbolic link itself.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `link` function on Unix
|
||||
/// and the `CreateHardLink` function on Windows.
|
||||
/// This function currently corresponds to the `linkat` function with no flags
|
||||
/// on Unix and the `CreateHardLink` function on Windows.
|
||||
/// Note that, this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
|
@ -1337,3 +1337,54 @@ fn metadata_access_times() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test creating hard links to symlinks.
|
||||
#[test]
|
||||
fn symlink_hard_link() {
|
||||
let tmpdir = tmpdir();
|
||||
|
||||
// Create "file", a file.
|
||||
check!(fs::File::create(tmpdir.join("file")));
|
||||
|
||||
// Create "symlink", a symlink to "file".
|
||||
check!(symlink_file("file", tmpdir.join("symlink")));
|
||||
|
||||
// Create "hard_link", a hard link to "symlink".
|
||||
check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
|
||||
|
||||
// "hard_link" should appear as a symlink.
|
||||
assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
|
||||
|
||||
// We sould be able to open "file" via any of the above names.
|
||||
let _ = check!(fs::File::open(tmpdir.join("file")));
|
||||
assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
|
||||
let _ = check!(fs::File::open(tmpdir.join("symlink")));
|
||||
let _ = check!(fs::File::open(tmpdir.join("hard_link")));
|
||||
|
||||
// Rename "file" to "file.renamed".
|
||||
check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
|
||||
|
||||
// Now, the symlink and the hard link should be dangling.
|
||||
assert!(fs::File::open(tmpdir.join("file")).is_err());
|
||||
let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
|
||||
assert!(fs::File::open(tmpdir.join("symlink")).is_err());
|
||||
assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
|
||||
|
||||
// The symlink and the hard link should both still point to "file".
|
||||
assert!(fs::read_link(tmpdir.join("file")).is_err());
|
||||
assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
|
||||
assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
|
||||
assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
|
||||
|
||||
// Remove "file.renamed".
|
||||
check!(fs::remove_file(tmpdir.join("file.renamed")));
|
||||
|
||||
// Now, we can't open the file by any name.
|
||||
assert!(fs::File::open(tmpdir.join("file")).is_err());
|
||||
assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
|
||||
assert!(fs::File::open(tmpdir.join("symlink")).is_err());
|
||||
assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
|
||||
|
||||
// "hard_link" should still appear as a symlink.
|
||||
assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
|
||||
}
|
||||
|
@ -1067,7 +1067,10 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
let src = cstr(src)?;
|
||||
let dst = cstr(dst)?;
|
||||
cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
|
||||
// Use `linkat` with `AT_FDCWD` instead of `link` as `link` leaves it
|
||||
// implmentation-defined whether it follows symlinks. Pass 0 as the
|
||||
// `linkat` flags argument so that we don't follow symlinks.
|
||||
cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user