]> granicus.if.org Git - python/commitdiff
Issue #13772: In os.symlink() under Windows, do not try to guess the link
authorAntoine Pitrou <solipsis@pitrou.net>
Tue, 24 Jan 2012 08:05:18 +0000 (09:05 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Tue, 24 Jan 2012 08:05:18 +0000 (09:05 +0100)
target's type (file or directory).  The detection was buggy and made the
call non-atomic (therefore prone to race conditions).

1  2 
Doc/library/os.rst
Lib/test/test_os.py
Misc/NEWS
Modules/posixmodule.c

Simple merge
Simple merge
diff --cc Misc/NEWS
Simple merge
index 61a0439539f3635c9592ebfa402e8d61af009fa2,0afab3cd93c3f363c1d9a783c6042c1c6fd0f4a0..1797a2f64a479fb54b9a54cff7c1d7ef0021b99d
@@@ -6361,815 -5956,679 +6361,804 @@@ posix_wait(PyObject *self, PyObject *no
  }
  #endif
  
 -#ifdef HAVE_DEVICE_MACROS
 -PyDoc_STRVAR(posix_major__doc__,
 -"major(device) -> major number\n\
 -Extracts a device major number from a raw device number.");
 +
 +PyDoc_STRVAR(posix_lstat__doc__,
 +"lstat(path) -> stat result\n\n\
 +Like stat(path), but do not follow symbolic links.");
  
  static PyObject *
 -posix_major(PyObject *self, PyObject *args)
 +posix_lstat(PyObject *self, PyObject *args)
  {
 -    int device;
 -    if (!PyArg_ParseTuple(args, "i:major", &device))
 -        return NULL;
 -    return PyLong_FromLong((long)major(device));
 +#ifdef HAVE_LSTAT
 +    return posix_do_stat(self, args, "O&:lstat", lstat, NULL, NULL);
 +#else /* !HAVE_LSTAT */
 +#ifdef MS_WINDOWS
 +    return posix_do_stat(self, args, "O&:lstat", win32_lstat, "U:lstat",
 +                         win32_lstat_w);
 +#else
 +    return posix_do_stat(self, args, "O&:lstat", STAT, NULL, NULL);
 +#endif
 +#endif /* !HAVE_LSTAT */
  }
  
 -PyDoc_STRVAR(posix_minor__doc__,
 -"minor(device) -> minor number\n\
 -Extracts a device minor number from a raw device number.");
 +
 +#ifdef HAVE_READLINK
 +PyDoc_STRVAR(posix_readlink__doc__,
 +"readlink(path) -> path\n\n\
 +Return a string representing the path to which the symbolic link points.");
  
  static PyObject *
 -posix_minor(PyObject *self, PyObject *args)
 +posix_readlink(PyObject *self, PyObject *args)
  {
 -    int device;
 -    if (!PyArg_ParseTuple(args, "i:minor", &device))
 +    PyObject* v;
 +    char buf[MAXPATHLEN];
 +    PyObject *opath;
 +    char *path;
 +    int n;
 +    int arg_is_unicode = 0;
 +
 +    if (!PyArg_ParseTuple(args, "O&:readlink",
 +                          PyUnicode_FSConverter, &opath))
          return NULL;
 -    return PyLong_FromLong((long)minor(device));
 +    path = PyBytes_AsString(opath);
 +    v = PySequence_GetItem(args, 0);
 +    if (v == NULL) {
 +        Py_DECREF(opath);
 +        return NULL;
 +    }
 +
 +    if (PyUnicode_Check(v)) {
 +        arg_is_unicode = 1;
 +    }
 +    Py_DECREF(v);
 +
 +    Py_BEGIN_ALLOW_THREADS
 +    n = readlink(path, buf, (int) sizeof buf);
 +    Py_END_ALLOW_THREADS
 +    if (n < 0)
 +        return posix_error_with_allocated_filename(opath);
 +
 +    Py_DECREF(opath);
 +    if (arg_is_unicode)
 +        return PyUnicode_DecodeFSDefaultAndSize(buf, n);
 +    else
 +        return PyBytes_FromStringAndSize(buf, n);
  }
 +#endif /* HAVE_READLINK */
  
 -PyDoc_STRVAR(posix_makedev__doc__,
 -"makedev(major, minor) -> device number\n\
 -Composes a raw device number from the major and minor device numbers.");
 +
 +#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
 +PyDoc_STRVAR(posix_symlink__doc__,
 +"symlink(src, dst)\n\n\
 +Create a symbolic link pointing to src named dst.");
  
  static PyObject *
 -posix_makedev(PyObject *self, PyObject *args)
 +posix_symlink(PyObject *self, PyObject *args)
  {
 -    int major, minor;
 -    if (!PyArg_ParseTuple(args, "ii:makedev", &major, &minor))
 -        return NULL;
 -    return PyLong_FromLong((long)makedev(major, minor));
 +    return posix_2str(args, "O&O&:symlink", symlink);
  }
 -#endif /* device macros */
 +#endif /* HAVE_SYMLINK */
  
 +#if !defined(HAVE_READLINK) && defined(MS_WINDOWS)
  
 -#ifdef HAVE_FTRUNCATE
 -PyDoc_STRVAR(posix_ftruncate__doc__,
 -"ftruncate(fd, length)\n\n\
 -Truncate a file to a specified length.");
 +PyDoc_STRVAR(win_readlink__doc__,
 +"readlink(path) -> path\n\n\
 +Return a string representing the path to which the symbolic link points.");
  
 +/* Windows readlink implementation */
  static PyObject *
 -posix_ftruncate(PyObject *self, PyObject *args)
 +win_readlink(PyObject *self, PyObject *args)
  {
 -    int fd;
 -    off_t length;
 -    int res;
 -    PyObject *lenobj;
 +    wchar_t *path;
 +    DWORD n_bytes_returned;
 +    DWORD io_result;
 +    PyObject *po, *result;
 +    HANDLE reparse_point_handle;
  
 -    if (!PyArg_ParseTuple(args, "iO:ftruncate", &fd, &lenobj))
 -        return NULL;
 +    char target_buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
 +    REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)target_buffer;
 +    wchar_t *print_name;
  
 -#if !defined(HAVE_LARGEFILE_SUPPORT)
 -    length = PyLong_AsLong(lenobj);
 -#else
 -    length = PyLong_Check(lenobj) ?
 -        PyLong_AsLongLong(lenobj) : PyLong_AsLong(lenobj);
 -#endif
 -    if (PyErr_Occurred())
 +    if (!PyArg_ParseTuple(args,
 +                  "U:readlink",
 +                  &po))
 +        return NULL;
 +    path = PyUnicode_AsUnicode(po);
 +    if (path == NULL)
          return NULL;
  
 +    /* First get a handle to the reparse point */
      Py_BEGIN_ALLOW_THREADS
 -    res = ftruncate(fd, length);
 +    reparse_point_handle = CreateFileW(
 +        path,
 +        0,
 +        0,
 +        0,
 +        OPEN_EXISTING,
 +        FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS,
 +        0);
      Py_END_ALLOW_THREADS
 -    if (res < 0)
 -        return posix_error();
 -    Py_INCREF(Py_None);
 -    return Py_None;
 -}
 -#endif
  
 -#ifdef HAVE_PUTENV
 -PyDoc_STRVAR(posix_putenv__doc__,
 -"putenv(key, value)\n\n\
 -Change or add an environment variable.");
 +    if (reparse_point_handle==INVALID_HANDLE_VALUE)
 +        return win32_error_object("readlink", po);
  
 -/* Save putenv() parameters as values here, so we can collect them when they
 - * get re-set with another call for the same key. */
 -static PyObject *posix_putenv_garbage;
 +    Py_BEGIN_ALLOW_THREADS
 +    /* New call DeviceIoControl to read the reparse point */
 +    io_result = DeviceIoControl(
 +        reparse_point_handle,
 +        FSCTL_GET_REPARSE_POINT,
 +        0, 0, /* in buffer */
 +        target_buffer, sizeof(target_buffer),
 +        &n_bytes_returned,
 +        0 /* we're not using OVERLAPPED_IO */
 +        );
 +    CloseHandle(reparse_point_handle);
 +    Py_END_ALLOW_THREADS
  
 -static PyObject *
 -posix_putenv(PyObject *self, PyObject *args)
 -{
 -#ifdef MS_WINDOWS
 -    wchar_t *s1, *s2;
 -    wchar_t *newenv;
 -#else
 -    PyObject *os1, *os2;
 -    char *s1, *s2;
 -    char *newenv;
 -#endif
 -    PyObject *newstr = NULL;
 -    size_t len;
 +    if (io_result==0)
 +        return win32_error_object("readlink", po);
  
 -#ifdef MS_WINDOWS
 -    if (!PyArg_ParseTuple(args,
 -                          "uu:putenv",
 -                          &s1, &s2))
 -        return NULL;
 -#else
 -    if (!PyArg_ParseTuple(args,
 -                          "O&O&:putenv",
 -                          PyUnicode_FSConverter, &os1,
 -                          PyUnicode_FSConverter, &os2))
 +    if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK)
 +    {
 +        PyErr_SetString(PyExc_ValueError,
 +                "not a symbolic link");
          return NULL;
 -    s1 = PyBytes_AsString(os1);
 -    s2 = PyBytes_AsString(os2);
 -#endif
 +    }
 +    print_name = rdb->SymbolicLinkReparseBuffer.PathBuffer +
 +                 rdb->SymbolicLinkReparseBuffer.PrintNameOffset;
  
 -#if defined(PYOS_OS2)
 -    if (stricmp(s1, "BEGINLIBPATH") == 0) {
 -        APIRET rc;
 +    result = PyUnicode_FromWideChar(print_name,
 +                    rdb->SymbolicLinkReparseBuffer.PrintNameLength/2);
 +    return result;
 +}
  
 -        rc = DosSetExtLIBPATH(s2, BEGIN_LIBPATH);
 -        if (rc != NO_ERROR) {
 -            os2_error(rc);
 -            goto error;
 -        }
 +#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
  
 -    } else if (stricmp(s1, "ENDLIBPATH") == 0) {
 -        APIRET rc;
 +#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
  
 -        rc = DosSetExtLIBPATH(s2, END_LIBPATH);
 -        if (rc != NO_ERROR) {
 -            os2_error(rc);
 -            goto error;
 -        }
 -    } else {
 -#endif
 -    /* XXX This can leak memory -- not easy to fix :-( */
 -    /* len includes space for a trailing \0; the size arg to
 -       PyBytes_FromStringAndSize does not count that */
 -#ifdef MS_WINDOWS
 -    len = wcslen(s1) + wcslen(s2) + 2;
 -    if (_MAX_ENV < (len - 1)) {
 -        PyErr_Format(PyExc_ValueError,
 -                     "the environment variable is longer than %u characters",
 -                     _MAX_ENV);
 -        goto error;
 +/* Grab CreateSymbolicLinkW dynamically from kernel32 */
 +static int has_CreateSymbolicLinkW = 0;
 +static DWORD (CALLBACK *Py_CreateSymbolicLinkW)(LPWSTR, LPWSTR, DWORD);
 +static int
 +check_CreateSymbolicLinkW()
 +{
 +    HINSTANCE hKernel32;
 +    /* only recheck */
 +    if (has_CreateSymbolicLinkW)
 +        return has_CreateSymbolicLinkW;
 +    hKernel32 = GetModuleHandleW(L"KERNEL32");
 +    *(FARPROC*)&Py_CreateSymbolicLinkW = GetProcAddress(hKernel32,
 +                                                        "CreateSymbolicLinkW");
 +    if (Py_CreateSymbolicLinkW)
 +        has_CreateSymbolicLinkW = 1;
 +    return has_CreateSymbolicLinkW;
 +}
 +
 +PyDoc_STRVAR(win_symlink__doc__,
 +"symlink(src, dst, target_is_directory=False)\n\n\
 +Create a symbolic link pointing to src named dst.\n\
 +target_is_directory is required if the target is to be interpreted as\n\
 +a directory.\n\
 +This function requires Windows 6.0 or greater, and raises a\n\
 +NotImplementedError otherwise.");
 +
 +static PyObject *
 +win_symlink(PyObject *self, PyObject *args, PyObject *kwargs)
 +{
 +    static char *kwlist[] = {"src", "dest", "target_is_directory", NULL};
 +    PyObject *osrc, *odest;
 +    PyObject *usrc = NULL, *udest = NULL;
 +    wchar_t *wsrc, *wdest;
 +    int target_is_directory = 0;
 +    DWORD res;
-     WIN32_FILE_ATTRIBUTE_DATA src_info;
 +
 +    if (!check_CreateSymbolicLinkW())
 +    {
 +        /* raise NotImplementedError */
 +        return PyErr_Format(PyExc_NotImplementedError,
 +            "CreateSymbolicLinkW not found");
      }
 -    newstr = PyUnicode_FromUnicode(NULL, (int)len - 1);
 -#else
 -    len = PyBytes_GET_SIZE(os1) + PyBytes_GET_SIZE(os2) + 2;
 -    newstr = PyBytes_FromStringAndSize(NULL, (int)len - 1);
 -#endif
 -    if (newstr == NULL) {
 -        PyErr_NoMemory();
 +    if (!PyArg_ParseTupleAndKeywords(
 +            args, kwargs, "OO|i:symlink", kwlist,
 +            &osrc, &odest, &target_is_directory))
 +        return NULL;
 +
 +    usrc = win32_decode_filename(osrc);
 +    if (!usrc)
 +        return NULL;
 +    udest = win32_decode_filename(odest);
 +    if (!udest)
          goto error;
 -    }
 -#ifdef MS_WINDOWS
 -    newenv = PyUnicode_AsUnicode(newstr);
 -    _snwprintf(newenv, len, L"%s=%s", s1, s2);
 -    if (_wputenv(newenv)) {
 -        posix_error();
 +
 +    if (win32_can_symlink == 0)
 +        return PyErr_Format(PyExc_OSError, "symbolic link privilege not held");
 +
 +    wsrc = PyUnicode_AsUnicode(usrc);
 +    if (wsrc == NULL)
          goto error;
 -    }
 -#else
 -    newenv = PyBytes_AS_STRING(newstr);
 -    PyOS_snprintf(newenv, len, "%s=%s", s1, s2);
 -    if (putenv(newenv)) {
 -        posix_error();
 +    wdest = PyUnicode_AsUnicode(udest);
 +    if (wsrc == NULL)
          goto error;
 -    }
 -#endif
  
-     /* if src is a directory, ensure target_is_directory==1 */
-     if(
-         GetFileAttributesExW(
-             wsrc, GetFileExInfoStandard, &src_info
-         ))
-     {
-         target_is_directory = target_is_directory ||
-             (src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
 -    /* Install the first arg and newstr in posix_putenv_garbage;
 -     * this will cause previous value to be collected.  This has to
 -     * happen after the real putenv() call because the old value
 -     * was still accessible until then. */
 -    if (PyDict_SetItem(posix_putenv_garbage,
 -#ifdef MS_WINDOWS
 -                       PyTuple_GET_ITEM(args, 0),
 -#else
 -                       os1,
 -#endif
 -                       newstr)) {
 -        /* really not much we can do; just leak */
 -        PyErr_Clear();
--    }
 -    else {
 -        Py_DECREF(newstr);
 -    }
 +    Py_BEGIN_ALLOW_THREADS
 +    res = Py_CreateSymbolicLinkW(wdest, wsrc, target_is_directory);
 +    Py_END_ALLOW_THREADS
  
 -#if defined(PYOS_OS2)
 -    }
 -#endif
 +    Py_DECREF(usrc);
 +    Py_DECREF(udest);
 +    if (!res)
 +        return win32_error_object("symlink", osrc);
  
 -#ifndef MS_WINDOWS
 -    Py_DECREF(os1);
 -    Py_DECREF(os2);
 -#endif
 -    Py_RETURN_NONE;
 +    Py_INCREF(Py_None);
 +    return Py_None;
  
  error:
 -#ifndef MS_WINDOWS
 -    Py_DECREF(os1);
 -    Py_DECREF(os2);
 -#endif
 -    Py_XDECREF(newstr);
 +    Py_XDECREF(usrc);
 +    Py_XDECREF(udest);
      return NULL;
  }
 -#endif /* putenv */
 -
 -#ifdef HAVE_UNSETENV
 -PyDoc_STRVAR(posix_unsetenv__doc__,
 -"unsetenv(key)\n\n\
 -Delete an environment variable.");
 +#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
  
 -static PyObject *
 -posix_unsetenv(PyObject *self, PyObject *args)
 +#ifdef HAVE_TIMES
 +#if defined(PYCC_VACPP) && defined(PYOS_OS2)
 +static long
 +system_uptime(void)
  {
 -    PyObject *os1;
 -    char *s1;
 -#ifndef HAVE_BROKEN_UNSETENV
 -    int err;
 -#endif
 -
 -    if (!PyArg_ParseTuple(args, "O&:unsetenv",
 -                          PyUnicode_FSConverter, &os1))
 -        return NULL;
 -    s1 = PyBytes_AsString(os1);
 -
 -#ifdef HAVE_BROKEN_UNSETENV
 -    unsetenv(s1);
 -#else
 -    err = unsetenv(s1);
 -    if (err) {
 -        Py_DECREF(os1);
 -        return posix_error();
 -    }
 -#endif
 +    ULONG     value = 0;
  
 -    /* Remove the key from posix_putenv_garbage;
 -     * this will cause it to be collected.  This has to
 -     * happen after the real unsetenv() call because the
 -     * old value was still accessible until then.
 -     */
 -    if (PyDict_DelItem(posix_putenv_garbage, os1)) {
 -        /* really not much we can do; just leak */
 -        PyErr_Clear();
 -    }
 +    Py_BEGIN_ALLOW_THREADS
 +    DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &value, sizeof(value));
 +    Py_END_ALLOW_THREADS
  
 -    Py_DECREF(os1);
 -    Py_RETURN_NONE;
 +    return value;
  }
 -#endif /* unsetenv */
 -
 -PyDoc_STRVAR(posix_strerror__doc__,
 -"strerror(code) -> string\n\n\
 -Translate an error code to a message string.");
  
  static PyObject *
 -posix_strerror(PyObject *self, PyObject *args)
 +posix_times(PyObject *self, PyObject *noargs)
  {
 -    int code;
 -    char *message;
 -    if (!PyArg_ParseTuple(args, "i:strerror", &code))
 -        return NULL;
 -    message = strerror(code);
 -    if (message == NULL) {
 -        PyErr_SetString(PyExc_ValueError,
 -                        "strerror() argument out of range");
 -        return NULL;
 -    }
 -    return PyUnicode_FromString(message);
 +    /* Currently Only Uptime is Provided -- Others Later */
 +    return Py_BuildValue("ddddd",
 +                         (double)0 /* t.tms_utime / HZ */,
 +                         (double)0 /* t.tms_stime / HZ */,
 +                         (double)0 /* t.tms_cutime / HZ */,
 +                         (double)0 /* t.tms_cstime / HZ */,
 +                         (double)system_uptime() / 1000);
  }
 -
 -
 -#ifdef HAVE_SYS_WAIT_H
 -
 -#ifdef WCOREDUMP
 -PyDoc_STRVAR(posix_WCOREDUMP__doc__,
 -"WCOREDUMP(status) -> bool\n\n\
 -Return True if the process returning 'status' was dumped to a core file.");
 -
 +#else /* not OS2 */
 +#define NEED_TICKS_PER_SECOND
 +static long ticks_per_second = -1;
  static PyObject *
 -posix_WCOREDUMP(PyObject *self, PyObject *args)
 +posix_times(PyObject *self, PyObject *noargs)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &WAIT_STATUS_INT(status)))
 -        return NULL;
 -
 -    return PyBool_FromLong(WCOREDUMP(status));
 +    struct tms t;
 +    clock_t c;
 +    errno = 0;
 +    c = times(&t);
 +    if (c == (clock_t) -1)
 +        return posix_error();
 +    return Py_BuildValue("ddddd",
 +                         (double)t.tms_utime / ticks_per_second,
 +                         (double)t.tms_stime / ticks_per_second,
 +                         (double)t.tms_cutime / ticks_per_second,
 +                         (double)t.tms_cstime / ticks_per_second,
 +                         (double)c / ticks_per_second);
  }
 -#endif /* WCOREDUMP */
 +#endif /* not OS2 */
 +#endif /* HAVE_TIMES */
  
 -#ifdef WIFCONTINUED
 -PyDoc_STRVAR(posix_WIFCONTINUED__doc__,
 -"WIFCONTINUED(status) -> bool\n\n\
 -Return True if the process returning 'status' was continued from a\n\
 -job control stop.");
  
 +#ifdef MS_WINDOWS
 +#define HAVE_TIMES      /* so the method table will pick it up */
  static PyObject *
 -posix_WIFCONTINUED(PyObject *self, PyObject *args)
 +posix_times(PyObject *self, PyObject *noargs)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WCONTINUED", &WAIT_STATUS_INT(status)))
 -        return NULL;
 -
 -    return PyBool_FromLong(WIFCONTINUED(status));
 -}
 -#endif /* WIFCONTINUED */
 +    FILETIME create, exit, kernel, user;
 +    HANDLE hProc;
 +    hProc = GetCurrentProcess();
 +    GetProcessTimes(hProc, &create, &exit, &kernel, &user);
 +    /* The fields of a FILETIME structure are the hi and lo part
 +       of a 64-bit value expressed in 100 nanosecond units.
 +       1e7 is one second in such units; 1e-7 the inverse.
 +       429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
 +    */
 +    return Py_BuildValue(
 +        "ddddd",
 +        (double)(user.dwHighDateTime*429.4967296 +
 +                 user.dwLowDateTime*1e-7),
 +        (double)(kernel.dwHighDateTime*429.4967296 +
 +                 kernel.dwLowDateTime*1e-7),
 +        (double)0,
 +        (double)0,
 +        (double)0);
 +}
 +#endif /* MS_WINDOWS */
  
 -#ifdef WIFSTOPPED
 -PyDoc_STRVAR(posix_WIFSTOPPED__doc__,
 -"WIFSTOPPED(status) -> bool\n\n\
 -Return True if the process returning 'status' was stopped.");
 +#ifdef HAVE_TIMES
 +PyDoc_STRVAR(posix_times__doc__,
 +"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n\
 +Return a tuple of floating point numbers indicating process times.");
 +#endif
 +
 +
 +#ifdef HAVE_GETSID
 +PyDoc_STRVAR(posix_getsid__doc__,
 +"getsid(pid) -> sid\n\n\
 +Call the system call getsid().");
  
  static PyObject *
 -posix_WIFSTOPPED(PyObject *self, PyObject *args)
 +posix_getsid(PyObject *self, PyObject *args)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &WAIT_STATUS_INT(status)))
 +    pid_t pid;
 +    int sid;
 +    if (!PyArg_ParseTuple(args, _Py_PARSE_PID ":getsid", &pid))
          return NULL;
 -
 -    return PyBool_FromLong(WIFSTOPPED(status));
 +    sid = getsid(pid);
 +    if (sid < 0)
 +        return posix_error();
 +    return PyLong_FromLong((long)sid);
  }
 -#endif /* WIFSTOPPED */
 +#endif /* HAVE_GETSID */
  
 -#ifdef WIFSIGNALED
 -PyDoc_STRVAR(posix_WIFSIGNALED__doc__,
 -"WIFSIGNALED(status) -> bool\n\n\
 -Return True if the process returning 'status' was terminated by a signal.");
 +
 +#ifdef HAVE_SETSID
 +PyDoc_STRVAR(posix_setsid__doc__,
 +"setsid()\n\n\
 +Call the system call setsid().");
  
  static PyObject *
 -posix_WIFSIGNALED(PyObject *self, PyObject *args)
 +posix_setsid(PyObject *self, PyObject *noargs)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 +    if (setsid() < 0)
 +        return posix_error();
 +    Py_INCREF(Py_None);
 +    return Py_None;
 +}
 +#endif /* HAVE_SETSID */
  
 -    if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &WAIT_STATUS_INT(status)))
 -        return NULL;
 +#ifdef HAVE_SETPGID
 +PyDoc_STRVAR(posix_setpgid__doc__,
 +"setpgid(pid, pgrp)\n\n\
 +Call the system call setpgid().");
  
 -    return PyBool_FromLong(WIFSIGNALED(status));
 +static PyObject *
 +posix_setpgid(PyObject *self, PyObject *args)
 +{
 +    pid_t pid;
 +    int pgrp;
 +    if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i:setpgid", &pid, &pgrp))
 +        return NULL;
 +    if (setpgid(pid, pgrp) < 0)
 +        return posix_error();
 +    Py_INCREF(Py_None);
 +    return Py_None;
  }
 -#endif /* WIFSIGNALED */
 +#endif /* HAVE_SETPGID */
  
 -#ifdef WIFEXITED
 -PyDoc_STRVAR(posix_WIFEXITED__doc__,
 -"WIFEXITED(status) -> bool\n\n\
 -Return true if the process returning 'status' exited using the exit()\n\
 -system call.");
 +
 +#ifdef HAVE_TCGETPGRP
 +PyDoc_STRVAR(posix_tcgetpgrp__doc__,
 +"tcgetpgrp(fd) -> pgid\n\n\
 +Return the process group associated with the terminal given by a fd.");
  
  static PyObject *
 -posix_WIFEXITED(PyObject *self, PyObject *args)
 +posix_tcgetpgrp(PyObject *self, PyObject *args)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WIFEXITED", &WAIT_STATUS_INT(status)))
 +    int fd;
 +    pid_t pgid;
 +    if (!PyArg_ParseTuple(args, "i:tcgetpgrp", &fd))
          return NULL;
 -
 -    return PyBool_FromLong(WIFEXITED(status));
 +    pgid = tcgetpgrp(fd);
 +    if (pgid < 0)
 +        return posix_error();
 +    return PyLong_FromPid(pgid);
  }
 -#endif /* WIFEXITED */
 +#endif /* HAVE_TCGETPGRP */
  
 -#ifdef WEXITSTATUS
 -PyDoc_STRVAR(posix_WEXITSTATUS__doc__,
 -"WEXITSTATUS(status) -> integer\n\n\
 -Return the process return code from 'status'.");
 +
 +#ifdef HAVE_TCSETPGRP
 +PyDoc_STRVAR(posix_tcsetpgrp__doc__,
 +"tcsetpgrp(fd, pgid)\n\n\
 +Set the process group associated with the terminal given by a fd.");
  
  static PyObject *
 -posix_WEXITSTATUS(PyObject *self, PyObject *args)
 +posix_tcsetpgrp(PyObject *self, PyObject *args)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &WAIT_STATUS_INT(status)))
 +    int fd;
 +    pid_t pgid;
 +    if (!PyArg_ParseTuple(args, "i" _Py_PARSE_PID ":tcsetpgrp", &fd, &pgid))
          return NULL;
 -
 -    return Py_BuildValue("i", WEXITSTATUS(status));
 +    if (tcsetpgrp(fd, pgid) < 0)
 +        return posix_error();
 +    Py_INCREF(Py_None);
 +    return Py_None;
  }
 -#endif /* WEXITSTATUS */
 +#endif /* HAVE_TCSETPGRP */
  
 -#ifdef WTERMSIG
 -PyDoc_STRVAR(posix_WTERMSIG__doc__,
 -"WTERMSIG(status) -> integer\n\n\
 -Return the signal that terminated the process that provided the 'status'\n\
 -value.");
 +/* Functions acting on file descriptors */
 +
 +PyDoc_STRVAR(posix_open__doc__,
 +"open(filename, flag [, mode=0777]) -> fd\n\n\
 +Open a file (for low level IO).");
  
  static PyObject *
 -posix_WTERMSIG(PyObject *self, PyObject *args)
 +posix_open(PyObject *self, PyObject *args)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 +    PyObject *ofile;
 +    char *file;
 +    int flag;
 +    int mode = 0777;
 +    int fd;
  
 -    if (!PyArg_ParseTuple(args, "i:WTERMSIG", &WAIT_STATUS_INT(status)))
 -        return NULL;
 +#ifdef MS_WINDOWS
 +    PyObject *po;
 +    if (PyArg_ParseTuple(args, "Ui|i:open", &po, &flag, &mode)) {
 +        wchar_t *wpath = PyUnicode_AsUnicode(po);
 +        if (wpath == NULL)
 +            return NULL;
  
 -    return Py_BuildValue("i", WTERMSIG(status));
 +        Py_BEGIN_ALLOW_THREADS
 +        fd = _wopen(wpath, flag, mode);
 +        Py_END_ALLOW_THREADS
 +        if (fd < 0)
 +            return posix_error();
 +        return PyLong_FromLong((long)fd);
 +    }
 +    /* Drop the argument parsing error as narrow strings
 +       are also valid. */
 +    PyErr_Clear();
 +#endif
 +
 +    if (!PyArg_ParseTuple(args, "O&i|i:open",
 +                          PyUnicode_FSConverter, &ofile,
 +                          &flag, &mode))
 +        return NULL;
 +#ifdef MS_WINDOWS
 +    if (win32_warn_bytes_api()) {
 +        Py_DECREF(ofile);
 +        return NULL;
 +    }
 +#endif
 +    file = PyBytes_AsString(ofile);
 +    Py_BEGIN_ALLOW_THREADS
 +    fd = open(file, flag, mode);
 +    Py_END_ALLOW_THREADS
 +    if (fd < 0)
 +        return posix_error_with_allocated_filename(ofile);
 +    Py_DECREF(ofile);
 +    return PyLong_FromLong((long)fd);
  }
 -#endif /* WTERMSIG */
  
 -#ifdef WSTOPSIG
 -PyDoc_STRVAR(posix_WSTOPSIG__doc__,
 -"WSTOPSIG(status) -> integer\n\n\
 -Return the signal that stopped the process that provided\n\
 -the 'status' value.");
 +
 +PyDoc_STRVAR(posix_close__doc__,
 +"close(fd)\n\n\
 +Close a file descriptor (for low level IO).");
  
  static PyObject *
 -posix_WSTOPSIG(PyObject *self, PyObject *args)
 +posix_close(PyObject *self, PyObject *args)
  {
 -    WAIT_TYPE status;
 -    WAIT_STATUS_INT(status) = 0;
 -
 -    if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &WAIT_STATUS_INT(status)))
 +    int fd, res;
 +    if (!PyArg_ParseTuple(args, "i:close", &fd))
          return NULL;
 -
 -    return Py_BuildValue("i", WSTOPSIG(status));
 +    if (!_PyVerify_fd(fd))
 +        return posix_error();
 +    Py_BEGIN_ALLOW_THREADS
 +    res = close(fd);
 +    Py_END_ALLOW_THREADS
 +    if (res < 0)
 +        return posix_error();
 +    Py_INCREF(Py_None);
 +    return Py_None;
  }
 -#endif /* WSTOPSIG */
 -
 -#endif /* HAVE_SYS_WAIT_H */
  
  
 -#if defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H)
 -#ifdef _SCO_DS
 -/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the
 -   needed definitions in sys/statvfs.h */
 -#define _SVID3
 -#endif
 -#include <sys/statvfs.h>
 +PyDoc_STRVAR(posix_closerange__doc__,
 +"closerange(fd_low, fd_high)\n\n\
 +Closes all file descriptors in [fd_low, fd_high), ignoring errors.");
  
 -static PyObject*
 -_pystatvfs_fromstructstatvfs(struct statvfs st) {
 -    PyObject *v = PyStructSequence_New(&StatVFSResultType);
 -    if (v == NULL)
 +static PyObject *
 +posix_closerange(PyObject *self, PyObject *args)
 +{
 +    int fd_from, fd_to, i;
 +    if (!PyArg_ParseTuple(args, "ii:closerange", &fd_from, &fd_to))
          return NULL;
 +    Py_BEGIN_ALLOW_THREADS
 +    for (i = fd_from; i < fd_to; i++)
 +        if (_PyVerify_fd(i))
 +            close(i);
 +    Py_END_ALLOW_THREADS
 +    Py_RETURN_NONE;
 +}
  
 -#if !defined(HAVE_LARGEFILE_SUPPORT)
 -    PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long) st.f_bsize));
 -    PyStructSequence_SET_ITEM(v, 1, PyLong_FromLong((long) st.f_frsize));
 -    PyStructSequence_SET_ITEM(v, 2, PyLong_FromLong((long) st.f_blocks));
 -    PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long) st.f_bfree));
 -    PyStructSequence_SET_ITEM(v, 4, PyLong_FromLong((long) st.f_bavail));
 -    PyStructSequence_SET_ITEM(v, 5, PyLong_FromLong((long) st.f_files));
 -    PyStructSequence_SET_ITEM(v, 6, PyLong_FromLong((long) st.f_ffree));
 -    PyStructSequence_SET_ITEM(v, 7, PyLong_FromLong((long) st.f_favail));
 -    PyStructSequence_SET_ITEM(v, 8, PyLong_FromLong((long) st.f_flag));
 -    PyStructSequence_SET_ITEM(v, 9, PyLong_FromLong((long) st.f_namemax));
 -#else
 -    PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long) st.f_bsize));
 -    PyStructSequence_SET_ITEM(v, 1, PyLong_FromLong((long) st.f_frsize));
 -    PyStructSequence_SET_ITEM(v, 2,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_blocks));
 -    PyStructSequence_SET_ITEM(v, 3,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_bfree));
 -    PyStructSequence_SET_ITEM(v, 4,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_bavail));
 -    PyStructSequence_SET_ITEM(v, 5,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_files));
 -    PyStructSequence_SET_ITEM(v, 6,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_ffree));
 -    PyStructSequence_SET_ITEM(v, 7,
 -                              PyLong_FromLongLong((PY_LONG_LONG) st.f_favail));
 -    PyStructSequence_SET_ITEM(v, 8, PyLong_FromLong((long) st.f_flag));
 -    PyStructSequence_SET_ITEM(v, 9, PyLong_FromLong((long) st.f_namemax));
 -#endif
  
 -    return v;
 +PyDoc_STRVAR(posix_dup__doc__,
 +"dup(fd) -> fd2\n\n\
 +Return a duplicate of a file descriptor.");
 +
 +static PyObject *
 +posix_dup(PyObject *self, PyObject *args)
 +{
 +    int fd;
 +    if (!PyArg_ParseTuple(args, "i:dup", &fd))
 +        return NULL;
 +    if (!_PyVerify_fd(fd))
 +        return posix_error();
 +    fd = dup(fd);
 +    if (fd < 0)
 +        return posix_error();
 +    return PyLong_FromLong((long)fd);
  }
  
 -PyDoc_STRVAR(posix_fstatvfs__doc__,
 -"fstatvfs(fd) -> statvfs result\n\n\
 -Perform an fstatvfs system call on the given fd.");
 +
 +PyDoc_STRVAR(posix_dup2__doc__,
 +"dup2(old_fd, new_fd)\n\n\
 +Duplicate file descriptor.");
  
  static PyObject *
 -posix_fstatvfs(PyObject *self, PyObject *args)
 +posix_dup2(PyObject *self, PyObject *args)
  {
 -    int fd, res;
 -    struct statvfs st;
 +    int fd, fd2, res;
 +    if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2))
 +        return NULL;
 +    if (!_PyVerify_fd_dup2(fd, fd2))
 +        return posix_error();
 +    res = dup2(fd, fd2);
 +    if (res < 0)
 +        return posix_error();
 +    Py_INCREF(Py_None);
 +    return Py_None;
 +}
  
 -    if (!PyArg_ParseTuple(args, "i:fstatvfs", &fd))
 +#ifdef HAVE_LOCKF
 +PyDoc_STRVAR(posix_lockf__doc__,
 +"lockf(fd, cmd, len)\n\n\
 +Apply, test or remove a POSIX lock on an open file descriptor.\n\n\
 +fd is an open file descriptor.\n\
 +cmd specifies the command to use - one of F_LOCK, F_TLOCK, F_ULOCK or\n\
 +F_TEST.\n\
 +len specifies the section of the file to lock.");
 +
 +static PyObject *
 +posix_lockf(PyObject *self, PyObject *args)
 +{
 +    int fd, cmd, res;
 +    off_t len;
 +    if (!PyArg_ParseTuple(args, "iiO&:lockf",
 +            &fd, &cmd, _parse_off_t, &len))
          return NULL;
 +
      Py_BEGIN_ALLOW_THREADS
 -    res = fstatvfs(fd, &st);
 +    res = lockf(fd, cmd, len);
      Py_END_ALLOW_THREADS
 -    if (res != 0)
 +
 +    if (res < 0)
          return posix_error();
  
 -    return _pystatvfs_fromstructstatvfs(st);
 +    Py_RETURN_NONE;
  }
 -#endif /* HAVE_FSTATVFS && HAVE_SYS_STATVFS_H */
 -
 +#endif
  
 -#if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H)
 -#include <sys/statvfs.h>
  
 -PyDoc_STRVAR(posix_statvfs__doc__,
 -"statvfs(path) -> statvfs result\n\n\
 -Perform a statvfs system call on the given path.");
 +PyDoc_STRVAR(posix_lseek__doc__,
 +"lseek(fd, pos, how) -> newpos\n\n\
 +Set the current position of a file descriptor.\n\
 +Return the new cursor position in bytes, starting from the beginning.");
  
  static PyObject *
 -posix_statvfs(PyObject *self, PyObject *args)
 +posix_lseek(PyObject *self, PyObject *args)
  {
 -    char *path;
 -    int res;
 -    struct statvfs st;
 -    if (!PyArg_ParseTuple(args, "s:statvfs", &path))
 +    int fd, how;
 +#if defined(MS_WIN64) || defined(MS_WINDOWS)
 +    PY_LONG_LONG pos, res;
 +#else
 +    off_t pos, res;
 +#endif
 +    PyObject *posobj;
 +    if (!PyArg_ParseTuple(args, "iOi:lseek", &fd, &posobj, &how))
 +        return NULL;
 +#ifdef SEEK_SET
 +    /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
 +    switch (how) {
 +    case 0: how = SEEK_SET; break;
 +    case 1: how = SEEK_CUR; break;
 +    case 2: how = SEEK_END; break;
 +    }
 +#endif /* SEEK_END */
 +
 +#if !defined(HAVE_LARGEFILE_SUPPORT)
 +    pos = PyLong_AsLong(posobj);
 +#else
 +    pos = PyLong_AsLongLong(posobj);
 +#endif
 +    if (PyErr_Occurred())
          return NULL;
 +
 +    if (!_PyVerify_fd(fd))
 +        return posix_error();
      Py_BEGIN_ALLOW_THREADS
 -    res = statvfs(path, &st);
 +#if defined(MS_WIN64) || defined(MS_WINDOWS)
 +    res = _lseeki64(fd, pos, how);
 +#else
 +    res = lseek(fd, pos, how);
 +#endif
      Py_END_ALLOW_THREADS
 -    if (res != 0)
 -        return posix_error_with_filename(path);
 +    if (res < 0)
 +        return posix_error();
  
 -    return _pystatvfs_fromstructstatvfs(st);
 +#if !defined(HAVE_LARGEFILE_SUPPORT)
 +    return PyLong_FromLong(res);
 +#else
 +    return PyLong_FromLongLong(res);
 +#endif
  }
 -#endif /* HAVE_STATVFS */
  
 -/* This is used for fpathconf(), pathconf(), confstr() and sysconf().
 - * It maps strings representing configuration variable names to
 - * integer values, allowing those functions to be called with the
 - * magic names instead of polluting the module's namespace with tons of
 - * rarely-used constants.  There are three separate tables that use
 - * these definitions.
 - *
 - * This code is always included, even if none of the interfaces that
 - * need it are included.  The #if hackery needed to avoid it would be
 - * sufficiently pervasive that it's not worth the loss of readability.
 - */
 -struct constdef {
 -    char *name;
 -    long value;
 -};
  
 -static int
 -conv_confname(PyObject *arg, int *valuep, struct constdef *table,
 -              size_t tablesize)
 +PyDoc_STRVAR(posix_read__doc__,
 +"read(fd, buffersize) -> string\n\n\
 +Read a file descriptor.");
 +
 +static PyObject *
 +posix_read(PyObject *self, PyObject *args)
  {
 -    if (PyLong_Check(arg)) {
 -        *valuep = PyLong_AS_LONG(arg);
 -        return 1;
 +    int fd, size;
 +    Py_ssize_t n;
 +    PyObject *buffer;
 +    if (!PyArg_ParseTuple(args, "ii:read", &fd, &size))
 +        return NULL;
 +    if (size < 0) {
 +        errno = EINVAL;
 +        return posix_error();
      }
 -    else {
 -        /* look up the value in the table using a binary search */
 -        size_t lo = 0;
 -        size_t mid;
 -        size_t hi = tablesize;
 -        int cmp;
 -        const char *confname;
 -        if (!PyUnicode_Check(arg)) {
 -            PyErr_SetString(PyExc_TypeError,
 -                "configuration names must be strings or integers");
 -            return 0;
 -        }
 -        confname = _PyUnicode_AsString(arg);
 -        if (confname == NULL)
 -            return 0;
 -        while (lo < hi) {
 -            mid = (lo + hi) / 2;
 -            cmp = strcmp(confname, table[mid].name);
 -            if (cmp < 0)
 -                hi = mid;
 -            else if (cmp > 0)
 -                lo = mid + 1;
 -            else {
 -                *valuep = table[mid].value;
 -                return 1;
 -            }
 +    buffer = PyBytes_FromStringAndSize((char *)NULL, size);
 +    if (buffer == NULL)
 +        return NULL;
 +    if (!_PyVerify_fd(fd)) {
 +        Py_DECREF(buffer);
 +        return posix_error();
 +    }
 +    Py_BEGIN_ALLOW_THREADS
 +    n = read(fd, PyBytes_AS_STRING(buffer), size);
 +    Py_END_ALLOW_THREADS
 +    if (n < 0) {
 +        Py_DECREF(buffer);
 +        return posix_error();
 +    }
 +    if (n != size)
 +        _PyBytes_Resize(&buffer, n);
 +    return buffer;
 +}
 +
 +#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \
 +    || defined(__APPLE__))) || defined(HAVE_READV) || defined(HAVE_WRITEV)
 +static Py_ssize_t
 +iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, int cnt, int type)
 +{
 +    int i, j;
 +    Py_ssize_t blen, total = 0;
 +
 +    *iov = PyMem_New(struct iovec, cnt);
 +    if (*iov == NULL) {
 +        PyErr_NoMemory();
 +        return total;
 +    }
 +
 +    *buf = PyMem_New(Py_buffer, cnt);
 +    if (*buf == NULL) {
 +        PyMem_Del(*iov);
 +        PyErr_NoMemory();
 +        return total;
 +    }
 +
 +    for (i = 0; i < cnt; i++) {
 +        PyObject *item = PySequence_GetItem(seq, i);
 +        if (item == NULL)
 +            goto fail;
 +        if (PyObject_GetBuffer(item, &(*buf)[i], type) == -1) {
 +            Py_DECREF(item);
 +            goto fail;
          }
 -        PyErr_SetString(PyExc_ValueError, "unrecognized configuration name");
 -        return 0;
 +        Py_DECREF(item);
 +        (*iov)[i].iov_base = (*buf)[i].buf;
 +        blen = (*buf)[i].len;
 +        (*iov)[i].iov_len = blen;
 +        total += blen;
 +    }
 +    return total;
 +
 +fail:
 +    PyMem_Del(*iov);
 +    for (j = 0; j < i; j++) {
 +        PyBuffer_Release(&(*buf)[j]);
 +    }
 +    PyMem_Del(*buf);
 +    return 0;
 +}
 +
 +static void
 +iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt)
 +{
 +    int i;
 +    PyMem_Del(iov);
 +    for (i = 0; i < cnt; i++) {
 +        PyBuffer_Release(&buf[i]);
      }
 +    PyMem_Del(buf);
  }
 +#endif
  
 +#ifdef HAVE_READV
 +PyDoc_STRVAR(posix_readv__doc__,
 +"readv(fd, buffers) -> bytesread\n\n\
 +Read from a file descriptor into a number of writable buffers. buffers\n\
 +is an arbitrary sequence of writable buffers.\n\
 +Returns the total number of bytes read.");
  
 -#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF)
 -static struct constdef  posix_constants_pathconf[] = {
 -#ifdef _PC_ABI_AIO_XFER_MAX
 -    {"PC_ABI_AIO_XFER_MAX",     _PC_ABI_AIO_XFER_MAX},
 +static PyObject *
 +posix_readv(PyObject *self, PyObject *args)
 +{
 +    int fd, cnt;
 +    Py_ssize_t n;
 +    PyObject *seq;
 +    struct iovec *iov;
 +    Py_buffer *buf;
 +
 +    if (!PyArg_ParseTuple(args, "iO:readv", &fd, &seq))
 +        return NULL;
 +    if (!PySequence_Check(seq)) {
 +        PyErr_SetString(PyExc_TypeError,
 +            "readv() arg 2 must be a sequence");
 +        return NULL;
 +    }
 +    cnt = PySequence_Size(seq);
 +
 +    if (!iov_setup(&iov, &buf, seq, cnt, PyBUF_WRITABLE))
 +        return NULL;
 +
 +    Py_BEGIN_ALLOW_THREADS
 +    n = readv(fd, iov, cnt);
 +    Py_END_ALLOW_THREADS
 +
 +    iov_cleanup(iov, buf, cnt);
 +    return PyLong_FromSsize_t(n);
 +}
  #endif
 -#ifdef _PC_ABI_ASYNC_IO
 -    {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO},
 -#endif
 -#ifdef _PC_ASYNC_IO
 -    {"PC_ASYNC_IO",     _PC_ASYNC_IO},
 -#endif
 -#ifdef _PC_CHOWN_RESTRICTED
 -    {"PC_CHOWN_RESTRICTED",     _PC_CHOWN_RESTRICTED},
 -#endif
 -#ifdef _PC_FILESIZEBITS
 -    {"PC_FILESIZEBITS", _PC_FILESIZEBITS},
 -#endif
 -#ifdef _PC_LAST
 -    {"PC_LAST", _PC_LAST},
 -#endif
 -#ifdef _PC_LINK_MAX
 -    {"PC_LINK_MAX",     _PC_LINK_MAX},
 -#endif
 -#ifdef _PC_MAX_CANON
 -    {"PC_MAX_CANON",    _PC_MAX_CANON},
 -#endif
 -#ifdef _PC_MAX_INPUT
 -    {"PC_MAX_INPUT",    _PC_MAX_INPUT},
 -#endif
 -#ifdef _PC_NAME_MAX
 -    {"PC_NAME_MAX",     _PC_NAME_MAX},
 -#endif
 -#ifdef _PC_NO_TRUNC
 -    {"PC_NO_TRUNC",     _PC_NO_TRUNC},
 -#endif
 -#ifdef _PC_PATH_MAX
 -    {"PC_PATH_MAX",     _PC_PATH_MAX},
 -#endif
 -#ifdef _PC_PIPE_BUF
 -    {"PC_PIPE_BUF",     _PC_PIPE_BUF},
 -#endif
 -#ifdef _PC_PRIO_IO
 -    {"PC_PRIO_IO",      _PC_PRIO_IO},
 -#endif
 -#ifdef _PC_SOCK_MAXBUF
 -    {"PC_SOCK_MAXBUF",  _PC_SOCK_MAXBUF},
 -#endif
 -#ifdef _PC_SYNC_IO
 -    {"PC_SYNC_IO",      _PC_SYNC_IO},
 -#endif
 -#ifdef _PC_VDISABLE
 -    {"PC_VDISABLE",     _PC_VDISABLE},
 -#endif
 -#ifdef _PC_ACL_ENABLED
 -    {"PC_ACL_ENABLED",  _PC_ACL_ENABLED},
 -#endif
 -#ifdef _PC_MIN_HOLE_SIZE
 -    {"PC_MIN_HOLE_SIZE",    _PC_MIN_HOLE_SIZE},
 -#endif
 -#ifdef _PC_ALLOC_SIZE_MIN
 -    {"PC_ALLOC_SIZE_MIN",   _PC_ALLOC_SIZE_MIN},
 -#endif
 -#ifdef _PC_REC_INCR_XFER_SIZE
 -    {"PC_REC_INCR_XFER_SIZE",   _PC_REC_INCR_XFER_SIZE},
 -#endif
 -#ifdef _PC_REC_MAX_XFER_SIZE
 -    {"PC_REC_MAX_XFER_SIZE",    _PC_REC_MAX_XFER_SIZE},
 -#endif
 -#ifdef _PC_REC_MIN_XFER_SIZE
 -    {"PC_REC_MIN_XFER_SIZE",    _PC_REC_MIN_XFER_SIZE},
 -#endif
 -#ifdef _PC_REC_XFER_ALIGN
 -    {"PC_REC_XFER_ALIGN",   _PC_REC_XFER_ALIGN},
 -#endif
 -#ifdef _PC_SYMLINK_MAX
 -    {"PC_SYMLINK_MAX",  _PC_SYMLINK_MAX},
 -#endif
 -#ifdef _PC_XATTR_ENABLED
 -    {"PC_XATTR_ENABLED",    _PC_XATTR_ENABLED},
 -#endif
 -#ifdef _PC_XATTR_EXISTS
 -    {"PC_XATTR_EXISTS", _PC_XATTR_EXISTS},
 -#endif
 -#ifdef _PC_TIMESTAMP_RESOLUTION
 -    {"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION},
 -#endif
 -};
  
 -static int
 -conv_path_confname(PyObject *arg, int *valuep)
 +#ifdef HAVE_PREAD
 +PyDoc_STRVAR(posix_pread__doc__,
 +"pread(fd, buffersize, offset) -> string\n\n\
 +Read from a file descriptor, fd, at a position of offset. It will read up\n\
 +to buffersize number of bytes. The file offset remains unchanged.");
 +
 +static PyObject *
 +posix_pread(PyObject *self, PyObject *args)
  {
 -    return conv_confname(arg, valuep, posix_constants_pathconf,
 -                         sizeof(posix_constants_pathconf)
 -                           / sizeof(struct constdef));
 +    int fd, size;
 +    off_t offset;
 +    Py_ssize_t n;
 +    PyObject *buffer;
 +    if (!PyArg_ParseTuple(args, "iiO&:pread", &fd, &size, _parse_off_t, &offset))
 +        return NULL;
 +
 +    if (size < 0) {
 +        errno = EINVAL;
 +        return posix_error();
 +    }
 +    buffer = PyBytes_FromStringAndSize((char *)NULL, size);
 +    if (buffer == NULL)
 +        return NULL;
 +    if (!_PyVerify_fd(fd)) {
 +        Py_DECREF(buffer);
 +        return posix_error();
 +    }
 +    Py_BEGIN_ALLOW_THREADS
 +    n = pread(fd, PyBytes_AS_STRING(buffer), size, offset);
 +    Py_END_ALLOW_THREADS
 +    if (n < 0) {
 +        Py_DECREF(buffer);
 +        return posix_error();
 +    }
 +    if (n != size)
 +        _PyBytes_Resize(&buffer, n);
 +    return buffer;
  }
  #endif