summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Python/fileutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Python/fileutils.c')
-rw-r--r--contrib/tools/python3/Python/fileutils.c274
1 files changed, 214 insertions, 60 deletions
diff --git a/contrib/tools/python3/Python/fileutils.c b/contrib/tools/python3/Python/fileutils.c
index 45de2b891d9..81be9d5e8ab 100644
--- a/contrib/tools/python3/Python/fileutils.c
+++ b/contrib/tools/python3/Python/fileutils.c
@@ -2,8 +2,11 @@
#include "pycore_fileutils.h" // fileutils definitions
#include "pycore_runtime.h" // _PyRuntime
#include "osdefs.h" // SEP
-#include <locale.h>
+
#include <stdlib.h> // mbstowcs()
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> // getcwd()
+#endif
#ifdef MS_WINDOWS
# include <malloc.h>
@@ -19,7 +22,7 @@ extern int winerror_to_errno(int);
#endif
#ifdef HAVE_LANGINFO_H
-#include <langinfo.h>
+# include <langinfo.h> // nl_langinfo(CODESET)
#endif
#ifdef HAVE_SYS_IOCTL_H
@@ -27,12 +30,12 @@ extern int winerror_to_errno(int);
#endif
#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION
-#error #include <iconv.h>
+# error #include <iconv.h> // iconv_open()
#endif
#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
+# include <fcntl.h> // fcntl(F_GETFD)
+#endif
#ifdef O_CLOEXEC
/* Does open() support the O_CLOEXEC flag? Possible values:
@@ -105,14 +108,14 @@ _Py_device_encoding(int fd)
#else
if (_PyRuntime.preconfig.utf8_mode) {
_Py_DECLARE_STR(utf_8, "utf-8");
- return Py_NewRef(&_Py_STR(utf_8));
+ return &_Py_STR(utf_8);
}
return _Py_GetLocaleEncodingObject();
#endif
}
-static size_t
+static int
is_valid_wide_char(wchar_t ch)
{
#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION
@@ -520,15 +523,7 @@ decode_current_locale(const char* arg, wchar_t **wstr, size_t *wlen,
break;
}
- if (converted == INCOMPLETE_CHARACTER) {
- /* Incomplete character. This should never happen,
- since we provide everything that we have -
- unless there is a bug in the C library, or I
- misunderstood how mbrtowc works. */
- goto decode_error;
- }
-
- if (converted == DECODE_ERROR) {
+ if (converted == DECODE_ERROR || converted == INCOMPLETE_CHARACTER) {
if (!surrogateescape) {
goto decode_error;
}
@@ -1499,7 +1494,7 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
#else
#if defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
- if (ioctl_works != 0 && raise != 0) {
+ if (raise != 0 && _Py_atomic_load_int_relaxed(&ioctl_works) != 0) {
/* fast-path: ioctl() only requires one syscall */
/* caveat: raise=0 is an indicator that we must be async-signal-safe
* thus avoid using ioctl() so we skip the fast-path. */
@@ -1509,7 +1504,9 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
request = FIOCLEX;
err = ioctl(fd, request, NULL);
if (!err) {
- ioctl_works = 1;
+ if (_Py_atomic_load_int_relaxed(&ioctl_works) == -1) {
+ _Py_atomic_store_int_relaxed(&ioctl_works, 1);
+ }
return 0;
}
@@ -1536,7 +1533,7 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
with EACCES. While FIOCLEX is safe operation it may be
unavailable because ioctl was denied altogether.
This can be the case on Android. */
- ioctl_works = 0;
+ _Py_atomic_store_int_relaxed(&ioctl_works, 0);
}
/* fallback to fcntl() if ioctl() does not work */
}
@@ -2292,6 +2289,99 @@ PathCchCombineEx(wchar_t *buffer, size_t bufsize, const wchar_t *dirname,
#endif /* defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP) */
+void
+_Py_skiproot(const wchar_t *path, Py_ssize_t size, Py_ssize_t *drvsize,
+ Py_ssize_t *rootsize)
+{
+ assert(drvsize);
+ assert(rootsize);
+#ifndef MS_WINDOWS
+#define IS_SEP(x) (*(x) == SEP)
+ *drvsize = 0;
+ if (!IS_SEP(&path[0])) {
+ // Relative path, e.g.: 'foo'
+ *rootsize = 0;
+ }
+ else if (!IS_SEP(&path[1]) || IS_SEP(&path[2])) {
+ // Absolute path, e.g.: '/foo', '///foo', '////foo', etc.
+ *rootsize = 1;
+ }
+ else {
+ // Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see
+ // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
+ *rootsize = 2;
+ }
+#undef IS_SEP
+#else
+ const wchar_t *pEnd = size >= 0 ? &path[size] : NULL;
+#define IS_END(x) (pEnd ? (x) == pEnd : !*(x))
+#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP)
+#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x))
+ if (IS_SEP(&path[0])) {
+ if (IS_SEP(&path[1])) {
+ // Device drives, e.g. \\.\device or \\?\device
+ // UNC drives, e.g. \\server\share or \\?\UNC\server\share
+ Py_ssize_t idx;
+ if (path[2] == L'?' && IS_SEP(&path[3]) &&
+ (path[4] == L'U' || path[4] == L'u') &&
+ (path[5] == L'N' || path[5] == L'n') &&
+ (path[6] == L'C' || path[6] == L'c') &&
+ IS_SEP(&path[7]))
+ {
+ idx = 8;
+ }
+ else {
+ idx = 2;
+ }
+ while (!SEP_OR_END(&path[idx])) {
+ idx++;
+ }
+ if (IS_END(&path[idx])) {
+ *drvsize = idx;
+ *rootsize = 0;
+ }
+ else {
+ idx++;
+ while (!SEP_OR_END(&path[idx])) {
+ idx++;
+ }
+ *drvsize = idx;
+ if (IS_END(&path[idx])) {
+ *rootsize = 0;
+ }
+ else {
+ *rootsize = 1;
+ }
+ }
+ }
+ else {
+ // Relative path with root, e.g. \Windows
+ *drvsize = 0;
+ *rootsize = 1;
+ }
+ }
+ else if (!IS_END(&path[0]) && path[1] == L':') {
+ *drvsize = 2;
+ if (IS_SEP(&path[2])) {
+ // Absolute drive-letter path, e.g. X:\Windows
+ *rootsize = 1;
+ }
+ else {
+ // Relative path with drive, e.g. X:Windows
+ *rootsize = 0;
+ }
+ }
+ else {
+ // Relative path, e.g. Windows
+ *drvsize = 0;
+ *rootsize = 0;
+ }
+#undef SEP_OR_END
+#undef IS_SEP
+#undef IS_END
+#endif
+}
+
// The caller must ensure "buffer" is big enough.
static int
join_relfile(wchar_t *buffer, size_t bufsize,
@@ -2408,49 +2498,40 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize)
#endif
#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x))
- // Skip leading '.\'
- if (p1[0] == L'.' && IS_SEP(&p1[1])) {
- path = &path[2];
- while (IS_SEP(path) && !IS_END(path)) {
- path++;
- }
- p1 = p2 = minP2 = path;
- lastC = SEP;
- }
-#ifdef MS_WINDOWS
- // Skip past drive segment and update minP2
- else if (p1[0] && p1[1] == L':') {
- *p2++ = *p1++;
- *p2++ = *p1++;
- minP2 = p2;
- lastC = L':';
- }
- // Skip past all \\-prefixed paths, including \\?\, \\.\,
- // and network paths, including the first segment.
- else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1])) {
- int sepCount = 2;
- *p2++ = SEP;
- *p2++ = SEP;
- p1 += 2;
- for (; !IS_END(p1) && sepCount; ++p1) {
- if (IS_SEP(p1)) {
- --sepCount;
- *p2++ = lastC = SEP;
- } else {
- *p2++ = lastC = *p1;
+ Py_ssize_t drvsize, rootsize;
+ _Py_skiproot(path, size, &drvsize, &rootsize);
+ if (drvsize || rootsize) {
+ // Skip past root and update minP2
+ p1 = &path[drvsize + rootsize];
+#ifndef ALTSEP
+ p2 = p1;
+#else
+ for (; p2 < p1; ++p2) {
+ if (*p2 == ALTSEP) {
+ *p2 = SEP;
}
}
+#endif
minP2 = p2 - 1;
+ lastC = *minP2;
+#ifdef MS_WINDOWS
+ if (lastC != SEP) {
+ minP2++;
+ }
+#endif
}
-#else
- // Skip past two leading SEPs
- else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1]) && !IS_SEP(&p1[2])) {
- *p2++ = *p1++;
- *p2++ = *p1++;
- minP2 = p2 - 1; // Absolute path has SEP at minP2
- lastC = SEP;
+ if (p1[0] == L'.' && SEP_OR_END(&p1[1])) {
+ // Skip leading '.\'
+ lastC = *++p1;
+#ifdef ALTSEP
+ if (lastC == ALTSEP) {
+ lastC = SEP;
+ }
+#endif
+ while (IS_SEP(p1)) {
+ p1++;
+ }
}
-#endif /* MS_WINDOWS */
/* if pEnd is specified, check that. Else, check for null terminator */
for (; !IS_END(p1); ++p1) {
@@ -2875,9 +2956,9 @@ done:
* non-opened fd in the middle.
* 2b. If fdwalk(3) isn't available, just do a plain close(2) loop.
*/
-#ifdef __FreeBSD__
+#ifdef HAVE_CLOSEFROM
# define USE_CLOSEFROM
-#endif /* __FreeBSD__ */
+#endif /* HAVE_CLOSEFROM */
#ifdef HAVE_FDWALK
# define USE_FDWALK
@@ -2919,7 +3000,7 @@ _Py_closerange(int first, int last)
#ifdef USE_CLOSEFROM
if (last >= sysconf(_SC_OPEN_MAX)) {
/* Any errors encountered while closing file descriptors are ignored */
- closefrom(first);
+ (void)closefrom(first);
}
else
#endif /* USE_CLOSEFROM */
@@ -2940,3 +3021,76 @@ _Py_closerange(int first, int last)
#endif /* USE_FDWALK */
_Py_END_SUPPRESS_IPH
}
+
+
+#ifndef MS_WINDOWS
+// Ticks per second used by clock() and times() functions.
+// See os.times() and time.process_time() implementations.
+int
+_Py_GetTicksPerSecond(long *ticks_per_second)
+{
+#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
+ long value = sysconf(_SC_CLK_TCK);
+ if (value < 1) {
+ return -1;
+ }
+ *ticks_per_second = value;
+#elif defined(HZ)
+ assert(HZ >= 1);
+ *ticks_per_second = HZ;
+#else
+ // Magic fallback value; may be bogus
+ *ticks_per_second = 60;
+#endif
+ return 0;
+}
+#endif
+
+
+/* Check if a file descriptor is valid or not.
+ Return 0 if the file descriptor is invalid, return non-zero otherwise. */
+int
+_Py_IsValidFD(int fd)
+{
+/* dup() is faster than fstat(): fstat() can require input/output operations,
+ whereas dup() doesn't. There is a low risk of EMFILE/ENFILE at Python
+ startup. Problem: dup() doesn't check if the file descriptor is valid on
+ some platforms.
+
+ fcntl(fd, F_GETFD) is even faster, because it only checks the process table.
+ It is preferred over dup() when available, since it cannot fail with the
+ "too many open files" error (EMFILE).
+
+ bpo-30225: On macOS Tiger, when stdout is redirected to a pipe and the other
+ side of the pipe is closed, dup(1) succeed, whereas fstat(1, &st) fails with
+ EBADF. FreeBSD has similar issue (bpo-32849).
+
+ Only use dup() on Linux where dup() is enough to detect invalid FD
+ (bpo-32849).
+*/
+ if (fd < 0) {
+ return 0;
+ }
+#if defined(F_GETFD) && ( \
+ defined(__linux__) || \
+ defined(__APPLE__) || \
+ (defined(__wasm__) && !defined(__wasi__)))
+ return fcntl(fd, F_GETFD) >= 0;
+#elif defined(__linux__)
+ int fd2 = dup(fd);
+ if (fd2 >= 0) {
+ close(fd2);
+ }
+ return (fd2 >= 0);
+#elif defined(MS_WINDOWS)
+ HANDLE hfile;
+ _Py_BEGIN_SUPPRESS_IPH
+ hfile = (HANDLE)_get_osfhandle(fd);
+ _Py_END_SUPPRESS_IPH
+ return (hfile != INVALID_HANDLE_VALUE
+ && GetFileType(hfile) != FILE_TYPE_UNKNOWN);
+#else
+ struct stat st;
+ return (fstat(fd, &st) == 0);
+#endif
+}