about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-11-09 00:36:58 +0000
committerbors <bors@rust-lang.org>2020-11-09 00:36:58 +0000
commitfe8f02690804d5ee696bd3bca9515f5f71857e3b (patch)
tree9cc07f8445977fac41e92110426c83ae0a8b7aa9 /library/std/src
parent1773f60ea5d42e86b8fdf78d2fc5221ead222bc1 (diff)
parent92adac9b10e4890124b80eecbcf44dd22e9c92e6 (diff)
downloadrust-fe8f02690804d5ee696bd3bca9515f5f71857e3b.tar.gz
rust-fe8f02690804d5ee696bd3bca9515f5f71857e3b.zip
Auto merge of #78889 - Dylan-DPC:rollup-6zjhahf, r=Dylan-DPC
Rollup of 12 pull requests

Successful merges:

 - #77640 (Refactor IntErrorKind to avoid "underflow" terminology)
 - #78026 (Define `fs::hard_link` to not follow symlinks.)
 - #78114 (Recognize `private_intra_doc_links` as a lint)
 - #78228 (Promote aarch64-unknown-linux-gnu to Tier 1)
 - #78345 (Fix handling of item names for HIR)
 - #78437 (BTreeMap: stop mistaking node for an orderly place)
 - #78476 (fix some incorrect aliasing in the BTree)
 - #78674 (inliner: Use substs_for_mir_body)
 - #78748 (Implement destructuring assignment for tuples)
 - #78868 (Fix tab focus on restyled switches)
 - #78878 (Avoid overlapping cfg attributes when both macOS and aarch64)
 - #78882 (Nicer hunk headers for rust files)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/fs.rs8
-rw-r--r--library/std/src/fs/tests.rs51
-rw-r--r--library/std/src/sys/unix/fs.rs15
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs25
4 files changed, 86 insertions, 13 deletions
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 161bfe3795c..c256f556b3c 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -1701,10 +1701,14 @@ 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 platform-specific whether the symbolic
+/// link is followed. On platforms where it's possible to not follow it, it is
+/// not followed, and 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
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 38fd470a1c3..0642dca8e48 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1336,3 +1336,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());
+}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index d27d6e2c565..96594095cc3 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1081,7 +1081,20 @@ 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()) })?;
+    cfg_if::cfg_if! {
+        if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] {
+            // VxWorks, Redox, and old versions of Android lack `linkat`, so use
+            // `link` instead. POSIX leaves it implementation-defined whether
+            // `link` follows symlinks, so rely on the `symlink_hard_link` test
+            // in library/std/src/fs/tests.rs to check the behavior.
+            cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
+        } else {
+            // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives
+            // us a flag to specify how symlinks should be handled. Pass 0 as
+            // the flags argument, meaning don't follow symlinks.
+            cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?;
+        }
+    }
     Ok(())
 }
 
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
index e72fbf0beb4..10aa34e9443 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -14,17 +14,22 @@ macro_rules! t {
     };
 }
 
-// See #14232 for more information, but it appears that signal delivery to a
-// newly spawned process may just be raced in the macOS, so to prevent this
-// test from being flaky we ignore it on macOS.
 #[test]
-#[cfg_attr(target_os = "macos", ignore)]
-// When run under our current QEMU emulation test suite this test fails,
-// although the reason isn't very clear as to why. For now this test is
-// ignored there.
-#[cfg_attr(target_arch = "arm", ignore)]
-#[cfg_attr(target_arch = "aarch64", ignore)]
-#[cfg_attr(target_arch = "riscv64", ignore)]
+#[cfg_attr(
+    any(
+        // See #14232 for more information, but it appears that signal delivery to a
+        // newly spawned process may just be raced in the macOS, so to prevent this
+        // test from being flaky we ignore it on macOS.
+        target_os = "macos",
+        // When run under our current QEMU emulation test suite this test fails,
+        // although the reason isn't very clear as to why. For now this test is
+        // ignored there.
+        target_arch = "arm",
+        target_arch = "aarch64",
+        target_arch = "riscv64",
+    ),
+    ignore
+)]
 fn test_process_mask() {
     unsafe {
         // Test to make sure that a signal mask does not get inherited.