}
int link_fd(int fd, int newdirfd, const char *newpath) {
- int r, k;
+ int r;
assert(fd >= 0);
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
assert(newpath);
- /* Try linking via /proc/self/fd/ first. */
- r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
- if (r != -ENOENT)
- return r;
-
- /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
- * more recent kernel, but does not require /proc/ mounted) */
- k = proc_mounted();
- if (k < 0)
- return r;
- if (k > 0)
- return -EBADF;
+ /* Try to link via AT_EMPTY_PATH first. This fails with ENOENT if we don't have CAP_DAC_READ_SEARCH
+ * on kernels < 6.10, in which case we'd then resort to /proc/self/fd/ dance.
+ *
+ * See also: https://github.com/torvalds/linux/commit/42bd2af5950456d46fdaa91c3a8fb02e680f19f5 */
+ r = RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
+ if (r == -ENOENT) {
+ r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
+ if (r == -ENOENT && proc_mounted() == 0) /* No proc_fd_enoent_errno() here because we don't
+ know if it's the target path that's missing. */
+ return -ENOSYS;
+ }
- return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
+ return r;
}
int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {