Permalink
Cannot retrieve contributors at this time
16738 lines (14297 sloc)
437 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
cpython/Modules/posixmodule.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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'. */ | |
#define PY_SSIZE_T_CLEAN | |
#include "Python.h" | |
#ifdef __VXWORKS__ | |
# include "pycore_bitutils.h" // _Py_popcount32() | |
#endif | |
#include "pycore_call.h" // _PyObject_CallNoArgs() | |
#include "pycore_ceval.h" // _PyEval_ReInitThreads() | |
#include "pycore_fileutils.h" // _Py_closerange() | |
#include "pycore_import.h" // _PyImport_ReInitLock() | |
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION() | |
#include "pycore_moduleobject.h" // _PyModule_GetState() | |
#include "pycore_object.h" // _PyObject_LookupSpecial() | |
#include "pycore_pystate.h" // _PyInterpreterState_GET() | |
#include "pycore_signal.h" // Py_NSIG | |
#ifdef MS_WINDOWS | |
# include <windows.h> | |
# if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP) | |
# include <pathcch.h> | |
# endif | |
# include <winioctl.h> | |
# include <lmcons.h> // UNLEN | |
# include "osdefs.h" // SEP | |
# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) | |
# define HAVE_SYMLINK | |
# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ | |
#endif | |
#include "structmember.h" // PyMemberDef | |
#ifndef MS_WINDOWS | |
# include "posixmodule.h" | |
#else | |
# include "pycore_fileutils_windows.h" | |
# include "winreparse.h" | |
#endif | |
#if !defined(EX_OK) && defined(EXIT_SUCCESS) | |
# define EX_OK EXIT_SUCCESS | |
#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> // ctermid() | |
#include <stdlib.h> // system() | |
/* | |
* A number of APIs are available on macOS from a certain macOS version. | |
* To support building with a new SDK while deploying to older versions | |
* the availability test is split into two: | |
* - HAVE_<FUNCTION>: The configure check for compile time availability | |
* - HAVE_<FUNCTION>_RUNTIME: Runtime check for availability | |
* | |
* The latter is always true when not on macOS, or when using a compiler | |
* that does not support __has_builtin (older versions of Xcode). | |
* | |
* Due to compiler restrictions there is one valid use of HAVE_<FUNCTION>_RUNTIME: | |
* if (HAVE_<FUNCTION>_RUNTIME) { ... } | |
* | |
* In mixing the test with other tests or using negations will result in compile | |
* errors. | |
*/ | |
#if defined(__APPLE__) | |
#include <mach/mach.h> | |
#if defined(__has_builtin) | |
#if __has_builtin(__builtin_available) | |
#define HAVE_BUILTIN_AVAILABLE 1 | |
#endif | |
#endif | |
#ifdef HAVE_BUILTIN_AVAILABLE | |
# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) | |
# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) | |
# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) | |
# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) | |
# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) | |
# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) | |
# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) | |
#else /* Xcode 8 or earlier */ | |
/* __builtin_available is not present in these compilers, but | |
* some of the symbols might be weak linked (10.10 SDK or later | |
* deploying on 10.9. | |
* | |
* Fall back to the older style of availability checking for | |
* symbols introduced in macOS 10.10. | |
*/ | |
# ifdef HAVE_FSTATAT | |
# define HAVE_FSTATAT_RUNTIME (fstatat != NULL) | |
# endif | |
# ifdef HAVE_FACCESSAT | |
# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL) | |
# endif | |
# ifdef HAVE_FCHMODAT | |
# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL) | |
# endif | |
# ifdef HAVE_FCHOWNAT | |
# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL) | |
# endif | |
# ifdef HAVE_LINKAT | |
# define HAVE_LINKAT_RUNTIME (linkat != NULL) | |
# endif | |
# ifdef HAVE_FDOPENDIR | |
# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL) | |
# endif | |
# ifdef HAVE_MKDIRAT | |
# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL) | |
# endif | |
# ifdef HAVE_RENAMEAT | |
# define HAVE_RENAMEAT_RUNTIME (renameat != NULL) | |
# endif | |
# ifdef HAVE_UNLINKAT | |
# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL) | |
# endif | |
# ifdef HAVE_OPENAT | |
# define HAVE_OPENAT_RUNTIME (openat != NULL) | |
# endif | |
# ifdef HAVE_READLINKAT | |
# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL) | |
# endif | |
# ifdef HAVE_SYMLINKAT | |
# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL) | |
# endif | |
# ifdef HAVE_UTIMENSAT | |
# define HAVE_UTIMENSAT_RUNTIME (utimensat != NULL) | |
# endif | |
# ifdef HAVE_FUTIMENS | |
# define HAVE_FUTIMENS_RUNTIME (futimens != NULL) | |
# endif | |
# ifdef HAVE_PWRITEV | |
# define HAVE_PWRITEV_RUNTIME (pwritev != NULL) | |
# endif | |
#endif | |
#ifdef HAVE_FUTIMESAT | |
/* Some of the logic for weak linking depends on this assertion */ | |
# error "HAVE_FUTIMESAT unexpectedly defined" | |
#endif | |
#else | |
# define HAVE_FSTATAT_RUNTIME 1 | |
# define HAVE_FACCESSAT_RUNTIME 1 | |
# define HAVE_FCHMODAT_RUNTIME 1 | |
# define HAVE_FCHOWNAT_RUNTIME 1 | |
# define HAVE_LINKAT_RUNTIME 1 | |
# define HAVE_FDOPENDIR_RUNTIME 1 | |
# define HAVE_MKDIRAT_RUNTIME 1 | |
# define HAVE_RENAMEAT_RUNTIME 1 | |
# define HAVE_UNLINKAT_RUNTIME 1 | |
# define HAVE_OPENAT_RUNTIME 1 | |
# define HAVE_READLINKAT_RUNTIME 1 | |
# define HAVE_SYMLINKAT_RUNTIME 1 | |
# define HAVE_FUTIMENS_RUNTIME 1 | |
# define HAVE_UTIMENSAT_RUNTIME 1 | |
# define HAVE_PWRITEV_RUNTIME 1 | |
# define HAVE_MKFIFOAT_RUNTIME 1 | |
# define HAVE_MKNODAT_RUNTIME 1 | |
#endif | |
#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(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) | |
# define USE_XATTRS | |
# include <linux/limits.h> // Needed for XATTR_SIZE_MAX on musl libc. | |
#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 | |
#ifdef HAVE_WINDOWS_CONSOLE_IO | |
# 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 /* HAVE_WINDOWS_CONSOLE_IO */ | |
/* 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> | |
#elif defined( _MSC_VER) | |
/* Microsoft compiler */ | |
# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) | |
# define HAVE_GETPPID 1 | |
# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_APP | MS_WINDOWS_SYSTEM */ | |
# if defined(MS_WINDOWS_DESKTOP) | |
# define HAVE_GETLOGIN 1 | |
# endif /* MS_WINDOWS_DESKTOP */ | |
# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) | |
# define HAVE_SPAWNV 1 | |
# define HAVE_EXECV 1 | |
# define HAVE_WSPAWNV 1 | |
# define HAVE_WEXECV 1 | |
# define HAVE_SYSTEM 1 | |
# define HAVE_CWAIT 1 | |
# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ | |
# define HAVE_PIPE 1 | |
# define HAVE_FSYNC 1 | |
# define fsync _commit | |
#endif /* ! __WATCOMC__ || __QNX__ */ | |
/*[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 | |
# include <malloc.h> | |
#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" | |
# define MODNAME_OBJ &_Py_ID(nt) | |
#else | |
# define INITFUNC PyInit_posix | |
# define MODNAME "posix" | |
# define MODNAME_OBJ &_Py_ID(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 | |
/* eventfd() */ | |
#ifdef HAVE_SYS_EVENTFD_H | |
# include <sys/eventfd.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_CallNoArgs(func); | |
if (res == NULL) | |
PyErr_WriteUnraisable(func); | |
else | |
Py_DECREF(res); | |
} | |
Py_DECREF(cpy); | |
} | |
} | |
} | |
void | |
PyOS_BeforeFork(void) | |
{ | |
PyInterpreterState *interp = _PyInterpreterState_GET(); | |
run_at_forkers(interp->before_forkers, 1); | |
_PyImport_AcquireLock(interp); | |
} | |
void | |
PyOS_AfterFork_Parent(void) | |
{ | |
PyInterpreterState *interp = _PyInterpreterState_GET(); | |
if (_PyImport_ReleaseLock(interp) <= 0) { | |
Py_FatalError("failed releasing import lock after fork"); | |
} | |
run_at_forkers(interp->after_forkers_parent, 0); | |
} | |
void | |
PyOS_AfterFork_Child(void) | |
{ | |
PyStatus status; | |
_PyRuntimeState *runtime = &_PyRuntime; | |
status = _PyRuntimeState_ReInitThreads(runtime); | |
if (_PyStatus_EXCEPTION(status)) { | |
goto fatal_error; | |
} | |
PyThreadState *tstate = _PyThreadState_GET(); | |
_Py_EnsureTstateNotNULL(tstate); | |
#ifdef PY_HAVE_THREAD_NATIVE_ID | |
tstate->native_thread_id = PyThread_get_thread_native_id(); | |
#endif | |
status = _PyEval_ReInitThreads(tstate); | |
if (_PyStatus_EXCEPTION(status)) { | |
goto fatal_error; | |
} | |
status = _PyImport_ReInitLock(tstate->interp); | |
if (_PyStatus_EXCEPTION(status)) { | |
goto fatal_error; | |
} | |
_PySignal_AfterFork(); | |
status = _PyInterpreterState_DeleteExceptMain(runtime); | |
if (_PyStatus_EXCEPTION(status)) { | |
goto fatal_error; | |
} | |
assert(_PyThreadState_GET() == tstate); | |
status = _PyPerfTrampoline_AfterFork_Child(); | |
if (_PyStatus_EXCEPTION(status)) { | |
goto fatal_error; | |
} | |
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, | |
FILE_BASIC_INFO *, FILE_ID_INFO *, | |
struct _Py_stat_struct *); | |
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *, | |
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, uid_t *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); | |
*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, gid_t *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); | |
*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)) || defined(HAVE_DEVICE_MACROS) | |
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) || HAVE_DEVICE_MACROS */ | |
#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 | |
newfunc statresult_new_orig; | |
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) | |
{ | |
wchar_t *wide = (wchar_t *)path->wide; | |
path->wide = NULL; | |
PyMem_Free(wide); | |
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_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_bytes = PyBytes_Check(o); | |
is_unicode = PyUnicode_Check(o); | |
if (!is_index && !is_unicode && !is_bytes) { | |
/* Inline PyOS_FSPath() for better error messages. */ | |
PyObject *func, *res; | |
func = _PyObject_LookupSpecial(o, &_Py_ID(__fspath__)); | |
if (NULL == func) { | |
goto error_format; | |
} | |
res = _PyObject_CallNoArgs(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_SETREF(o, res); | |
} | |
if (is_unicode) { | |
#ifdef MS_WINDOWS | |
wide = PyUnicode_AsWideCharString(o, &length); | |
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; | |
wide = NULL; | |
goto success_exit; | |
#else | |
if (!PyUnicode_FSConverter(o, &bytes)) { | |
goto error_exit; | |
} | |
#endif | |
} | |
else if (is_bytes) { | |
bytes = Py_NewRef(o); | |
} | |
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; | |
} | |
wide = PyUnicode_AsWideCharString(wo, &length); | |
Py_DECREF(wo); | |
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); | |
wide = NULL; | |
#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 | |
PyMem_Free(wide); | |
#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 >= Py_NSIG) { | |
if (overflow || signum != -1 || !PyErr_Occurred()) { | |
PyErr_Format(PyExc_ValueError, | |
"signal number %ld out of range [1; %i]", | |
signum, Py_NSIG - 1); | |
} | |
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 */ | |
/* 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(). */ | |
(void)_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_SetDefault(d, k, v) == NULL) { | |
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; | |
LPCWSTR filename = pszFile; | |
size_t n = wcslen(pszFile); | |
if (n && (pszFile[n - 1] == L'\\' || pszFile[n - 1] == L'/')) { | |
// cannot use PyMem_Malloc here because we do not hold the GIL | |
filename = (LPCWSTR)malloc((n + 1) * sizeof(filename[0])); | |
if(!filename) { | |
SetLastError(ERROR_NOT_ENOUGH_MEMORY); | |
return FALSE; | |
} | |
wcsncpy_s((LPWSTR)filename, n + 1, pszFile, n); | |
while (--n > 0 && (filename[n] == L'\\' || filename[n] == L'/')) { | |
((LPWSTR)filename)[n] = L'\0'; | |
} | |
if (!n || (n == 1 && filename[1] == L':')) { | |
// Nothing left to query | |
free((void *)filename); | |
return FALSE; | |
} | |
} | |
hFindFile = FindFirstFileW(filename, &FileData); | |
if (pszFile != filename) { | |
free((void *)filename); | |
} | |
if (hFindFile == INVALID_HANDLE_VALUE) { | |
return FALSE; | |
} | |
FindClose(hFindFile); | |
find_data_to_file_info(&FileData, info, reparse_tag); | |
return TRUE; | |
} | |
static void | |
update_st_mode_from_path(const wchar_t *path, DWORD attr, | |
struct _Py_stat_struct *result) | |
{ | |
if (!(attr & 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; | |
} | |
} | |
} | |
} | |
static int | |
win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result, | |
BOOL traverse) | |
{ | |
HANDLE hFile; | |
BY_HANDLE_FILE_INFORMATION fileInfo; | |
FILE_BASIC_INFO basicInfo; | |
FILE_ID_INFO idInfo; | |
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. */ | |
switch (GetLastError()) { | |
case ERROR_FILE_NOT_FOUND: /* File cannot be found */ | |
case ERROR_PATH_NOT_FOUND: /* File parent directory cannot be found */ | |
case ERROR_NOT_READY: /* Drive exists but unavailable */ | |
case ERROR_BAD_NET_NAME: /* Remote drive unavailable */ | |
break; | |
/* Restore the error from CreateFileW(). */ | |
default: | |
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_slow_impl(path, result, TRUE); | |
} | |
} | |
} | |
if (!GetFileInformationByHandle(hFile, &fileInfo) || | |
!GetFileInformationByHandleEx(hFile, FileBasicInfo, | |
&basicInfo, sizeof(basicInfo)) || | |
!GetFileInformationByHandleEx(hFile, FileIdInfo, | |
&idInfo, sizeof(idInfo))) { | |
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, &basicInfo, &idInfo, result); | |
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result); | |
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_impl(const wchar_t *path, struct _Py_stat_struct *result, | |
BOOL traverse) | |
{ | |
FILE_STAT_BASIC_INFORMATION statInfo; | |
if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, | |
&statInfo, sizeof(statInfo))) { | |
if (// Cannot use fast path for reparse points ... | |
!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) | |
// ... unless it's a name surrogate (symlink) and we're not following | |
|| (!traverse && IsReparseTagNameSurrogate(statInfo.ReparseTag)) | |
) { | |
_Py_stat_basic_info_to_stat(&statInfo, result); | |
update_st_mode_from_path(path, statInfo.FileAttributes, result); | |
return 0; | |
} | |
} else { | |
switch(GetLastError()) { | |
case ERROR_FILE_NOT_FOUND: | |
case ERROR_PATH_NOT_FOUND: | |
case ERROR_NOT_READY: | |
case ERROR_BAD_NET_NAME: | |
/* These errors aren't worth retrying with the slow path */ | |
return -1; | |
case ERROR_NOT_SUPPORTED: | |
/* indicates the API couldn't be loaded */ | |
break; | |
} | |
} | |
return win32_xstat_slow_impl(path, result, traverse); | |
} | |
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; | |
/* ctime is only deprecated from 3.12, so we copy birthtime across */ | |
result->st_ctime = result->st_birthtime; | |
result->st_ctime_nsec = result->st_birthtime_nsec; | |
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 | |
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) | |
{"st_birthtime", "time of creation"}, | |
#endif | |
#ifdef MS_WINDOWS | |
{"st_birthtime_ns", "time of creation in nanoseconds"}, | |
#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 | |
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) | |
#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) | |
#else | |
#define ST_BIRTHTIME_IDX ST_GEN_IDX | |
#endif | |
#ifdef MS_WINDOWS | |
#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1) | |
#else | |
#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX | |
#endif | |
#if defined(HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES) || defined(MS_WINDOWS) | |
#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_NS_IDX+1) | |
#else | |
#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_NS_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 PyObject * | |
statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
{ | |
PyStructSequence *result; | |
int i; | |
// ht_module doesn't get set in PyStructSequence_NewType(), | |
// so we can't use PyType_GetModule(). | |
PyObject *mod = PyImport_GetModule(MODNAME_OBJ); | |
if (mod == NULL) { | |
return NULL; | |
} | |
_posixstate *state = get_posix_state(mod); | |
Py_DECREF(mod); | |
if (state == NULL) { | |
return NULL; | |
} | |
#define structseq_new state->statresult_new_orig | |
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); | |
result->ob_item[i+3] = Py_NewRef(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 s_index, int f_index, int ns_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; | |
} | |
if (s_index >= 0) { | |
PyStructSequence_SET_ITEM(v, s_index, s); | |
s = NULL; | |
} | |
if (f_index >= 0) { | |
PyStructSequence_SET_ITEM(v, f_index, float_s); | |
float_s = NULL; | |
} | |
if (ns_index >= 0) { | |
PyStructSequence_SET_ITEM(v, ns_index, ns_total); | |
ns_total = NULL; | |
} | |
exit: | |
Py_XDECREF(s); | |
Py_XDECREF(ns_fractional); | |
Py_XDECREF(s_in_ns); | |
Py_XDECREF(ns_total); | |
Py_XDECREF(float_s); | |
} | |
#ifdef MS_WINDOWS | |
static PyObject* | |
_pystat_l128_from_l64_l64(uint64_t low, uint64_t high) | |
{ | |
PyObject *o_low = PyLong_FromUnsignedLongLong(low); | |
if (!o_low || !high) { | |
return o_low; | |
} | |
PyObject *o_high = PyLong_FromUnsignedLongLong(high); | |
PyObject *l64 = o_high ? PyLong_FromLong(64) : NULL; | |
if (!l64) { | |
Py_XDECREF(o_high); | |
Py_DECREF(o_low); | |
return NULL; | |
} | |
Py_SETREF(o_high, PyNumber_Lshift(o_high, l64)); | |
Py_DECREF(l64); | |
if (!o_high) { | |
Py_DECREF(o_low); | |
return NULL; | |
} | |
Py_SETREF(o_low, PyNumber_Add(o_low, o_high)); | |
Py_DECREF(o_high); | |
return o_low; | |
} | |
#endif | |
/* 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)); | |
#ifdef MS_WINDOWS | |
PyStructSequence_SET_ITEM(v, 1, _pystat_l128_from_l64_l64(st->st_ino, st->st_ino_high)); | |
PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLongLong(st->st_dev)); | |
#else | |
static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), | |
"stat.st_ino is larger than unsigned long long"); | |
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino)); | |
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 | |
static_assert(sizeof(long long) >= sizeof(st->st_size), | |
"stat.st_size is larger than long long"); | |
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, 10, 13, st->st_atime, ansec); | |
fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec); | |
fill_time(module, v, 9, 12, 15, 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 | |
#if defined(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); | |
} | |
#elif defined(MS_WINDOWS) | |
fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, | |
st->st_birthtime, st->st_birthtime_nsec); | |
#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; | |
#ifdef HAVE_FSTATAT | |
int fstatat_unavailable = 0; | |
#endif | |
#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) { | |
if (HAVE_FSTATAT_RUNTIME) { | |
result = fstatat(dir_fd, path->narrow, &st, | |
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); | |
} else { | |
fstatat_unavailable = 1; | |
} | |
} else | |
#endif /* HAVE_FSTATAT */ | |
result = STAT(path->narrow, &st); | |
#endif /* MS_WINDOWS */ | |
Py_END_ALLOW_THREADS | |
#ifdef HAVE_FSTATAT | |
if (fstatat_unavailable) { | |
argument_unavailable_error("stat", "dir_fd"); | |
return NULL; | |
} | |
#endif | |
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 | |
#ifdef HAVE_FACCESSAT | |
int faccessat_unavailable = 0; | |
#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) { | |
if (HAVE_FACCESSAT_RUNTIME) { | |
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 { | |
faccessat_unavailable = 1; | |
} | |
} | |
else | |
#endif | |
result = access(path->narrow, mode); | |
Py_END_ALLOW_THREADS | |
#ifdef HAVE_FACCESSAT | |
if (faccessat_unavailable) { | |
if (dir_fd != DEFAULT_DIR_FD) { | |
argument_unavailable_error("access", "dir_fd"); | |
return |