Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
15076 lines (12870 sloc) 392 KB
/* POSIX module implementation */
/* This file is also used for Windows NT/MS-Win. In that case the
module actually calls itself 'nt', not 'posix', and a few
functions are either unimplemented or implemented differently. The source
assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
of the compiler used. Different compilers define their own feature
test macro, e.g. '_MSC_VER'. */
#ifdef __APPLE__
/*
* Step 1 of support for weak-linking a number of symbols existing on
* OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
* at the end of this file for more information.
*/
# pragma weak lchown
# pragma weak statvfs
# pragma weak fstatvfs
#endif /* __APPLE__ */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_fileutils.h"
#ifdef MS_WINDOWS
/* include <windows.h> early to avoid conflict with pycore_condvar.h:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
FSCTL_GET_REPARSE_POINT is not exported with WIN32_LEAN_AND_MEAN. */
# include <windows.h>
#endif
#ifdef __VXWORKS__
# include "pycore_bitutils.h" // _Py_popcount32()
#endif
#include "pycore_ceval.h" // _PyEval_ReInitThreads()
#include "pycore_import.h" // _PyImport_ReInitLock()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "structmember.h" // PyMemberDef
#ifndef MS_WINDOWS
# include "posixmodule.h"
#else
# include "winreparse.h"
#endif
/* On android API level 21, 'AT_EACCESS' is not declared although
* HAVE_FACCESSAT is defined. */
#ifdef __ANDROID__
# undef HAVE_FACCESSAT
#endif
#include <stdio.h> /* needed for ctermid() */
#ifdef __cplusplus
extern "C" {
#endif
PyDoc_STRVAR(posix__doc__,
"This module provides access to operating system functionality that is\n\
standardized by the C Standard and the POSIX standard (a thinly\n\
disguised Unix interface). Refer to the library manual and\n\
corresponding Unix manual entries for more information on calls.");
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
/* GNU C Library: major(), minor(), makedev() */
# include <sys/sysmacros.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h> // WNOHANG
#endif
#ifdef HAVE_LINUX_WAIT_H
# include <linux/wait.h> // P_PIDFD
#endif
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_GRP_H
# include <grp.h>
#endif
#ifdef HAVE_SYSEXITS_H
# include <sysexits.h>
#endif
#ifdef HAVE_SYS_LOADAVG_H
# include <sys/loadavg.h>
#endif
#ifdef HAVE_SYS_SENDFILE_H
# include <sys/sendfile.h>
#endif
#if defined(__APPLE__)
# include <copyfile.h>
#endif
#ifdef HAVE_SCHED_H
# include <sched.h>
#endif
#ifdef HAVE_COPY_FILE_RANGE
# include <unistd.h>
#endif
#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY)
# undef HAVE_SCHED_SETAFFINITY
#endif
#if defined(HAVE_SYS_XATTR_H) && defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__)
# define USE_XATTRS
#endif
#ifdef USE_XATTRS
# include <sys/xattr.h>
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
# ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
# endif
#endif
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif
#ifdef __hpux
# include <sys/mpctl.h>
#endif
#if defined(__DragonFly__) || \
defined(__OpenBSD__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__APPLE__)
# include <sys/sysctl.h>
#endif
#ifdef HAVE_LINUX_RANDOM_H
# include <linux/random.h>
#endif
#ifdef HAVE_GETRANDOM_SYSCALL
# include <sys/syscall.h>
#endif
#if defined(MS_WINDOWS)
# define TERMSIZE_USE_CONIO
#elif defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h>
# if defined(HAVE_TERMIOS_H)
# include <termios.h>
# endif
# if defined(TIOCGWINSZ)
# define TERMSIZE_USE_IOCTL
# endif
#endif /* MS_WINDOWS */
/* Various compilers have only certain posix functions */
/* XXX Gosh I wish these were all moved into pyconfig.h */
#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */
# define HAVE_OPENDIR 1
# define HAVE_SYSTEM 1
# include <process.h>
#else
# ifdef _MSC_VER
/* Microsoft compiler */
# define HAVE_GETPPID 1
# define HAVE_GETLOGIN 1
# define HAVE_SPAWNV 1
# define HAVE_EXECV 1
# define HAVE_WSPAWNV 1
# define HAVE_WEXECV 1
# define HAVE_PIPE 1
# define HAVE_SYSTEM 1
# define HAVE_CWAIT 1
# define HAVE_FSYNC 1
# define fsync _commit
# else
/* Unix functions that the configure script doesn't check for */
# ifndef __VXWORKS__
# define HAVE_EXECV 1
# define HAVE_FORK 1
# if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */
# define HAVE_FORK1 1
# endif
# endif
# define HAVE_GETEGID 1
# define HAVE_GETEUID 1
# define HAVE_GETGID 1
# define HAVE_GETPPID 1
# define HAVE_GETUID 1
# define HAVE_KILL 1
# define HAVE_OPENDIR 1
# define HAVE_PIPE 1
# define HAVE_SYSTEM 1
# define HAVE_WAIT 1
# define HAVE_TTYNAME 1
# endif /* _MSC_VER */
#endif /* ! __WATCOMC__ || __QNX__ */
_Py_IDENTIFIER(__fspath__);
/*[clinic input]
# one of the few times we lie about this name!
module os
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=94a0f0f978acae17]*/
#ifndef _MSC_VER
#if defined(__sgi)&&_COMPILER_VERSION>=700
/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode
(default) */
extern char *ctermid_r(char *);
#endif
#endif /* !_MSC_VER */
#if defined(__VXWORKS__)
# include <vxCpuLib.h>
# include <rtpLib.h>
# include <wait.h>
# include <taskLib.h>
# ifndef _P_WAIT
# define _P_WAIT 0
# define _P_NOWAIT 1
# define _P_NOWAITO 1
# endif
#endif /* __VXWORKS__ */
#ifdef HAVE_POSIX_SPAWN
# include <spawn.h>
#endif
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif /* HAVE_UTIME_H */
#ifdef HAVE_SYS_UTIME_H
# include <sys/utime.h>
# define HAVE_UTIME_H /* pretend we do for the rest of this file */
#endif /* HAVE_SYS_UTIME_H */
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif /* HAVE_SYS_TIMES_H */
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#ifdef HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif /* HAVE_SYS_UTSNAME_H */
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# if defined(__WATCOMC__) && !defined(__QNX__)
# include <direct.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
# else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# endif
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#ifdef _MSC_VER
# ifdef HAVE_DIRECT_H
# include <direct.h>
# endif
# ifdef HAVE_IO_H
# include <io.h>
# endif
# ifdef HAVE_PROCESS_H
# include <process.h>
# endif
# ifndef IO_REPARSE_TAG_SYMLINK
# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
# endif
# ifndef IO_REPARSE_TAG_MOUNT_POINT
# define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
# endif
# include "osdefs.h" // SEP
# include <malloc.h>
# include <windows.h>
# include <shellapi.h> // ShellExecute()
# include <lmcons.h> // UNLEN
# define HAVE_SYMLINK
#endif /* _MSC_VER */
#ifndef MAXPATHLEN
# if defined(PATH_MAX) && PATH_MAX > 1024
# define MAXPATHLEN PATH_MAX
# else
# define MAXPATHLEN 1024
# endif
#endif /* MAXPATHLEN */
#ifdef UNION_WAIT
/* Emulate some macros on systems that have a union instead of macros */
# ifndef WIFEXITED
# define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump)
# endif
# ifndef WEXITSTATUS
# define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1)
# endif
# ifndef WTERMSIG
# define WTERMSIG(u_wait) ((u_wait).w_termsig)
# endif
# define WAIT_TYPE union wait
# define WAIT_STATUS_INT(s) (s.w_status)
#else
/* !UNION_WAIT */
# define WAIT_TYPE int
# define WAIT_STATUS_INT(s) (s)
#endif /* UNION_WAIT */
/* Don't use the "_r" form if we don't need it (also, won't have a
prototype for it, at least on Solaris -- maybe others as well?). */
#if defined(HAVE_CTERMID_R)
# define USE_CTERMID_R
#endif
/* choose the appropriate stat and fstat functions and return structs */
#undef STAT
#undef FSTAT
#undef STRUCT_STAT
#ifdef MS_WINDOWS
# define STAT win32_stat
# define LSTAT win32_lstat
# define FSTAT _Py_fstat_noraise
# define STRUCT_STAT struct _Py_stat_struct
#else
# define STAT stat
# define LSTAT lstat
# define FSTAT fstat
# define STRUCT_STAT struct stat
#endif
#if defined(MAJOR_IN_MKDEV)
# include <sys/mkdev.h>
#else
# if defined(MAJOR_IN_SYSMACROS)
# include <sys/sysmacros.h>
# endif
# if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H)
# include <sys/mkdev.h>
# endif
#endif
#ifdef MS_WINDOWS
# define INITFUNC PyInit_nt
# define MODNAME "nt"
#else
# define INITFUNC PyInit_posix
# define MODNAME "posix"
#endif
#if defined(__sun)
/* Something to implement in autoconf, not present in autoconf 2.69 */
# define HAVE_STRUCT_STAT_ST_FSTYPE 1
#endif
/* memfd_create is either defined in sys/mman.h or sys/memfd.h
* linux/memfd.h defines additional flags
*/
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#ifdef HAVE_SYS_MEMFD_H
# include <sys/memfd.h>
#endif
#ifdef HAVE_LINUX_MEMFD_H
# include <linux/memfd.h>
#endif
#ifdef _Py_MEMORY_SANITIZER
# include <sanitizer/msan_interface.h>
#endif
#ifdef HAVE_FORK
static void
run_at_forkers(PyObject *lst, int reverse)
{
Py_ssize_t i;
PyObject *cpy;
if (lst != NULL) {
assert(PyList_CheckExact(lst));
/* Use a list copy in case register_at_fork() is called from
* one of the callbacks.
*/
cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst));
if (cpy == NULL)
PyErr_WriteUnraisable(lst);
else {
if (reverse)
PyList_Reverse(cpy);
for (i = 0; i < PyList_GET_SIZE(cpy); i++) {
PyObject *func, *res;
func = PyList_GET_ITEM(cpy, i);
res = _PyObject_CallNoArg(func);
if (res == NULL)
PyErr_WriteUnraisable(func);
else
Py_DECREF(res);
}
Py_DECREF(cpy);
}
}
}
void
PyOS_BeforeFork(void)
{
run_at_forkers(_PyInterpreterState_GET()->before_forkers, 1);
_PyImport_AcquireLock();
}
void
PyOS_AfterFork_Parent(void)
{
if (_PyImport_ReleaseLock() <= 0)
Py_FatalError("failed releasing import lock after fork");
run_at_forkers(_PyInterpreterState_GET()->after_forkers_parent, 0);
}
void
PyOS_AfterFork_Child(void)
{
PyStatus status;
_PyRuntimeState *runtime = &_PyRuntime;
status = _PyGILState_Reinit(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
PyThreadState *tstate = _PyThreadState_GET();
_Py_EnsureTstateNotNULL(tstate);
status = _PyEval_ReInitThreads(tstate);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
status = _PyImport_ReInitLock();
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
_PySignal_AfterFork();
status = _PyRuntimeState_ReInitThreads(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
status = _PyInterpreterState_DeleteExceptMain(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
assert(_PyThreadState_GET() == tstate);
run_at_forkers(tstate->interp->after_forkers_child, 0);
return;
fatal_error:
Py_ExitStatusException(status);
}
static int
register_at_forker(PyObject **lst, PyObject *func)
{
if (func == NULL) /* nothing to register? do nothing. */
return 0;
if (*lst == NULL) {
*lst = PyList_New(0);
if (*lst == NULL)
return -1;
}
return PyList_Append(*lst, func);
}
#endif /* HAVE_FORK */
/* Legacy wrapper */
void
PyOS_AfterFork(void)
{
#ifdef HAVE_FORK
PyOS_AfterFork_Child();
#endif
}
#ifdef MS_WINDOWS
/* defined in fileutils.c */
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *,
ULONG, struct _Py_stat_struct *);
#endif
#ifndef MS_WINDOWS
PyObject *
_PyLong_FromUid(uid_t uid)
{
if (uid == (uid_t)-1)
return PyLong_FromLong(-1);
return PyLong_FromUnsignedLong(uid);
}
PyObject *
_PyLong_FromGid(gid_t gid)
{
if (gid == (gid_t)-1)
return PyLong_FromLong(-1);
return PyLong_FromUnsignedLong(gid);
}
int
_Py_Uid_Converter(PyObject *obj, void *p)
{
uid_t uid;
PyObject *index;
int overflow;
long result;
unsigned long uresult;
index = _PyNumber_Index(obj);
if (index == NULL) {
PyErr_Format(PyExc_TypeError,
"uid should be integer, not %.200s",
_PyType_Name(Py_TYPE(obj)));
return 0;
}
/*
* Handling uid_t is complicated for two reasons:
* * Although uid_t is (always?) unsigned, it still
* accepts -1.
* * We don't know its size in advance--it may be
* bigger than an int, or it may be smaller than
* a long.
*
* So a bit of defensive programming is in order.
* Start with interpreting the value passed
* in as a signed long and see if it works.
*/
result = PyLong_AsLongAndOverflow(index, &overflow);
if (!overflow) {
uid = (uid_t)result;
if (result == -1) {
if (PyErr_Occurred())
goto fail;
/* It's a legitimate -1, we're done. */
goto success;
}
/* Any other negative number is disallowed. */
if (result < 0)
goto underflow;
/* Ensure the value wasn't truncated. */
if (sizeof(uid_t) < sizeof(long) &&
(long)uid != result)
goto underflow;
goto success;
}
if (overflow < 0)
goto underflow;
/*
* Okay, the value overflowed a signed long. If it
* fits in an *unsigned* long, it may still be okay,
* as uid_t may be unsigned long on this platform.
*/
uresult = PyLong_AsUnsignedLong(index);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
goto overflow;
goto fail;
}
uid = (uid_t)uresult;
/*
* If uid == (uid_t)-1, the user actually passed in ULONG_MAX,
* but this value would get interpreted as (uid_t)-1 by chown
* and its siblings. That's not what the user meant! So we
* throw an overflow exception instead. (We already
* handled a real -1 with PyLong_AsLongAndOverflow() above.)
*/
if (uid == (uid_t)-1)
goto overflow;
/* Ensure the value wasn't truncated. */
if (sizeof(uid_t) < sizeof(long) &&
(unsigned long)uid != uresult)
goto overflow;
/* fallthrough */
success:
Py_DECREF(index);
*(uid_t *)p = uid;
return 1;
underflow:
PyErr_SetString(PyExc_OverflowError,
"uid is less than minimum");
goto fail;
overflow:
PyErr_SetString(PyExc_OverflowError,
"uid is greater than maximum");
/* fallthrough */
fail:
Py_DECREF(index);
return 0;
}
int
_Py_Gid_Converter(PyObject *obj, void *p)
{
gid_t gid;
PyObject *index;
int overflow;
long result;
unsigned long uresult;
index = _PyNumber_Index(obj);
if (index == NULL) {
PyErr_Format(PyExc_TypeError,
"gid should be integer, not %.200s",
_PyType_Name(Py_TYPE(obj)));
return 0;
}
/*
* Handling gid_t is complicated for two reasons:
* * Although gid_t is (always?) unsigned, it still
* accepts -1.
* * We don't know its size in advance--it may be
* bigger than an int, or it may be smaller than
* a long.
*
* So a bit of defensive programming is in order.
* Start with interpreting the value passed
* in as a signed long and see if it works.
*/
result = PyLong_AsLongAndOverflow(index, &overflow);
if (!overflow) {
gid = (gid_t)result;
if (result == -1) {
if (PyErr_Occurred())
goto fail;
/* It's a legitimate -1, we're done. */
goto success;
}
/* Any other negative number is disallowed. */
if (result < 0) {
goto underflow;
}
/* Ensure the value wasn't truncated. */
if (sizeof(gid_t) < sizeof(long) &&
(long)gid != result)
goto underflow;
goto success;
}
if (overflow < 0)
goto underflow;
/*
* Okay, the value overflowed a signed long. If it
* fits in an *unsigned* long, it may still be okay,
* as gid_t may be unsigned long on this platform.
*/
uresult = PyLong_AsUnsignedLong(index);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
goto overflow;
goto fail;
}
gid = (gid_t)uresult;
/*
* If gid == (gid_t)-1, the user actually passed in ULONG_MAX,
* but this value would get interpreted as (gid_t)-1 by chown
* and its siblings. That's not what the user meant! So we
* throw an overflow exception instead. (We already
* handled a real -1 with PyLong_AsLongAndOverflow() above.)
*/
if (gid == (gid_t)-1)
goto overflow;
/* Ensure the value wasn't truncated. */
if (sizeof(gid_t) < sizeof(long) &&
(unsigned long)gid != uresult)
goto overflow;
/* fallthrough */
success:
Py_DECREF(index);
*(gid_t *)p = gid;
return 1;
underflow:
PyErr_SetString(PyExc_OverflowError,
"gid is less than minimum");
goto fail;
overflow:
PyErr_SetString(PyExc_OverflowError,
"gid is greater than maximum");
/* fallthrough */
fail:
Py_DECREF(index);
return 0;
}
#endif /* MS_WINDOWS */
#define _PyLong_FromDev PyLong_FromLongLong
#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)
static int
_Py_Dev_Converter(PyObject *obj, void *p)
{
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
if (PyErr_Occurred())
return 0;
return 1;
}
#endif /* HAVE_MKNOD && HAVE_MAKEDEV */
#ifdef AT_FDCWD
/*
* Why the (int) cast? Solaris 10 defines AT_FDCWD as 0xffd19553 (-3041965);
* without the int cast, the value gets interpreted as uint (4291925331),
* which doesn't play nicely with all the initializer lines in this file that
* look like this:
* int dir_fd = DEFAULT_DIR_FD;
*/
#define DEFAULT_DIR_FD (int)AT_FDCWD
#else
#define DEFAULT_DIR_FD (-100)
#endif
static int
_fd_converter(PyObject *o, int *p)
{
int overflow;
long long_value;
PyObject *index = _PyNumber_Index(o);
if (index == NULL) {
return 0;
}
assert(PyLong_Check(index));
long_value = PyLong_AsLongAndOverflow(index, &overflow);
Py_DECREF(index);
assert(!PyErr_Occurred());
if (overflow > 0 || long_value > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"fd is greater than maximum");
return 0;
}
if (overflow < 0 || long_value < INT_MIN) {
PyErr_SetString(PyExc_OverflowError,
"fd is less than minimum");
return 0;
}
*p = (int)long_value;
return 1;
}
static int
dir_fd_converter(PyObject *o, void *p)
{
if (o == Py_None) {
*(int *)p = DEFAULT_DIR_FD;
return 1;
}
else if (PyIndex_Check(o)) {
return _fd_converter(o, (int *)p);
}
else {
PyErr_Format(PyExc_TypeError,
"argument should be integer or None, not %.200s",
_PyType_Name(Py_TYPE(o)));
return 0;
}
}
typedef struct {
PyObject *billion;
PyObject *DirEntryType;
PyObject *ScandirIteratorType;
#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
PyObject *SchedParamType;
#endif
PyObject *StatResultType;
PyObject *StatVFSResultType;
PyObject *TerminalSizeType;
PyObject *TimesResultType;
PyObject *UnameResultType;
#if defined(HAVE_WAITID) && !defined(__APPLE__)
PyObject *WaitidResultType;
#endif
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
PyObject *struct_rusage;
#endif
PyObject *st_mode;
} _posixstate;
static inline _posixstate*
get_posix_state(PyObject *module)
{
void *state = PyModule_GetState(module);
assert(state != NULL);
return (_posixstate *)state;
}
/*
* A PyArg_ParseTuple "converter" function
* that handles filesystem paths in the manner
* preferred by the os module.
*
* path_converter accepts (Unicode) strings and their
* subclasses, and bytes and their subclasses. What
* it does with the argument depends on the platform:
*
* * On Windows, if we get a (Unicode) string we
* extract the wchar_t * and return it; if we get
* bytes we decode to wchar_t * and return that.
*
* * On all other platforms, strings are encoded
* to bytes using PyUnicode_FSConverter, then we
* extract the char * from the bytes object and
* return that.
*
* path_converter also optionally accepts signed
* integers (representing open file descriptors) instead
* of path strings.
*
* Input fields:
* path.nullable
* If nonzero, the path is permitted to be None.
* path.allow_fd
* If nonzero, the path is permitted to be a file handle
* (a signed int) instead of a string.
* path.function_name
* If non-NULL, path_converter will use that as the name
* of the function in error messages.
* (If path.function_name is NULL it omits the function name.)
* path.argument_name
* If non-NULL, path_converter will use that as the name
* of the parameter in error messages.
* (If path.argument_name is NULL it uses "path".)
*
* Output fields:
* path.wide
* Points to the path if it was expressed as Unicode
* and was not encoded. (Only used on Windows.)
* path.narrow
* Points to the path if it was expressed as bytes,
* or it was Unicode and was encoded to bytes. (On Windows,
* is a non-zero integer if the path was expressed as bytes.
* The type is deliberately incompatible to prevent misuse.)
* path.fd
* Contains a file descriptor if path.accept_fd was true
* and the caller provided a signed integer instead of any
* sort of string.
*
* WARNING: if your "path" parameter is optional, and is
* unspecified, path_converter will never get called.
* So if you set allow_fd, you *MUST* initialize path.fd = -1
* yourself!
* path.length
* The length of the path in characters, if specified as
* a string.
* path.object
* The original object passed in (if get a PathLike object,
* the result of PyOS_FSPath() is treated as the original object).
* Own a reference to the object.
* path.cleanup
* For internal use only. May point to a temporary object.
* (Pay no attention to the man behind the curtain.)
*
* At most one of path.wide or path.narrow will be non-NULL.
* If path was None and path.nullable was set,
* or if path was an integer and path.allow_fd was set,
* both path.wide and path.narrow will be NULL
* and path.length will be 0.
*
* path_converter takes care to not write to the path_t
* unless it's successful. However it must reset the
* "cleanup" field each time it's called.
*
* Use as follows:
* path_t path;
* memset(&path, 0, sizeof(path));
* PyArg_ParseTuple(args, "O&", path_converter, &path);
* // ... use values from path ...
* path_cleanup(&path);
*
* (Note that if PyArg_Parse fails you don't need to call
* path_cleanup(). However it is safe to do so.)
*/
typedef struct {
const char *function_name;
const char *argument_name;
int nullable;
int allow_fd;
const wchar_t *wide;
#ifdef MS_WINDOWS
BOOL narrow;
#else
const char *narrow;
#endif
int fd;
Py_ssize_t length;
PyObject *object;
PyObject *cleanup;
} path_t;
#ifdef MS_WINDOWS
#define PATH_T_INITIALIZE(function_name, argument_name, nullable, allow_fd) \
{function_name, argument_name, nullable, allow_fd, NULL, FALSE, -1, 0, NULL, NULL}
#else
#define PATH_T_INITIALIZE(function_name, argument_name, nullable, allow_fd) \
{function_name, argument_name, nullable, allow_fd, NULL, NULL, -1, 0, NULL, NULL}
#endif
static void
path_cleanup(path_t *path)
{
#if !USE_UNICODE_WCHAR_CACHE
wchar_t *wide = (wchar_t *)path->wide;
path->wide = NULL;
PyMem_Free(wide);
#endif /* USE_UNICODE_WCHAR_CACHE */
Py_CLEAR(path->object);
Py_CLEAR(path->cleanup);
}
static int
path_converter(PyObject *o, void *p)
{
path_t *path = (path_t *)p;
PyObject *bytes = NULL;
Py_ssize_t length = 0;
int is_index, is_buffer, is_bytes, is_unicode;
const char *narrow;
#ifdef MS_WINDOWS
PyObject *wo = NULL;
wchar_t *wide = NULL;
#endif
#define FORMAT_EXCEPTION(exc, fmt) \
PyErr_Format(exc, "%s%s" fmt, \
path->function_name ? path->function_name : "", \
path->function_name ? ": " : "", \
path->argument_name ? path->argument_name : "path")
/* Py_CLEANUP_SUPPORTED support */
if (o == NULL) {
path_cleanup(path);
return 1;
}
/* Ensure it's always safe to call path_cleanup(). */
path->object = path->cleanup = NULL;
/* path->object owns a reference to the original object */
Py_INCREF(o);
if ((o == Py_None) && path->nullable) {
path->wide = NULL;
#ifdef MS_WINDOWS
path->narrow = FALSE;
#else
path->narrow = NULL;
#endif
path->fd = -1;
goto success_exit;
}
/* Only call this here so that we don't treat the return value of
os.fspath() as an fd or buffer. */
is_index = path->allow_fd && PyIndex_Check(o);
is_buffer = PyObject_CheckBuffer(o);
is_bytes = PyBytes_Check(o);
is_unicode = PyUnicode_Check(o);
if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
/* Inline PyOS_FSPath() for better error messages. */
PyObject *func, *res;
func = _PyObject_LookupSpecial(o, &PyId___fspath__);
if (NULL == func) {
goto error_format;
}
res = _PyObject_CallNoArg(func);
Py_DECREF(func);
if (NULL == res) {
goto error_exit;
}
else if (PyUnicode_Check(res)) {
is_unicode = 1;
}
else if (PyBytes_Check(res)) {
is_bytes = 1;
}
else {
PyErr_Format(PyExc_TypeError,
"expected %.200s.__fspath__() to return str or bytes, "
"not %.200s", _PyType_Name(Py_TYPE(o)),
_PyType_Name(Py_TYPE(res)));
Py_DECREF(res);
goto error_exit;
}
/* still owns a reference to the original object */
Py_DECREF(o);
o = res;
}
if (is_unicode) {
#ifdef MS_WINDOWS
#if USE_UNICODE_WCHAR_CACHE
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
wide = PyUnicode_AsUnicodeAndSize(o, &length);
_Py_COMP_DIAG_POP
#else /* USE_UNICODE_WCHAR_CACHE */
wide = PyUnicode_AsWideCharString(o, &length);
#endif /* USE_UNICODE_WCHAR_CACHE */
if (!wide) {
goto error_exit;
}
if (length > 32767) {
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
goto error_exit;
}
if (wcslen(wide) != length) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
goto error_exit;
}
path->wide = wide;
path->narrow = FALSE;
path->fd = -1;
#if !USE_UNICODE_WCHAR_CACHE
wide = NULL;
#endif /* USE_UNICODE_WCHAR_CACHE */
goto success_exit;
#else
if (!PyUnicode_FSConverter(o, &bytes)) {
goto error_exit;
}
#endif
}
else if (is_bytes) {
bytes = o;
Py_INCREF(bytes);
}
else if (is_buffer) {
/* XXX Replace PyObject_CheckBuffer with PyBytes_Check in other code
after removing support of non-bytes buffer objects. */
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"%s%s%s should be %s, not %.200s",
path->function_name ? path->function_name : "",
path->function_name ? ": " : "",
path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
"integer or None" :
path->allow_fd ? "string, bytes, os.PathLike or integer" :
path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike",
_PyType_Name(Py_TYPE(o)))) {
goto error_exit;
}
bytes = PyBytes_FromObject(o);
if (!bytes) {
goto error_exit;
}
}
else if (is_index) {
if (!_fd_converter(o, &path->fd)) {
goto error_exit;
}
path->wide = NULL;
#ifdef MS_WINDOWS
path->narrow = FALSE;
#else
path->narrow = NULL;
#endif
goto success_exit;
}
else {
error_format:
PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s",
path->function_name ? path->function_name : "",
path->function_name ? ": " : "",
path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
"integer or None" :
path->allow_fd ? "string, bytes, os.PathLike or integer" :
path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike",
_PyType_Name(Py_TYPE(o)));
goto error_exit;
}
length = PyBytes_GET_SIZE(bytes);
narrow = PyBytes_AS_STRING(bytes);
if ((size_t)length != strlen(narrow)) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
goto error_exit;
}
#ifdef MS_WINDOWS
wo = PyUnicode_DecodeFSDefaultAndSize(
narrow,
length
);
if (!wo) {
goto error_exit;
}
#if USE_UNICODE_WCHAR_CACHE
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
wide = PyUnicode_AsUnicodeAndSize(wo, &length);
_Py_COMP_DIAG_POP
#else /* USE_UNICODE_WCHAR_CACHE */
wide = PyUnicode_AsWideCharString(wo, &length);
Py_DECREF(wo);
#endif /* USE_UNICODE_WCHAR_CACHE */
if (!wide) {
goto error_exit;
}
if (length > 32767) {
FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
goto error_exit;
}
if (wcslen(wide) != length) {
FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
goto error_exit;
}
path->wide = wide;
path->narrow = TRUE;
Py_DECREF(bytes);
#if USE_UNICODE_WCHAR_CACHE
path->cleanup = wo;
#else /* USE_UNICODE_WCHAR_CACHE */
wide = NULL;
#endif /* USE_UNICODE_WCHAR_CACHE */
#else
path->wide = NULL;
path->narrow = narrow;
if (bytes == o) {
/* Still a reference owned by path->object, don't have to
worry about path->narrow is used after free. */
Py_DECREF(bytes);
}
else {
path->cleanup = bytes;
}
#endif
path->fd = -1;
success_exit:
path->length = length;
path->object = o;
return Py_CLEANUP_SUPPORTED;
error_exit:
Py_XDECREF(o);
Py_XDECREF(bytes);
#ifdef MS_WINDOWS
#if USE_UNICODE_WCHAR_CACHE
Py_XDECREF(wo);
#else /* USE_UNICODE_WCHAR_CACHE */
PyMem_Free(wide);
#endif /* USE_UNICODE_WCHAR_CACHE */
#endif
return 0;
}
static void
argument_unavailable_error(const char *function_name, const char *argument_name)
{
PyErr_Format(PyExc_NotImplementedError,
"%s%s%s unavailable on this platform",
(function_name != NULL) ? function_name : "",
(function_name != NULL) ? ": ": "",
argument_name);
}
static int
dir_fd_unavailable(PyObject *o, void *p)
{
int dir_fd;
if (!dir_fd_converter(o, &dir_fd))
return 0;
if (dir_fd != DEFAULT_DIR_FD) {
argument_unavailable_error(NULL, "dir_fd");
return 0;
}
*(int *)p = dir_fd;
return 1;
}
static int
fd_specified(const char *function_name, int fd)
{
if (fd == -1)
return 0;
argument_unavailable_error(function_name, "fd");
return 1;
}
static int
follow_symlinks_specified(const char *function_name, int follow_symlinks)
{
if (follow_symlinks)
return 0;
argument_unavailable_error(function_name, "follow_symlinks");
return 1;
}
static int
path_and_dir_fd_invalid(const char *function_name, path_t *path, int dir_fd)
{
if (!path->wide && (dir_fd != DEFAULT_DIR_FD)
#ifndef MS_WINDOWS
&& !path->narrow
#endif
) {
PyErr_Format(PyExc_ValueError,
"%s: can't specify dir_fd without matching path",
function_name);
return 1;
}
return 0;
}
static int
dir_fd_and_fd_invalid(const char *function_name, int dir_fd, int fd)
{
if ((dir_fd != DEFAULT_DIR_FD) && (fd != -1)) {
PyErr_Format(PyExc_ValueError,
"%s: can't specify both dir_fd and fd",
function_name);
return 1;
}
return 0;
}
static int
fd_and_follow_symlinks_invalid(const char *function_name, int fd,
int follow_symlinks)
{
if ((fd > 0) && (!follow_symlinks)) {
PyErr_Format(PyExc_ValueError,
"%s: cannot use fd and follow_symlinks together",
function_name);
return 1;
}
return 0;
}
static int
dir_fd_and_follow_symlinks_invalid(const char *function_name, int dir_fd,
int follow_symlinks)
{
if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) {
PyErr_Format(PyExc_ValueError,
"%s: cannot use dir_fd and follow_symlinks together",
function_name);
return 1;
}
return 0;
}
#ifdef MS_WINDOWS
typedef long long Py_off_t;
#else
typedef off_t Py_off_t;
#endif
static int
Py_off_t_converter(PyObject *arg, void *addr)
{
#ifdef HAVE_LARGEFILE_SUPPORT
*((Py_off_t *)addr) = PyLong_AsLongLong(arg);
#else
*((Py_off_t *)addr) = PyLong_AsLong(arg);
#endif
if (PyErr_Occurred())
return 0;
return 1;
}
static PyObject *
PyLong_FromPy_off_t(Py_off_t offset)
{
#ifdef HAVE_LARGEFILE_SUPPORT
return PyLong_FromLongLong(offset);
#else
return PyLong_FromLong(offset);
#endif
}
#ifdef HAVE_SIGSET_T
/* Convert an iterable of integers to a sigset.
Return 1 on success, return 0 and raise an exception on error. */
int
_Py_Sigset_Converter(PyObject *obj, void *addr)
{
sigset_t *mask = (sigset_t *)addr;
PyObject *iterator, *item;
long signum;
int overflow;
// The extra parens suppress the unreachable-code warning with clang on MacOS
if (sigemptyset(mask) < (0)) {
/* Probably only if mask == NULL. */
PyErr_SetFromErrno(PyExc_OSError);
return 0;
}
iterator = PyObject_GetIter(obj);
if (iterator == NULL) {
return 0;
}
while ((item = PyIter_Next(iterator)) != NULL) {
signum = PyLong_AsLongAndOverflow(item, &overflow);
Py_DECREF(item);
if (signum <= 0 || signum >= NSIG) {
if (overflow || signum != -1 || !PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"signal number %ld out of range", signum);
}
goto error;
}
if (sigaddset(mask, (int)signum)) {
if (errno != EINVAL) {
/* Probably impossible */
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
/* For backwards compatibility, allow idioms such as
* `range(1, NSIG)` but warn about invalid signal numbers
*/
const char msg[] =
"invalid signal number %ld, please use valid_signals()";
if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) {
goto error;
}
}
}
if (!PyErr_Occurred()) {
Py_DECREF(iterator);
return 1;
}
error:
Py_DECREF(iterator);
return 0;
}
#endif /* HAVE_SIGSET_T */
#ifdef MS_WINDOWS
static int
win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag)
{
char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
_Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer;
DWORD n_bytes_returned;
if (0 == DeviceIoControl(
reparse_point_handle,
FSCTL_GET_REPARSE_POINT,
NULL, 0, /* in buffer */
target_buffer, sizeof(target_buffer),
&n_bytes_returned,
NULL)) /* we're not using OVERLAPPED_IO */
return FALSE;
if (reparse_tag)
*reparse_tag = rdb->ReparseTag;
return TRUE;
}
#endif /* MS_WINDOWS */
/* Return a dictionary corresponding to the POSIX environment table */
#if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED))
/* On Darwin/MacOSX a shared library or framework has no access to
** environ directly, we must obtain it with _NSGetEnviron(). See also
** man environ(7).
*/
#include <crt_externs.h>
#elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__))
extern char **environ;
#endif /* !_MSC_VER */
static PyObject *
convertenviron(void)
{
PyObject *d;
#ifdef MS_WINDOWS
wchar_t **e;
#else
char **e;
#endif
d = PyDict_New();
if (d == NULL)
return NULL;
#ifdef MS_WINDOWS
/* _wenviron must be initialized in this way if the program is started
through main() instead of wmain(). */
_wgetenv(L"");
e = _wenviron;
#elif defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED))
/* environ is not accessible as an extern in a shared object on OSX; use
_NSGetEnviron to resolve it. The value changes if you add environment
variables between calls to Py_Initialize, so don't cache the value. */
e = *_NSGetEnviron();
#else
e = environ;
#endif
if (e == NULL)
return d;
for (; *e != NULL; e++) {
PyObject *k;
PyObject *v;
#ifdef MS_WINDOWS
const wchar_t *p = wcschr(*e, L'=');
#else
const char *p = strchr(*e, '=');
#endif
if (p == NULL)
continue;
#ifdef MS_WINDOWS
k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e));
#else
k = PyBytes_FromStringAndSize(*e, (int)(p-*e));
#endif
if (k == NULL) {
Py_DECREF(d);
return NULL;
}
#ifdef MS_WINDOWS
v = PyUnicode_FromWideChar(p+1, wcslen(p+1));
#else
v = PyBytes_FromStringAndSize(p+1, strlen(p+1));
#endif
if (v == NULL) {
Py_DECREF(k);
Py_DECREF(d);
return NULL;
}
if (PyDict_GetItemWithError(d, k) == NULL) {
if (PyErr_Occurred() || PyDict_SetItem(d, k, v) != 0) {
Py_DECREF(v);
Py_DECREF(k);
Py_DECREF(d);
return NULL;
}
}
Py_DECREF(k);
Py_DECREF(v);
}
return d;
}
/* Set a POSIX-specific error from errno, and return NULL */
static PyObject *
posix_error(void)
{
return PyErr_SetFromErrno(PyExc_OSError);
}
#ifdef MS_WINDOWS
static PyObject *
win32_error(const char* function, const char* filename)
{
/* XXX We should pass the function name along in the future.
(winreg.c also wants to pass the function name.)
This would however require an additional param to the
Windows error object, which is non-trivial.
*/
errno = GetLastError();
if (filename)
return PyErr_SetFromWindowsErrWithFilename(errno, filename);
else
return PyErr_SetFromWindowsErr(errno);
}
static PyObject *
win32_error_object_err(const char* function, PyObject* filename, DWORD err)
{
/* XXX - see win32_error for comments on 'function' */
if (filename)
return PyErr_SetExcFromWindowsErrWithFilenameObject(
PyExc_OSError,
err,
filename);
else
return PyErr_SetFromWindowsErr(err);
}
static PyObject *
win32_error_object(const char* function, PyObject* filename)
{
errno = GetLastError();
return win32_error_object_err(function, filename, errno);
}
#endif /* MS_WINDOWS */
static PyObject *
posix_path_object_error(PyObject *path)
{
return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
}
static PyObject *
path_object_error(PyObject *path)
{
#ifdef MS_WINDOWS
return PyErr_SetExcFromWindowsErrWithFilenameObject(
PyExc_OSError, 0, path);
#else
return posix_path_object_error(path);
#endif
}
static PyObject *
path_object_error2(PyObject *path, PyObject *path2)
{
#ifdef MS_WINDOWS
return PyErr_SetExcFromWindowsErrWithFilenameObjects(
PyExc_OSError, 0, path, path2);
#else
return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, path, path2);
#endif
}
static PyObject *
path_error(path_t *path)
{
return path_object_error(path->object);
}
static PyObject *
posix_path_error(path_t *path)
{
return posix_path_object_error(path->object);
}
static PyObject *
path_error2(path_t *path, path_t *path2)
{
return path_object_error2(path->object, path2->object);
}
/* POSIX generic methods */
static PyObject *
posix_fildes_fd(int fd, int (*func)(int))
{
int res;
int async_err = 0;
do {
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
res = (*func)(fd);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE;
}
#ifdef MS_WINDOWS
/* This is a reimplementation of the C library's chdir function,
but one that produces Win32 errors instead of DOS error codes.
chdir is essentially a wrapper around SetCurrentDirectory; however,
it also needs to set "magic" environment variables indicating
the per-drive current directory, which are of the form =<drive>: */
static BOOL __stdcall
win32_wchdir(LPCWSTR path)
{
wchar_t path_buf[MAX_PATH], *new_path = path_buf;
int result;
wchar_t env[4] = L"=x:";
if(!SetCurrentDirectoryW(path))
return FALSE;
result = GetCurrentDirectoryW(Py_ARRAY_LENGTH(path_buf), new_path);
if (!result)
return FALSE;
if (result > Py_ARRAY_LENGTH(path_buf)) {
new_path = PyMem_RawMalloc(result * sizeof(wchar_t));
if (!new_path) {
SetLastError(ERROR_OUTOFMEMORY);
return FALSE;
}
result = GetCurrentDirectoryW(result, new_path);
if (!result) {
PyMem_RawFree(new_path);
return FALSE;
}
}
int is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 ||
wcsncmp(new_path, L"//", 2) == 0);
if (!is_unc_like_path) {
env[1] = new_path[0];
result = SetEnvironmentVariableW(env, new_path);
}
if (new_path != path_buf)
PyMem_RawFree(new_path);
return result ? TRUE : FALSE;
}
#endif
#ifdef MS_WINDOWS
/* The CRT of Windows has a number of flaws wrt. its stat() implementation:
- time stamps are restricted to second resolution
- file modification times suffer from forth-and-back conversions between
UTC and local time
Therefore, we implement our own stat, based on the Win32 API directly.
*/
#define HAVE_STAT_NSEC 1
#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1
#define HAVE_STRUCT_STAT_ST_REPARSE_TAG 1
static void
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
BY_HANDLE_FILE_INFORMATION *info,
ULONG *reparse_tag)
{
memset(info, 0, sizeof(*info));
info->dwFileAttributes = pFileData->dwFileAttributes;
info->ftCreationTime = pFileData->ftCreationTime;
info->ftLastAccessTime = pFileData->ftLastAccessTime;
info->ftLastWriteTime = pFileData->ftLastWriteTime;
info->nFileSizeHigh = pFileData->nFileSizeHigh;
info->nFileSizeLow = pFileData->nFileSizeLow;
/* info->nNumberOfLinks = 1; */
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
*reparse_tag = pFileData->dwReserved0;
else
*reparse_tag = 0;
}
static BOOL
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
{
HANDLE hFindFile;
WIN32_FIND_DATAW FileData;
hFindFile = FindFirstFileW(pszFile, &FileData);
if (hFindFile == INVALID_HANDLE_VALUE)
return FALSE;
FindClose(hFindFile);
find_data_to_file_info(&FileData, info, reparse_tag);
return TRUE;
}
static int
win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
BOOL traverse)
{
HANDLE hFile;
BY_HANDLE_FILE_INFORMATION fileInfo;
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
DWORD fileType, error;
BOOL isUnhandledTag = FALSE;
int retval = 0;
DWORD access = FILE_READ_ATTRIBUTES;
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; /* Allow opening directories. */
if (!traverse) {
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, flags, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
/* Either the path doesn't exist, or the caller lacks access. */
error = GetLastError();
switch (error) {
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
/* Try reading the parent directory. */
if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
/* Cannot read the parent directory. */
SetLastError(error);
return -1;
}
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (traverse ||
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
/* The stat call has to traverse but cannot, so fail. */
SetLastError(error);
return -1;
}
}
break;
case ERROR_INVALID_PARAMETER:
/* \\.\con requires read or write access. */
hFile = CreateFileW(path, access | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, flags, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
SetLastError(error);
return -1;
}
break;
case ERROR_CANT_ACCESS_FILE:
/* bpo37834: open unhandled reparse points if traverse fails. */
if (traverse) {
traverse = FALSE;
isUnhandledTag = TRUE;
hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING,
flags | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
}
if (hFile == INVALID_HANDLE_VALUE) {
SetLastError(error);
return -1;
}
break;
default:
return -1;
}
}
if (hFile != INVALID_HANDLE_VALUE) {
/* Handle types other than files on disk. */
fileType = GetFileType(hFile);
if (fileType != FILE_TYPE_DISK) {
if (fileType == FILE_TYPE_UNKNOWN && GetLastError() != 0) {
retval = -1;
goto cleanup;
}
DWORD fileAttributes = GetFileAttributesW(path);
memset(result, 0, sizeof(*result));
if (fileAttributes != INVALID_FILE_ATTRIBUTES &&
fileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
/* \\.\pipe\ or \\.\mailslot\ */
result->st_mode = _S_IFDIR;
} else if (fileType == FILE_TYPE_CHAR) {
/* \\.\nul */
result->st_mode = _S_IFCHR;
} else if (fileType == FILE_TYPE_PIPE) {
/* \\.\pipe\spam */
result->st_mode = _S_IFIFO;
}
/* FILE_TYPE_UNKNOWN, e.g. \\.\mailslot\waitfor.exe\spam */
goto cleanup;
}
/* Query the reparse tag, and traverse a non-link. */
if (!traverse) {
if (!GetFileInformationByHandleEx(hFile, FileAttributeTagInfo,
&tagInfo, sizeof(tagInfo))) {
/* Allow devices that do not support FileAttributeTagInfo. */
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
case ERROR_NOT_SUPPORTED:
tagInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
tagInfo.ReparseTag = 0;
break;
default:
retval = -1;
goto cleanup;
}
} else if (tagInfo.FileAttributes &
FILE_ATTRIBUTE_REPARSE_POINT) {
if (IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
if (isUnhandledTag) {
/* Traversing previously failed for either this link
or its target. */
SetLastError(ERROR_CANT_ACCESS_FILE);
retval = -1;
goto cleanup;
}
/* Traverse a non-link, but not if traversing already failed
for an unhandled tag. */
} else if (!isUnhandledTag) {
CloseHandle(hFile);
return win32_xstat_impl(path, result, TRUE);
}
}
}
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
case ERROR_NOT_SUPPORTED:
/* Volumes and physical disks are block devices, e.g.
\\.\C: and \\.\PhysicalDrive0. */
memset(result, 0, sizeof(*result));
result->st_mode = 0x6000; /* S_IFBLK */
goto cleanup;
}
retval = -1;
goto cleanup;
}
}
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, result);
if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
/* Fix the file execute permissions. This hack sets S_IEXEC if
the filename has an extension that is commonly used by files
that CreateProcessW can execute. A real implementation calls
GetSecurityInfo, OpenThreadToken/OpenProcessToken, and
AccessCheck to check for generic read, write, and execute
access. */
const wchar_t *fileExtension = wcsrchr(path, '.');
if (fileExtension) {
if (_wcsicmp(fileExtension, L".exe") == 0 ||
_wcsicmp(fileExtension, L".bat") == 0 ||
_wcsicmp(fileExtension, L".cmd") == 0 ||
_wcsicmp(fileExtension, L".com") == 0) {
result->st_mode |= 0111;
}
}
}
cleanup:
if (hFile != INVALID_HANDLE_VALUE) {
/* Preserve last error if we are failing */
error = retval ? GetLastError() : 0;
if (!CloseHandle(hFile)) {
retval = -1;
} else if (retval) {
/* Restore last error */
SetLastError(error);
}
}
return retval;
}
static int
win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse)
{
/* Protocol violation: we explicitly clear errno, instead of
setting it to a POSIX error. Callers should use GetLastError. */
int code = win32_xstat_impl(path, result, traverse);
errno = 0;
return code;
}
/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w
In Posix, stat automatically traverses symlinks and returns the stat
structure for the target. In Windows, the equivalent GetFileAttributes by
default does not traverse symlinks and instead returns attributes for
the symlink.
Instead, we will open the file (which *does* traverse symlinks by default)
and GetFileInformationByHandle(). */
static int
win32_lstat(const wchar_t* path, struct _Py_stat_struct *result)
{
return win32_xstat(path, result, FALSE);
}
static int
win32_stat(const wchar_t* path, struct _Py_stat_struct *result)
{
return win32_xstat(path, result, TRUE);
}
#endif /* MS_WINDOWS */
PyDoc_STRVAR(stat_result__doc__,
"stat_result: Result from stat, fstat, or lstat.\n\n\
This object may be accessed either as a tuple of\n\
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\
or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\
\n\
Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\
or st_flags, they are available as attributes only.\n\
\n\
See os.stat for more information.");
static PyStructSequence_Field stat_result_fields[] = {
{"st_mode", "protection bits"},
{"st_ino", "inode"},
{"st_dev", "device"},
{"st_nlink", "number of hard links"},
{"st_uid", "user ID of owner"},
{"st_gid", "group ID of owner"},
{"st_size", "total size, in bytes"},
/* The NULL is replaced with PyStructSequence_UnnamedField later. */
{NULL, "integer time of last access"},
{NULL, "integer time of last modification"},
{NULL, "integer time of last change"},
{"st_atime", "time of last access"},
{"st_mtime", "time of last modification"},
{"st_ctime", "time of last change"},
{"st_atime_ns", "time of last access in nanoseconds"},
{"st_mtime_ns", "time of last modification in nanoseconds"},
{"st_ctime_ns", "time of last change in nanoseconds"},
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
{"st_blksize", "blocksize for filesystem I/O"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
{"st_blocks", "number of blocks allocated"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
{"st_rdev", "device type (if inode device)"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
{"st_flags", "user defined flags for file"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
{"st_gen", "generation number"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{"st_birthtime", "time of creation"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
{"st_file_attributes", "Windows file attribute bits"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
{"st_fstype", "Type of filesystem"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG
{"st_reparse_tag", "Windows reparse tag"},
#endif
{0}
};
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
#define ST_BLKSIZE_IDX 16
#else
#define ST_BLKSIZE_IDX 15
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1)
#else
#define ST_BLOCKS_IDX ST_BLKSIZE_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
#define ST_RDEV_IDX (ST_BLOCKS_IDX+1)
#else
#define ST_RDEV_IDX ST_BLOCKS_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
#define ST_FLAGS_IDX (ST_RDEV_IDX+1)
#else
#define ST_FLAGS_IDX ST_RDEV_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
#define ST_GEN_IDX (ST_FLAGS_IDX+1)
#else
#define ST_GEN_IDX ST_FLAGS_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1)
#else
#define ST_BIRTHTIME_IDX ST_GEN_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1)
#else
#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
#define ST_FSTYPE_IDX (ST_FILE_ATTRIBUTES_IDX+1)
#else
#define ST_FSTYPE_IDX ST_FILE_ATTRIBUTES_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG
#define ST_REPARSE_TAG_IDX (ST_FSTYPE_IDX+1)
#else
#define ST_REPARSE_TAG_IDX ST_FSTYPE_IDX
#endif
static PyStructSequence_Desc stat_result_desc = {
"stat_result", /* name */
stat_result__doc__, /* doc */
stat_result_fields,
10
};
PyDoc_STRVAR(statvfs_result__doc__,
"statvfs_result: Result from statvfs or fstatvfs.\n\n\
This object may be accessed either as a tuple of\n\
(bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\
or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\
\n\
See os.statvfs for more information.");
static PyStructSequence_Field statvfs_result_fields[] = {
{"f_bsize", },
{"f_frsize", },
{"f_blocks", },
{"f_bfree", },
{"f_bavail", },
{"f_files", },
{"f_ffree", },
{"f_favail", },
{"f_flag", },
{"f_namemax",},
{"f_fsid", },
{0}
};
static PyStructSequence_Desc statvfs_result_desc = {
"statvfs_result", /* name */
statvfs_result__doc__, /* doc */
statvfs_result_fields,
10
};
#if defined(HAVE_WAITID) && !defined(__APPLE__)
PyDoc_STRVAR(waitid_result__doc__,
"waitid_result: Result from waitid.\n\n\
This object may be accessed either as a tuple of\n\
(si_pid, si_uid, si_signo, si_status, si_code),\n\
or via the attributes si_pid, si_uid, and so on.\n\
\n\
See os.waitid for more information.");
static PyStructSequence_Field waitid_result_fields[] = {
{"si_pid", },
{"si_uid", },
{"si_signo", },
{"si_status", },
{"si_code", },
{0}
};
static PyStructSequence_Desc waitid_result_desc = {
"waitid_result", /* name */
waitid_result__doc__, /* doc */
waitid_result_fields,
5
};
#endif
static newfunc structseq_new;
static PyObject *
statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyStructSequence *result;
int i;
result = (PyStructSequence*)structseq_new(type, args, kwds);
if (!result)
return NULL;
/* If we have been initialized from a tuple,
st_?time might be set to None. Initialize it
from the int slots. */
for (i = 7; i <= 9; i++) {
if (result->ob_item[i+3] == Py_None) {
Py_DECREF(Py_None);
Py_INCREF(result->ob_item[i]);
result->ob_item[i+3] = result->ob_item[i];
}
}
return (PyObject*)result;
}
static int
_posix_clear(PyObject *module)
{
_posixstate *state = get_posix_state(module);
Py_CLEAR(state->billion);
Py_CLEAR(state->DirEntryType);
Py_CLEAR(state->ScandirIteratorType);
#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
Py_CLEAR(state->SchedParamType);
#endif
Py_CLEAR(state->StatResultType);
Py_CLEAR(state->StatVFSResultType);
Py_CLEAR(state->TerminalSizeType);
Py_CLEAR(state->TimesResultType);
Py_CLEAR(state->UnameResultType);
#if defined(HAVE_WAITID) && !defined(__APPLE__)
Py_CLEAR(state->WaitidResultType);
#endif
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
Py_CLEAR(state->struct_rusage);
#endif
Py_CLEAR(state->st_mode);
return 0;
}
static int
_posix_traverse(PyObject *module, visitproc visit, void *arg)
{
_posixstate *state = get_posix_state(module);
Py_VISIT(state->billion);
Py_VISIT(state->DirEntryType);
Py_VISIT(state->ScandirIteratorType);
#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
Py_VISIT(state->SchedParamType);
#endif
Py_VISIT(state->StatResultType);
Py_VISIT(state->StatVFSResultType);
Py_VISIT(state->TerminalSizeType);
Py_VISIT(state->TimesResultType);
Py_VISIT(state->UnameResultType);
#if defined(HAVE_WAITID) && !defined(__APPLE__)
Py_VISIT(state->WaitidResultType);
#endif
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
Py_VISIT(state->struct_rusage);
#endif
Py_VISIT(state->st_mode);
return 0;
}
static void
_posix_free(void *module)
{
_posix_clear((PyObject *)module);
}
static void
fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long nsec)
{
PyObject *s = _PyLong_FromTime_t(sec);
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
PyObject *s_in_ns = NULL;
PyObject *ns_total = NULL;
PyObject *float_s = NULL;
if (!(s && ns_fractional))
goto exit;
s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion);
if (!s_in_ns)
goto exit;
ns_total = PyNumber_Add(s_in_ns, ns_fractional);
if (!ns_total)
goto exit;
float_s = PyFloat_FromDouble(sec + 1e-9*nsec);
if (!float_s) {
goto exit;
}
PyStructSequence_SET_ITEM(v, index, s);
PyStructSequence_SET_ITEM(v, index+3, float_s);
PyStructSequence_SET_ITEM(v, index+6, ns_total);
s = NULL;
float_s = NULL;
ns_total = NULL;
exit:
Py_XDECREF(s);
Py_XDECREF(ns_fractional);
Py_XDECREF(s_in_ns);
Py_XDECREF(ns_total);
Py_XDECREF(float_s);
}
/* pack a system stat C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
{
unsigned long ansec, mnsec, cnsec;
PyObject *StatResultType = get_posix_state(module)->StatResultType;
PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType);
if (v == NULL)
return NULL;
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode));
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(st->st_ino));
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino));
#ifdef MS_WINDOWS
PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev));
#else
PyStructSequence_SET_ITEM(v, 2, _PyLong_FromDev(st->st_dev));
#endif
PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)st->st_nlink));
#if defined(MS_WINDOWS)
PyStructSequence_SET_ITEM(v, 4, PyLong_FromLong(0));
PyStructSequence_SET_ITEM(v, 5, PyLong_FromLong(0));
#else
PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid));
PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid));
#endif
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(st->st_size));
PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size));
#if defined(HAVE_STAT_TV_NSEC)
ansec = st->st_atim.tv_nsec;
mnsec = st->st_mtim.tv_nsec;
cnsec = st->st_ctim.tv_nsec;
#elif defined(HAVE_STAT_TV_NSEC2)
ansec = st->st_atimespec.tv_nsec;
mnsec = st->st_mtimespec.tv_nsec;
cnsec = st->st_ctimespec.tv_nsec;
#elif defined(HAVE_STAT_NSEC)
ansec = st->st_atime_nsec;
mnsec = st->st_mtime_nsec;
cnsec = st->st_ctime_nsec;
#else
ansec = mnsec = cnsec = 0;
#endif
fill_time(module, v, 7, st->st_atime, ansec);
fill_time(module, v, 8, st->st_mtime, mnsec);
fill_time(module, v, 9, st->st_ctime, cnsec);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
PyLong_FromLong((long)st->st_blksize));
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX,
PyLong_FromLong((long)st->st_blocks));
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
PyStructSequence_SET_ITEM(v, ST_RDEV_IDX,
PyLong_FromLong((long)st->st_rdev));
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
PyStructSequence_SET_ITEM(v, ST_GEN_IDX,
PyLong_FromLong((long)st->st_gen));
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{
PyObject *val;
unsigned long bsec,bnsec;
bsec = (long)st->st_birthtime;
#ifdef HAVE_STAT_TV_NSEC2
bnsec = st->st_birthtimespec.tv_nsec;
#else
bnsec = 0;
#endif
val = PyFloat_FromDouble(bsec + 1e-9*bnsec);
PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
val);
}
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
PyLong_FromLong((long)st->st_flags));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX,
PyLong_FromUnsignedLong(st->st_file_attributes));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
PyStructSequence_SET_ITEM(v, ST_FSTYPE_IDX,
PyUnicode_FromString(st->st_fstype));
#endif
#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG
PyStructSequence_SET_ITEM(v, ST_REPARSE_TAG_IDX,
PyLong_FromUnsignedLong(st->st_reparse_tag));
#endif
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
}
/* POSIX methods */
static PyObject *
posix_do_stat(PyObject *module, const char *function_name, path_t *path,
int dir_fd, int follow_symlinks)
{
STRUCT_STAT st;
int result;
#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT)
if (follow_symlinks_specified(function_name, follow_symlinks))
return NULL;
#endif
if (path_and_dir_fd_invalid("stat", path, dir_fd) ||
dir_fd_and_fd_invalid("stat", dir_fd, path->fd) ||
fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks))
return NULL;
Py_BEGIN_ALLOW_THREADS
if (path->fd != -1)
result = FSTAT(path->fd, &st);
#ifdef MS_WINDOWS
else if (follow_symlinks)
result = win32_stat(path->wide, &st);
else
result = win32_lstat(path->wide, &st);
#else
else
#if defined(HAVE_LSTAT)
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = LSTAT(path->narrow, &st);
else
#endif /* HAVE_LSTAT */
#ifdef HAVE_FSTATAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks)
result = fstatat(dir_fd, path->narrow, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
else
#endif /* HAVE_FSTATAT */
result = STAT(path->narrow, &st);
#endif /* MS_WINDOWS */
Py_END_ALLOW_THREADS
if (result != 0) {
return path_error(path);
}
return _pystat_fromstructstat(module, &st);
}
/*[python input]
for s in """
FACCESSAT
FCHMODAT
FCHOWNAT
FSTATAT
LINKAT
MKDIRAT
MKFIFOAT
MKNODAT
OPENAT
READLINKAT
SYMLINKAT
UNLINKAT
""".strip().split():
s = s.strip()
print("""
#ifdef HAVE_{s}
#define {s}_DIR_FD_CONVERTER dir_fd_converter
#else
#define {s}_DIR_FD_CONVERTER dir_fd_unavailable
#endif
""".rstrip().format(s=s))
for s in """
FCHDIR
FCHMOD
FCHOWN
FDOPENDIR
FEXECVE
FPATHCONF
FSTATVFS
FTRUNCATE
""".strip().split():
s = s.strip()
print("""
#ifdef HAVE_{s}
#define PATH_HAVE_{s} 1
#else
#define PATH_HAVE_{s} 0
#endif
""".rstrip().format(s=s))
[python start generated code]*/
#ifdef HAVE_FACCESSAT
#define FACCESSAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define FACCESSAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_FCHMODAT
#define FCHMODAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define FCHMODAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_FCHOWNAT
#define FCHOWNAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define FCHOWNAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_FSTATAT
#define FSTATAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define FSTATAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_LINKAT
#define LINKAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define LINKAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_MKDIRAT
#define MKDIRAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define MKDIRAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_MKFIFOAT
#define MKFIFOAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define MKFIFOAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_MKNODAT
#define MKNODAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define MKNODAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_OPENAT
#define OPENAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define OPENAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_READLINKAT
#define READLINKAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define READLINKAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_SYMLINKAT
#define SYMLINKAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define SYMLINKAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_UNLINKAT
#define UNLINKAT_DIR_FD_CONVERTER dir_fd_converter
#else
#define UNLINKAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif
#ifdef HAVE_FCHDIR
#define PATH_HAVE_FCHDIR 1
#else
#define PATH_HAVE_FCHDIR 0
#endif
#ifdef HAVE_FCHMOD
#define PATH_HAVE_FCHMOD 1
#else
#define PATH_HAVE_FCHMOD 0
#endif
#ifdef HAVE_FCHOWN
#define PATH_HAVE_FCHOWN 1
#else
#define PATH_HAVE_FCHOWN 0
#endif
#ifdef HAVE_FDOPENDIR
#define PATH_HAVE_FDOPENDIR 1
#else
#define PATH_HAVE_FDOPENDIR 0
#endif
#ifdef HAVE_FEXECVE
#define PATH_HAVE_FEXECVE 1
#else
#define PATH_HAVE_FEXECVE 0
#endif
#ifdef HAVE_FPATHCONF
#define PATH_HAVE_FPATHCONF 1
#else
#define PATH_HAVE_FPATHCONF 0
#endif
#ifdef HAVE_FSTATVFS
#define PATH_HAVE_FSTATVFS 1
#else
#define PATH_HAVE_FSTATVFS 0
#endif
#ifdef HAVE_FTRUNCATE
#define PATH_HAVE_FTRUNCATE 1
#else
#define PATH_HAVE_FTRUNCATE 0
#endif
/*[python end generated code: output=4bd4f6f7d41267f1 input=80b4c890b6774ea5]*/
#ifdef MS_WINDOWS
#undef PATH_HAVE_FTRUNCATE
#define PATH_HAVE_FTRUNCATE 1
#endif
/*[python input]
class path_t_converter(CConverter):
type = "path_t"
impl_by_reference = True
parse_by_reference = True
converter = 'path_converter'
def converter_init(self, *, allow_fd=False, nullable=False):
# right now path_t doesn't support default values.
# to support a default value, you'll need to override initialize().
if self.default not in (unspecified, None):
fail("Can't specify a default to the path_t converter!")
if self.c_default not in (None, 'Py_None'):
raise RuntimeError("Can't specify a c_default to the path_t converter!")
self.nullable = nullable
self.allow_fd = allow_fd
def pre_render(self):
def strify(value):
if isinstance(value, str):
return value
return str(int(bool(value)))
# add self.py_name here when merging with posixmodule conversion
self.c_default = 'PATH_T_INITIALIZE("{}", "{}", {}, {})'.format(
self.function.name,
self.name,
strify(self.nullable),
strify(self.allow_fd),
)
def cleanup(self):
return "path_cleanup(&" + self.name + ");\n"
class dir_fd_converter(CConverter):
type = 'int'
def converter_init(self, requires=None):
if self.default in (unspecified, None):
self.c_default = 'DEFAULT_DIR_FD'
if isinstance(requires, str):
self.converter = requires.upper() + '_DIR_FD_CONVERTER'
else:
self.converter = 'dir_fd_converter'
class uid_t_converter(CConverter):
type = "uid_t"
converter = '_Py_Uid_Converter'
class gid_t_converter(CConverter):
type = "gid_t"
converter = '_Py_Gid_Converter'
class dev_t_converter(CConverter):
type = 'dev_t'
converter = '_Py_Dev_Converter'
class dev_t_return_converter(unsigned_long_return_converter):
type = 'dev_t'
conversion_fn = '_PyLong_FromDev'
unsigned_cast = '(dev_t)'
class FSConverter_converter(CConverter):
type = 'PyObject *'
converter = 'PyUnicode_FSConverter'
def converter_init(self):
if self.default is not unspecified:
fail("FSConverter_converter does not support default values")
self.c_default = 'NULL'
def cleanup(self):
return "Py_XDECREF(" + self.name + ");\n"
class pid_t_converter(CConverter):
type = 'pid_t'
format_unit = '" _Py_PARSE_PID "'
class idtype_t_converter(int_converter):
type = 'idtype_t'
class id_t_converter(CConverter):
type = 'id_t'
format_unit = '" _Py_PARSE_PID "'
class intptr_t_converter(CConverter):
type = 'intptr_t'
format_unit = '" _Py_PARSE_INTPTR "'
class Py_off_t_converter(CConverter):
type = 'Py_off_t'
converter = 'Py_off_t_converter'
class Py_off_t_return_converter(long_return_converter):
type = 'Py_off_t'
conversion_fn = 'PyLong_FromPy_off_t'
class path_confname_converter(CConverter):
type="int"
converter="conv_path_confname"
class confstr_confname_converter(path_confname_converter):
converter='conv_confstr_confname'
class sysconf_confname_converter(path_confname_converter):
converter="conv_sysconf_confname"
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=3338733161aa7879]*/
/*[clinic input]
os.stat
path : path_t(allow_fd=True)
Path to be examined; can be string, bytes, a path-like object or
open-file-descriptor int.
*
dir_fd : dir_fd(requires='fstatat') = None
If not None, it should be a file descriptor open to a directory,
and path should be a relative string; path will then be relative to
that directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
stat will examine the symbolic link itself instead of the file
the link points to.
Perform a stat system call on the given path.
dir_fd and follow_symlinks may not be implemented
on your platform. If they are unavailable, using them will raise a
NotImplementedError.
It's an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
[clinic start generated code]*/
static PyObject *
os_stat_impl(PyObject *module, path_t *path, int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=7d4976e6f18a59c5 input=01d362ebcc06996b]*/
{
return posix_do_stat(module, "stat", path, dir_fd, follow_symlinks);
}
/*[clinic input]
os.lstat
path : path_t
*
dir_fd : dir_fd(requires='fstatat') = None
Perform a stat system call on the given path, without following symbolic links.
Like stat(), but do not follow symbolic links.
Equivalent to stat(path, follow_symlinks=False).
[clinic start generated code]*/
static PyObject *
os_lstat_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=ef82a5d35ce8ab37 input=0b7474765927b925]*/
{
int follow_symlinks = 0;
return posix_do_stat(module, "lstat", path, dir_fd, follow_symlinks);
}
/*[clinic input]
os.access -> bool
path: path_t
Path to be tested; can be string, bytes, or a path-like object.
mode: int
Operating-system mode bitfield. Can be F_OK to test existence,
or the inclusive-OR of R_OK, W_OK, and X_OK.
*
dir_fd : dir_fd(requires='faccessat') = None
If not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that
directory.
effective_ids: bool = False
If True, access will use the effective uid/gid instead of
the real uid/gid.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
access will examine the symbolic link itself instead of the file
the link points to.
Use the real uid/gid to test for access to a path.
{parameters}
dir_fd, effective_ids, and follow_symlinks may not be implemented
on your platform. If they are unavailable, using them will raise a
NotImplementedError.
Note that most operations will use the effective uid/gid, therefore this
routine can be used in a suid/sgid environment to test if the invoking user
has the specified access to the path.
[clinic start generated code]*/
static int
os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd,
int effective_ids, int follow_symlinks)
/*[clinic end generated code: output=cf84158bc90b1a77 input=3ffe4e650ee3bf20]*/
{
int return_value;
#ifdef MS_WINDOWS
DWORD attr;
#else
int result;
#endif
#ifndef HAVE_FACCESSAT
if (follow_symlinks_specified("access", follow_symlinks))
return -1;
if (effective_ids) {
argument_unavailable_error("access", "effective_ids");
return -1;
}
#endif
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
attr = GetFileAttributesW(path->wide);
Py_END_ALLOW_THREADS
/*
* Access is possible if
* * we didn't get a -1, and
* * write access wasn't requested,
* * or the file isn't read-only,
* * or it's a directory.
* (Directories cannot be read-only on Windows.)
*/
return_value = (attr != INVALID_FILE_ATTRIBUTES) &&
(!(mode & 2) ||
!(attr & FILE_ATTRIBUTE_READONLY) ||
(attr & FILE_ATTRIBUTE_DIRECTORY));
#else
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FACCESSAT
if ((dir_fd != DEFAULT_DIR_FD) ||
effective_ids ||
!follow_symlinks) {
int flags = 0;
if (!follow_symlinks)
flags |= AT_SYMLINK_NOFOLLOW;
if (effective_ids)
flags |= AT_EACCESS;
result = faccessat(dir_fd, path->narrow, mode, flags);
}
else
#endif
result = access(path->narrow, mode);
Py_END_ALLOW_THREADS
return_value = !result;
#endif
return return_value;
}
#ifndef F_OK
#define F_OK 0
#endif
#ifndef R_OK
#define R_OK 4
#endif
#ifndef W_OK
#define W_OK 2
#endif
#ifndef X_OK
#define X_OK 1
#endif
#ifdef HAVE_TTYNAME
/*[clinic input]
os.ttyname
fd: int
Integer file descriptor handle.
/
Return the name of the terminal device connected to 'fd'.
[clinic start generated code]*/
static PyObject *
os_ttyname_impl(PyObject *module, int fd)
/*[clinic end generated code: output=c424d2e9d1cd636a input=9ff5a58b08115c55]*/
{
long size = sysconf(_SC_TTY_NAME_MAX);
if (size == -1) {
return posix_error();
}
char *buffer = (char *)PyMem_RawMalloc(size);
if (buffer == NULL) {
return PyErr_NoMemory();
}
int ret = ttyname_r(fd, buffer, size);
if (ret != 0) {
PyMem_RawFree(buffer);
errno = ret;
return posix_error();
}
PyObject *res = PyUnicode_DecodeFSDefault(buffer);
PyMem_RawFree(buffer);
return res;
}
#endif
#ifdef HAVE_CTERMID
/*[clinic input]
os.ctermid
Return the name of the controlling terminal for this process.
[clinic start generated code]*/
static PyObject *
os_ctermid_impl(PyObject *module)
/*[clinic end generated code: output=02f017e6c9e620db input=3b87fdd52556382d]*/
{
char *ret;
char buffer[L_ctermid];
#ifdef USE_CTERMID_R
ret = ctermid_r(buffer);
#else
ret = ctermid(buffer);
#endif
if (ret == NULL)
return posix_error();
return PyUnicode_DecodeFSDefault(buffer);
}
#endif /* HAVE_CTERMID */
/*[clinic input]
os.chdir
path: path_t(allow_fd='PATH_HAVE_FCHDIR')
Change the current working directory to the specified path.
path may always be specified as a string.
On some platforms, path may also be specified as an open file descriptor.
If this functionality is unavailable, using it raises an exception.
[clinic start generated code]*/
static PyObject *
os_chdir_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=3be6400eee26eaae input=1a4a15b4d12cb15d]*/
{
int result;
if (PySys_Audit("os.chdir", "(O)", path->object) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
/* on unix, success = 0, on windows, success = !0 */
result = !win32_wchdir(path->wide);
#else
#ifdef HAVE_FCHDIR
if (path->fd != -1)
result = fchdir(path->fd);
else
#endif
result = chdir(path->narrow);
#endif
Py_END_ALLOW_THREADS
if (result) {
return path_error(path);
}
Py_RETURN_NONE;
}
#ifdef HAVE_FCHDIR
/*[clinic input]
os.fchdir
fd: fildes
Change to the directory of the given file descriptor.
fd must be opened on a directory, not a file.
Equivalent to os.chdir(fd).
[clinic start generated code]*/
static PyObject *
os_fchdir_impl(PyObject *module, int fd)
/*[clinic end generated code: output=42e064ec4dc00ab0 input=18e816479a2fa985]*/
{
if (PySys_Audit("os.chdir", "(i)", fd) < 0) {
return NULL;
}
return posix_fildes_fd(fd, fchdir);
}
#endif /* HAVE_FCHDIR */
/*[clinic input]
os.chmod
path: path_t(allow_fd='PATH_HAVE_FCHMOD')
Path to be modified. May always be specified as a str, bytes, or a path-like object.
On some platforms, path may also be specified as an open file descriptor.
If this functionality is unavailable, using it raises an exception.
mode: int
Operating-system mode bitfield.
*
dir_fd : dir_fd(requires='fchmodat') = None
If not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that
directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
chmod will modify the symbolic link itself instead of the file
the link points to.
Change the access permissions of a file.
It is an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
dir_fd and follow_symlinks may not be implemented on your platform.
If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
int follow_symlinks)
/*[clinic end generated code: output=5cf6a94915cc7bff input=989081551c00293b]*/
{
int result;
#ifdef MS_WINDOWS
DWORD attr;
#endif
#ifdef HAVE_FCHMODAT
int fchmodat_nofollow_unsupported = 0;
#endif
#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD))
if (follow_symlinks_specified("chmod", follow_symlinks))
return NULL;
#endif
if (PySys_Audit("os.chmod", "Oii", path->object, mode,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
return NULL;
}
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
attr = GetFileAttributesW(path->wide);
if (attr == INVALID_FILE_ATTRIBUTES)
result = 0;
else {
if (mode & _S_IWRITE)
attr &= ~FILE_ATTRIBUTE_READONLY;
else
attr |= FILE_ATTRIBUTE_READONLY;
result = SetFileAttributesW(path->wide, attr);
}
Py_END_ALLOW_THREADS
if (!result) {
return path_error(path);
}
#else /* MS_WINDOWS */
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FCHMOD
if (path->fd != -1)
result = fchmod(path->fd, mode);
else
#endif
#ifdef HAVE_LCHMOD
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchmod(path->narrow, mode);
else
#endif
#ifdef HAVE_FCHMODAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
/*
* fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
* The documentation specifically shows how to use it,
* and then says it isn't implemented yet.
* (true on linux with glibc 2.15, and openindiana 3.x)
*
* Once it is supported, os.chmod will automatically
* support dir_fd and follow_symlinks=False. (Hopefully.)
* Until then, we need to be careful what exception we raise.
*/
result = fchmodat(dir_fd, path->narrow, mode,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
/*
* But wait! We can't throw the exception without allowing threads,
* and we can't do that in this nested scope. (Macro trickery, sigh.)
*/
fchmodat_nofollow_unsupported =
result &&
((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
!follow_symlinks;
}
else
#endif
result = chmod(path->narrow, mode);
Py_END_ALLOW_THREADS
if (result) {
#ifdef HAVE_FCHMODAT
if (fchmodat_nofollow_unsupported) {
if (dir_fd != DEFAULT_DIR_FD)
dir_fd_and_follow_symlinks_invalid("chmod",
dir_fd, follow_symlinks);
else
follow_symlinks_specified("chmod", follow_symlinks);
return NULL;
}
else
#endif
return path_error(path);
}
#endif
Py_RETURN_NONE;
}
#ifdef HAVE_FCHMOD
/*[clinic input]
os.fchmod
fd: int
mode: int
Change the access permissions of the file given by file descriptor fd.
Equivalent to os.chmod(fd, mode).
[clinic start generated code]*/
static PyObject *
os_fchmod_impl(PyObject *module, int fd, int mode)
/*[clinic end generated code: output=afd9bc05b4e426b3 input=8ab11975ca01ee5b]*/
{
int res;
int async_err = 0;
if (PySys_Audit("os.chmod", "iii", fd, mode, -1) < 0) {
return NULL;
}
do {
Py_BEGIN_ALLOW_THREADS
res = fchmod(fd, mode);
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE;
}
#endif /* HAVE_FCHMOD */
#ifdef HAVE_LCHMOD
/*[clinic input]
os.lchmod
path: path_t
mode: int
Change the access permissions of a file, without following symbolic links.
If path is a symlink, this affects the link itself rather than the target.
Equivalent to chmod(path, mode, follow_symlinks=False)."
[clinic start generated code]*/
static PyObject *
os_lchmod_impl(PyObject *module, path_t *path, int mode)
/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/
{
int res;
if (PySys_Audit("os.chmod", "Oii", path->object, mode, -1) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
res = lchmod(path->narrow, mode);
Py_END_ALLOW_THREADS
if (res < 0) {
path_error(path);
return NULL;
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHMOD */
#ifdef HAVE_CHFLAGS
/*[clinic input]
os.chflags
path: path_t
flags: unsigned_long(bitwise=True)
follow_symlinks: bool=True
Set file flags.
If follow_symlinks is False, and the last element of the path is a symbolic
link, chflags will change flags on the symbolic link itself instead of the
file the link points to.
follow_symlinks may not be implemented on your platform. If it is
unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chflags_impl(PyObject *module, path_t *path, unsigned long flags,
int follow_symlinks)
/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/
{
int result;
#ifndef HAVE_LCHFLAGS
if (follow_symlinks_specified("chflags", follow_symlinks))
return NULL;
#endif
if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LCHFLAGS
if (!follow_symlinks)
result = lchflags(path->narrow, flags);
else
#endif
result = chflags(path->narrow, flags);
Py_END_ALLOW_THREADS
if (result)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHFLAGS */
#ifdef HAVE_LCHFLAGS
/*[clinic input]
os.lchflags
path: path_t
flags: unsigned_long(bitwise=True)
Set file flags.
This function will not follow symbolic links.
Equivalent to chflags(path, flags, follow_symlinks=False).
[clinic start generated code]*/
static PyObject *
os_lchflags_impl(PyObject *module, path_t *path, unsigned long flags)
/*[clinic end generated code: output=30ae958695c07316 input=f9f82ea8b585ca9d]*/
{
int res;
if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
res = lchflags(path->narrow, flags);
Py_END_ALLOW_THREADS
if (res < 0) {
return path_error(path);
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHFLAGS */
#ifdef HAVE_CHROOT
/*[clinic input]
os.chroot
path: path_t
Change root directory to path.
[clinic start generated code]*/
static PyObject *
os_chroot_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/
{
int res;
Py_BEGIN_ALLOW_THREADS
res = chroot(path->narrow);
Py_END_ALLOW_THREADS
if (res < 0)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHROOT */
#ifdef HAVE_FSYNC
/*[clinic input]
os.fsync
fd: fildes
Force write of fd to disk.
[clinic start generated code]*/
static PyObject *
os_fsync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=4a10d773f52b3584 input=21c3645c056967f2]*/
{
return posix_fildes_fd(fd, fsync);
}
#endif /* HAVE_FSYNC */
#ifdef HAVE_SYNC
/*[clinic input]
os.sync
Force write of everything to disk.
[clinic start generated code]*/
static PyObject *
os_sync_impl(PyObject *module)
/*[clinic end generated code: output=2796b1f0818cd71c input=84749fe5e9b404ff]*/
{
Py_BEGIN_ALLOW_THREADS
sync();
Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
#endif /* HAVE_SYNC */
#ifdef HAVE_FDATASYNC
#ifdef __hpux
extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */
#endif
/*[clinic input]
os.fdatasync
fd: fildes
Force write of fd to disk without forcing update of metadata.
[clinic start generated code]*/
static PyObject *
os_fdatasync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=b4b9698b5d7e26dd input=bc74791ee54dd291]*/
{
return posix_fildes_fd(fd, fdatasync);
}
#endif /* HAVE_FDATASYNC */
#ifdef HAVE_CHOWN
/*[clinic input]
os.chown
path : path_t(allow_fd='PATH_HAVE_FCHOWN')
Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.
uid: uid_t
gid: gid_t
*
dir_fd : dir_fd(requires='fchownat') = None
If not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that
directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
stat will examine the symbolic link itself instead of the file
the link points to.
Change the owner and group id of path to the numeric uid and gid.\
path may always be specified as a string.
On some platforms, path may also be specified as an open file descriptor.
If this functionality is unavailable, using it raises an exception.
If dir_fd is not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that directory.
If follow_symlinks is False, and the last element of the path is a symbolic
link, chown will modify the symbolic link itself instead of the file the
link points to.
It is an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
dir_fd and follow_symlinks may not be implemented on your platform.
If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/
{
int result;
#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT))
if (follow_symlinks_specified("chown", follow_symlinks))
return NULL;
#endif
if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) ||
fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks))
return NULL;
#ifdef __APPLE__
/*
* This is for Mac OS X 10.3, which doesn't have lchown.
* (But we still have an lchown symbol because of weak-linking.)
* It doesn't have fchownat either. So there's no possibility
* of a graceful failover.
*/
if ((!follow_symlinks) && (lchown == NULL)) {
follow_symlinks_specified("chown", follow_symlinks);
return NULL;
}
#endif
if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FCHOWN
if (path->fd != -1)
result = fchown(path->fd, uid, gid);
else
#endif
#ifdef HAVE_LCHOWN
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchown(path->narrow, uid, gid);
else
#endif
#ifdef HAVE_FCHOWNAT
if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
result = fchownat(dir_fd, path->narrow, uid, gid,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
else
#endif
result = chown(path->narrow, uid, gid);
Py_END_ALLOW_THREADS
if (result)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHOWN */
#ifdef HAVE_FCHOWN
/*[clinic input]
os.fchown
fd: int
uid: uid_t
gid: gid_t
Change the owner and group id of the file specified by file descriptor.
Equivalent to os.chown(fd, uid, gid).
[clinic start generated code]*/
static PyObject *
os_fchown_impl(PyObject *module, int fd, uid_t uid, gid_t gid)
/*[clinic end generated code: output=97d21cbd5a4350a6 input=3af544ba1b13a0d7]*/
{
int res;
int async_err = 0;
if (PySys_Audit("os.chown", "iIIi", fd, uid, gid, -1) < 0) {
return NULL;
}
do {
Py_BEGIN_ALLOW_THREADS
res = fchown(fd, uid, gid);
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE;
}
#endif /* HAVE_FCHOWN */
#ifdef HAVE_LCHOWN
/*[clinic input]
os.lchown
path : path_t
uid: uid_t
gid: gid_t
Change the owner and group id of path to the numeric uid and gid.
This function will not follow symbolic links.
Equivalent to os.chown(path, uid, gid, follow_symlinks=False).
[clinic start generated code]*/
static PyObject *
os_lchown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid)
/*[clinic end generated code: output=25eaf6af412fdf2f input=b1c6014d563a7161]*/
{
int res;
if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, -1) < 0) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
res = lchown(path->narrow, uid, gid);
Py_END_ALLOW_THREADS
if (res < 0) {
return path_error(path);
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHOWN */
static PyObject *
posix_getcwd(int use_bytes)
{
#ifdef MS_WINDOWS
wchar_t wbuf[MAXPATHLEN];
wchar_t *wbuf2 = wbuf;
DWORD len;
Py_BEGIN_ALLOW_THREADS
len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf);
/* If the buffer is large enough, len does not include the
terminating \0. If the buffer is too small, len includes
the space needed for the terminator. */
if (len >= Py_ARRAY_LENGTH(wbuf)) {
if (len <= PY_SSIZE_T_MAX / sizeof(wchar_t)) {
wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t));
}
else {
wbuf2 = NULL;
}
if (wbuf2) {
len = GetCurrentDirectoryW(len, wbuf2);
}
}
Py_END_ALLOW_THREADS
if (!wbuf2) {
PyErr_NoMemory();
return NULL;
}
if (!len) {
if (wbuf2 != wbuf)
PyMem_RawFree(wbuf2);
return PyErr_SetFromWindowsErr(0);
}
PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len);
if (wbuf2 != wbuf) {
PyMem_RawFree(wbuf2);
}
if (use_bytes) {
if (resobj == NULL) {
return NULL;
}
Py_SETREF(resobj, PyUnicode_EncodeFSDefault(resobj));
}
return resobj;
#else
const size_t chunk = 1024;
char *buf = NULL;
char *cwd = NULL;
size_t buflen = 0;
Py_BEGIN_ALLOW_THREADS
do {
char *newbuf;
if (buflen <= PY_SSIZE_T_MAX - chunk) {
buflen += chunk;
newbuf = PyMem_RawRealloc(buf, buflen);
}
else {
newbuf = NULL;
}
if (newbuf == NULL) {
PyMem_RawFree(buf);
buf = NULL;
break;
}
buf = newbuf;
cwd = getcwd(buf, buflen);
} while (cwd == NULL && errno == ERANGE);
Py_END_ALLOW_THREADS
if (buf == NULL) {
return PyErr_NoMemory();
}
if (cwd == NULL) {
PyMem_RawFree(buf);
return posix_error();
}
PyObject *obj;
if (use_bytes) {
obj = PyBytes_FromStringAndSize(buf, strlen(buf));
}
else {
obj = PyUnicode_DecodeFSDefault(buf);
}
PyMem_RawFree(buf);
return obj;
#endif /* !MS_WINDOWS */
}
/*[clinic input]
os.getcwd
Return a unicode string representing the current working directory.
[clinic start generated code]*/
static PyObject *
os_getcwd_impl(PyObject *module)
/*[clinic end generated code: output=21badfae2ea99ddc input=f069211bb70e3d39]*/
{
return posix_getcwd(0);
}
/*[clinic input]
os.getcwdb
Return a bytes string representing the current working directory.
[clinic start generated code]*/
static PyObject *
os_getcwdb_impl(PyObject *module)
/*[clinic end generated code: output=3dd47909480e4824 input=f6f6a378dad3d9cb]*/
{
return posix_getcwd(1);
}
#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS))
#define HAVE_LINK 1
#endif
#ifdef HAVE_LINK
/*[clinic input]
os.link
src : path_t
dst : path_t
*
src_dir_fd : dir_fd = None
dst_dir_fd : dir_fd = None
follow_symlinks: bool = True
Create a hard link to a file.
If either src_dir_fd or dst_dir_fd is not None, it should be a file
descriptor open to a directory, and the respective path string (src or dst)
should be relative; the path will then be relative to that directory.
If follow_symlinks is False, and the last element of src is a symbolic
link, link will create a link to the symbolic link itself instead of the
file the link points to.
src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your
platform. If they are unavailable, using them will raise a
NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
int dst_dir_fd, int follow_symlinks)
/*[clinic end generated code: output=7f00f6007fd5269a input=b0095ebbcbaa7e04]*/
{
#ifdef MS_WINDOWS
BOOL result = FALSE;
#else
int result;
#endif
#ifndef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
argument_unavailable_error("link", "src_dir_fd and dst_dir_fd");
return NULL;
}
#endif
#ifndef MS_WINDOWS
if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) {
PyErr_SetString(PyExc_NotImplementedError,
"link: src and dst must be the same type");
return NULL;
}
#endif
if (PySys_Audit("os.link", "OOii", src->object, dst->object,
src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd,
dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) {
return NULL;
}
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
result = CreateHardLinkW(dst->wide, src->wide, NULL);
Py_END_ALLOW_THREADS
if (!result)
return path_error2(src, dst);
#else
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) ||
(dst_dir_fd != DEFAULT_DIR_FD) ||
(!follow_symlinks))
result = linkat(src_dir_fd, src->narrow,
dst_dir_fd, dst->narrow,
follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
else
#endif /* HAVE_LINKAT */
result = link(src->narrow, dst->narrow);
Py_END_ALLOW_THREADS
if (result)
return path_error2(src, dst);
#endif /* MS_WINDOWS */
Py_RETURN_NONE;
}
#endif
#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
static PyObject *
_listdir_windows_no_opendir(path_t *path, PyObject *list)
{
PyObject *v;
HANDLE hFindFile = INVALID_HANDLE_VALUE;
BOOL result;
wchar_t namebuf[MAX_PATH+4]; /* Overallocate for "\*.*" */
/* only claim to have space for MAX_PATH */
Py_ssize_t len = Py_ARRAY_LENGTH(namebuf)-4;
wchar_t *wnamebuf = NULL;
WIN32_FIND_DATAW wFileData;
const wchar_t *po_wchars;
if (!path->wide) { /* Default arg: "." */
po_wchars = L".";
len = 1;
} else {
po_wchars = path->wide;
len = wcslen(path->wide);
}
/* The +5 is so we can append "\\*.*\0" */
wnamebuf = PyMem_New(wchar_t, len + 5);
if (!wnamebuf) {
PyErr_NoMemory();
goto exit;
}
wcscpy(wnamebuf, po_wchars);
if (len > 0) {
wchar_t wch = wnamebuf[len-1];
if (wch != SEP && wch != ALTSEP && wch != L':')
wnamebuf[len++] = SEP;
wcscpy(wnamebuf + len, L"*.*");
}
if ((list = PyList_New(0)) == NULL) {
goto exit;
}
Py_BEGIN_ALLOW_THREADS
hFindFile = FindFirstFileW(wnamebuf, &wFileData);
Py_END_ALLOW_THREADS
if (hFindFile == INVALID_HANDLE_VALUE) {
int error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND)
goto exit;
Py_DECREF(list);
list = path_error(path);
goto exit;
}
do {
/* Skip over . and .. */
if (wcscmp(wFileData.cFileName, L".") != 0 &&
wcscmp(wFileData.cFileName, L"..") != 0) {
v = PyUnicode_FromWideChar(wFileData.cFileName,
wcslen(wFileData.cFileName));
if (path->narrow && v) {
Py_SETREF(v, PyUnicode_EncodeFSDefault(v));
}
if (v == NULL) {
Py_DECREF(list);
list = NULL;
break;
}
if (PyList_Append(list, v) != 0) {
Py_DECREF(v);
Py_DECREF(list);
list = NULL;
break;
}
Py_DECREF(v);
}
Py_BEGIN_ALLOW_THREADS
result = FindNextFileW(hFindFile, &wFileData);
Py_END_ALLOW_THREADS
/* FindNextFile sets error to ERROR_NO_MORE_FILES if
it got to the end of the directory. */
if (!result && GetLastError() != ERROR_NO_MORE_FILES) {
Py_DECREF(list);
list = path_error(path);
goto exit;
}
} while (result == TRUE);
exit:
if (hFindFile != INVALID_HANDLE_VALUE) {
if (FindClose(hFindFile) == FALSE) {
if (list != NULL) {
Py_DECREF(list);
list = path_error(path);
}
}
}
PyMem_Free(wnamebuf);
return list;
} /* end of _listdir_windows_no_opendir */
#else /* thus POSIX, ie: not (MS_WINDOWS and not HAVE_OPENDIR) */
static PyObject *
_posix_listdir(path_t *path, PyObject *list)
{
PyObject *v;
DIR *dirp = NULL;
struct dirent *ep;
int return_str; /* if false, return bytes */
#ifdef HAVE_FDOPENDIR
int fd = -1;
#endif
errno = 0;
#ifdef HAVE_FDOPENDIR
if (path->fd != -1) {
/* closedir() closes the FD, so we duplicate it */
fd = _Py_dup(path->fd);
if (fd == -1)
return NULL;
return_str = 1;
Py_BEGIN_ALLOW_THREADS
dirp = fdopendir(fd);
Py_END_ALLOW_THREADS
}
else
#endif
{
const char *name;
if (path->narrow) {
name = path->narrow;
/* only return bytes if they specified a bytes-like object */
return_str = !PyObject_CheckBuffer(path->object);
}
else {
name = ".";
return_str = 1;
}
Py_BEGIN_ALLOW_THREADS
dirp = opendir(name);
Py_END_ALLOW_THREADS
}
if (dirp == NULL) {
list = path_error(path);
#ifdef HAVE_FDOPENDIR
if (fd != -1) {
Py_BEGIN_ALLOW_THREADS
close(fd);
Py_END_ALLOW_THREADS
}
#endif
goto exit;
}
if ((list = PyList_New(0)) == NULL) {
goto exit;
}
for (;;) {
errno = 0;
Py_BEGIN_ALLOW_THREADS
ep = readdir(dirp);
Py_END_ALLOW_THREADS
if (ep == NULL) {
if (errno == 0) {
break;
} else {
Py_DECREF(list);
list = path_error(path);
goto exit;
}
}
if (ep->d_name[0] == '.' &&
(NAMLEN(ep) == 1 ||
(ep->d_name[1] == '.' && NAMLEN(ep) == 2)))