Permalink
Fetching contributors…
Cannot retrieve contributors at this time
13913 lines (11854 sloc) 357 KB
/* POSIX module implementation */
/* This file is also used for Windows NT/MS-Win. In that case the
module actually calls itself 'nt', not 'posix', and a few
functions are either unimplemented or implemented differently. The source
assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
of the compiler used. Different compilers define their own feature
test macro, e.g. '_MSC_VER'. */
#ifdef __APPLE__
/*
* Step 1 of support for weak-linking a number of symbols existing on
* OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
* at the end of this file for more information.
*/
# pragma weak lchown
# pragma weak statvfs
# pragma weak fstatvfs
#endif /* __APPLE__ */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pythread.h"
#include "structmember.h"
#ifndef MS_WINDOWS
#include "posixmodule.h"
#else
#include "winreparse.h"
#endif
/* On android API level 21, 'AT_EACCESS' is not declared although
* HAVE_FACCESSAT is defined. */
#ifdef __ANDROID__
#undef HAVE_FACCESSAT
#endif
#include <stdio.h> /* needed for ctermid() */
#ifdef __cplusplus
extern "C" {
#endif
PyDoc_STRVAR(posix__doc__,
"This module provides access to operating system functionality that is\n\
standardized by the C Standard and the POSIX standard (a thinly\n\
disguised Unix interface). Refer to the library manual and\n\
corresponding Unix manual entries for more information on calls.");
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
/* GNU C Library: major(), minor(), makedev() */
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h> /* For WNOHANG */
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_SYSEXITS_H
#include <sysexits.h>
#endif /* HAVE_SYSEXITS_H */
#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
#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY)
#undef HAVE_SCHED_SETAFFINITY
#endif
#if defined(HAVE_SYS_XATTR_H) && defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__)
#define USE_XATTRS
#endif
#ifdef USE_XATTRS
#include <sys/xattr.h>
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#endif
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#ifdef __hpux
#include <sys/mpctl.h>
#endif
#if defined(__DragonFly__) || \
defined(__OpenBSD__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#ifdef HAVE_LINUX_RANDOM_H
# include <linux/random.h>
#endif
#ifdef HAVE_GETRANDOM_SYSCALL
# include <sys/syscall.h>
#endif
#if defined(MS_WINDOWS)
# define TERMSIZE_USE_CONIO
#elif defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h>
# if defined(HAVE_TERMIOS_H)
# include <termios.h>
# endif
# if defined(TIOCGWINSZ)
# define TERMSIZE_USE_IOCTL
# endif
#endif /* MS_WINDOWS */
/* Various compilers have only certain posix functions */
/* XXX Gosh I wish these were all moved into pyconfig.h */
#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */
#define HAVE_OPENDIR 1
#define HAVE_SYSTEM 1
#include <process.h>
#else
#ifdef _MSC_VER /* Microsoft compiler */
#define HAVE_GETPPID 1
#define HAVE_GETLOGIN 1
#define HAVE_SPAWNV 1
#define HAVE_EXECV 1
#define HAVE_WSPAWNV 1
#define HAVE_WEXECV 1
#define HAVE_PIPE 1
#define HAVE_SYSTEM 1
#define HAVE_CWAIT 1
#define HAVE_FSYNC 1
#define fsync _commit
#else
/* Unix functions that the configure script doesn't check for */
#define HAVE_EXECV 1
#define HAVE_FORK 1
#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */
#define HAVE_FORK1 1
#endif
#define HAVE_GETEGID 1
#define HAVE_GETEUID 1
#define HAVE_GETGID 1
#define HAVE_GETPPID 1
#define HAVE_GETUID 1
#define HAVE_KILL 1
#define HAVE_OPENDIR 1
#define HAVE_PIPE 1
#define HAVE_SYSTEM 1
#define HAVE_WAIT 1
#define HAVE_TTYNAME 1
#endif /* _MSC_VER */
#endif /* ! __WATCOMC__ || __QNX__ */
/*[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
#ifndef HAVE_UNISTD_H
#if ( defined(__WATCOMC__) || defined(_MSC_VER) ) && !defined(__QNX__)
extern int mkdir(const char *);
#else
extern int mkdir(const char *, mode_t);
#endif
#if defined(__IBMC__) || defined(__IBMCPP__)
extern int chdir(char *);
extern int rmdir(char *);
#else
extern int chdir(const char *);
extern int rmdir(const char *);
#endif
extern int chmod(const char *, mode_t);
/*#ifdef HAVE_FCHMOD
extern int fchmod(int, mode_t);
#endif*/
/*#ifdef HAVE_LCHMOD
extern int lchmod(const char *, mode_t);
#endif*/
extern int chown(const char *, uid_t, gid_t);
extern char *getcwd(char *, int);
extern char *strerror(int);
extern int link(const char *, const char *);
extern int rename(const char *, const char *);
extern int stat(const char *, struct stat *);
extern int unlink(const char *);
#ifdef HAVE_SYMLINK
extern int symlink(const char *, const char *);
#endif /* HAVE_SYMLINK */
#ifdef HAVE_LSTAT
extern int lstat(const char *, struct stat *);
#endif /* HAVE_LSTAT */
#endif /* !HAVE_UNISTD_H */
#endif /* !_MSC_VER */
#ifdef HAVE_POSIX_SPAWN
#include <spawn.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif /* HAVE_UTIME_H */
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#define HAVE_UTIME_H /* pretend we do for the rest of this file */
#endif /* HAVE_SYS_UTIME_H */
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
#endif /* HAVE_SYS_TIMES_H */
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif /* HAVE_SYS_UTSNAME_H */
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#if defined(__WATCOMC__) && !defined(__QNX__)
#include <direct.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#define dirent direct
#define NAMLEN(dirent) (dirent)->d_namlen
#endif
#ifdef HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#ifdef HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#ifdef HAVE_NDIR_H
#include <ndir.h>
#endif
#endif
#ifdef _MSC_VER
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif
#ifndef IO_REPARSE_TAG_SYMLINK
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
#endif
#ifndef IO_REPARSE_TAG_MOUNT_POINT
#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
#endif
#include "osdefs.h"
#include <malloc.h>
#include <windows.h>
#include <shellapi.h> /* for ShellExecute() */
#include <lmcons.h> /* for UNLEN */
#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
#define HAVE_SYMLINK
static int win32_can_symlink = 0;
#endif
#endif /* _MSC_VER */
#ifndef MAXPATHLEN
#if defined(PATH_MAX) && PATH_MAX > 1024
#define MAXPATHLEN PATH_MAX
#else
#define MAXPATHLEN 1024
#endif
#endif /* MAXPATHLEN */
#ifdef UNION_WAIT
/* Emulate some macros on systems that have a union instead of macros */
#ifndef WIFEXITED
#define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1)
#endif
#ifndef WTERMSIG
#define WTERMSIG(u_wait) ((u_wait).w_termsig)
#endif
#define WAIT_TYPE union wait
#define WAIT_STATUS_INT(s) (s.w_status)
#else /* !UNION_WAIT */
#define WAIT_TYPE int
#define WAIT_STATUS_INT(s) (s)
#endif /* UNION_WAIT */
/* Don't use the "_r" form if we don't need it (also, won't have a
prototype for it, at least on Solaris -- maybe others as well?). */
#if defined(HAVE_CTERMID_R)
#define USE_CTERMID_R
#endif
/* choose the appropriate stat and fstat functions and return structs */
#undef STAT
#undef FSTAT
#undef STRUCT_STAT
#ifdef MS_WINDOWS
# define STAT win32_stat
# define LSTAT win32_lstat
# define FSTAT _Py_fstat_noraise
# define STRUCT_STAT struct _Py_stat_struct
#else
# define STAT stat
# define LSTAT lstat
# define FSTAT fstat
# define STRUCT_STAT struct stat
#endif
#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#else
#if defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H)
#include <sys/mkdev.h>
#endif
#endif
#ifdef MS_WINDOWS
#define INITFUNC PyInit_nt
#define MODNAME "nt"
#else
#define INITFUNC PyInit_posix
#define MODNAME "posix"
#endif
#if defined(__sun)
/* Something to implement in autoconf, not present in autoconf 2.69 */
#define HAVE_STRUCT_STAT_ST_FSTYPE 1
#endif
#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_CallObject(func, NULL);
if (res == NULL)
PyErr_WriteUnraisable(func);
else
Py_DECREF(res);
}
Py_DECREF(cpy);
}
}
}
void
PyOS_BeforeFork(void)
{
run_at_forkers(_PyInterpreterState_Get()->before_forkers, 1);
_PyImport_AcquireLock();
}
void
PyOS_AfterFork_Parent(void)
{
if (_PyImport_ReleaseLock() <= 0)
Py_FatalError("failed releasing import lock after fork");
run_at_forkers(_PyInterpreterState_Get()->after_forkers_parent, 0);
}
void
PyOS_AfterFork_Child(void)
{
_PyGILState_Reinit();
PyEval_ReInitThreads();
_PyImport_ReInitLock();
_PySignal_AfterFork();
run_at_forkers(_PyInterpreterState_Get()->after_forkers_child, 0);
}
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
/* Legacy wrapper */
void
PyOS_AfterFork(void)
{
#ifdef HAVE_FORK
PyOS_AfterFork_Child();
#endif
}
#ifdef MS_WINDOWS
/* defined in fileutils.c */
PyAPI_FUNC(void) _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
PyAPI_FUNC(void) _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *,
ULONG, struct _Py_stat_struct *);
#endif
#ifdef MS_WINDOWS
static int
win32_warn_bytes_api()
{
return PyErr_WarnEx(PyExc_DeprecationWarning,
"The Windows bytes API has been deprecated, "
"use Unicode filenames instead",
1);
}
#endif
#ifndef MS_WINDOWS
PyObject *
_PyLong_FromUid(uid_t uid)
{
if (uid == (uid_t)-1)
return PyLong_FromLong(-1);
return PyLong_FromUnsignedLong(uid);
}
PyObject *
_PyLong_FromGid(gid_t gid)
{
if (gid == (gid_t)-1)
return PyLong_FromLong(-1);
return PyLong_FromUnsignedLong(gid);
}
int
_Py_Uid_Converter(PyObject *obj, void *p)
{
uid_t uid;
PyObject *index;
int overflow;
long result;
unsigned long uresult;
index = PyNumber_Index(obj);
if (index == NULL) {
PyErr_Format(PyExc_TypeError,
"uid should be integer, not %.200s",
Py_TYPE(obj)->tp_name);
return 0;
}
/*
* Handling uid_t is complicated for two reasons:
* * Although uid_t is (always?) unsigned, it still
* accepts -1.
* * We don't know its size in advance--it may be
* bigger than an int, or it may be smaller than
* a long.
*
* So a bit of defensive programming is in order.
* Start with interpreting the value passed
* in as a signed long and see if it works.
*/
result = PyLong_AsLongAndOverflow(index, &overflow);
if (!overflow) {
uid = (uid_t)result;
if (result == -1) {
if (PyErr_Occurred())
goto fail;
/* It's a legitimate -1, we're done. */
goto success;
}
/* Any other negative number is disallowed. */
if (result < 0)
goto underflow;
/* Ensure the value wasn't truncated. */
if (sizeof(uid_t) < sizeof(long) &&
(long)uid != result)
goto underflow;
goto success;
}
if (overflow < 0)
goto underflow;
/*
* Okay, the value overflowed a signed long. If it
* fits in an *unsigned* long, it may still be okay,
* as uid_t may be unsigned long on this platform.
*/
uresult = PyLong_AsUnsignedLong(index);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
goto overflow;
goto fail;
}
uid = (uid_t)uresult;
/*
* If uid == (uid_t)-1, the user actually passed in ULONG_MAX,
* but this value would get interpreted as (uid_t)-1 by chown
* and its siblings. That's not what the user meant! So we
* throw an overflow exception instead. (We already
* handled a real -1 with PyLong_AsLongAndOverflow() above.)
*/
if (uid == (uid_t)-1)
goto overflow;
/* Ensure the value wasn't truncated. */
if (sizeof(uid_t) < sizeof(long) &&
(unsigned long)uid != uresult)
goto overflow;
/* fallthrough */
success:
Py_DECREF(index);
*(uid_t *)p = uid;
return 1;
underflow:
PyErr_SetString(PyExc_OverflowError,
"uid is less than minimum");
goto fail;
overflow:
PyErr_SetString(PyExc_OverflowError,
"uid is greater than maximum");
/* fallthrough */
fail:
Py_DECREF(index);
return 0;
}
int
_Py_Gid_Converter(PyObject *obj, void *p)
{
gid_t gid;
PyObject *index;
int overflow;
long result;
unsigned long uresult;
index = PyNumber_Index(obj);
if (index == NULL) {
PyErr_Format(PyExc_TypeError,
"gid should be integer, not %.200s",
Py_TYPE(obj)->tp_name);
return 0;
}
/*
* Handling gid_t is complicated for two reasons:
* * Although gid_t is (always?) unsigned, it still
* accepts -1.
* * We don't know its size in advance--it may be
* bigger than an int, or it may be smaller than
* a long.
*
* So a bit of defensive programming is in order.
* Start with interpreting the value passed
* in as a signed long and see if it works.
*/
result = PyLong_AsLongAndOverflow(index, &overflow);
if (!overflow) {
gid = (gid_t)result;
if (result == -1) {
if (PyErr_Occurred())
goto fail;
/* It's a legitimate -1, we're done. */
goto success;
}
/* Any other negative number is disallowed. */
if (result < 0) {
goto underflow;
}
/* Ensure the value wasn't truncated. */
if (sizeof(gid_t) < sizeof(long) &&
(long)gid != result)
goto underflow;
goto success;
}
if (overflow < 0)
goto underflow;
/*
* Okay, the value overflowed a signed long. If it
* fits in an *unsigned* long, it may still be okay,
* as gid_t may be unsigned long on this platform.
*/
uresult = PyLong_AsUnsignedLong(index);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
goto overflow;
goto fail;
}
gid = (gid_t)uresult;
/*
* If gid == (gid_t)-1, the user actually passed in ULONG_MAX,
* but this value would get interpreted as (gid_t)-1 by chown
* and its siblings. That's not what the user meant! So we
* throw an overflow exception instead. (We already
* handled a real -1 with PyLong_AsLongAndOverflow() above.)
*/
if (gid == (gid_t)-1)
goto overflow;
/* Ensure the value wasn't truncated. */
if (sizeof(gid_t) < sizeof(long) &&
(unsigned long)gid != uresult)
goto overflow;
/* fallthrough */
success:
Py_DECREF(index);
*(gid_t *)p = gid;
return 1;
underflow:
PyErr_SetString(PyExc_OverflowError,
"gid is less than minimum");
goto fail;
overflow:
PyErr_SetString(PyExc_OverflowError,
"gid is greater than maximum");
/* fallthrough */
fail:
Py_DECREF(index);
return 0;
}
#endif /* MS_WINDOWS */
#define _PyLong_FromDev PyLong_FromLongLong
#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)
static int
_Py_Dev_Converter(PyObject *obj, void *p)
{
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
if (PyErr_Occurred())
return 0;
return 1;
}
#endif /* HAVE_MKNOD && HAVE_MAKEDEV */
#ifdef AT_FDCWD
/*
* Why the (int) cast? Solaris 10 defines AT_FDCWD as 0xffd19553 (-3041965);
* without the int cast, the value gets interpreted as uint (4291925331),
* which doesn't play nicely with all the initializer lines in this file that
* look like this:
* int dir_fd = DEFAULT_DIR_FD;
*/
#define DEFAULT_DIR_FD (int)AT_FDCWD
#else
#define DEFAULT_DIR_FD (-100)
#endif
static int
_fd_converter(PyObject *o, int *p)
{
int overflow;
long long_value;
PyObject *index = PyNumber_Index(o);
if (index == NULL) {
return 0;
}
assert(PyLong_Check(index));
long_value = PyLong_AsLongAndOverflow(index, &overflow);
Py_DECREF(index);
assert(!PyErr_Occurred());
if (overflow > 0 || long_value > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"fd is greater than maximum");
return 0;
}
if (overflow < 0 || long_value < INT_MIN) {
PyErr_SetString(PyExc_OverflowError,
"fd is less than minimum");
return 0;
}
*p = (int)long_value;
return 1;
}
static int
dir_fd_converter(PyObject *o, void *p)
{
if (o == Py_None) {
*(int *)p = DEFAULT_DIR_FD;
return 1;
}
else if (PyIndex_Check(o)) {
return _fd_converter(o, (int *)p);
}
else {
PyErr_Format(PyExc_TypeError,
"argument should be integer or None, not %.200s",
Py_TYPE(o)->tp_name);
return 0;
}
}
/*
* 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)
{
Py_CLEAR(path->object);
Py_CLEAR(path->cleanup);
}
static int
path_converter(PyObject *o, void *p)
{
path_t *path = (path_t *)p;
PyObject *bytes = NULL;
Py_ssize_t length = 0;
int is_index, is_buffer, is_bytes, is_unicode;
const char *narrow;
#ifdef MS_WINDOWS
PyObject *wo = NULL;
const wchar_t *wide;
#endif
#define FORMAT_EXCEPTION(exc, fmt) \
PyErr_Format(exc, "%s%s" fmt, \
path->function_name ? path->function_name : "", \
path->function_name ? ": " : "", \
path->argument_name ? path->argument_name : "path")
/* Py_CLEANUP_SUPPORTED support */
if (o == NULL) {
path_cleanup(path);
return 1;
}
/* Ensure it's always safe to call path_cleanup(). */
path->object = path->cleanup = NULL;
/* path->object owns a reference to the original object */
Py_INCREF(o);
if ((o == Py_None) && path->nullable) {
path->wide = NULL;
#ifdef MS_WINDOWS
path->narrow = FALSE;
#else
path->narrow = NULL;
#endif
path->fd = -1;
goto success_exit;
}
/* Only call this here so that we don't treat the return value of
os.fspath() as an fd or buffer. */
is_index = path->allow_fd && PyIndex_Check(o);
is_buffer = PyObject_CheckBuffer(o);
is_bytes = PyBytes_Check(o);
is_unicode = PyUnicode_Check(o);
if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
/* Inline PyOS_FSPath() for better error messages. */
_Py_IDENTIFIER(__fspath__);
PyObject *func = NULL;
func = _PyObject_LookupSpecial(o, &PyId___fspath__);
if (NULL == func) {
goto error_format;
}
/* still owns a reference to the original object */
Py_DECREF(o);
o = _PyObject_CallNoArg(func);
Py_DECREF(func);
if (NULL == o) {
goto error_exit;
}
else if (PyUnicode_Check(o)) {
is_unicode = 1;
}
else if (PyBytes_Check(o)) {
is_bytes = 1;
}
else {
goto error_format;
}
}
if (is_unicode) {
#ifdef MS_WINDOWS
wide = PyUnicode_AsUnicodeAndSize(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;
goto success_exit;
#else
if (!PyUnicode_FSConverter(o, &bytes)) {
goto error_exit;
}
#endif
}
else if (is_bytes) {
bytes = o;
Py_INCREF(bytes);
}
else if (is_buffer) {
/* XXX Replace PyObject_CheckBuffer with PyBytes_Check in other code
after removing support of non-bytes buffer objects. */
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"%s%s%s should be %s, not %.200s",
path->function_name ? path->function_name : "",
path->function_name ? ": " : "",
path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
"integer or None" :
path->allow_fd ? "string, bytes, os.PathLike or integer" :
path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike",
Py_TYPE(o)->tp_name)) {
goto error_exit;
}
bytes = PyBytes_FromObject(o);
if (!bytes) {
goto error_exit;
}
}
else if (is_index) {
if (!_fd_converter(o, &path->fd)) {
goto error_exit;
}
path->wide = NULL;
#ifdef MS_WINDOWS
path->narrow = FALSE;
#else
path->narrow = NULL;
#endif
goto success_exit;
}
else {
error_format:
PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s",
path->function_name ? path->function_name : "",
path->function_name ? ": " : "",
path->argument_name ? path->argument_name : "path",
path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
"integer or None" :
path->allow_fd ? "string, bytes, os.PathLike or integer" :
path->nullable ? "string, bytes, os.PathLike or None" :
"string, bytes or os.PathLike",
Py_TYPE(o)->tp_name);
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_AsUnicodeAndSize(wo, &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 = TRUE;
path->cleanup = wo;
Py_DECREF(bytes);
#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
Py_XDECREF(wo);
#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;
if (sigemptyset(mask)) {
/* Probably only if mask == NULL. */
PyErr_SetFromErrno(PyExc_OSError);
return 0;
}
iterator = PyObject_GetIter(obj);
if (iterator == NULL) {
return 0;
}
while ((item = PyIter_Next(iterator)) != NULL) {
signum = PyLong_AsLongAndOverflow(item, &overflow);
Py_DECREF(item);
if (signum <= 0 || signum >= NSIG) {
if (overflow || signum != -1 || !PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"signal number %ld out of range", signum);
}
goto error;
}
if (sigaddset(mask, (int)signum)) {
if (errno != EINVAL) {
/* Probably impossible */
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
/* For backwards compatibility, allow idioms such as
* `range(1, NSIG)` but warn about invalid signal numbers
*/
const char msg[] =
"invalid signal number %ld, please use valid_signals()";
if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) {
goto error;
}
}
}
if (!PyErr_Occurred()) {
Py_DECREF(iterator);
return 1;
}
error:
Py_DECREF(iterator);
return 0;
}
#endif /* HAVE_SIGSET_T */
#ifdef MS_WINDOWS
static int
win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag)
{
char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
_Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer;
DWORD n_bytes_returned;
if (0 == DeviceIoControl(
reparse_point_handle,
FSCTL_GET_REPARSE_POINT,
NULL, 0, /* in buffer */
target_buffer, sizeof(target_buffer),
&n_bytes_returned,
NULL)) /* we're not using OVERLAPPED_IO */
return FALSE;
if (reparse_tag)
*reparse_tag = rdb->ReparseTag;
return TRUE;
}
#endif /* MS_WINDOWS */
/* Return a dictionary corresponding to the POSIX environment table */
#if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED))
/* On Darwin/MacOSX a shared library or framework has no access to
** environ directly, we must obtain it with _NSGetEnviron(). See also
** man environ(7).
*/
#include <crt_externs.h>
static char **environ;
#elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) )
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;
#if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED))
if (environ == NULL)
environ = *_NSGetEnviron();
#endif
#ifdef MS_WINDOWS
/* _wenviron must be initialized in this way if the program is started
through main() instead of wmain(). */
_wgetenv(L"");
if (_wenviron == NULL)
return d;
/* This part ignores errors */
for (e = _wenviron; *e != NULL; e++) {
PyObject *k;
PyObject *v;
const wchar_t *p = wcschr(*e, L'=');
if (p == NULL)
continue;
k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e));
if (k == NULL) {
PyErr_Clear();
continue;
}
v = PyUnicode_FromWideChar(p+1, wcslen(p+1));
if (v == NULL) {
PyErr_Clear();
Py_DECREF(k);
continue;
}
if (PyDict_GetItem(d, k) == NULL) {
if (PyDict_SetItem(d, k, v) != 0)
PyErr_Clear();
}
Py_DECREF(k);
Py_DECREF(v);
}
#else
if (environ == NULL)
return d;
/* This part ignores errors */
for (e = environ; *e != NULL; e++) {
PyObject *k;
PyObject *v;
const char *p = strchr(*e, '=');
if (p == NULL)
continue;
k = PyBytes_FromStringAndSize(*e, (int)(p-*e));
if (k == NULL) {
PyErr_Clear();
continue;
}
v = PyBytes_FromStringAndSize(p+1, strlen(p+1));
if (v == NULL) {
PyErr_Clear();
Py_DECREF(k);
continue;
}
if (PyDict_GetItem(d, k) == NULL) {
if (PyDict_SetItem(d, k, v) != 0)
PyErr_Clear();
}
Py_DECREF(k);
Py_DECREF(v);
}
#endif
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(const char* function, PyObject* filename)
{
/* XXX - see win32_error for comments on 'function' */
errno = GetLastError();
if (filename)
return PyErr_SetExcFromWindowsErrWithFilenameObject(
PyExc_OSError,
errno,
filename);
else
return PyErr_SetFromWindowsErr(errno);
}
#endif /* MS_WINDOWS */
static PyObject *
path_object_error(PyObject *path)
{
#ifdef MS_WINDOWS
return PyErr_SetExcFromWindowsErrWithFilenameObject(
PyExc_OSError, 0, path);
#else
return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, 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 *
path_error2(path_t *path, path_t *path2)
{
return path_object_error2(path->object, path2->object);
}
/* POSIX generic methods */
static int
fildes_converter(PyObject *o, void *p)
{
int fd;
int *pointer = (int *)p;
fd = PyObject_AsFileDescriptor(o);
if (fd < 0)
return 0;
*pointer = fd;
return 1;
}
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
static void
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
BY_HANDLE_FILE_INFORMATION *info,
ULONG *reparse_tag)
{
memset(info, 0, sizeof(*info));
info->dwFileAttributes = pFileData->dwFileAttributes;
info->ftCreationTime = pFileData->ftCreationTime;
info->ftLastAccessTime = pFileData->ftLastAccessTime;
info->ftLastWriteTime = pFileData->ftLastWriteTime;
info->nFileSizeHigh = pFileData->nFileSizeHigh;
info->nFileSizeLow = pFileData->nFileSizeLow;
/* info->nNumberOfLinks = 1; */
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
*reparse_tag = pFileData->dwReserved0;
else
*reparse_tag = 0;
}
static BOOL
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
{
HANDLE hFindFile;
WIN32_FIND_DATAW FileData;
hFindFile = FindFirstFileW(pszFile, &FileData);
if (hFindFile == INVALID_HANDLE_VALUE)
return FALSE;
FindClose(hFindFile);
find_data_to_file_info(&FileData, info, reparse_tag);
return TRUE;
}
static BOOL
get_target_path(HANDLE hdl, wchar_t **target_path)
{
int buf_size, result_length;
wchar_t *buf;
/* We have a good handle to the target, use it to determine
the target path name (then we'll call lstat on it). */
buf_size = GetFinalPathNameByHandleW(hdl, 0, 0,
VOLUME_NAME_DOS);
if(!buf_size)
return FALSE;
buf = (wchar_t *)PyMem_RawMalloc((buf_size + 1) * sizeof(wchar_t));
if (!buf) {
SetLastError(ERROR_OUTOFMEMORY);
return FALSE;
}
result_length = GetFinalPathNameByHandleW(hdl,
buf, buf_size, VOLUME_NAME_DOS);
if(!result_length) {
PyMem_RawFree(buf);
return FALSE;
}
if(!CloseHandle(hdl)) {
PyMem_RawFree(buf);
return FALSE;
}
buf[result_length] = 0;
*target_path = buf;
return TRUE;
}
static int
win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
BOOL traverse)
{
int code;
HANDLE hFile, hFile2;
BY_HANDLE_FILE_INFORMATION info;
ULONG reparse_tag = 0;
wchar_t *target_path;
const wchar_t *dot;
hFile = CreateFileW(
path,
FILE_READ_ATTRIBUTES, /* desired access */
0, /* share mode */
NULL, /* security attributes */
OPEN_EXISTING,
/* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
/* FILE_FLAG_OPEN_REPARSE_POINT does not follow the symlink.
Because of this, calls like GetFinalPathNameByHandle will return
the symlink path again and not the actual final path. */
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|
FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
/* Either the target doesn't exist, or we don't have access to
get a handle to it. If the former, we need to return an error.
If the latter, we can use attributes_from_dir. */
DWORD lastError = GetLastError();
if (lastError != ERROR_ACCESS_DENIED &&
lastError != ERROR_SHARING_VIOLATION)
return -1;
/* Could not get attributes on open file. Fall back to
reading the directory. */
if (!attributes_from_dir(path, &info, &reparse_tag))
/* Very strange. This should not fail now */
return -1;
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (traverse) {
/* Should traverse, but could not open reparse point handle */
SetLastError(lastError);
return -1;
}
}
} else {
if (!GetFileInformationByHandle(hFile, &info)) {
CloseHandle(hFile);
return -1;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (!win32_get_reparse_tag(hFile, &reparse_tag))
return -1;
/* Close the outer open file handle now that we're about to
reopen it with different flags. */
if (!CloseHandle(hFile))
return -1;
if (traverse) {
/* In order to call GetFinalPathNameByHandle we need to open
the file without the reparse handling flag set. */
hFile2 = CreateFileW(
path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFile2 == INVALID_HANDLE_VALUE)
return -1;
if (!get_target_path(hFile2, &target_path))
return -1;
code = win32_xstat_impl(target_path, result, FALSE);
PyMem_RawFree(target_path);
return code;
}
} else
CloseHandle(hFile);
}
_Py_attribute_data_to_stat(&info, reparse_tag, result);
/* Set S_IEXEC if it is an .exe, .bat, ... */
dot = wcsrchr(path, '.');
if (dot) {
if (_wcsicmp(dot, L".bat") == 0 || _wcsicmp(dot, L".cmd") == 0 ||
_wcsicmp(dot, L".exe") == 0 || _wcsicmp(dot, L".com") == 0)
result->st_mode |= 0111;
}
return 0;
}
static int
win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse)
{
/* Protocol violation: we explicitly clear errno, instead of
setting it to a POSIX error. Callers should use GetLastError. */
int code = win32_xstat_impl(path, result, traverse);
errno = 0;
return code;
}
/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w
In Posix, stat automatically traverses symlinks and returns the stat
structure for the target. In Windows, the equivalent GetFileAttributes by
default does not traverse symlinks and instead returns attributes for
the symlink.
Therefore, win32_lstat will get the attributes traditionally, and
win32_stat will first explicitly resolve the symlink target and then will
call win32_lstat on that result. */
static int
win32_lstat(const wchar_t* path, struct _Py_stat_struct *result)
{
return win32_xstat(path, result, FALSE);
}
static int
win32_stat(const wchar_t* path, struct _Py_stat_struct *result)
{
return win32_xstat(path, result, TRUE);
}
#endif /* MS_WINDOWS */
PyDoc_STRVAR(stat_result__doc__,
"stat_result: Result from stat, fstat, or lstat.\n\n\
This object may be accessed either as a tuple of\n\
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\
or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\
\n\
Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\
or st_flags, they are available as attributes only.\n\
\n\
See os.stat for more information.");
static PyStructSequence_Field stat_result_fields[] = {
{"st_mode", "protection bits"},
{"st_ino", "inode"},
{"st_dev", "device"},
{"st_nlink", "number of hard links"},
{"st_uid", "user ID of owner"},
{"st_gid", "group ID of owner"},
{"st_size", "total size, in bytes"},
/* The NULL is replaced with PyStructSequence_UnnamedField later. */
{NULL, "integer time of last access"},
{NULL, "integer time of last modification"},
{NULL, "integer time of last change"},
{"st_atime", "time of last access"},
{"st_mtime", "time of last modification"},
{"st_ctime", "time of last change"},
{"st_atime_ns", "time of last access in nanoseconds"},
{"st_mtime_ns", "time of last modification in nanoseconds"},
{"st_ctime_ns", "time of last change in nanoseconds"},
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
{"st_blksize", "blocksize for filesystem I/O"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
{"st_blocks", "number of blocks allocated"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
{"st_rdev", "device type (if inode device)"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
{"st_flags", "user defined flags for file"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
{"st_gen", "generation number"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{"st_birthtime", "time of creation"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
{"st_file_attributes", "Windows file attribute bits"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
{"st_fstype", "Type of filesystem"},
#endif
{0}
};
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
#define ST_BLKSIZE_IDX 16
#else
#define ST_BLKSIZE_IDX 15
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1)
#else
#define ST_BLOCKS_IDX ST_BLKSIZE_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
#define ST_RDEV_IDX (ST_BLOCKS_IDX+1)
#else
#define ST_RDEV_IDX ST_BLOCKS_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
#define ST_FLAGS_IDX (ST_RDEV_IDX+1)
#else
#define ST_FLAGS_IDX ST_RDEV_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
#define ST_GEN_IDX (ST_FLAGS_IDX+1)
#else
#define ST_GEN_IDX ST_FLAGS_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1)
#else
#define ST_BIRTHTIME_IDX ST_GEN_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1)
#else
#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
#define ST_FSTYPE_IDX (ST_FILE_ATTRIBUTES_IDX+1)
#else
#define ST_FSTYPE_IDX ST_FILE_ATTRIBUTES_IDX
#endif
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
};
static PyTypeObject WaitidResultType;
#endif
static int initialized;
static PyTypeObject StatResultType;
static PyTypeObject StatVFSResultType;
#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER)
static PyTypeObject SchedParamType;
#endif
static newfunc structseq_new;
static PyObject *
statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyStructSequence *result;
int i;
result = (PyStructSequence*)structseq_new(type, args, kwds);
if (!result)
return NULL;
/* If we have been initialized from a tuple,
st_?time might be set to None. Initialize it
from the int slots. */
for (i = 7; i <= 9; i++) {
if (result->ob_item[i+3] == Py_None) {
Py_DECREF(Py_None);
Py_INCREF(result->ob_item[i]);
result->ob_item[i+3] = result->ob_item[i];
}
}
return (PyObject*)result;
}
static PyObject *billion = NULL;
static void
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
{
PyObject *s = _PyLong_FromTime_t(sec);
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
PyObject *s_in_ns = NULL;
PyObject *ns_total = NULL;
PyObject *float_s = NULL;
if (!(s && ns_fractional))
goto exit;
s_in_ns = PyNumber_Multiply(s, billion);
if (!s_in_ns)
goto exit;
ns_total = PyNumber_Add(s_in_ns, ns_fractional);
if (!ns_total)
goto exit;
float_s = PyFloat_FromDouble(sec + 1e-9*nsec);
if (!float_s) {
goto exit;
}
PyStructSequence_SET_ITEM(v, index, s);
PyStructSequence_SET_ITEM(v, index+3, float_s);
PyStructSequence_SET_ITEM(v, index+6, ns_total);
s = NULL;
float_s = NULL;
ns_total = NULL;
exit:
Py_XDECREF(s);
Py_XDECREF(ns_fractional);
Py_XDECREF(s_in_ns);
Py_XDECREF(ns_total);
Py_XDECREF(float_s);
}
/* pack a system stat C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
_pystat_fromstructstat(STRUCT_STAT *st)
{
unsigned long ansec, mnsec, cnsec;
PyObject *v = PyStructSequence_New(&StatResultType);
if (v == NULL)
return NULL;
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode));
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(st->st_ino));
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino));
#ifdef MS_WINDOWS
PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev));
#else
PyStructSequence_SET_ITEM(v, 2, _PyLong_FromDev(st->st_dev));
#endif
PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)st->st_nlink));
#if defined(MS_WINDOWS)
PyStructSequence_SET_ITEM(v, 4, PyLong_FromLong(0));
PyStructSequence_SET_ITEM(v, 5, PyLong_FromLong(0));
#else
PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid));
PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid));
#endif
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(st->st_size));
PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size));
#if defined(HAVE_STAT_TV_NSEC)
ansec = st->st_atim.tv_nsec;
mnsec = st->st_mtim.tv_nsec;
cnsec = st->st_ctim.tv_nsec;
#elif defined(HAVE_STAT_TV_NSEC2)
ansec = st->st_atimespec.tv_nsec;
mnsec = st->st_mtimespec.tv_nsec;
cnsec = st->st_ctimespec.tv_nsec;
#elif defined(HAVE_STAT_NSEC)
ansec = st->st_atime_nsec;
mnsec = st->st_mtime_nsec;
cnsec = st->st_ctime_nsec;
#else
ansec = mnsec = cnsec = 0;
#endif
fill_time(v, 7, st->st_atime, ansec);
fill_time(v, 8, st->st_mtime, mnsec);
fill_time(v, 9, st->st_ctime, cnsec);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
PyLong_FromLong((long)st->st_blksize));
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX,
PyLong_FromLong((long)st->st_blocks));
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
PyStructSequence_SET_ITEM(v, ST_RDEV_IDX,
PyLong_FromLong((long)st->st_rdev));
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
PyStructSequence_SET_ITEM(v, ST_GEN_IDX,
PyLong_FromLong((long)st->st_gen));
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{
PyObject *val;
unsigned long bsec,bnsec;
bsec = (long)st->st_birthtime;
#ifdef HAVE_STAT_TV_NSEC2
bnsec = st->st_birthtimespec.tv_nsec;
#else
bnsec = 0;
#endif
val = PyFloat_FromDouble(bsec + 1e-9*bnsec);
PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
val);
}
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
PyLong_FromLong((long)st->st_flags));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
PyStructSequence_SET_ITEM(v, ST_FILE_ATTRIBUTES_IDX,
PyLong_FromUnsignedLong(st->st_file_attributes));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
PyStructSequence_SET_ITEM(v, ST_FSTYPE_IDX,
PyUnicode_FromString(st->st_fstype));
#endif
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
}
/* POSIX methods */
static PyObject *
posix_do_stat(const char *function_name, path_t *path,
int dir_fd, int follow_symlinks)
{
STRUCT_STAT st;
int result;
#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT)
if (follow_symlinks_specified(function_name, follow_symlinks))
return NULL;
#endif
if (path_and_dir_fd_invalid("stat", path, dir_fd) ||
dir_fd_and_fd_invalid("stat", dir_fd, path->fd) ||
fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks))
return NULL;
Py_BEGIN_ALLOW_THREADS
if (path->fd != -1)
result = FSTAT(path->fd, &st);
#ifdef MS_WINDOWS
else if (follow_symlinks)
result = win32_stat(path->wide, &st);
else
result = win32_lstat(path->wide, &st);
#else
else
#if defined(HAVE_LSTAT)
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = LSTAT(path->narrow, &st);
else
#endif /* HAVE_LSTAT */
#ifdef HAVE_FSTATAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks)
result = fstatat(dir_fd, path->narrow, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
else
#endif /* HAVE_FSTATAT */
result = STAT(path->narrow, &st);
#endif /* MS_WINDOWS */
Py_END_ALLOW_THREADS
if (result != 0) {
return path_error(path);
}
return _pystat_fromstructstat(&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 fildes_converter(CConverter):
type = 'int'
converter = 'fildes_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"
class sched_param_converter(CConverter):
type = 'struct sched_param'
converter = 'convert_sched_param'
impl_by_reference = True;
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=418fce0e01144461]*/
/*[clinic input]
os.stat
path : path_t(allow_fd=True)
Path to be examined; can be string, bytes, 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=270bd64e7bb3c8f7]*/
{
return posix_do_stat("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("lstat", path, dir_fd, follow_symlinks);
}
/*[clinic input]
os.access -> bool
path: path_t
Path to be tested; can be string or bytes
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=8e8c3a6ba791fee3]*/
{
int return_value;
#ifdef MS_WINDOWS
DWORD attr;
#else
int result;
#endif
#ifndef HAVE_FACCESSAT
if (follow_symlinks_specified("access", follow_symlinks))
return -1;
if (effective_ids) {
argument_unavailable_error("access", "effective_ids");
return -1;
}
#endif
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
attr = GetFileAttributesW(path->wide);
Py_END_ALLOW_THREADS
/*
* Access is possible if
* * we didn't get a -1, and
* * write access wasn't requested,
* * or the file isn't read-only,
* * or it's a directory.
* (Directories cannot be read-only on Windows.)
*/
return_value = (attr != INVALID_FILE_ATTRIBUTES) &&
(!(mode & 2) ||
!(attr & FILE_ATTRIBUTE_READONLY) ||
(attr & FILE_ATTRIBUTE_DIRECTORY));
#else
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FACCESSAT
if ((dir_fd != DEFAULT_DIR_FD) ||
effective_ids ||
!follow_symlinks) {
int flags = 0;
if (!follow_symlinks)
flags |= AT_SYMLINK_NOFOLLOW;
if (effective_ids)
flags |= AT_EACCESS;
result = faccessat(dir_fd, path->narrow, mode, flags);
}
else
#endif
result = access(path->narrow, mode);
Py_END_ALLOW_THREADS
return_value = !result;
#endif
return return_value;
}
#ifndef F_OK
#define F_OK 0
#endif
#ifndef R_OK
#define R_OK 4
#endif
#ifndef W_OK
#define W_OK 2
#endif
#ifndef X_OK
#define X_OK 1
#endif
#ifdef HAVE_TTYNAME
/*[clinic input]
os.ttyname -> DecodeFSDefault
fd: int
Integer file descriptor handle.
/
Return the name of the terminal device connected to 'fd'.
[clinic start generated code]*/
static char *
os_ttyname_impl(PyObject *module, int fd)
/*[clinic end generated code: output=ed16ad216d813591 input=5f72ca83e76b3b45]*/
{
char *ret;
ret = ttyname(fd);
if (ret == NULL)
posix_error();
return ret;
}
#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;
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]*/
{
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 or bytes.
On some platforms, path may also be specified as an open file descriptor.
If this functionality is unavailable, using it raises an exception.
mode: int
Operating-system mode bitfield.
*
dir_fd : dir_fd(requires='fchmodat') = None
If not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that
directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
chmod will modify the symbolic link itself instead of the file
the link points to.
Change the access permissions of a file.
It is an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
dir_fd and follow_symlinks may not be implemented on your platform.
If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
int follow_symlinks)
/*[clinic end generated code: output=5cf6a94915cc7bff input=7f1618e5e15cc196]*/
{
int result;
#ifdef MS_WINDOWS
DWORD attr;
#endif
#ifdef HAVE_FCHMODAT
int fchmodat_nofollow_unsupported = 0;
#endif
#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD))
if (follow_symlinks_specified("chmod", follow_symlinks))
return NULL;
#endif
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
attr = GetFileAttributesW(path->wide);
if (attr == INVALID_FILE_ATTRIBUTES)
result = 0;
else {
if (mode & _S_IWRITE)
attr &= ~FILE_ATTRIBUTE_READONLY;
else
attr |= FILE_ATTRIBUTE_READONLY;
result = SetFileAttributesW(path->wide, attr);
}
Py_END_ALLOW_THREADS
if (!result) {
return path_error(path);
}
#else /* MS_WINDOWS */
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FCHMOD
if (path->fd != -1)
result = fchmod(path->fd, mode);
else
#endif
#ifdef HAVE_LCHMOD
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchmod(path->narrow, mode);
else
#endif
#ifdef HAVE_FCHMODAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
/*
* fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
* The documentation specifically shows how to use it,
* and then says it isn't implemented yet.
* (true on linux with glibc 2.15, and openindiana 3.x)
*
* Once it is supported, os.chmod will automatically
* support dir_fd and follow_symlinks=False. (Hopefully.)
* Until then, we need to be careful what exception we raise.
*/
result = fchmodat(dir_fd, path->narrow, mode,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
/*
* But wait! We can't throw the exception without allowing threads,
* and we can't do that in this nested scope. (Macro trickery, sigh.)
*/
fchmodat_nofollow_unsupported =
result &&
((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
!follow_symlinks;
}
else
#endif
result = chmod(path->narrow, mode);
Py_END_ALLOW_THREADS
if (result) {
#ifdef HAVE_FCHMODAT
if (fchmodat_nofollow_unsupported) {
if (dir_fd != DEFAULT_DIR_FD)
dir_fd_and_follow_symlinks_invalid("chmod",
dir_fd, follow_symlinks);
else
follow_symlinks_specified("chmod", follow_symlinks);
return NULL;
}
else
#endif
return path_error(path);
}
#endif
Py_RETURN_NONE;
}
#ifdef HAVE_FCHMOD
/*[clinic input]
os.fchmod
fd: int
mode: int
Change the access permissions of the file given by file descriptor fd.
Equivalent to os.chmod(fd, mode).
[clinic start generated code]*/
static PyObject *
os_fchmod_impl(PyObject *module, int fd, int mode)
/*[clinic end generated code: output=afd9bc05b4e426b3 input=8ab11975ca01ee5b]*/
{
int res;
int async_err = 0;
do {
Py_BEGIN_ALLOW_THREADS
res = fchmod(fd, mode);
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE;
}
#endif /* HAVE_FCHMOD */
#ifdef HAVE_LCHMOD
/*[clinic input]
os.lchmod
path: path_t
mode: int
Change the access permissions of a file, without following symbolic links.
If path is a symlink, this affects the link itself rather than the target.
Equivalent to chmod(path, mode, follow_symlinks=False)."
[clinic start generated code]*/
static PyObject *
os_lchmod_impl(PyObject *module, path_t *path, int mode)
/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/
{
int res;
Py_BEGIN_ALLOW_THREADS
res = lchmod(path->narrow, mode);
Py_END_ALLOW_THREADS
if (res < 0) {
path_error(path);
return NULL;
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHMOD */
#ifdef HAVE_CHFLAGS
/*[clinic input]
os.chflags
path: path_t
flags: unsigned_long(bitwise=True)
follow_symlinks: bool=True
Set file flags.
If follow_symlinks is False, and the last element of the path is a symbolic
link, chflags will change flags on the symbolic link itself instead of the
file the link points to.
follow_symlinks may not be implemented on your platform. If it is
unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chflags_impl(PyObject *module, path_t *path, unsigned long flags,
int follow_symlinks)
/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/
{
int result;
#ifndef HAVE_LCHFLAGS
if (follow_symlinks_specified("chflags", follow_symlinks))
return NULL;
#endif
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LCHFLAGS
if (!follow_symlinks)
result = lchflags(path->narrow, flags);
else
#endif
result = chflags(path->narrow, flags);
Py_END_ALLOW_THREADS
if (result)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHFLAGS */
#ifdef HAVE_LCHFLAGS
/*[clinic input]
os.lchflags
path: path_t
flags: unsigned_long(bitwise=True)
Set file flags.
This function will not follow symbolic links.
Equivalent to chflags(path, flags, follow_symlinks=False).
[clinic start generated code]*/
static PyObject *
os_lchflags_impl(PyObject *module, path_t *path, unsigned long flags)
/*[clinic end generated code: output=30ae958695c07316 input=f9f82ea8b585ca9d]*/
{
int res;
Py_BEGIN_ALLOW_THREADS
res = lchflags(path->narrow, flags);
Py_END_ALLOW_THREADS
if (res < 0) {
return path_error(path);
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHFLAGS */
#ifdef HAVE_CHROOT
/*[clinic input]
os.chroot
path: path_t
Change root directory to path.
[clinic start generated code]*/
static PyObject *
os_chroot_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/
{
int res;
Py_BEGIN_ALLOW_THREADS
res = chroot(path->narrow);
Py_END_ALLOW_THREADS
if (res < 0)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHROOT */
#ifdef HAVE_FSYNC
/*[clinic input]
os.fsync
fd: fildes
Force write of fd to disk.
[clinic start generated code]*/
static PyObject *
os_fsync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=4a10d773f52b3584 input=21c3645c056967f2]*/
{
return posix_fildes_fd(fd, fsync);
}
#endif /* HAVE_FSYNC */
#ifdef HAVE_SYNC
/*[clinic input]
os.sync
Force write of everything to disk.
[clinic start generated code]*/
static PyObject *
os_sync_impl(PyObject *module)
/*[clinic end generated code: output=2796b1f0818cd71c input=84749fe5e9b404ff]*/
{
Py_BEGIN_ALLOW_THREADS
sync();
Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
#endif /* HAVE_SYNC */
#ifdef HAVE_FDATASYNC
#ifdef __hpux
extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */
#endif
/*[clinic input]
os.fdatasync
fd: fildes
Force write of fd to disk without forcing update of metadata.
[clinic start generated code]*/
static PyObject *
os_fdatasync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=b4b9698b5d7e26dd input=bc74791ee54dd291]*/
{
return posix_fildes_fd(fd, fdatasync);
}
#endif /* HAVE_FDATASYNC */
#ifdef HAVE_CHOWN
/*[clinic input]
os.chown
path : path_t(allow_fd='PATH_HAVE_FCHOWN')
Path to be examined; can be string, bytes, or open-file-descriptor int.
uid: uid_t
gid: gid_t
*
dir_fd : dir_fd(requires='fchownat') = None
If not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that
directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
stat will examine the symbolic link itself instead of the file
the link points to.
Change the owner and group id of path to the numeric uid and gid.\
path may always be specified as a string.
On some platforms, path may also be specified as an open file descriptor.
If this functionality is unavailable, using it raises an exception.
If dir_fd is not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that directory.
If follow_symlinks is False, and the last element of the path is a symbolic
link, chown will modify the symbolic link itself instead of the file the
link points to.
It is an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
dir_fd and follow_symlinks may not be implemented on your platform.
If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=4beadab0db5f70cd input=a61cc35574814d5d]*/
{
int result;
#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT))
if (follow_symlinks_specified("chown", follow_symlinks))
return NULL;
#endif
if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) ||
fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks))
return NULL;
#ifdef __APPLE__
/*
* This is for Mac OS X 10.3, which doesn't have lchown.
* (But we still have an lchown symbol because of weak-linking.)
* It doesn't have fchownat either. So there's no possibility
* of a graceful failover.
*/
if ((!follow_symlinks) && (lchown == NULL)) {
follow_symlinks_specified("chown", follow_symlinks);
return NULL;
}
#endif
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FCHOWN
if (path->fd != -1)
result = fchown(path->fd, uid, gid);
else
#endif
#ifdef HAVE_LCHOWN
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchown(path->narrow, uid, gid);
else
#endif
#ifdef HAVE_FCHOWNAT
if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
result = fchownat(dir_fd, path->narrow, uid, gid,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
else
#endif
result = chown(path->narrow, uid, gid);
Py_END_ALLOW_THREADS
if (result)
return path_error(path);
Py_RETURN_NONE;
}
#endif /* HAVE_CHOWN */
#ifdef HAVE_FCHOWN
/*[clinic input]
os.fchown
fd: int
uid: uid_t
gid: gid_t
Change the owner and group id of the file specified by file descriptor.
Equivalent to os.chown(fd, uid, gid).
[clinic start generated code]*/
static PyObject *
os_fchown_impl(PyObject *module, int fd, uid_t uid, gid_t gid)
/*[clinic end generated code: output=97d21cbd5a4350a6 input=3af544ba1b13a0d7]*/
{
int res;
int async_err = 0;
do {
Py_BEGIN_ALLOW_THREADS
res = fchown(fd, uid, gid);
Py_END_ALLOW_THREADS
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
Py_RETURN_NONE;
}
#endif /* HAVE_FCHOWN */
#ifdef HAVE_LCHOWN
/*[clinic input]
os.lchown
path : path_t
uid: uid_t
gid: gid_t
Change the owner and group id of path to the numeric uid and gid.
This function will not follow symbolic links.
Equivalent to os.chown(path, uid, gid, follow_symlinks=False).
[clinic start generated code]*/
static PyObject *
os_lchown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid)
/*[clinic end generated code: output=25eaf6af412fdf2f input=b1c6014d563a7161]*/
{
int res;
Py_BEGIN_ALLOW_THREADS
res = lchown(path->narrow, uid, gid);
Py_END_ALLOW_THREADS
if (res < 0) {
return path_error(path);
}
Py_RETURN_NONE;
}
#endif /* HAVE_LCHOWN */
static PyObject *
posix_getcwd(int use_bytes)
{
char *buf, *tmpbuf;
char *cwd;
const size_t chunk = 1024;
size_t buflen = 0;
PyObject *obj;
#ifdef MS_WINDOWS
if (!use_bytes) {
wchar_t wbuf[MAXPATHLEN];
wchar_t *wbuf2 = wbuf;
PyObject *resobj;
DWORD len;
Py_BEGIN_ALLOW_THREADS
len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf);
/* If the buffer is large enough, len does not include the
terminating \0. If the buffer is too small, len includes
the space needed for the terminator. */
if (len >= Py_ARRAY_LENGTH(wbuf)) {
wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t));
if (wbuf2)
len = GetCurrentDirectoryW(len, wbuf2);
}
Py_END_ALLOW_THREADS
if (!wbuf2) {
PyErr_NoMemory();
return NULL;
}
if (!len) {
if (wbuf2 != wbuf)
PyMem_RawFree(wbuf2);
return PyErr_SetFromWindowsErr(0);
}
resobj = PyUnicode_FromWideChar(wbuf2, len);
if (wbuf2 != wbuf)
PyMem_RawFree(wbuf2);
return resobj;
}
if (win32_warn_bytes_api())
return NULL;
#endif
buf = cwd = NULL;
Py_BEGIN_ALLOW_THREADS
do {
buflen += chunk;
#ifdef MS_WINDOWS
if (buflen > INT_MAX) {
PyErr_NoMemory();
break;
}
#endif
tmpbuf = PyMem_RawRealloc(buf, buflen);
if (tmpbuf == NULL)
break;
buf = tmpbuf;
#ifdef MS_WINDOWS
cwd = getcwd(buf, (int)buflen);
#else
cwd = getcwd(buf, buflen);
#endif
} while (cwd == NULL && errno == ERANGE);
Py_END_ALLOW_THREADS
if (cwd == NULL) {
PyMem_RawFree(buf);
return posix_error();
}
if (use_bytes)
obj = PyBytes_FromStringAndSize(buf, strlen(buf));
else
obj = PyUnicode_DecodeFSDefault(buf);
PyMem_RawFree(buf);
return obj;
}
/*[clinic input]
os.getcwd
Return a unicode string representing the current working directory.
[clinic start generated code]*/
static PyObject *
os_getcwd_impl(PyObject *module)
/*[clinic end generated code: output=21badfae2ea99ddc input=f069211bb70e3d39]*/
{
return posix_getcwd(0);
}
/*[clinic input]
os.getcwdb
Return a bytes string representing the current working directory.
[clinic start generated code]*/
static PyObject *
os_getcwdb_impl(PyObject *module)
/*[clinic end generated code: output=3dd47909480e4824 input=f6f6a378dad3d9cb]*/
{
return posix_getcwd(1);
}
#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS))
#define HAVE_LINK 1
#endif
#ifdef HAVE_LINK
/*[clinic input]
os.link
src : path_t
dst : path_t
*
src_dir_fd : dir_fd = None
dst_dir_fd : dir_fd = None
follow_symlinks: bool = True
Create a hard link to a file.
If either src_dir_fd or dst_dir_fd is not None, it should be a file
descriptor open to a directory, and the respective path string (src or dst)
should be relative; the path will then be relative to that directory.
If follow_symlinks is False, and the last element of src is a symbolic
link, link will create a link to the symbolic link itself instead of the
file the link points to.
src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your
platform. If they are unavailable, using them will raise a
NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
int dst_dir_fd, int follow_symlinks)
/*[clinic end generated code: output=7f00f6007fd5269a input=b0095ebbcbaa7e04]*/
{
#ifdef MS_WINDOWS
BOOL result = FALSE;
#else
int result;
#endif
#ifndef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
argument_unavailable_error("link", "src_dir_fd and dst_dir_fd");
return NULL;
}
#endif
#ifndef MS_WINDOWS
if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) {
PyErr_SetString(PyExc_NotImplementedError,
"link: src and dst must be the same type");
return NULL;
}
#endif
#ifdef MS_WINDOWS
Py_BEGIN_ALLOW_THREADS
result = CreateHardLinkW(dst->wide, src->wide, NULL);
Py_END_ALLOW_THREADS
if (!result)
return path_error2(src, dst);
#else
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) ||
(dst_dir_fd != DEFAULT_DIR_FD) ||
(!follow_symlinks))
result = linkat(src_dir_fd, src->narrow,
dst_dir_fd, dst->narrow,
follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
else
#endif /* HAVE_LINKAT */
result = link(src->narrow, dst->narrow);
Py_END_ALLOW_THREADS
if (result)
return path_error2(src, dst);
#endif /* MS_WINDOWS */
Py_RETURN_NONE;
}
#endif
#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
static PyObject *
_listdir_windows_no_opendir(path_t *path, PyObject *list)
{
PyObject *v;
HANDLE hFindFile = INVALID_HANDLE_VALUE;
BOOL result;
wchar_t namebuf[MAX_PATH+4]; /* Overallocate for "\*.*" */
/* only claim to have space for MAX_PATH */
Py_ssize_t len = Py_ARRAY_LENGTH(namebuf)-4;
wchar_t *wnamebuf = NULL;
WIN32_FIND_DATAW wFileData;
const wchar_t *po_wchars;
if (!path->wide) { /* Default arg: "." */
po_wchars = L".";
len = 1;
} else {
po_wchars = path->wide;
len = wcslen(path->wide);
}
/* The +5 is so we can append "\\*.*\0" */
wnamebuf = PyMem_New(wchar_t, len + 5);
if (!wnamebuf) {
PyErr_NoMemory();
goto exit;
}
wcscpy(wnamebuf, po_wchars);
if (len > 0) {
wchar_t wch = wnamebuf[len-1];
if (wch != SEP && wch != ALTSEP && wch != L':')
wnamebuf[len++] = SEP;
wcscpy(wnamebuf + len, L"*.*");
}
if ((list = PyList_New(0)) == NULL) {
goto exit;
}
Py_BEGIN_ALLOW_THREADS
hFindFile = FindFirstFileW(wnamebuf, &wFileData);
Py_END_ALLOW_THREADS
if (hFindFile == INVALID_HANDLE_VALUE) {
int error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND)
goto exit;
Py_DECREF(list);
list = path_error(path);
goto exit;
}
do {
/* Skip over . and .. */
if (wcscmp(wFileData.cFileName, L".") != 0 &&
wcscmp(wFileData.cFileName, L"..") != 0) {
v = PyUnicode_FromWideChar(wFileData.cFileName,
wcslen(wFileData.cFileName));
if (path->narrow && v) {
Py_SETREF(v, PyUnicode_EncodeFSDefault(v));
}
if (v == NULL) {
Py_DECREF(list);
list = NULL;
break;
}
if (PyList_Append(list, v) != 0) {
Py_DECREF(v);
Py_DECREF(list);
list = NULL;
break;
}
Py_DECREF(v);
}
Py_BEGIN_ALLOW_THREADS
result = FindNextFileW(hFindFile, &wFileData);
Py_END_ALLOW_THREADS
/* FindNextFile sets error to ERROR_NO_MORE_FILES if
it got to the end of the directory. */
if (!result && GetLastError() != ERROR_NO_MORE_FILES) {
Py_DECREF(list);
list = path_error(path);
goto exit;
}
} while (result == TRUE);
exit:
if (hFindFile != INVALID_HANDLE_VALUE) {
if (FindClose(hFindFile) == FALSE) {
if (list != NULL) {
Py_DECREF(list);
list = path_error(path);
}
}
}
PyMem_Free(wnamebuf);
return list;
} /* end of _listdir_windows_no_opendir */
#else /* thus POSIX, ie: not (MS_WINDOWS and not HAVE_OPENDIR) */
static PyObject *
_posix_listdir(path_t *path, PyObject *list)
{
PyObject *v;
DIR *dirp = NULL;
struct dirent *ep;
int return_str; /* if false, return bytes */
#ifdef HAVE_FDOPENDIR
int fd = -1;
#endif
errno = 0;
#ifdef HAVE_FDOPENDIR
if (path->fd != -1) {
/* closedir() closes the FD, so we duplicate it */
fd = _Py_dup(path->fd);
if (fd == -1)
return NULL;
return_str = 1;
Py_BEGIN_ALLOW_THREADS
dirp = fdopendir(fd);
Py_END_ALLOW_THREADS
}
else
#endif
{
const char *name;
if (path->narrow) {
name = path->narrow;
/* only return bytes if they specified a bytes-like object */
return_str = !PyObject_CheckBuffer(path->object);
}
else {
name = ".";
return_str = 1;
}
Py_BEGIN_ALLOW_THREADS
dirp = opendir(name);
Py_END_ALLOW_THREADS
}
if (dirp == NULL) {
list = path_error(path);
#ifdef HAVE_FDOPENDIR
if (fd != -1) {
Py_BEGIN_ALLOW_THREADS
close(fd);
Py_END_ALLOW_THREADS
}
#endif
goto exit;
}
if ((list = PyList_New(0)) == NULL) {
goto exit;
}
for (;;) {
errno = 0;
Py_BEGIN_ALLOW_THREADS
ep = readdir(dirp);
Py_END_ALLOW_THREADS
if (ep == NULL) {
if (errno == 0) {
break;
} else {
Py_DECREF(list);
list = path_error(path);
goto exit;
}
}
if (ep->d_name[0] == '.' &&
(NAMLEN(ep) == 1 ||
(ep->d_name[1] == '.' && NAMLEN(ep) == 2)))
continue;
if (return_str)
v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep));
else
v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep));
if (v == NULL) {
Py_CLEAR(list);
break;
}
if (PyList_Append(list, v) != 0) {
Py_DECREF(v);
Py_CLEAR(list);
break;
}
Py_DECREF(v);
}
exit:
if (dirp != NULL) {
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FDOPENDIR
if (fd > -1)
rewinddir(dirp);
#endif
closedir(dirp);
Py_END_ALLOW_THREADS
}
return list;
} /* end of _posix_listdir */
#endif /* which OS */
/*[clinic input]
os.listdir
path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None
Return a list containing the names of the files in the directory.
path can be specified as either str or bytes. If path is bytes,
the filenames returned will also be bytes; in all other circumstances
the filenames returned will be str.
If path is None, uses the path='.'.
On some platforms, path may also be specified as an open file descriptor;\
the file descriptor must refer to a directory.
If this functionality is unavailable, using it raises NotImplementedError.
The list is in arbitrary order. It does not include the special
entries '.' and '..' even if they are present in the directory.
[clinic start generated code]*/
static PyObject *
os_listdir_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=293045673fcd1a75 input=09e300416e3cd729]*/
{
#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
return _listdir_windows_no_opendir(path, NULL);
#else
return _posix_listdir(path, NULL);
#endif
}
#ifdef MS_WINDOWS
/* A helper function for abspath on win32 */
/*[clinic input]
os._getfullpathname
path: path_t
/
[clinic start generated code]*/
static PyObject *
os__getfullpathname_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=bb8679d56845bc9b input=332ed537c29d0a3e]*/
{
wchar_t woutbuf[MAX_PATH], *woutbufp = woutbuf;
wchar_t *wtemp;
DWORD result;
PyObject *v;
result = GetFullPathNameW(path->wide,
Py_ARRAY_LENGTH(woutbuf),
woutbuf, &wtemp);
if (result > Py_ARRAY_LENGTH(woutbuf)) {
woutbufp = PyMem_New(wchar_t, result);
if (!woutbufp)
return PyErr_NoMemory();
result = GetFullPathNameW(path->wide, result, woutbufp, &wtemp);
}
if (result) {
v = PyUnicode_FromWideChar(woutbufp, wcslen(woutbufp));
if (path->narrow)
Py_SETREF(v, PyUnicode_EncodeFSDefault(v));
} else
v = win32_error_object("GetFullPathNameW", path->object);
if (woutbufp != woutbuf)
PyMem_Free(woutbufp);
return v;
}
/*[clinic input]
os._getfinalpathname
path: path_t
/
A helper function for samepath on windows.
[clinic start generated code]*/
static PyObject *
os__getfinalpathname_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=621a3c79bc29ebfa input=2b6b6c7cbad5fb84]*/
{
HANDLE hFile;
wchar_t buf[MAXPATHLEN], *target_path = buf;
int buf_size = Py_ARRAY_LENGTH(buf);
int result_length;
PyObject *result;
Py_BEGIN_ALLOW_THREADS
hFile = CreateFileW(
path->wide,
0, /* desired access */
0, /* share mode */
NULL, /* security attributes */
OPEN_EXISTING,
/* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
Py_END_ALLOW_THREADS
if (hFile == INVALID_HANDLE_VALUE) {
return win32_error_object("CreateFileW", path->object);
}
/* We have a good handle to the target, use it to determine the
target path name. */
while (1) {
Py_BEGIN_ALLOW_THREADS
result_length = GetFinalPathNameByHandleW(hFile, target_path,
buf_size, VOLUME_NAME_DOS);
Py_END_ALLOW_THREADS
if (!result_length) {
result = win32_error_object("GetFinalPathNameByHandleW",
path->object);
goto cleanup;
}
if (result_length < buf_size) {
break;
}
wchar_t *tmp;
tmp = PyMem_Realloc(target_path != buf ? target_path : NULL,
result_length * sizeof(*tmp));
if (!tmp) {
result = PyErr_NoMemory();
goto cleanup;
}
buf_size = result_length;
target_path = tmp;
}
result = PyUnicode_FromWideChar(target_path, result_length);
if (path->narrow)
Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
cleanup:
if (target_path != buf) {
PyMem_Free(target_path);
}