summaryrefslogtreecommitdiffstats
path: root/Python/fileutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/fileutils.c')
-rw-r--r--Python/fileutils.c380
1 files changed, 348 insertions, 32 deletions
diff --git a/Python/fileutils.c b/Python/fileutils.c
index b7c42e8..074888e 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -9,20 +9,39 @@
#include <langinfo.h>
#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
#ifdef __APPLE__
extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char *s, Py_ssize_t size);
#endif
+#ifdef O_CLOEXEC
+/* Does open() support the O_CLOEXEC flag? Possible values:
+
+ -1: unknown
+ 0: open() ignores O_CLOEXEC flag, ex: Linux kernel older than 2.6.23
+ 1: open() supports O_CLOEXEC flag, close-on-exec is set
+
+ The flag is used by _Py_open(), io.FileIO and os.open() */
+int _Py_open_cloexec_works = -1;
+#endif
+
PyObject *
_Py_device_encoding(int fd)
{
-#if defined(MS_WINDOWS) || defined(MS_WIN64)
+#if defined(MS_WINDOWS)
UINT cp;
#endif
if (!_PyVerify_fd(fd) || !isatty(fd)) {
Py_RETURN_NONE;
}
-#if defined(MS_WINDOWS) || defined(MS_WIN64)
+#if defined(MS_WINDOWS)
if (fd == 0)
cp = GetConsoleCP();
else if (fd == 1 || fd == 2)
@@ -60,7 +79,7 @@ extern int _Py_normalize_encoding(const char *, char *, size_t);
workaround is also enabled on error, for example if getting the locale
failed.
- Values of locale_is_ascii:
+ Values of force_ascii:
1: the workaround is used: _Py_wchar2char() uses
encode_ascii_surrogateescape() and _Py_char2wchar() uses
@@ -201,7 +220,7 @@ decode_ascii_surrogateescape(const char *arg, size_t *size)
unsigned char *in;
wchar_t *out;
- res = PyMem_Malloc((strlen(arg)+1)*sizeof(wchar_t));
+ res = PyMem_RawMalloc((strlen(arg)+1)*sizeof(wchar_t));
if (!res)
return NULL;
@@ -229,7 +248,7 @@ decode_ascii_surrogateescape(const char *arg, size_t *size)
Use _Py_wchar2char() to encode the character string back to a byte string.
Return a pointer to a newly allocated wide character string (use
- PyMem_Free() to free the memory) and write the number of written wide
+ PyMem_RawFree() to free the memory) and write the number of written wide
characters excluding the null character into *size if size is not NULL, or
NULL on error (decoding or memory allocation error). If size is not NULL,
*size is set to (size_t)-1 on memory error and (size_t)-2 on decoding
@@ -254,9 +273,9 @@ _Py_char2wchar(const char* arg, size_t *size)
wchar_t *res;
size_t argsize;
size_t count;
+#ifdef HAVE_MBRTOWC
unsigned char *in;
wchar_t *out;
-#ifdef HAVE_MBRTOWC
mbstate_t mbs;
#endif
@@ -283,7 +302,7 @@ _Py_char2wchar(const char* arg, size_t *size)
argsize = mbstowcs(NULL, arg, 0);
#endif
if (argsize != (size_t)-1) {
- res = (wchar_t *)PyMem_Malloc((argsize+1)*sizeof(wchar_t));
+ res = (wchar_t *)PyMem_RawMalloc((argsize+1)*sizeof(wchar_t));
if (!res)
goto oom;
count = mbstowcs(res, arg, argsize+1);
@@ -292,7 +311,7 @@ _Py_char2wchar(const char* arg, size_t *size)
/* Only use the result if it contains no
surrogate characters. */
for (tmp = res; *tmp != 0 &&
- (*tmp < 0xd800 || *tmp > 0xdfff); tmp++)
+ !Py_UNICODE_IS_SURROGATE(*tmp); tmp++)
;
if (*tmp == 0) {
if (size != NULL)
@@ -300,7 +319,7 @@ _Py_char2wchar(const char* arg, size_t *size)
return res;
}
}
- PyMem_Free(res);
+ PyMem_RawFree(res);
}
/* Conversion failed. Fall back to escaping with surrogateescape. */
#ifdef HAVE_MBRTOWC
@@ -309,7 +328,7 @@ _Py_char2wchar(const char* arg, size_t *size)
/* Overallocate; as multi-byte characters are in the argument, the
actual output could use less memory. */
argsize = strlen(arg) + 1;
- res = (wchar_t*)PyMem_Malloc(argsize*sizeof(wchar_t));
+ res = (wchar_t*)PyMem_RawMalloc(argsize*sizeof(wchar_t));
if (!res)
goto oom;
in = (unsigned char*)arg;
@@ -325,7 +344,7 @@ _Py_char2wchar(const char* arg, size_t *size)
since we provide everything that we have -
unless there is a bug in the C library, or I
misunderstood how mbrtowc works. */
- PyMem_Free(res);
+ PyMem_RawFree(res);
if (size != NULL)
*size = (size_t)-2;
return NULL;
@@ -338,7 +357,7 @@ _Py_char2wchar(const char* arg, size_t *size)
memset(&mbs, 0, sizeof mbs);
continue;
}
- if (*out >= 0xd800 && *out <= 0xdfff) {
+ if (Py_UNICODE_IS_SURROGATE(*out)) {
/* Surrogate character. Escape the original
byte sequence with surrogateescape. */
argsize -= converted;
@@ -547,14 +566,215 @@ _Py_stat(PyObject *path, struct stat *statbuf)
#endif
-/* Open a file. Use _wfopen() on Windows, encode the path to the locale
- encoding and use fopen() otherwise. */
+int
+get_inheritable(int fd, int raise)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD flags;
+
+ if (!_PyVerify_fd(fd)) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ if (!GetHandleInformation(handle, &flags)) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ return (flags & HANDLE_FLAG_INHERIT);
+#else
+ int flags;
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags == -1) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return !(flags & FD_CLOEXEC);
+#endif
+}
+
+/* Get the inheritable flag of the specified file descriptor.
+ Return 1 if the file descriptor can be inherited, 0 if it cannot,
+ raise an exception and return -1 on error. */
+int
+_Py_get_inheritable(int fd)
+{
+ return get_inheritable(fd, 1);
+}
+
+static int
+set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD flags;
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+ int request;
+ int err;
+#elif defined(HAVE_FCNTL_H)
+ int flags;
+ int res;
+#endif
+
+ /* atomic_flag_works can only be used to make the file descriptor
+ non-inheritable */
+ assert(!(atomic_flag_works != NULL && inheritable));
+
+ if (atomic_flag_works != NULL && !inheritable) {
+ if (*atomic_flag_works == -1) {
+ int inheritable = get_inheritable(fd, raise);
+ if (inheritable == -1)
+ return -1;
+ *atomic_flag_works = !inheritable;
+ }
+
+ if (*atomic_flag_works)
+ return 0;
+ }
+
+#ifdef MS_WINDOWS
+ if (!_PyVerify_fd(fd)) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ if (inheritable)
+ flags = HANDLE_FLAG_INHERIT;
+ else
+ flags = 0;
+ if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+ return 0;
+
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+ if (inheritable)
+ request = FIONCLEX;
+ else
+ request = FIOCLEX;
+ err = ioctl(fd, request);
+ if (err) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return 0;
+
+#else
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ if (inheritable)
+ flags &= ~FD_CLOEXEC;
+ else
+ flags |= FD_CLOEXEC;
+ res = fcntl(fd, F_SETFD, flags);
+ if (res < 0) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+/* Make the file descriptor non-inheritable.
+ Return 0 on success, set errno and return -1 on error. */
+static int
+make_non_inheritable(int fd)
+{
+ return set_inheritable(fd, 0, 0, NULL);
+}
+
+/* Set the inheritable flag of the specified file descriptor.
+ On success: return 0, on error: raise an exception if raise is nonzero
+ and return -1.
+
+ If atomic_flag_works is not NULL:
+
+ * if *atomic_flag_works==-1, check if the inheritable is set on the file
+ descriptor: if yes, set *atomic_flag_works to 1, otherwise set to 0 and
+ set the inheritable flag
+ * if *atomic_flag_works==1: do nothing
+ * if *atomic_flag_works==0: set inheritable flag to False
+
+ Set atomic_flag_works to NULL if no atomic flag was used to create the
+ file descriptor.
+
+ atomic_flag_works can only be used to make a file descriptor
+ non-inheritable: atomic_flag_works must be NULL if inheritable=1. */
+int
+_Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works)
+{
+ return set_inheritable(fd, inheritable, 1, atomic_flag_works);
+}
+
+/* Open a file with the specified flags (wrapper to open() function).
+ The file descriptor is created non-inheritable. */
+int
+_Py_open(const char *pathname, int flags)
+{
+ int fd;
+#ifdef MS_WINDOWS
+ fd = open(pathname, flags | O_NOINHERIT);
+ if (fd < 0)
+ return fd;
+#else
+
+ int *atomic_flag_works;
+#ifdef O_CLOEXEC
+ atomic_flag_works = &_Py_open_cloexec_works;
+ flags |= O_CLOEXEC;
+#else
+ atomic_flag_works = NULL;
+#endif
+ fd = open(pathname, flags);
+ if (fd < 0)
+ return fd;
+
+ if (set_inheritable(fd, 0, 0, atomic_flag_works) < 0) {
+ close(fd);
+ return -1;
+ }
+#endif /* !MS_WINDOWS */
+ return fd;
+}
+/* Open a file. Use _wfopen() on Windows, encode the path to the locale
+ encoding and use fopen() otherwise. The file descriptor is created
+ non-inheritable. */
FILE *
_Py_wfopen(const wchar_t *path, const wchar_t *mode)
{
-#ifndef MS_WINDOWS
FILE *f;
+#ifndef MS_WINDOWS
char *cpath;
char cmode[10];
size_t r;
@@ -568,21 +788,42 @@ _Py_wfopen(const wchar_t *path, const wchar_t *mode)
return NULL;
f = fopen(cpath, cmode);
PyMem_Free(cpath);
- return f;
#else
- return _wfopen(path, mode);
+ f = _wfopen(path, mode);
#endif
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
}
-/* Call _wfopen() on Windows, or encode the path to the filesystem encoding and
- call fopen() otherwise.
+/* Wrapper to fopen(). The file descriptor is created non-inheritable. */
+FILE*
+_Py_fopen(const char *pathname, const char *mode)
+{
+ FILE *f = fopen(pathname, mode);
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
+}
- Return the new file object on success, or NULL if the file cannot be open or
- (if PyErr_Occurred()) on unicode error */
+/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem
+ encoding and call fopen() otherwise. The file descriptor is created
+ non-inheritable.
+ Return the new file object on success, or NULL if the file cannot be open or
+ (if PyErr_Occurred()) on unicode error. */
FILE*
-_Py_fopen(PyObject *path, const char *mode)
+_Py_fopen_obj(PyObject *path, const char *mode)
{
+ FILE *f;
#ifdef MS_WINDOWS
wchar_t *wpath;
wchar_t wmode[10];
@@ -602,16 +843,21 @@ _Py_fopen(PyObject *path, const char *mode)
if (usize == 0)
return NULL;
- return _wfopen(wpath, wmode);
+ f = _wfopen(wpath, wmode);
#else
- FILE *f;
PyObject *bytes;
if (!PyUnicode_FSConverter(path, &bytes))
return NULL;
f = fopen(PyBytes_AS_STRING(bytes), mode);
Py_DECREF(bytes);
- return f;
#endif
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
}
#ifdef HAVE_READLINK
@@ -648,12 +894,12 @@ _Py_wreadlink(const wchar_t *path, wchar_t *buf, size_t bufsiz)
return -1;
}
if (bufsiz <= r1) {
- PyMem_Free(wbuf);
+ PyMem_RawFree(wbuf);
errno = EINVAL;
return -1;
}
wcsncpy(buf, wbuf, bufsiz);
- PyMem_Free(wbuf);
+ PyMem_RawFree(wbuf);
return (int)r1;
}
#endif
@@ -689,12 +935,12 @@ _Py_wrealpath(const wchar_t *path,
return NULL;
}
if (resolved_path_size <= r) {
- PyMem_Free(wresolved_path);
+ PyMem_RawFree(wresolved_path);
errno = EINVAL;
return NULL;
}
wcsncpy(resolved_path, wresolved_path, resolved_path_size);
- PyMem_Free(wresolved_path);
+ PyMem_RawFree(wresolved_path);
return resolved_path;
}
#endif
@@ -707,7 +953,8 @@ wchar_t*
_Py_wgetcwd(wchar_t *buf, size_t size)
{
#ifdef MS_WINDOWS
- return _wgetcwd(buf, size);
+ int isize = (int)Py_MIN(size, INT_MAX);
+ return _wgetcwd(buf, isize);
#else
char fname[PATH_MAX];
wchar_t *wname;
@@ -719,12 +966,81 @@ _Py_wgetcwd(wchar_t *buf, size_t size)
if (wname == NULL)
return NULL;
if (size <= len) {
- PyMem_Free(wname);
+ PyMem_RawFree(wname);
return NULL;
}
wcsncpy(buf, wname, size);
- PyMem_Free(wname);
+ PyMem_RawFree(wname);
return buf;
#endif
}
+/* Duplicate a file descriptor. The new file descriptor is created as
+ non-inheritable. Return a new file descriptor on success, raise an OSError
+ exception and return -1 on error.
+
+ The GIL is released to call dup(). The caller must hold the GIL. */
+int
+_Py_dup(int fd)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD ftype;
+#endif
+
+ if (!_PyVerify_fd(fd)) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+#ifdef MS_WINDOWS
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ /* get the file type, ignore the error if it failed */
+ ftype = GetFileType(handle);
+
+ Py_BEGIN_ALLOW_THREADS
+ fd = dup(fd);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ /* Character files like console cannot be make non-inheritable */
+ if (ftype != FILE_TYPE_CHAR) {
+ if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+ close(fd);
+ return -1;
+ }
+ }
+#elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC)
+ Py_BEGIN_ALLOW_THREADS
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+#else
+ Py_BEGIN_ALLOW_THREADS
+ fd = dup(fd);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+ close(fd);
+ return -1;
+ }
+#endif
+ return fd;
+}
+