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..eecbb3b 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. */ +static 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; +} + | 
