From: Antoine Pitrou Date: Tue, 24 Jan 2012 08:05:18 +0000 (+0100) Subject: Issue #13772: In os.symlink() under Windows, do not try to guess the link X-Git-Tag: v3.3.0a1~312^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=91ecea24f53b99aa190f6b17c2b51d95cb09d233;p=python Issue #13772: In os.symlink() under Windows, do not try to guess the link target's type (file or directory). The detection was buggy and made the call non-atomic (therefore prone to race conditions). --- 91ecea24f53b99aa190f6b17c2b51d95cb09d233 diff --cc Modules/posixmodule.c index 61a0439539,0afab3cd93..1797a2f64a --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@@ -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 +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 -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