mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-81793: Always call linkat() from os.link(), if available (GH-132517)
This fixes os.link() on platforms (like Linux and OpenIndiana) where the system link() function does not follow symlinks. * On Linux, it now follows symlinks by default and if follow_symlinks=True is specified. * On Windows, it now raises error if follow_symlinks=True is passed. * On macOS, it now raises error if follow_symlinks=False is passed and the system linkat() function is not available at runtime. * On other platforms, it now raises error if follow_symlinks is passed with a value that does not match the system link() function behavior if if the behavior is not known. Co-authored-by: Joachim Henke <37883863+jo-he@users.noreply.github.com> Co-authored-by: Thomas Kluyver <takowl@gmail.com>
This commit is contained in:
parent
e9253ebf74
commit
5a57248b22
6 changed files with 100 additions and 67 deletions
|
@ -573,7 +573,11 @@ extern char *ctermid_r(char *);
|
|||
# define HAVE_FACCESSAT_RUNTIME 1
|
||||
# define HAVE_FCHMODAT_RUNTIME 1
|
||||
# define HAVE_FCHOWNAT_RUNTIME 1
|
||||
#ifdef __wasi__
|
||||
# define HAVE_LINKAT_RUNTIME 0
|
||||
# else
|
||||
# define HAVE_LINKAT_RUNTIME 1
|
||||
# endif
|
||||
# define HAVE_FDOPENDIR_RUNTIME 1
|
||||
# define HAVE_MKDIRAT_RUNTIME 1
|
||||
# define HAVE_RENAMEAT_RUNTIME 1
|
||||
|
@ -4346,7 +4350,7 @@ os.link
|
|||
*
|
||||
src_dir_fd : dir_fd = None
|
||||
dst_dir_fd : dir_fd = None
|
||||
follow_symlinks: bool = True
|
||||
follow_symlinks: bool(c_default="-1", py_default="(os.name != 'nt')") = PLACEHOLDER
|
||||
|
||||
Create a hard link to a file.
|
||||
|
||||
|
@ -4364,31 +4368,46 @@ src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your
|
|||
static PyObject *
|
||||
os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
|
||||
int dst_dir_fd, int follow_symlinks)
|
||||
/*[clinic end generated code: output=7f00f6007fd5269a input=b0095ebbcbaa7e04]*/
|
||||
/*[clinic end generated code: output=7f00f6007fd5269a input=1d5e602d115fed7b]*/
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
BOOL result = FALSE;
|
||||
#else
|
||||
int result;
|
||||
#endif
|
||||
#if defined(HAVE_LINKAT)
|
||||
int linkat_unavailable = 0;
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LINKAT
|
||||
if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
|
||||
argument_unavailable_error("link", "src_dir_fd and dst_dir_fd");
|
||||
return NULL;
|
||||
#ifdef HAVE_LINKAT
|
||||
if (HAVE_LINKAT_RUNTIME) {
|
||||
if (follow_symlinks < 0) {
|
||||
follow_symlinks = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
|
||||
argument_unavailable_error("link", "src_dir_fd and dst_dir_fd");
|
||||
return NULL;
|
||||
}
|
||||
/* See issue 85527: link() on Linux works like linkat without AT_SYMLINK_FOLLOW,
|
||||
but on Mac it works like linkat *with* AT_SYMLINK_FOLLOW. */
|
||||
#if defined(MS_WINDOWS) || defined(__linux__)
|
||||
if (follow_symlinks == 1) {
|
||||
argument_unavailable_error("link", "follow_symlinks=True");
|
||||
return NULL;
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || (defined(__sun) && defined(__SVR4))
|
||||
if (follow_symlinks == 0) {
|
||||
argument_unavailable_error("link", "follow_symlinks=False");
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
if (follow_symlinks >= 0) {
|
||||
argument_unavailable_error("link", "follow_symlinks");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) {
|
||||
PyErr_SetString(PyExc_NotImplementedError,
|
||||
"link: src and dst must be the same type");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (PySys_Audit("os.link", "OOii", src->object, dst->object,
|
||||
src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd,
|
||||
|
@ -4406,44 +4425,18 @@ os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
|
|||
#else
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_LINKAT
|
||||
if ((src_dir_fd != DEFAULT_DIR_FD) ||
|
||||
(dst_dir_fd != DEFAULT_DIR_FD) ||
|
||||
(!follow_symlinks)) {
|
||||
|
||||
if (HAVE_LINKAT_RUNTIME) {
|
||||
|
||||
result = linkat(src_dir_fd, src->narrow,
|
||||
dst_dir_fd, dst->narrow,
|
||||
follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
|
||||
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
else {
|
||||
if (src_dir_fd == DEFAULT_DIR_FD && dst_dir_fd == DEFAULT_DIR_FD) {
|
||||
/* See issue 41355: This matches the behaviour of !HAVE_LINKAT */
|
||||
result = link(src->narrow, dst->narrow);
|
||||
} else {
|
||||
linkat_unavailable = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (HAVE_LINKAT_RUNTIME) {
|
||||
result = linkat(src_dir_fd, src->narrow,
|
||||
dst_dir_fd, dst->narrow,
|
||||
follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_LINKAT */
|
||||
result = link(src->narrow, dst->narrow);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
#ifdef HAVE_LINKAT
|
||||
if (linkat_unavailable) {
|
||||
/* Either or both dir_fd arguments were specified */
|
||||
if (src_dir_fd != DEFAULT_DIR_FD) {
|
||||
argument_unavailable_error("link", "src_dir_fd");
|
||||
} else {
|
||||
argument_unavailable_error("link", "dst_dir_fd");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
{
|
||||
/* linkat not available */
|
||||
result = link(src->narrow, dst->narrow);
|
||||
}
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (result)
|
||||
return path_error2(src, dst);
|
||||
|
@ -5935,12 +5928,6 @@ internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is
|
|||
return path_error2(src, dst);
|
||||
|
||||
#else
|
||||
if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"%s: src and dst must be the same type", function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_RENAMEAT
|
||||
if (dir_fd_specified) {
|
||||
|
@ -10613,12 +10600,6 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
|
|||
|
||||
#else
|
||||
|
||||
if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"symlink: src and dst must be the same type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_SYMLINKAT
|
||||
if (dir_fd != DEFAULT_DIR_FD) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue