diff options
Diffstat (limited to 'Python/fileutils.c')
-rw-r--r-- | Python/fileutils.c | 380 |
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; +} + |