about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-11-09 01:13:28 +0100
committerGitHub <noreply@github.com>2020-11-09 01:13:28 +0100
commit41134be153ec0e152b0bb5cf79731abdde7c4e04 (patch)
tree711a7cbc785c67e8bc87613a2d13200d2bcdb210 /library/std/src/sys
parentd69ee57f977d03644acd8fbfd1799410e2c02db3 (diff)
parent6249cda78f0cd32b60fb11702b7ffef3e3bab0b2 (diff)
downloadrust-41134be153ec0e152b0bb5cf79731abdde7c4e04.tar.gz
rust-41134be153ec0e152b0bb5cf79731abdde7c4e04.zip
Rollup merge of #78026 - sunfishcode:symlink-hard-link, r=dtolnay
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.

[implementation-defined]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/fs.rs15
1 files changed, 14 insertions, 1 deletions
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(())
 }