summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorDan Gohman <dev@sunfishcode.online>2020-10-16 09:09:20 -0700
committerDan Gohman <dev@sunfishcode.online>2020-10-16 12:05:49 -0700
commit91a9f83dd1d73cfd451f81306361df3fafad84a5 (patch)
treeb3eff22b5672b6fbd8099552216dbc58c65ee78f /library/std/src/sys
parenta78a62fc996ba16f7a111c99520b23f77029f4eb (diff)
downloadrust-91a9f83dd1d73cfd451f81306361df3fafad84a5.tar.gz
rust-91a9f83dd1d73cfd451f81306361df3fafad84a5.zip
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.
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/fs.rs5
1 files changed, 4 insertions, 1 deletions
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 819e8ef1841..88693e4786c 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -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(())
 }