blob: 8a0c1608ab099c44ee20aee19680e6c7e54ebe6a [file] [log] [blame]
/* 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
# ifdef HAVE_MKFIFOAT
# define HAVE_MKFIFOAT_RUNTIME (mkfifoat != NULL)
# endif
# ifdef HAVE_MKNODAT
# define HAVE_MKNODAT_RUNTIME (mknodat != 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_ID_INFO *pIdInfo = &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))) {
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;
}
}
if (!GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
/* Failed to get FileIdInfo, so do not pass it along */
pIdInfo = NULL;
}
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, 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 -1;
}
if (follow_symlinks_specified("access", follow_symlinks))
return -1;
if (effective_ids) {
argument_unavailable_error("access", "effective_ids");
return -1;
}
/* should be unreachable */
return -1;
}
#endif
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.
Be careful when using number literals for *mode*. The conventional UNIX notation for
numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in
Python.
*
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=674a14bc998de09d]*/
{
int result;
#ifdef MS_WINDOWS
DWORD attr;
#endif
#ifdef HAVE_FCHMODAT
int fchmodat_nofollow_unsupported = 0;
int fchmodat_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 /* HAVE_CHMOD */
#ifdef HAVE_LCHMOD
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchmod(path->narrow, mode);
else
#endif /* HAVE_LCHMOD */
#ifdef HAVE_FCHMODAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
if (HAVE_FCHMODAT_RUNTIME) {
/*
* 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 {
fchmodat_unsupported = 1;
fchmodat_nofollow_unsupported = 1;
result = -1;
}
}
else
#endif /* HAVE_FHCMODAT */
{
#ifdef HAVE_CHMOD
result = chmod(path->narrow, mode);
#elif defined(__wasi__)
// WASI SDK 15.0 does not support chmod.
// Ignore missing syscall for now.
result = 0;
#else
result = -1;
errno = ENOSYS;
#endif
}
Py_END_ALLOW_THREADS
if (result) {
#ifdef HAVE_FCHMODAT
if (fchmodat_unsupported) {
if (dir_fd != DEFAULT_DIR_FD) {
argument_unavailable_error("chmod", "dir_fd");
return NULL;
}
}
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 /* HAVE_FCHMODAT */
return path_error(path);
}
#endif /* MS_WINDOWS */
Py_RETURN_NONE;
}