summaryrefslogtreecommitdiffstats
path: root/Python/fileutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/fileutils.c')
-rw-r--r--Python/fileutils.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 54853ba..e6a5391 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -3050,3 +3050,52 @@ _Py_GetTicksPerSecond(long *ticks_per_second)
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
+}