Skip to content

Commit dfd7b8d

Browse files
committed
Auto merge of #85953 - inquisitivecrystal:weak-linkat-in-fs-hardlink, r=joshtriplett
Fix linker error Currently, `fs::hard_link` determines whether platforms have `linkat` based on the OS, and uses `link` if they don't. However, this heuristic does not work well if a platform provides `linkat` on newer versions but not on older ones. On old MacOS, this currently causes a linking error. This commit fixes `fs::hard_link` by telling it to use `weak!` on macOS. This means that, on that operating system, we now check for `linkat` at runtime and use `link` if it is not available. Fixes #80804. `@rustbot` label T-libs-impl
2 parents 432e145 + 5999a5f commit dfd7b8d

File tree

10 files changed

+91
-17
lines changed

10 files changed

+91
-17
lines changed

library/std/src/fs.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1736,8 +1736,11 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
17361736
///
17371737
/// # Platform-specific behavior
17381738
///
1739-
/// This function currently corresponds to the `linkat` function with no flags
1740-
/// on Unix and the `CreateHardLink` function on Windows.
1739+
/// This function currently corresponds the `CreateHardLink` function on Windows.
1740+
/// On most Unix systems, it corresponds to the `linkat` function with no flags.
1741+
/// On Android, VxWorks, and Redox, it instead corresponds to the `link` function.
1742+
/// On MacOS, it uses the `linkat` function if it is available, but on very old
1743+
/// systems where `linkat` is not available, `link` is selected at runtime instead.
17411744
/// Note that, this [may change in the future][changes].
17421745
///
17431746
/// [changes]: io#platform-specific-behavior

library/std/src/fs/tests.rs

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ use crate::os::unix::fs::symlink as symlink_junction;
1919
use crate::os::windows::fs::{symlink_dir, symlink_file};
2020
#[cfg(windows)]
2121
use crate::sys::fs::symlink_junction;
22+
#[cfg(target_os = "macos")]
23+
use crate::sys::weak::weak;
24+
#[cfg(target_os = "macos")]
25+
use libc::{c_char, c_int};
2226

2327
macro_rules! check {
2428
($e:expr) => {
@@ -79,6 +83,17 @@ pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
7983
}
8084
}
8185

86+
#[cfg(target_os = "macos")]
87+
fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
88+
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
89+
linkat.get().is_some()
90+
}
91+
92+
#[cfg(not(target_os = "macos"))]
93+
fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
94+
return true;
95+
}
96+
8297
#[test]
8398
fn file_test_io_smoke_test() {
8499
let message = "it's alright. have a good time";
@@ -1347,6 +1362,9 @@ fn symlink_hard_link() {
13471362
if !got_symlink_permission(&tmpdir) {
13481363
return;
13491364
};
1365+
if !able_to_not_follow_symlinks_while_hard_linking() {
1366+
return;
1367+
}
13501368

13511369
// Create "file", a file.
13521370
check!(fs::File::create(tmpdir.join("file")));

library/std/src/sys/unix/android.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
2222
use libc::{ftruncate, pread, pwrite};
2323

24-
use super::{cvt, cvt_r};
24+
use super::{cvt, cvt_r, weak::weak};
2525
use crate::io;
2626

2727
// The `log2` and `log2f` functions apparently appeared in android-18, or at

library/std/src/sys/unix/fs.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,23 @@ use crate::sys::time::SystemTime;
1212
use crate::sys::{cvt, cvt_r};
1313
use crate::sys_common::{AsInner, FromInner};
1414

15+
#[cfg(any(
16+
all(target_os = "linux", target_env = "gnu"),
17+
target_os = "macos",
18+
target_os = "ios",
19+
))]
20+
use crate::sys::weak::syscall;
21+
#[cfg(target_os = "macos")]
22+
use crate::sys::weak::weak;
23+
1524
use libc::{c_int, mode_t};
1625

26+
#[cfg(any(
27+
target_os = "macos",
28+
target_os = "ios",
29+
all(target_os = "linux", target_env = "gnu")
30+
))]
31+
use libc::c_char;
1732
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
1833
use libc::dirfd;
1934
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
@@ -92,7 +107,7 @@ cfg_has_statx! {{
92107
// Default `stat64` contains no creation time.
93108
unsafe fn try_statx(
94109
fd: c_int,
95-
path: *const libc::c_char,
110+
path: *const c_char,
96111
flags: i32,
97112
mask: u32,
98113
) -> Option<io::Result<FileAttr>> {
@@ -107,7 +122,7 @@ cfg_has_statx! {{
107122
syscall! {
108123
fn statx(
109124
fd: c_int,
110-
pathname: *const libc::c_char,
125+
pathname: *const c_char,
111126
flags: c_int,
112127
mask: libc::c_uint,
113128
statxbuf: *mut libc::statx
@@ -756,7 +771,7 @@ impl File {
756771
cfg_has_statx! {
757772
if let Some(ret) = unsafe { try_statx(
758773
fd,
759-
b"\0" as *const _ as *const libc::c_char,
774+
b"\0" as *const _ as *const c_char,
760775
libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
761776
libc::STATX_ALL,
762777
) } {
@@ -1087,15 +1102,28 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
10871102
let link = cstr(link)?;
10881103
cfg_if::cfg_if! {
10891104
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] {
1090-
// VxWorks, Redox, and old versions of Android lack `linkat`, so use
1091-
// `link` instead. POSIX leaves it implementation-defined whether
1092-
// `link` follows symlinks, so rely on the `symlink_hard_link` test
1093-
// in library/std/src/fs/tests.rs to check the behavior.
1105+
// VxWorks and Redox lack `linkat`, so use `link` instead. POSIX leaves
1106+
// it implementation-defined whether `link` follows symlinks, so rely on the
1107+
// `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1108+
// Android has `linkat` on newer versions, but we happen to know `link`
1109+
// always has the correct behavior, so it's here as well.
10941110
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1111+
} else if #[cfg(target_os = "macos")] {
1112+
// On MacOS, older versions (<=10.9) lack support for linkat while newer
1113+
// versions have it. We want to use linkat if it is available, so we use weak!
1114+
// to check. `linkat` is preferable to `link` ecause it gives us a flag to
1115+
// specify how symlinks should be handled. We pass 0 as the flags argument,
1116+
// meaning it shouldn't follow symlinks.
1117+
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
1118+
1119+
if let Some(f) = linkat.get() {
1120+
cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1121+
} else {
1122+
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1123+
};
10951124
} else {
1096-
// Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives
1097-
// us a flag to specify how symlinks should be handled. Pass 0 as
1098-
// the flags argument, meaning don't follow symlinks.
1125+
// Where we can, use `linkat` instead of `link`; see the comment above
1126+
// this one for details on why.
10991127
cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
11001128
}
11011129
}
@@ -1278,7 +1306,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
12781306
fn fclonefileat(
12791307
srcfd: libc::c_int,
12801308
dst_dirfd: libc::c_int,
1281-
dst: *const libc::c_char,
1309+
dst: *const c_char,
12821310
flags: libc::c_int
12831311
) -> libc::c_int
12841312
}

library/std/src/sys/unix/kernel_copy.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
6161
use crate::ptr;
6262
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
6363
use crate::sys::cvt;
64+
use crate::sys::weak::syscall;
6465
use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
6566

6667
#[cfg(test)]

library/std/src/sys/unix/os.rs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ use crate::sys::memchr;
2323
use crate::sys_common::rwlock::{StaticRWLock, StaticRWLockReadGuard};
2424
use crate::vec;
2525

26+
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
27+
use crate::sys::weak::weak;
28+
2629
use libc::{c_char, c_int, c_void};
2730

2831
const TMPBUF_SZ: usize = 128;

library/std/src/sys/unix/process/process_unix.rs

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ use crate::sys;
99
use crate::sys::cvt;
1010
use crate::sys::process::process_common::*;
1111

12+
#[cfg(any(
13+
target_os = "macos",
14+
target_os = "freebsd",
15+
all(target_os = "linux", target_env = "gnu"),
16+
all(target_os = "linux", target_env = "musl"),
17+
))]
18+
use crate::sys::weak::weak;
19+
1220
#[cfg(target_os = "vxworks")]
1321
use libc::RTP_ID as pid_t;
1422

library/std/src/sys/unix/rand.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ mod imp {
2525
use crate::fs::File;
2626
use crate::io::Read;
2727

28+
#[cfg(any(target_os = "linux", target_os = "android"))]
29+
use crate::sys::weak::syscall;
30+
2831
#[cfg(any(target_os = "linux", target_os = "android"))]
2932
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
3033
// A weak symbol allows interposition, e.g. for perf measurements that want to
@@ -108,6 +111,7 @@ mod imp {
108111
use crate::fs::File;
109112
use crate::io::Read;
110113
use crate::sys::os::errno;
114+
use crate::sys::weak::weak;
111115
use libc::{c_int, c_void, size_t};
112116

113117
fn getentropy_fill_bytes(v: &mut [u8]) -> bool {

library/std/src/sys/unix/thread.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::ptr;
77
use crate::sys::{os, stack_overflow};
88
use crate::time::Duration;
99

10+
#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
11+
use crate::sys::weak::weak;
1012
#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))]
1113
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
1214
#[cfg(target_os = "l4re")]

library/std/src/sys/unix/weak.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ use crate::marker;
2626
use crate::mem;
2727
use crate::sync::atomic::{self, AtomicUsize, Ordering};
2828

29-
macro_rules! weak {
29+
// Temporary null documentation to work around #57569 until the fix is beta
30+
#[cfg_attr(bootstrap, doc = "")]
31+
pub(crate) macro weak {
3032
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
33+
#[allow(non_upper_case_globals)]
3134
static $name: crate::sys::weak::Weak<unsafe extern "C" fn($($t),*) -> $ret> =
3235
crate::sys::weak::Weak::new(concat!(stringify!($name), '\0'));
3336
)
@@ -100,8 +103,10 @@ unsafe fn fetch(name: &str) -> usize {
100103
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
101104
}
102105

106+
// Temporary null documentation to work around #57569 until the fix is beta
107+
#[cfg_attr(bootstrap, doc = "")]
103108
#[cfg(not(any(target_os = "linux", target_os = "android")))]
104-
macro_rules! syscall {
109+
pub(crate) macro syscall {
105110
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
106111
unsafe fn $name($($arg_name: $t),*) -> $ret {
107112
use super::os;
@@ -118,10 +123,12 @@ macro_rules! syscall {
118123
)
119124
}
120125

126+
#[cfg_attr(bootstrap, doc = "")]
121127
#[cfg(any(target_os = "linux", target_os = "android"))]
122-
macro_rules! syscall {
128+
pub(crate) macro syscall {
123129
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
124130
unsafe fn $name($($arg_name:$t),*) -> $ret {
131+
use weak;
125132
// This looks like a hack, but concat_idents only accepts idents
126133
// (not paths).
127134
use libc::*;

0 commit comments

Comments
 (0)