/* 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'. */

// --- Python includes ------------------------------------------------------

#include "Python.h"

#ifdef __VXWORKS__
#  include "pycore_bitutils.h"    // _Py_popcount32()
#endif
#include "pycore_abstract.h"      // _PyNumber_Index()
#include "pycore_call.h"          // _PyObject_CallNoArgs()
#include "pycore_ceval.h"         // _PyEval_ReInitThreads()
#include "pycore_fileutils.h"     // _Py_closerange()
#include "pycore_import.h"        // _PyImport_AcquireLock()
#include "pycore_initconfig.h"    // _PyStatus_EXCEPTION()
#include "pycore_long.h"          // _PyLong_IsNegative()
#include "pycore_moduleobject.h"  // _PyModule_GetState()
#include "pycore_object.h"        // _PyObject_LookupSpecial()
#include "pycore_pylifecycle.h"   // _PyOS_URandom()
#include "pycore_pystate.h"       // _PyInterpreterState_GET()
#include "pycore_signal.h"        // Py_NSIG
#include "pycore_time.h"          // _PyLong_FromTime_t()
#include "pycore_typeobject.h"    // _PyType_AddMethod()

#ifndef MS_WINDOWS
#  include "posixmodule.h"        // _PyLong_FromUid()
#else
#  include "pycore_fileutils_windows.h" // _Py_GetFileInformationByName()
#  include "osdefs.h"             // SEP
#  include "winreparse.h"         // _Py_REPARSE_DATA_BUFFER
#endif


// --- System includes ------------------------------------------------------

#include <stddef.h>               // offsetof()
#include <stdio.h>                // ctermid()
#include <stdlib.h>               // system()

#ifdef HAVE_UNISTD_H
#  include <unistd.h>             // symlink()
#endif

#ifdef __APPLE__
   /* Needed for the implementation of os.statvfs */
#  include <sys/param.h>
#  include <sys/mount.h>
#endif

#ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>           // futimes()
#endif

#ifdef HAVE_SYS_PIDFD_H
#  include <sys/pidfd.h>          // PIDFD_NONBLOCK
#endif

#ifdef __EMSCRIPTEN__
#  include "emscripten.h"         // emscripten_debugger()
#endif

#ifdef HAVE_SYS_UIO_H
#  include <sys/uio.h>
#endif

#ifdef HAVE_SYS_TYPES_H
   /* Should be included before <sys/sysmacros.h> on HP-UX v3 */
#  include <sys/types.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
   /* GNU C Library: major(), minor(), makedev() */
#  include <sys/sysmacros.h>
#endif

#ifdef HAVE_SYS_STAT_H
#  include <sys/stat.h>
#endif

#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>              // fcntl()
#endif

#ifdef HAVE_GRP_H
#  include <grp.h>                // setgroups()
#endif

#ifdef HAVE_SYSEXITS_H
#  include <sysexits.h>           // EX_OK
#endif

#ifdef HAVE_SYS_LOADAVG_H
#  include <sys/loadavg.h>        // getloadavg()
#endif

#ifdef HAVE_SYS_SENDFILE_H
#  include <sys/sendfile.h>       // sendfile()
#endif

#if defined(__APPLE__)
#  include <copyfile.h>           // fcopyfile()
#endif

#ifdef HAVE_SCHED_H
#  include <sched.h>              // sched_setscheduler()
#endif
#ifdef HAVE_LINUX_SCHED_H
#  include <linux/sched.h>        // SCHED_IDLE, SCHED_RR
#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>       // GRND_RANDOM
#endif
#ifdef HAVE_GETRANDOM_SYSCALL
#  include <sys/syscall.h>        // syscall()
#endif

#ifdef HAVE_POSIX_SPAWN
#  include <spawn.h>              // posix_spawn()
#endif

#ifdef HAVE_UTIME_H
#  include <utime.h>              // utime()
#endif

#ifdef HAVE_SYS_UTIME_H
#  include <sys/utime.h>
#  define HAVE_UTIME_H /* pretend we do for the rest of this file */
#endif

#ifdef HAVE_SYS_TIMES_H
#  include <sys/times.h>          // times()
#endif

#ifdef HAVE_SYS_PARAM_H
#  include <sys/param.h>
#endif

#ifdef HAVE_SYS_UTSNAME_H
#  include <sys/utsname.h>        // uname()
#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>           // memfd_create()
#endif
#ifdef HAVE_SYS_MEMFD_H
#  include <sys/memfd.h>          // memfd_create()
#endif
#ifdef HAVE_LINUX_MEMFD_H
#  include <linux/memfd.h>        // memfd_create(), MFD_CLOEXEC
#endif

#ifdef HAVE_SYS_EVENTFD_H
#  include <sys/eventfd.h>        // eventfd()
#endif

#ifdef HAVE_SYS_TIMERFD_H
#  include <sys/timerfd.h>        // timerfd_create()
#endif

#ifdef _Py_MEMORY_SANITIZER
#  include <sanitizer/msan_interface.h> // __msan_unpoison()
#endif


// --- More complex system includes -----------------------------------------

#ifdef MS_WINDOWS
#  include <windows.h>
#  if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP)
#    include <pathcch.h>          // PathCchSkipRoot()
#  endif
#  include <aclapi.h>             // SetEntriesInAcl
#  include <lmcons.h>             // UNLEN
#  include <sddl.h>               // SDDL_REVISION_1
#  include <winioctl.h>           // FSCTL_GET_REPARSE_POINT
#  if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)
#    define HAVE_SYMLINK
#  endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */
#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>          // getpid(), _cwait()
#  endif
#  include <malloc.h>
#endif /* _MSC_VER */


#ifdef HAVE__GETPTY
#  include <sys/types.h>          // mode_t
   // SGI apparently needs this forward declaration
   extern char * _getpty(int *, int, mode_t, int);
#endif


#if defined(HAVE_SYS_XATTR_H)
#  if defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__)
#    define USE_XATTRS
#    include <linux/limits.h>  // Needed for XATTR_SIZE_MAX on musl libc.
#  endif
#  if defined(__CYGWIN__)
#    define USE_XATTRS
#    include <cygwin/limits.h>  // Needed for XATTR_SIZE_MAX and XATTR_LIST_MAX.
#  endif
#endif
#ifdef USE_XATTRS
#  include <sys/xattr.h>          // fgetxattr()
#endif


#ifdef HAVE_WINDOWS_CONSOLE_IO
#  define TERMSIZE_USE_CONIO
#elif defined(HAVE_SYS_IOCTL_H)
#  include <sys/ioctl.h>          // ioctl(), TIOCGWINSZ
#  if defined(HAVE_TERMIOS_H)
#    include <termios.h>
#  endif
#  if defined(TIOCGWINSZ)
#    define TERMSIZE_USE_IOCTL
#  endif
#endif


/* 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


#if !defined(_MSC_VER) && 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


#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_DIRENT_H
#  include <dirent.h>             // opendir()
#  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


#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


// --- Macros ---------------------------------------------------------------

#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

#ifdef HAVE_STATX
/* until we can assume glibc 2.28 at runtime, we must weakly link */
#  pragma weak statx
static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
#ifdef STATX_MNT_ID
                                             | STATX_MNT_ID
#endif
#ifdef STATX_DIOALIGN
                                             | STATX_DIOALIGN
#endif
#ifdef STATX_MNT_ID_UNIQUE
                                             | STATX_MNT_ID_UNIQUE
#endif
#ifdef STATX_SUBVOL
                                             | STATX_SUBVOL
#endif
#ifdef STATX_WRITE_ATOMIC
                                             | STATX_WRITE_ATOMIC
#endif
#ifdef STATX_DIO_READ_ALIGN
                                             | STATX_DIO_READ_ALIGN
#endif
                                            );
#endif /* HAVE_STATX */


#if !defined(EX_OK) && defined(EXIT_SUCCESS)
#  define EX_OK EXIT_SUCCESS
#endif

#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY)
#  undef HAVE_SCHED_SETAFFINITY
#endif

/* On android API level 21, 'AT_EACCESS' is not declared although
 * HAVE_FACCESSAT is defined. */
#ifdef __ANDROID__
#  undef HAVE_FACCESSAT
#endif

#if defined(__sun)
/* Something to implement in autoconf, not present in autoconf 2.69 */
#  define HAVE_STRUCT_STAT_ST_FSTYPE 1
#endif


// --- Apple __builtin_available() macros -----------------------------------

/*
 * 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_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *)

#  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

#  ifdef HAVE_PTSNAME_R
#    define HAVE_PTSNAME_R_RUNTIME (ptsname_r != 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
#ifdef __wasi__
#  define HAVE_LINKAT_RUNTIME 0
# else
#  define HAVE_LINKAT_RUNTIME 1
# endif
#  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
#  define HAVE_PTSNAME_R_RUNTIME 1
#endif


// --- os module ------------------------------------------------------------

#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

/*[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]*/

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.");


// --- Functions ------------------------------------------------------------

#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_FormatUnraisable("Exception ignored in atfork callback "
                                   "while copying list %R", 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_FormatUnraisable("Exception ignored "
                                           "in atfork callback %R", 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);
    _PyEval_StopTheWorldAll(&_PyRuntime);
    HEAD_LOCK(&_PyRuntime);
}

void
PyOS_AfterFork_Parent(void)
{
    HEAD_UNLOCK(&_PyRuntime);
    _PyEval_StartTheWorldAll(&_PyRuntime);

    PyInterpreterState *interp = _PyInterpreterState_GET();
    _PyImport_ReleaseLock(interp);
    run_at_forkers(interp->after_forkers_parent, 0);
}

static void
reset_remotedebug_data(PyThreadState *tstate)
{
    tstate->remote_debugger_support.debugger_pending_call = 0;
    memset(tstate->remote_debugger_support.debugger_script_path, 0,
           _Py_MAX_SCRIPT_PATH_SIZE);
}

static void
reset_asyncio_state(_PyThreadStateImpl *tstate)
{
    llist_init(&tstate->asyncio_tasks_head);
    tstate->asyncio_running_loop = NULL;
    tstate->asyncio_running_task = NULL;
}


void
PyOS_AfterFork_Child(void)
{
    PyStatus status;
    _PyRuntimeState *runtime = &_PyRuntime;

    // re-creates runtime->interpreters.mutex (HEAD_UNLOCK)
    status = _PyRuntimeState_ReInitThreads(runtime);
    if (_PyStatus_EXCEPTION(status)) {
        goto fatal_error;
    }

    PyThreadState *tstate = _PyThreadState_GET();
    _Py_EnsureTstateNotNULL(tstate);

    assert(tstate->thread_id == PyThread_get_thread_ident());
#ifdef PY_HAVE_THREAD_NATIVE_ID
    tstate->native_thread_id = PyThread_get_thread_native_id();
#endif

#ifdef Py_GIL_DISABLED
    _Py_brc_after_fork(tstate->interp);
    _Py_qsbr_after_fork((_PyThreadStateImpl *)tstate);
#endif

    // Ideally we could guarantee tstate is running main.
    _PyInterpreterState_ReinitRunningMain(tstate);

    status = _PyEval_ReInitThreads(tstate);
    if (_PyStatus_EXCEPTION(status)) {
        goto fatal_error;
    }

    reset_remotedebug_data(tstate);

    reset_asyncio_state((_PyThreadStateImpl *)tstate);

    // Remove the dead thread states. We "start the world" once we are the only
    // thread state left to undo the stop the world call in `PyOS_BeforeFork`.
    // That needs to happen before `_PyThreadState_DeleteList`, because that
    // may call destructors.
    PyThreadState *list = _PyThreadState_RemoveExcept(tstate);
    _PyEval_StartTheWorldAll(&_PyRuntime);
    _PyThreadState_DeleteList(list, /*is_after_fork=*/1);

    _PyImport_ReInitLock(tstate->interp);
    _PyImport_ReleaseLock(tstate->interp);

    _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 */


static PyObject *
_PyLong_FromDev(dev_t dev)
{
#ifdef NODEV
    if (dev == NODEV) {
        return PyLong_FromLongLong((long long)dev);
    }
#endif
    return PyLong_FromUnsignedLongLong((unsigned long long)dev);
}


#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS)
static int
_Py_Dev_Converter(PyObject *obj, void *p)
{
    if (!PyLong_Check(obj)) {
        obj = _PyNumber_Index(obj);
        if (obj == NULL) {
            return 0;
        }
    }
    else {
        Py_INCREF(obj);
    }
    assert(PyLong_Check(obj));
#ifdef NODEV
    if (_PyLong_IsNegative((PyLongObject *)obj)) {
        int overflow;
        long long result = PyLong_AsLongLongAndOverflow(obj, &overflow);
        if (result == -1 && PyErr_Occurred()) {
            Py_DECREF(obj);
            return 0;
        }
        if (!overflow && result == (long long)NODEV) {
            *((dev_t *)p) = NODEV;
            Py_DECREF(obj);
            return 1;
        }
    }
#endif

    unsigned long long result = PyLong_AsUnsignedLongLong(obj);
    Py_DECREF(obj);
    if (result == (unsigned long long)-1 && PyErr_Occurred()) {
        return 0;
    }
    if ((unsigned long long)(dev_t)result != result) {
        PyErr_SetString(PyExc_OverflowError,
                        "Python int too large to convert to C dev_t");
        return 0;
    }
    *((dev_t *)p) = (dev_t)result;
    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;

    if (PyBool_Check(o)) {
        if (PyErr_WarnEx(PyExc_RuntimeWarning,
                "bool is used as a file descriptor", 1))
        {
            return 0;
        }
    }
    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;
#ifdef HAVE_STATX
    PyObject *StatxResultType;
#endif
    PyObject *StatVFSResultType;
    PyObject *TerminalSizeType;
    PyObject *TimesResultType;
    PyObject *UnameResultType;
#if defined(HAVE_WAITID)
    PyObject *WaitidResultType;
#endif
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
    PyObject *struct_rusage;
#endif
    PyObject *st_mode;
#ifndef MS_WINDOWS
    // times() clock frequency in hertz; used by os.times()
    long ticks_per_second;
#endif
} _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 path.make_wide:
 *
 *   * If path.make_wide is nonzero, 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.
 *
 *   * If path.make_wide is zero, if we get bytes we extract
 *     the char_t * and return it; if we get a (Unicode)
 *     string we encode to char_t * 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.nonstrict
 *     If nonzero, the path is permitted to contain
 *     embedded null characters and have any length.
 *   path.make_wide
 *     If nonzero, the converter always uses wide, decoding if necessary, else
 *     it always uses narrow, encoding if necessary. The default value is
 *     nonzero on Windows, else zero.
 *   path.suppress_value_error
 *     If nonzero, raising ValueError is suppressed.
 *   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
 *     or if it was bytes and decoded to Unicode.
 *   path.narrow
 *     Points to the path if it was expressed as bytes,
 *     or if it was Unicode and encoded to bytes.
 *   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.value_error
 *     If nonzero, then suppress_value_error was specified and a ValueError
 *     occurred.
 *   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 {
    // Input fields
    const char *function_name;
    const char *argument_name;
    int nullable;
    int nonstrict;
    int make_wide;
    int suppress_value_error;
    int allow_fd;
    // Output fields
    const wchar_t *wide;
    const char *narrow;
    int fd;
    int value_error;
    Py_ssize_t length;
    PyObject *object;
    PyObject *cleanup;
} path_t;

#define PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, \
                          make_wide, suppress_value_error, allow_fd) \
    {function_name, argument_name, nullable, nonstrict, make_wide, \
     suppress_value_error, allow_fd, NULL, NULL, -1, 0, 0, NULL, NULL}
#ifdef MS_WINDOWS
#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \
                            nonstrict, suppress_value_error, allow_fd) \
    PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 1, \
                      suppress_value_error, allow_fd)
#else
#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \
                            nonstrict, suppress_value_error, allow_fd) \
    PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 0, \
                      suppress_value_error, allow_fd)
#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;
    PyObject *wo = NULL;
    wchar_t *wide = NULL;

#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;
        path->narrow = NULL;
        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) || (func == Py_None)) {
            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) {
        if (path->make_wide) {
            wide = PyUnicode_AsWideCharString(o, &length);
            if (!wide) {
                goto error_exit;
            }
#ifdef MS_WINDOWS
            if (!path->nonstrict && length > 32767) {
                FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
                goto error_exit;
            }
#endif
            if (!path->nonstrict && wcslen(wide) != (size_t)length) {
                FORMAT_EXCEPTION(PyExc_ValueError,
                                 "embedded null character in %s");
                goto error_exit;
            }

            path->wide = wide;
            path->narrow = NULL;
            path->fd = -1;
            wide = NULL;
            goto success_exit;
        }
        bytes = PyUnicode_EncodeFSDefault(o);
        if (!bytes) {
            goto error_exit;
        }
    }
    else if (is_bytes) {
        bytes = Py_NewRef(o);
    }
    else if (is_index) {
        if (!_fd_converter(o, &path->fd)) {
            goto error_exit;
        }
        path->wide = NULL;
        path->narrow = NULL;
        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 (!path->nonstrict && strlen(narrow) != (size_t)length) {
        FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s");
        goto error_exit;
    }

    if (path->make_wide) {
        wo = PyUnicode_DecodeFSDefaultAndSize(narrow, length);
        if (!wo) {
            goto error_exit;
        }

        wide = PyUnicode_AsWideCharString(wo, &length);
        Py_DECREF(wo);
        if (!wide) {
            goto error_exit;
        }
#ifdef MS_WINDOWS
        if (!path->nonstrict && length > 32767) {
            FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows");
            goto error_exit;
        }
#endif
        if (!path->nonstrict && wcslen(wide) != (size_t)length) {
            FORMAT_EXCEPTION(PyExc_ValueError,
                             "embedded null character in %s");
            goto error_exit;
        }
        path->wide = wide;
        path->narrow = NULL;
        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;
        }
    }
    path->fd = -1;

 success_exit:
    path->value_error = 0;
    path->length = length;
    path->object = o;
    return Py_CLEANUP_SUPPORTED;

 error_exit:
    Py_XDECREF(o);
    Py_XDECREF(bytes);
    PyMem_Free(wide);
    if (!path->suppress_value_error ||
        !PyErr_ExceptionMatches(PyExc_ValueError))
    {
        return 0;
    }
    PyErr_Clear();
    path->wide = NULL;
    path->narrow = NULL;
    path->fd = -1;
    path->value_error = 1;
    path->length = 0;
    path->object = NULL;
    return Py_CLEANUP_SUPPORTED;
}

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) && !path->narrow) {
        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;
}

#if defined(HAVE_WAITID)
static int
idtype_t_converter(PyObject *arg, void *addr)
{
    int value = PyLong_AsInt(arg);
    if (value == -1 && PyErr_Occurred()) {
        return 0;
    }
    *((idtype_t *)addr) = (idtype_t)(value);
    return 1;
}
#endif

#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>
#define USE_DARWIN_NS_GET_ENVIRON 1
#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(USE_DARWIN_NS_GET_ENVIRON)
    /* 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, (Py_ssize_t)(p-*e));
#endif
        if (k == NULL) {
            Py_DECREF(d);
            return NULL;
        }
#ifdef MS_WINDOWS
        v = PyUnicode_FromWideChar(p+1, -1);
#else
        v = PyBytes_FromStringAndSize(p+1, strlen(p+1));
#endif
        if (v == NULL) {
            Py_DECREF(k);
            Py_DECREF(d);
            return NULL;
        }
        if (PyDict_SetDefaultRef(d, k, v, NULL) < 0) {
            Py_DECREF(v);
            Py_DECREF(k);
            Py_DECREF(d);
            return NULL;
        }
        Py_DECREF(k);
        Py_DECREF(v);
    }
    return d;
}

/* Set a POSIX-specific error from errno, and return NULL */

static PyObject *
posix_error(void)
{
    return PyErr_SetFromErrno(PyExc_OSError);
}

#ifdef MS_WINDOWS
static PyObject *
win32_error(const char* function, const char* filename)
{
    /* XXX We should pass the function name along in the future.
       (winreg.c also wants to pass the function name.)
       This would however require an additional param to the
       Windows error object, which is non-trivial.
    */
    errno = GetLastError();
    if (filename)
        return PyErr_SetFromWindowsErrWithFilename(errno, filename);
    else
        return PyErr_SetFromWindowsErr(errno);
}

static PyObject *
win32_error_object_err(const char* function, PyObject* filename, DWORD err)
{
    /* XXX - see win32_error for comments on 'function' */
    if (filename)
        return PyErr_SetExcFromWindowsErrWithFilenameObject(
                    PyExc_OSError,
                    err,
                    filename);
    else
        return PyErr_SetFromWindowsErr(err);
}

static PyObject *
win32_error_object(const char* function, PyObject* filename)
{
    errno = GetLastError();
    return win32_error_object_err(function, filename, errno);
}

#endif /* MS_WINDOWS */

static PyObject *
posix_path_object_error(PyObject *path)
{
    return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
}

static PyObject *
path_object_error(PyObject *path)
{
#ifdef MS_WINDOWS
    return PyErr_SetExcFromWindowsErrWithFilenameObject(
                PyExc_OSError, 0, path);
#else
    return posix_path_object_error(path);
#endif
}

static PyObject *
path_object_error2(PyObject *path, PyObject *path2)
{
#ifdef MS_WINDOWS
    return PyErr_SetExcFromWindowsErrWithFilenameObjects(
                PyExc_OSError, 0, path, path2);
#else
    return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, path, path2);
#endif
}

static PyObject *
path_error(path_t *path)
{
    return path_object_error(path->object);
}

static PyObject *
posix_path_error(path_t *path)
{
    return posix_path_object_error(path->object);
}

static PyObject *
path_error2(path_t *path, path_t *path2)
{
    return path_object_error2(path->object, path2->object);
}


/* POSIX generic methods */

static PyObject *
posix_fildes_fd(int fd, int (*func)(int))
{
    int res;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        res = (*func)(fd);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res != 0)
        return (!async_err) ? posix_error() : NULL;
    Py_RETURN_NONE;
}


#ifdef MS_WINDOWS
/* This is a reimplementation of the C library's chdir function,
   but one that produces Win32 errors instead of DOS error codes.
   chdir is essentially a wrapper around SetCurrentDirectory; however,
   it also needs to set "magic" environment variables indicating
   the per-drive current directory, which are of the form =<drive>: */
static BOOL __stdcall
win32_wchdir(LPCWSTR path)
{
    wchar_t path_buf[MAX_PATH], *new_path = path_buf;
    int result;
    wchar_t env[4] = L"=x:";

    if(!SetCurrentDirectoryW(path))
        return FALSE;
    result = GetCurrentDirectoryW(Py_ARRAY_LENGTH(path_buf), new_path);
    if (!result)
        return FALSE;
    if (result > Py_ARRAY_LENGTH(path_buf)) {
        new_path = PyMem_RawMalloc(result * sizeof(wchar_t));
        if (!new_path) {
            SetLastError(ERROR_OUTOFMEMORY);
            return FALSE;
        }
        result = GetCurrentDirectoryW(result, new_path);
        if (!result) {
            PyMem_RawFree(new_path);
            return FALSE;
        }
    }
    int is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 ||
                            wcsncmp(new_path, L"//", 2) == 0);
    if (!is_unc_like_path) {
        env[1] = new_path[0];
        result = SetEnvironmentVariableW(env, new_path);
    }
    if (new_path != path_buf)
        PyMem_RawFree(new_path);
    return result ? TRUE : FALSE;
}
#endif

#ifdef MS_WINDOWS
/* The CRT of Windows has a number of flaws wrt. its stat() implementation:
   - time stamps are restricted to second resolution
   - file modification times suffer from forth-and-back conversions between
     UTC and local time
   Therefore, we implement our own stat, based on the Win32 API directly.
*/
#define HAVE_STAT_NSEC 1
#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1
#define HAVE_STRUCT_STAT_ST_REPARSE_TAG 1

static void
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
                       BY_HANDLE_FILE_INFORMATION *info,
                       ULONG *reparse_tag)
{
    memset(info, 0, sizeof(*info));
    info->dwFileAttributes = pFileData->dwFileAttributes;
    info->ftCreationTime   = pFileData->ftCreationTime;
    info->ftLastAccessTime = pFileData->ftLastAccessTime;
    info->ftLastWriteTime  = pFileData->ftLastWriteTime;
    info->nFileSizeHigh    = pFileData->nFileSizeHigh;
    info->nFileSizeLow     = pFileData->nFileSizeLow;
/*  info->nNumberOfLinks   = 1; */
    if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
        *reparse_tag = pFileData->dwReserved0;
    else
        *reparse_tag = 0;
}

static BOOL
attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag)
{
    HANDLE hFindFile;
    WIN32_FIND_DATAW FileData;
    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_BASIC_INFO *pBasicInfo = NULL;
    FILE_ID_INFO idInfo;
    FILE_ID_INFO *pIdInfo = NULL;
    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;
        }

        /* Successfully got FileBasicInfo, so we'll pass it along */
        pBasicInfo = &basicInfo;

        if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
            /* Successfully got FileIdInfo, so pass it along */
            pIdInfo = &idInfo;
        }
    }

    _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, 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 = {
    "os.stat_result", /* name; see issue gh-63408 */
    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 = {
    "os.statvfs_result", /* name; see issue gh-63408 */
    statvfs_result__doc__, /* doc */
    statvfs_result_fields,
    10
};

#if defined(HAVE_WAITID)
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 = {
    MODNAME ".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);
#ifdef HAVE_STATX
    Py_CLEAR(state->StatxResultType);
#endif
    Py_CLEAR(state->StatVFSResultType);
    Py_CLEAR(state->TerminalSizeType);
    Py_CLEAR(state->TimesResultType);
    Py_CLEAR(state->UnameResultType);
#if defined(HAVE_WAITID)
    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);
#ifdef HAVE_STATX
    Py_VISIT(state->StatxResultType);
#endif
    Py_VISIT(state->StatVFSResultType);
    Py_VISIT(state->TerminalSizeType);
    Py_VISIT(state->TimesResultType);
    Py_VISIT(state->UnameResultType);
#if defined(HAVE_WAITID)
    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);
}


#define SEC_TO_NS (1000000000LL)
static PyObject *
stat_nanosecond_timestamp(_posixstate *state, time_t sec, unsigned long nsec)
{
#if SIZEOF_TIME_T == 4
    return PyLong_FromLongLong(sec * SEC_TO_NS + nsec);
#else
    /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */
    if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) {
        return PyLong_FromLongLong(sec * SEC_TO_NS + nsec);
    }
    else
    {
        PyObject *ns_total = NULL;
        PyObject *s_in_ns = NULL;
        PyObject *s = _PyLong_FromTime_t(sec);
        PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
        if (s == NULL || ns_fractional == NULL) {
            goto exit;
        }

        s_in_ns = PyNumber_Multiply(s, state->billion);
        if (s_in_ns == NULL) {
            goto exit;
        }

        ns_total = PyNumber_Add(s_in_ns, ns_fractional);

    exit:
        Py_XDECREF(s);
        Py_XDECREF(ns_fractional);
        Py_XDECREF(s_in_ns);
        return ns_total;
    }
#endif
}

static int
fill_time(_posixstate *state, PyObject *v, int s_index, int f_index,
          int ns_index, time_t sec, unsigned long nsec)
{
    assert(!PyErr_Occurred());
    assert(nsec < SEC_TO_NS);

    if (s_index >= 0) {
        PyObject *s = _PyLong_FromTime_t(sec);
        if (s == NULL) {
            return -1;
        }
        PyStructSequence_SET_ITEM(v, s_index, s);
    }

    if (f_index >= 0) {
        PyObject *float_s = PyFloat_FromDouble((double)sec + 1e-9 * nsec);
        if (float_s == NULL) {
            return -1;
        }
        PyStructSequence_SET_ITEM(v, f_index, float_s);
    }

    if (ns_index >= 0) {
        PyObject *ns_total = stat_nanosecond_timestamp(state, sec, nsec);
        if (ns_total == NULL) {
            return -1;
        }
        PyStructSequence_SET_ITEM(v, ns_index, ns_total);
    }

    assert(!PyErr_Occurred());
    return 0;
}
#undef SEC_TO_NS

#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)
{
    assert(!PyErr_Occurred());

    _posixstate *state = get_posix_state(module);
    PyObject *StatResultType = state->StatResultType;
    PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType);
    if (v == NULL) {
        return NULL;
    }

#define SET_ITEM(pos, expr) \
    do { \
        PyObject *obj = (expr); \
        if (obj == NULL) { \
            goto error; \
        } \
        PyStructSequence_SET_ITEM(v, (pos), obj); \
    } while (0)

    SET_ITEM(0, PyLong_FromLong((long)st->st_mode));
#ifdef MS_WINDOWS
    SET_ITEM(1, _pystat_l128_from_l64_l64(st->st_ino, st->st_ino_high));
    SET_ITEM(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");
    SET_ITEM(1, PyLong_FromUnsignedLongLong(st->st_ino));
    SET_ITEM(2, _PyLong_FromDev(st->st_dev));
#endif
    SET_ITEM(3, PyLong_FromLong((long)st->st_nlink));
#if defined(MS_WINDOWS)
    SET_ITEM(4, PyLong_FromLong(0));
    SET_ITEM(5, PyLong_FromLong(0));
#else
    SET_ITEM(4, _PyLong_FromUid(st->st_uid));
    SET_ITEM(5, _PyLong_FromGid(st->st_gid));
#endif
    static_assert(sizeof(long long) >= sizeof(st->st_size),
                  "stat.st_size is larger than long long");
    SET_ITEM(6, PyLong_FromLongLong(st->st_size));

    // Set st_atime, st_mtime and st_ctime
    unsigned long ansec, mnsec, cnsec;
#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
    if (fill_time(state, v, 7, 10, 13, st->st_atime, ansec) < 0) {
        goto error;
    }
    if (fill_time(state, v, 8, 11, 14, st->st_mtime, mnsec) < 0) {
        goto error;
    }
    if (fill_time(state, v, 9, 12, 15, st->st_ctime, cnsec) < 0) {
        goto error;
    }

#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
    SET_ITEM(ST_BLKSIZE_IDX, PyLong_FromLong((long)st->st_blksize));
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
    SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->st_blocks));
#endif
#ifdef HAVE_STRUCT_STAT_ST_RDEV
    SET_ITEM(ST_RDEV_IDX, _PyLong_FromDev(st->st_rdev));
#endif
#ifdef HAVE_STRUCT_STAT_ST_GEN
    SET_ITEM(ST_GEN_IDX, PyLong_FromLong((long)st->st_gen));
#endif
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME)
    {
      unsigned long bsec, bnsec;
      bsec = (long)st->st_birthtime;
#ifdef HAVE_STAT_TV_NSEC2
      bnsec = st->st_birthtimespec.tv_nsec;
#else
      bnsec = 0;
#endif
      SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(bsec + bnsec * 1e-9));
    }
#elif defined(MS_WINDOWS)
    if (fill_time(state, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX,
                  st->st_birthtime, st->st_birthtime_nsec) < 0) {
        goto error;
    }
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
    SET_ITEM(ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
    SET_ITEM(ST_FILE_ATTRIBUTES_IDX,
             PyLong_FromUnsignedLong(st->st_file_attributes));
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
   SET_ITEM(ST_FSTYPE_IDX, PyUnicode_FromString(st->st_fstype));
#endif
#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG
    SET_ITEM(ST_REPARSE_TAG_IDX, PyLong_FromUnsignedLong(st->st_reparse_tag));
#endif

    assert(!PyErr_Occurred());
    return v;

error:
    Py_DECREF(v);
    return NULL;

#undef SET_ITEM
}

/* 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
    #undef PATH_HAVE_FCHMOD
    #define PATH_HAVE_FCHMOD 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, make_wide=None,
                       nonstrict=False, nullable=False,
                       suppress_value_error=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.nonstrict = nonstrict
        self.make_wide = make_wide
        self.suppress_value_error = suppress_value_error
        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
        if self.make_wide is None:
            self.c_default = 'PATH_T_INITIALIZE_P("{}", "{}", {}, {}, {}, {})'.format(
                self.function.name,
                self.name,
                strify(self.nullable),
                strify(self.nonstrict),
                strify(self.suppress_value_error),
                strify(self.allow_fd),
            )
        else:
            self.c_default = 'PATH_T_INITIALIZE("{}", "{}", {}, {}, {}, {}, {})'.format(
                self.function.name,
                self.name,
                strify(self.nullable),
                strify(self.nonstrict),
                strify(self.make_wide),
                strify(self.suppress_value_error),
                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 pid_t_converter(CConverter):
    type = 'pid_t'
    format_unit = '" _Py_PARSE_PID "'

    def parse_arg(self, argname, displayname, *, limited_capi):
        return self.format_code("""
            {paramname} = PyLong_AsPid({argname});
            if ({paramname} == (pid_t)(-1) && PyErr_Occurred()) {{{{
                goto exit;
            }}}}
            """, argname=argname)

class idtype_t_converter(CConverter):
    type = 'idtype_t'
    converter = 'idtype_t_converter'

class id_t_converter(CConverter):
    type = 'id_t'
    format_unit = '" _Py_PARSE_PID "'

    def parse_arg(self, argname, displayname, *, limited_capi):
        return self.format_code("""
            {paramname} = (id_t)PyLong_AsPid({argname});
            if ({paramname} == (id_t)(-1) && PyErr_Occurred()) {{{{
                goto exit;
            }}}}
            """, argname=argname)

class intptr_t_converter(CConverter):
    type = 'intptr_t'
    format_unit = '" _Py_PARSE_INTPTR "'

    def parse_arg(self, argname, displayname, *, limited_capi):
        return self.format_code("""
            {paramname} = (intptr_t)PyLong_AsVoidPtr({argname});
            if (!{paramname} && PyErr_Occurred()) {{{{
                goto exit;
            }}}}
            """, argname=argname)

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 confname_converter(CConverter):
    type="int"
    converter="conv_confname"

    def converter_init(self, *, table):
        self.table = table

    def parse_arg(self, argname, displayname, *, limited_capi):
        return self.format_code("""
            if (!{converter}(module, {argname}, &{paramname}, "{table}")) {{{{
                goto exit;
            }}}}
        """, argname=argname, converter=self.converter, table=self.table)

[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=d2759f2332cd39b3]*/

/*[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]
@permit_long_summary
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=024102124f88e743]*/
{
    int follow_symlinks = 0;
    return posix_do_stat(module, "lstat", path, dir_fd, follow_symlinks);
}


#ifdef HAVE_STATX
typedef struct {
    PyObject_HEAD
    dev_t rdev, dev;
    struct statx stx;
} Py_statx_result;

#define Py_statx_result_CAST(op) _Py_CAST(Py_statx_result*, (op))

#define M(attr, type, offset, doc) \
    {attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
#define MM(attr, type, member, doc) \
    M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
#define MX(attr, type, member, doc) \
    M(#attr, type, offsetof(Py_statx_result, member), doc)

static PyMemberDef pystatx_result_members[] = {
    MM(stx_mask, Py_T_UINT, mask, "member validity mask"),
    MM(stx_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"),
    MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"),
    MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask,
        "Mask of supported bits in stx_attributes"),
    MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"),
    MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"),
    MX(stx_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"),
    MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"),
    MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"),
    MX(stx_dev, Py_T_ULONGLONG, dev, "device"),
    {NULL},
};

#undef MX
#undef MM
#undef M


#define STATX_GET_UINT(ATTR, MASK) \
    static PyObject* \
    pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
    { \
        Py_statx_result *self = Py_statx_result_CAST(op); \
        if (!(self->stx.stx_mask & MASK)) { \
            Py_RETURN_NONE; \
        } \
        unsigned long value = self->stx.ATTR; \
        return PyLong_FromUnsignedLong(value); \
    }

STATX_GET_UINT(stx_uid, STATX_UID)
STATX_GET_UINT(stx_gid, STATX_GID)
STATX_GET_UINT(stx_nlink, STATX_NLINK)
#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN
STATX_GET_UINT(stx_dio_mem_align, STATX_DIOALIGN)
STATX_GET_UINT(stx_dio_offset_align, STATX_DIOALIGN)
#endif
#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN
STATX_GET_UINT(stx_dio_read_offset_align, STATX_DIO_READ_ALIGN)
#endif
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN
STATX_GET_UINT(stx_atomic_write_unit_min, STATX_WRITE_ATOMIC)
STATX_GET_UINT(stx_atomic_write_unit_max, STATX_WRITE_ATOMIC)
STATX_GET_UINT(stx_atomic_write_segments_max, STATX_WRITE_ATOMIC)
#endif
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
STATX_GET_UINT(stx_atomic_write_unit_max_opt, STATX_WRITE_ATOMIC)
#endif


static PyObject*
pystatx_result_get_stx_mode(PyObject *op, void *Py_UNUSED(context))
{
    Py_statx_result *self = Py_statx_result_CAST(op);
    if (!(self->stx.stx_mask & (STATX_TYPE | STATX_MODE))) {
        Py_RETURN_NONE;
    }
    return PyLong_FromUnsignedLong(self->stx.stx_mode);
}


#define STATX_GET_ULONGLONG(ATTR, MASK) \
    static PyObject* \
    pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
    { \
        Py_statx_result *self = Py_statx_result_CAST(op); \
        if (!(self->stx.stx_mask & MASK)) { \
            Py_RETURN_NONE; \
        } \
        unsigned long long value = self->stx.ATTR; \
        return PyLong_FromUnsignedLongLong(value); \
    }

STATX_GET_ULONGLONG(stx_blocks, STATX_BLOCKS)
STATX_GET_ULONGLONG(stx_ino, STATX_INO)
STATX_GET_ULONGLONG(stx_size, STATX_SIZE)
#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID)
STATX_GET_ULONGLONG(stx_mnt_id, STATX_MNT_ID)
#endif
#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL)
STATX_GET_ULONGLONG(stx_subvol, STATX_SUBVOL)
#endif


#define STATX_GET_DOUBLE(ATTR, MASK) \
    static PyObject* \
    pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \
    { \
        Py_statx_result *self = Py_statx_result_CAST(op); \
        if (!(self->stx.stx_mask & MASK)) { \
            Py_RETURN_NONE; \
        } \
        struct statx_timestamp *ts = &self->stx.ATTR; \
        double sec = ((double)ts->tv_sec + ts->tv_nsec * 1e-9); \
        return PyFloat_FromDouble(sec); \
    }

STATX_GET_DOUBLE(stx_atime, STATX_ATIME)
STATX_GET_DOUBLE(stx_btime, STATX_BTIME)
STATX_GET_DOUBLE(stx_ctime, STATX_CTIME)
STATX_GET_DOUBLE(stx_mtime, STATX_MTIME)

#define STATX_GET_NSEC(ATTR, MEMBER, MASK) \
    static PyObject* \
    pystatx_result_get_##ATTR(PyObject *op, void *context) \
    { \
        Py_statx_result *self = Py_statx_result_CAST(op); \
        if (!(self->stx.stx_mask & MASK)) { \
            Py_RETURN_NONE; \
        } \
        struct statx_timestamp *ts = &self->stx.MEMBER; \
        _posixstate *state = PyType_GetModuleState(Py_TYPE(op)); \
        assert(state != NULL); \
        return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \
    }

STATX_GET_NSEC(stx_atime_ns, stx_atime, STATX_ATIME)
STATX_GET_NSEC(stx_btime_ns, stx_btime, STATX_BTIME)
STATX_GET_NSEC(stx_ctime_ns, stx_ctime, STATX_CTIME)
STATX_GET_NSEC(stx_mtime_ns, stx_mtime, STATX_MTIME)

#define G(attr, doc) \
    {#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL}

static PyGetSetDef pystatx_result_getset[] = {
    G(stx_mode, "protection bits"),
    G(stx_nlink, "number of hard links"),
    G(stx_uid, "user ID of owner"),
    G(stx_gid, "group ID of owner"),
    G(stx_ino, "inode"),
    G(stx_size, "total size, in bytes"),
    G(stx_blocks, "number of blocks allocated"),
    G(stx_atime, "time of last access"),
    G(stx_atime_ns, "time of last access in nanoseconds"),
    G(stx_btime, "time of creation"),
    G(stx_btime_ns, "time of creation in nanoseconds"),
    G(stx_ctime, "time of last change"),
    G(stx_ctime_ns, "time of last change in nanoseconds"),
    G(stx_mtime, "time of last modification"),
    G(stx_mtime_ns, "time of last modification in nanoseconds"),
#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID)
    G(stx_mnt_id, "mount ID"),
#endif
#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN
    G(stx_dio_mem_align, "direct I/O memory buffer alignment"),
    G(stx_dio_offset_align, "direct I/O file offset alignment"),
#endif
#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL)
    G(stx_subvol, "subvolume ID"),
#endif
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN
    G(stx_atomic_write_unit_min,
      "minimum size for direct I/O with torn-write protection"),
    G(stx_atomic_write_unit_max,
        "maximum size for direct I/O with torn-write protection"),
    G(stx_atomic_write_segments_max,
        "maximum iovecs for direct I/O with torn-write protection"),
#endif
#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN
    G(stx_dio_read_offset_align, "direct I/O file offset alignment for reads"),
#endif
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
    G(stx_atomic_write_unit_max_opt,
        "maximum optimized size for direct I/O with torn-write protection"),
#endif
    {NULL},
};

#undef G

static PyObject *
pystatx_result_repr(PyObject *op)
{
    PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
    if (writer == NULL) {
        return NULL;
    }
#define WRITE_ASCII(s) \
    do { \
        if (PyUnicodeWriter_WriteASCII(writer, s, strlen(s)) < 0) { \
            goto error; \
        } \
    } while (0)

    WRITE_ASCII("os.statx_result(");

    for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) {
        if (i > 0) {
            WRITE_ASCII(", ");
        }

        PyMemberDef *d = &pystatx_result_members[i];
        WRITE_ASCII(d->name);
        WRITE_ASCII("=");

        PyObject *o = PyMember_GetOne((const char *)op, d);
        if (o == NULL) {
            goto error;
        }
        if (PyUnicodeWriter_WriteRepr(writer, o) < 0) {
            Py_DECREF(o);
            goto error;
        }
        Py_DECREF(o);
    }

    for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) {
        PyGetSetDef *d = &pystatx_result_getset[i];
        PyObject *o = d->get(op, d->closure);
        if (o == NULL) {
            goto error;
        }
        if (o == Py_None) {
            continue;
        }

        WRITE_ASCII(", ");
        WRITE_ASCII(d->name);
        WRITE_ASCII("=");
        if (PyUnicodeWriter_WriteRepr(writer, o) < 0) {
            Py_DECREF(o);
            goto error;
        }
        Py_DECREF(o);
    }

    WRITE_ASCII(")");
    return PyUnicodeWriter_Finish(writer);
#undef WRITE_ASCII

error:
    PyUnicodeWriter_Discard(writer);
    return NULL;
}

static int
pystatx_result_traverse(PyObject *self, visitproc visit, void *arg)
{
    Py_VISIT(Py_TYPE(self));
    return 0;
}

static void
pystatx_result_dealloc(PyObject *op)
{
    Py_statx_result *self = (Py_statx_result *) op;
    PyTypeObject *tp = Py_TYPE(self);
    PyObject_GC_UnTrack(self);
    tp->tp_free(self);
    Py_DECREF(tp);
}

static PyType_Slot pystatx_result_slots[] = {
    {Py_tp_repr, pystatx_result_repr},
    {Py_tp_traverse, pystatx_result_traverse},
    {Py_tp_dealloc, pystatx_result_dealloc},
    {Py_tp_members, pystatx_result_members},
    {Py_tp_getset, pystatx_result_getset},
    {0, NULL},
};

static PyType_Spec pystatx_result_spec = {
    .name = "os.statx_result",
    .basicsize = sizeof(Py_statx_result),
    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC |
             Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION,
    .slots = pystatx_result_slots,
};

/*[clinic input]

os.statx

    path : path_t(allow_fd=True)
        Path to be examined; can be string, bytes, a path-like object or
        open-file-descriptor int.

    mask: unsigned_int(bitwise=True)
        A bitmask of STATX_* constants defining the requested information.

    *

    flags: int = 0
        A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags.

    dir_fd : dir_fd = 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,
        statx will examine the symbolic link itself instead of the file
        the link points to.

Perform a statx system call on the given path.

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_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags,
              int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=e3765979ac6fe15b input=f0116380c5dc4f2f]*/
{
    if (path_and_dir_fd_invalid("statx", path, dir_fd) ||
        dir_fd_and_fd_invalid("statx", dir_fd, path->fd) ||
        fd_and_follow_symlinks_invalid("statx", path->fd, follow_symlinks)) {
        return NULL;
    }

    /* reject flags covered by kwargs, but allow unknown flags that may be
       future AT_STATX_* extensions */
    if (flags & (AT_SYMLINK_NOFOLLOW | AT_SYMLINK_FOLLOW)) {
        PyErr_Format(PyExc_ValueError,
                     "use follow_symlinks kwarg instead of AT_SYMLINK_* flag");
        return NULL;
    }
    if (flags & AT_EMPTY_PATH) {
        PyErr_Format(PyExc_ValueError,
                     "use dir_fd kwarg instead of AT_EMPTY_PATH flag");
        return NULL;
    }

    /* Future bits may refer to members beyond the current size of struct
       statx, so we need to mask them off to prevent memory corruption. */
    mask &= _Py_STATX_KNOWN;

    _posixstate *state = get_posix_state(module);
    PyTypeObject *tp = (PyTypeObject *)state->StatxResultType;
    Py_statx_result *v = (Py_statx_result *)tp->tp_alloc(tp, 0);
    if (v == NULL) {
        return NULL;
    }

    int result;
    Py_BEGIN_ALLOW_THREADS
    if (path->fd != -1) {
        result = statx(path->fd, "", flags | AT_EMPTY_PATH, mask, &v->stx);
    }
    else {
        result = statx(dir_fd, path->narrow, flags, mask, &v->stx);
    }
    Py_END_ALLOW_THREADS

    if (result != 0) {
        Py_DECREF(v);
        return path_error(path);
    }

    v->rdev = makedev(v->stx.stx_rdev_major, v->stx.stx_rdev_minor);
    v->dev = makedev(v->stx.stx_dev_major, v->stx.stx_dev_minor);

    assert(!PyErr_Occurred());
    return (PyObject *)v;
}
#endif /* HAVE_STATX */


/*[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=c33565f7584b99e4]*/
{
    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_R
/*[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=a74ceab5d72adf74]*/
{
    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 */

#ifdef MS_WINDOWS
# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 0
#else
# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 1
#endif

#ifdef MS_WINDOWS
static int
win32_lchmod(LPCWSTR path, int mode)
{
    DWORD attr = GetFileAttributesW(path);
    if (attr == INVALID_FILE_ATTRIBUTES) {
        return 0;
    }
    if (mode & _S_IWRITE) {
        attr &= ~FILE_ATTRIBUTE_READONLY;
    }
    else {
        attr |= FILE_ATTRIBUTE_READONLY;
    }
    return SetFileAttributesW(path, attr);
}

static int
win32_hchmod(HANDLE hfile, int mode)
{
    FILE_BASIC_INFO info;
    if (!GetFileInformationByHandleEx(hfile, FileBasicInfo,
                                      &info, sizeof(info)))
    {
        return 0;
    }
    if (mode & _S_IWRITE) {
        info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
    }
    else {
        info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
    }
    return SetFileInformationByHandle(hfile, FileBasicInfo,
                                      &info, sizeof(info));
}

static int
win32_fchmod(int fd, int mode)
{
    HANDLE hfile = _Py_get_osfhandle_noraise(fd);
    if (hfile == INVALID_HANDLE_VALUE) {
        SetLastError(ERROR_INVALID_HANDLE);
        return 0;
    }
    return win32_hchmod(hfile, mode);
}

#endif /* MS_WINDOWS */

/*[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(c_default="CHMOD_DEFAULT_FOLLOW_SYMLINKS", \
                          py_default="(os.name != 'nt')") = CHMOD_DEFAULT_FOLLOW_SYMLINKS
        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=fcf115d174b9f3d8]*/
{
    int result;

#ifdef HAVE_FCHMODAT
    int fchmodat_nofollow_unsupported = 0;
    int fchmodat_unsupported = 0;
#endif

#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD) || defined(MS_WINDOWS))
    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
    result = 0;
    Py_BEGIN_ALLOW_THREADS
    if (path->fd != -1) {
        result = win32_fchmod(path->fd, mode);
    }
    else if (follow_symlinks) {
        HANDLE hfile = CreateFileW(path->wide,
                                   FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
                                   0, NULL,
                                   OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
        if (hfile != INVALID_HANDLE_VALUE) {
            result = win32_hchmod(hfile, mode);
            (void)CloseHandle(hfile);
        }
    }
    else {
        result = win32_lchmod(path->wide, mode);
    }
    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;
}


#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS)
/*[clinic input]
os.fchmod

    fd: int
        The file descriptor of the file to be modified.
    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.

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=b5594618bbbc22df]*/
{
    int res;

    if (PySys_Audit("os.chmod", "iii", fd, mode, -1) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS
    res = 0;
    Py_BEGIN_ALLOW_THREADS
    res = win32_fchmod(fd, mode);
    Py_END_ALLOW_THREADS
    if (!res) {
        return PyErr_SetFromWindowsErr(0);
    }
#else /* MS_WINDOWS */
    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;
#endif /* MS_WINDOWS */

    Py_RETURN_NONE;
}
#endif /* HAVE_FCHMOD || MS_WINDOWS */


#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS)
/*[clinic input]
os.lchmod

    path: path_t
    mode: int

Change the access permissions of a file, without following symbolic links.

If path is a symlink, this affects the link itself rather than the target.
Equivalent to chmod(path, mode, follow_symlinks=False)."
[clinic start generated code]*/

static PyObject *
os_lchmod_impl(PyObject *module, path_t *path, int mode)
/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/
{
    int res;
    if (PySys_Audit("os.chmod", "Oii", path->object, mode, -1) < 0) {
        return NULL;
    }
#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    res = win32_lchmod(path->wide, mode);
    Py_END_ALLOW_THREADS
    if (!res) {
        path_error(path);
        return NULL;
    }
#else /* MS_WINDOWS */
    Py_BEGIN_ALLOW_THREADS
    res = lchmod(path->narrow, mode);
    Py_END_ALLOW_THREADS
    if (res < 0) {
        path_error(path);
        return NULL;
    }
#endif /* MS_WINDOWS */
    Py_RETURN_NONE;
}
#endif /* HAVE_LCHMOD || MS_WINDOWS */


#ifdef HAVE_CHFLAGS
/*[clinic input]
os.chflags

    path: path_t
    flags: unsigned_long(bitwise=True)
    follow_symlinks: bool=True

Set file flags.

If follow_symlinks is False, and the last element of the path is a symbolic
  link, chflags will change flags on the symbolic link itself instead of the
  file the link points to.
follow_symlinks may not be implemented on your platform.  If it is
unavailable, using it will raise a NotImplementedError.

[clinic start generated code]*/

static PyObject *
os_chflags_impl(PyObject *module, path_t *path, unsigned long flags,
                int follow_symlinks)
/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/
{
    int result;

#ifndef HAVE_LCHFLAGS
    if (follow_symlinks_specified("chflags", follow_symlinks))
        return NULL;
#endif

    if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LCHFLAGS
    if (!follow_symlinks)
        result = lchflags(path->narrow, flags);
    else
#endif
        result = chflags(path->narrow, flags);
    Py_END_ALLOW_THREADS

    if (result)
        return path_error(path);

    Py_RETURN_NONE;
}
#endif /* HAVE_CHFLAGS */


#ifdef HAVE_LCHFLAGS
/*[clinic input]
os.lchflags

    path: path_t
    flags: unsigned_long(bitwise=True)

Set file flags.

This function will not follow symbolic links.
Equivalent to chflags(path, flags, follow_symlinks=False).
[clinic start generated code]*/

static PyObject *
os_lchflags_impl(PyObject *module, path_t *path, unsigned long flags)
/*[clinic end generated code: output=30ae958695c07316 input=f9f82ea8b585ca9d]*/
{
    int res;
    if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) {
        return NULL;
    }
    Py_BEGIN_ALLOW_THREADS
    res = lchflags(path->narrow, flags);
    Py_END_ALLOW_THREADS
    if (res < 0) {
        return path_error(path);
    }
    Py_RETURN_NONE;
}
#endif /* HAVE_LCHFLAGS */


#ifdef HAVE_CHROOT
/*[clinic input]
os.chroot
    path: path_t

Change root directory to path.

[clinic start generated code]*/

static PyObject *
os_chroot_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/
{
    int res;
    Py_BEGIN_ALLOW_THREADS
    res = chroot(path->narrow);
    Py_END_ALLOW_THREADS
    if (res < 0)
        return path_error(path);
    Py_RETURN_NONE;
}
#endif /* HAVE_CHROOT */


#ifdef HAVE_FSYNC
/*[clinic input]
os.fsync

    fd: fildes

Force write of fd to disk.
[clinic start generated code]*/

static PyObject *
os_fsync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=4a10d773f52b3584 input=21c3645c056967f2]*/
{
    return posix_fildes_fd(fd, fsync);
}
#endif /* HAVE_FSYNC */


#ifdef HAVE_SYNC
/*[clinic input]
os.sync

Force write of everything to disk.
[clinic start generated code]*/

static PyObject *
os_sync_impl(PyObject *module)
/*[clinic end generated code: output=2796b1f0818cd71c input=84749fe5e9b404ff]*/
{
    Py_BEGIN_ALLOW_THREADS
    sync();
    Py_END_ALLOW_THREADS
    Py_RETURN_NONE;
}
#endif /* HAVE_SYNC */


#ifdef HAVE_FDATASYNC
#ifdef __hpux
extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */
#endif

/*[clinic input]
os.fdatasync

    fd: fildes

Force write of fd to disk without forcing update of metadata.
[clinic start generated code]*/

static PyObject *
os_fdatasync_impl(PyObject *module, int fd)
/*[clinic end generated code: output=b4b9698b5d7e26dd input=bc74791ee54dd291]*/
{
    return posix_fildes_fd(fd, fdatasync);
}
#endif /* HAVE_FDATASYNC */


#ifdef HAVE_CHOWN
/*[clinic input]
os.chown

    path : path_t(allow_fd='PATH_HAVE_FCHOWN')
        Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.

    uid: uid_t

    gid: gid_t

    *

    dir_fd : dir_fd(requires='fchownat') = None
        If not None, it should be a file descriptor open to a directory,
        and path should be relative; path will then be relative to that
        directory.

    follow_symlinks: bool = True
        If False, and the last element of the path is a symbolic link,
        stat will examine the symbolic link itself instead of the file
        the link points to.

Change the owner and group id of path to the numeric uid and gid.\

path may always be specified as a string.
On some platforms, path may also be specified as an open file descriptor.
  If this functionality is unavailable, using it raises an exception.
If dir_fd is not None, it should be a file descriptor open to a directory,
  and path should be relative; path will then be relative to that directory.
If follow_symlinks is False, and the last element of the path is a symbolic
  link, chown will modify the symbolic link itself instead of the file the
  link points to.
It is an error to use dir_fd or follow_symlinks when specifying path as
  an open file descriptor.
dir_fd and follow_symlinks may not be implemented on your platform.
  If they are unavailable, using them will raise a NotImplementedError.

[clinic start generated code]*/

static PyObject *
os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
              int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/
{
    int result;

#if defined(HAVE_FCHOWNAT)
    int fchownat_unsupported = 0;
#endif

#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;

    if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FCHOWN
    if (path->fd != -1)
        result = fchown(path->fd, uid, gid);
    else
#endif
#ifdef HAVE_LCHOWN
    if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
        result = lchown(path->narrow, uid, gid);
    else
#endif
#ifdef HAVE_FCHOWNAT
    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
      if (HAVE_FCHOWNAT_RUNTIME) {
        result = fchownat(dir_fd, path->narrow, uid, gid,
                          follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
      } else {
         fchownat_unsupported = 1;
      }
    } else
#endif
        result = chown(path->narrow, uid, gid);
    Py_END_ALLOW_THREADS

#ifdef HAVE_FCHOWNAT
    if (fchownat_unsupported) {
        /* This would be incorrect if the current platform
         * doesn't support lchown.
         */
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (result)
        return path_error(path);

    Py_RETURN_NONE;
}
#endif /* HAVE_CHOWN */


#ifdef HAVE_FCHOWN
/*[clinic input]
os.fchown

    fd: int
    uid: uid_t
    gid: gid_t

Change the owner and group id of the file specified by file descriptor.

Equivalent to os.chown(fd, uid, gid).

[clinic start generated code]*/

static PyObject *
os_fchown_impl(PyObject *module, int fd, uid_t uid, gid_t gid)
/*[clinic end generated code: output=97d21cbd5a4350a6 input=3af544ba1b13a0d7]*/
{
    int res;
    int async_err = 0;

    if (PySys_Audit("os.chown", "iIIi", fd, uid, gid, -1) < 0) {
        return NULL;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        res = fchown(fd, uid, gid);
        Py_END_ALLOW_THREADS
    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res != 0)
        return (!async_err) ? posix_error() : NULL;

    Py_RETURN_NONE;
}
#endif /* HAVE_FCHOWN */


#ifdef HAVE_LCHOWN
/*[clinic input]
os.lchown

    path : path_t
    uid: uid_t
    gid: gid_t

Change the owner and group id of path to the numeric uid and gid.

This function will not follow symbolic links.
Equivalent to os.chown(path, uid, gid, follow_symlinks=False).
[clinic start generated code]*/

static PyObject *
os_lchown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid)
/*[clinic end generated code: output=25eaf6af412fdf2f input=b1c6014d563a7161]*/
{
    int res;
    if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, -1) < 0) {
        return NULL;
    }
    Py_BEGIN_ALLOW_THREADS
    res = lchown(path->narrow, uid, gid);
    Py_END_ALLOW_THREADS
    if (res < 0) {
        return path_error(path);
    }
    Py_RETURN_NONE;
}
#endif /* HAVE_LCHOWN */


static PyObject *
posix_getcwd(int use_bytes)
{
#ifdef MS_WINDOWS
    wchar_t wbuf[MAXPATHLEN];
    wchar_t *wbuf2 = wbuf;
    DWORD len;

    Py_BEGIN_ALLOW_THREADS
    len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf);
    /* If the buffer is large enough, len does not include the
       terminating \0. If the buffer is too small, len includes
       the space needed for the terminator. */
    if (len >= Py_ARRAY_LENGTH(wbuf)) {
        if ((Py_ssize_t)len <= PY_SSIZE_T_MAX / sizeof(wchar_t)) {
            wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t));
        }
        else {
            wbuf2 = NULL;
        }
        if (wbuf2) {
            len = GetCurrentDirectoryW(len, wbuf2);
        }
    }
    Py_END_ALLOW_THREADS

    if (!wbuf2) {
        PyErr_NoMemory();
        return NULL;
    }
    if (!len) {
        PyErr_SetFromWindowsErr(0);
        if (wbuf2 != wbuf)
            PyMem_RawFree(wbuf2);
        return NULL;
    }

    PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len);
    if (wbuf2 != wbuf) {
        PyMem_RawFree(wbuf2);
    }

    if (use_bytes) {
        if (resobj == NULL) {
            return NULL;
        }
        Py_SETREF(resobj, PyUnicode_EncodeFSDefault(resobj));
    }

    return resobj;
#else
    const size_t chunk = 1024;

    char *buf = NULL;
    char *cwd = NULL;
    size_t buflen = 0;

    Py_BEGIN_ALLOW_THREADS
    do {
        char *newbuf;
        if (buflen <= PY_SSIZE_T_MAX - chunk) {
            buflen += chunk;
            newbuf = PyMem_RawRealloc(buf, buflen);
        }
        else {
            newbuf = NULL;
        }
        if (newbuf == NULL) {
            PyMem_RawFree(buf);
            buf = NULL;
            break;
        }
        buf = newbuf;

        cwd = getcwd(buf, buflen);
    } while (cwd == NULL && errno == ERANGE);
    Py_END_ALLOW_THREADS

    if (buf == NULL) {
        return PyErr_NoMemory();
    }
    if (cwd == NULL) {
        posix_error();
        PyMem_RawFree(buf);
        return NULL;
    }

    PyObject *obj;
    if (use_bytes) {
        obj = PyBytes_FromStringAndSize(buf, strlen(buf));
    }
    else {
        obj = PyUnicode_DecodeFSDefault(buf);
    }
#ifdef __linux__
    if (buf[0] != '/') {
        /*
         * On Linux >= 2.6.36 with glibc < 2.27, getcwd() can return a
         * relative pathname starting with '(unreachable)'. We detect this
         * and fail with ENOENT, matching newer glibc behaviour.
         */
        errno = ENOENT;
        path_object_error(obj);
        PyMem_RawFree(buf);
        return NULL;
    }
#endif
    assert(buf[0] == '/');
    PyMem_RawFree(buf);

    return obj;
#endif   /* !MS_WINDOWS */
}


/*[clinic input]
os.getcwd

Return a unicode string representing the current working directory.
[clinic start generated code]*/

static PyObject *
os_getcwd_impl(PyObject *module)
/*[clinic end generated code: output=21badfae2ea99ddc input=f069211bb70e3d39]*/
{
    return posix_getcwd(0);
}


/*[clinic input]
os.getcwdb

Return a bytes string representing the current working directory.
[clinic start generated code]*/

static PyObject *
os_getcwdb_impl(PyObject *module)
/*[clinic end generated code: output=3dd47909480e4824 input=f6f6a378dad3d9cb]*/
{
    return posix_getcwd(1);
}


#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS))
#define HAVE_LINK 1
#endif

#ifdef HAVE_LINK
/*[clinic input]

@permit_long_docstring_body
os.link

    src : path_t
    dst : path_t
    *
    src_dir_fd : dir_fd = None
    dst_dir_fd : dir_fd = None
    follow_symlinks: bool(c_default="-1", py_default="(os.name != 'nt')") = PLACEHOLDER

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=e2a50a6497050e44]*/
{
#ifdef MS_WINDOWS
    BOOL result = FALSE;
#else
    int result;
#endif

#ifdef HAVE_LINKAT
    if (HAVE_LINKAT_RUNTIME) {
        if (follow_symlinks < 0) {
            follow_symlinks = 1;
        }
    }
    else
#endif
    {
        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;
        }
/* See issue 85527: link() on Linux works like linkat without AT_SYMLINK_FOLLOW,
   but on Mac it works like linkat *with* AT_SYMLINK_FOLLOW. */
#if defined(MS_WINDOWS) || defined(__linux__)
        if (follow_symlinks == 1) {
            argument_unavailable_error("link", "follow_symlinks=True");
            return NULL;
        }
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || (defined(__sun) && defined(__SVR4))
        if (follow_symlinks == 0) {
            argument_unavailable_error("link", "follow_symlinks=False");
            return NULL;
        }
#else
        if (follow_symlinks >= 0) {
            argument_unavailable_error("link", "follow_symlinks");
            return NULL;
        }
#endif
    }

    if (PySys_Audit("os.link", "OOii", src->object, dst->object,
                    src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd,
                    dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    result = CreateHardLinkW(dst->wide, src->wide, NULL);
    Py_END_ALLOW_THREADS

    if (!result)
        return path_error2(src, dst);
#else
    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_LINKAT
    if (HAVE_LINKAT_RUNTIME) {
        result = linkat(src_dir_fd, src->narrow,
            dst_dir_fd, dst->narrow,
            follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
    }
    else
#endif
    {
        /* linkat not available */
        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, return_bytes;
    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;
        return_bytes = 0;
    } else {
        po_wchars = path->wide;
        len = wcslen(path->wide);
        return_bytes = PyBytes_Check(path->object);
    }
    /* 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;
        path_error(path);
        Py_CLEAR(list);
        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 (return_bytes && v) {
                Py_SETREF(v, PyUnicode_EncodeFSDefault(v));
            }
            if (v == NULL) {
                Py_CLEAR(list);
                break;
            }
            if (PyList_Append(list, v) != 0) {
                Py_DECREF(v);
                Py_CLEAR(list);
                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) {
            path_error(path);
            Py_CLEAR(list);
            goto exit;
        }
    } while (result == TRUE);

exit:
    if (hFindFile != INVALID_HANDLE_VALUE) {
        if (FindClose(hFindFile) == FALSE) {
            if (list != NULL) {
                path_error(path);
                Py_CLEAR(list);
            }
        }
    }
    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) {
      if (HAVE_FDOPENDIR_RUNTIME) {
        /* 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 {
        PyErr_SetString(PyExc_TypeError,
            "listdir: path should be string, bytes, os.PathLike or None, not int");
        return NULL;
      }
    }
    else
#endif
    {
        const char *name;
        if (path->narrow) {
            name = path->narrow;
            /* only return bytes if they specified a bytes object */
            return_str = !PyBytes_Check(path->object);
        }
        else {
            name = ".";
            return_str = 1;
        }

        Py_BEGIN_ALLOW_THREADS
        dirp = opendir(name);
        Py_END_ALLOW_THREADS
    }

    if (dirp == NULL) {
        path_error(path);
        list = NULL;
#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 {
                path_error(path);
                Py_CLEAR(list);
                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]
@permit_long_docstring_body
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, bytes, or a path-like object.  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=0bd1728387391b9a]*/
{
    if (PySys_Audit("os.listdir", "O",
                    path->object ? path->object : Py_None) < 0) {
        return NULL;
    }
#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
    return _listdir_windows_no_opendir(path, NULL);
#else
    return _posix_listdir(path, NULL);
#endif
}


#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)

/*[clinic input]
os.listdrives

Return a list containing the names of drives in the system.

A drive name typically looks like 'C:\\'.

[clinic start generated code]*/

static PyObject *
os_listdrives_impl(PyObject *module)
/*[clinic end generated code: output=aaece9dacdf682b5 input=1af9ccc9e583798e]*/
{
    /* Number of possible drives is limited, so 256 should always be enough.
       On the day when it is not, listmounts() will have to be used. */
    wchar_t buffer[256];
    DWORD buflen = Py_ARRAY_LENGTH(buffer);
    PyObject *result = NULL;
    if (PySys_Audit("os.listdrives", NULL) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS;
    buflen = GetLogicalDriveStringsW(buflen, buffer);
    Py_END_ALLOW_THREADS;

    if (!buflen) {
        PyErr_SetFromWindowsErr(0);
        return NULL;
    } else if (buflen >= Py_ARRAY_LENGTH(buffer)) {
        PyErr_SetFromWindowsErr(ERROR_MORE_DATA);
        return NULL;
    }

    /* buflen includes a null terminator, so remove it */
    PyObject *str = PyUnicode_FromWideChar(buffer, buflen - 1);
    if (str) {
        PyObject *nullchar = PyUnicode_FromStringAndSize("\0", 1);
        if (nullchar) {
            result = PyUnicode_Split(str, nullchar, -1);
            Py_DECREF(nullchar);
        }
        Py_DECREF(str);
    }
    return result;
}

#endif /* MS_WINDOWS_DESKTOP || MS_WINDOWS_SYSTEM */

#if defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM)

/*[clinic input]
os.listvolumes

Return a list containing the volumes in the system.

Volumes are typically represented as a GUID path.

[clinic start generated code]*/

static PyObject *
os_listvolumes_impl(PyObject *module)
/*[clinic end generated code: output=534e10ea2bf9d386 input=f6e4e70371f11e99]*/
{
    PyObject *result = PyList_New(0);
    HANDLE find = INVALID_HANDLE_VALUE;
    wchar_t buffer[MAX_PATH + 1];
    if (!result) {
        return NULL;
    }
    if (PySys_Audit("os.listvolumes", NULL) < 0) {
        Py_DECREF(result);
        return NULL;
    }

    int err = 0;
    Py_BEGIN_ALLOW_THREADS;
    find = FindFirstVolumeW(buffer, Py_ARRAY_LENGTH(buffer));
    if (find == INVALID_HANDLE_VALUE) {
        err = GetLastError();
    }
    Py_END_ALLOW_THREADS;

    while (!err) {
        PyObject *s = PyUnicode_FromWideChar(buffer, -1);
        if (!s || PyList_Append(result, s) < 0) {
            Py_XDECREF(s);
            Py_CLEAR(result);
            break;
        }
        Py_DECREF(s);

        Py_BEGIN_ALLOW_THREADS;
        if (!FindNextVolumeW(find, buffer, Py_ARRAY_LENGTH(buffer))) {
            err = GetLastError();
        }
        Py_END_ALLOW_THREADS;
    }

    if (find != INVALID_HANDLE_VALUE) {
        Py_BEGIN_ALLOW_THREADS;
        FindVolumeClose(find);
        Py_END_ALLOW_THREADS;
    }
    if (err && err != ERROR_NO_MORE_FILES) {
        PyErr_SetFromWindowsErr(err);
        Py_XDECREF(result);
        result = NULL;
    }
    return result;
}

#endif /* MS_WINDOWS_APP || MS_WINDOWS_SYSTEM */

#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)

/*[clinic input]
os.listmounts

    volume: path_t

Return a list containing mount points for a particular volume.

'volume' should be a GUID path as returned from os.listvolumes.

[clinic start generated code]*/

static PyObject *
os_listmounts_impl(PyObject *module, path_t *volume)
/*[clinic end generated code: output=06da49679de4512e input=a8a27178e3f67845]*/
{
    wchar_t default_buffer[MAX_PATH + 1];
    DWORD buflen = Py_ARRAY_LENGTH(default_buffer);
    LPWSTR buffer = default_buffer;
    DWORD attributes;
    PyObject *str = NULL;
    PyObject *nullchar = NULL;
    PyObject *result = NULL;

    /* Ensure we have a valid volume path before continuing */
    Py_BEGIN_ALLOW_THREADS
    attributes = GetFileAttributesW(volume->wide);
    Py_END_ALLOW_THREADS
    if (attributes == INVALID_FILE_ATTRIBUTES &&
        GetLastError() == ERROR_UNRECOGNIZED_VOLUME)
    {
        return PyErr_SetFromWindowsErr(ERROR_UNRECOGNIZED_VOLUME);
    }

    if (PySys_Audit("os.listmounts", "O", volume->object) < 0) {
        return NULL;
    }

    while (1) {
        BOOL success;
        Py_BEGIN_ALLOW_THREADS
        success = GetVolumePathNamesForVolumeNameW(volume->wide, buffer,
                                                   buflen, &buflen);
        Py_END_ALLOW_THREADS
        if (success) {
            break;
        }
        if (GetLastError() != ERROR_MORE_DATA) {
            PyErr_SetFromWindowsErr(0);
            goto exit;
        }
        if (buffer != default_buffer) {
            PyMem_Free((void *)buffer);
        }
        buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen);
        if (!buffer) {
            PyErr_NoMemory();
            goto exit;
        }
    }
    if (buflen < 2) {
        result = PyList_New(0);
        goto exit;
    }
    // buflen includes two null terminators, one for the last string
    // and one for the array of strings.
    str = PyUnicode_FromWideChar(buffer, buflen - 2);
    nullchar = PyUnicode_FromStringAndSize("\0", 1);
    if (str && nullchar) {
        result = PyUnicode_Split(str, nullchar, -1);
    }
exit:
    if (buffer != default_buffer) {
        PyMem_Free(buffer);
    }
    Py_XDECREF(nullchar);
    Py_XDECREF(str);
    return result;
}

#endif /* MS_WINDOWS_DESKTOP || MS_WINDOWS_SYSTEM */

#ifdef MS_WINDOWS

/*[clinic input]
os._path_isdevdrive

    path: path_t

Determines whether the specified path is on a Windows Dev Drive.

[clinic start generated code]*/

static PyObject *
os__path_isdevdrive_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=1f437ea6677433a2 input=ee83e4996a48e23d]*/
{
#ifndef PERSISTENT_VOLUME_STATE_DEV_VOLUME
    /* This flag will be documented at
       https://learn.microsoft.com/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_fs_persistent_volume_information
       after release, and will be available in the latest WinSDK.
       We include the flag to avoid a specific version dependency
       on the latest WinSDK. */
    const int PERSISTENT_VOLUME_STATE_DEV_VOLUME = 0x00002000;
#endif
    int err = 0;
    PyObject *r = NULL;
    wchar_t volume[MAX_PATH];

    Py_BEGIN_ALLOW_THREADS
    if (!GetVolumePathNameW(path->wide, volume, MAX_PATH)) {
        /* invalid path of some kind */
        /* Note that this also includes the case where a volume is mounted
           in a path longer than 260 characters. This is likely to be rare
           and problematic for other reasons, so a (soft) failure in this
           check seems okay. */
        err = GetLastError();
    } else if (GetDriveTypeW(volume) != DRIVE_FIXED) {
        /* only care about local dev drives */
        r = Py_False;
    } else {
        HANDLE hVolume = CreateFileW(
            volume,
            FILE_READ_ATTRIBUTES,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS,
            NULL
        );
        if (hVolume == INVALID_HANDLE_VALUE) {
            err = GetLastError();
        } else {
            FILE_FS_PERSISTENT_VOLUME_INFORMATION volumeState = {0};
            volumeState.Version = 1;
            volumeState.FlagMask = PERSISTENT_VOLUME_STATE_DEV_VOLUME;
            if (!DeviceIoControl(
                hVolume,
                FSCTL_QUERY_PERSISTENT_VOLUME_STATE,
                &volumeState,
                sizeof(volumeState),
                &volumeState,
                sizeof(volumeState),
                NULL,
                NULL
            )) {
                err = GetLastError();
            }
            CloseHandle(hVolume);
            if (err == ERROR_INVALID_PARAMETER) {
                /* not supported on this platform */
                r = Py_False;
            } else if (!err) {
                r = (volumeState.VolumeFlags & PERSISTENT_VOLUME_STATE_DEV_VOLUME)
                    ? Py_True : Py_False;
            }
        }
    }
    Py_END_ALLOW_THREADS

    if (err) {
        PyErr_SetFromWindowsErr(err);
        return NULL;
    }

    if (r) {
        return Py_NewRef(r);
    }

    return NULL;
}


int
_PyOS_getfullpathname(const wchar_t *path, wchar_t **abspath_p)
{
    wchar_t woutbuf[MAX_PATH], *woutbufp = woutbuf;
    DWORD result;

    result = GetFullPathNameW(path,
                              Py_ARRAY_LENGTH(woutbuf), woutbuf,
                              NULL);
    if (!result) {
        return -1;
    }

    if (result >= Py_ARRAY_LENGTH(woutbuf)) {
        if ((size_t)result <= (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) {
            woutbufp = PyMem_RawMalloc((size_t)result * sizeof(wchar_t));
        }
        else {
            woutbufp = NULL;
        }
        if (!woutbufp) {
            *abspath_p = NULL;
            return 0;
        }

        result = GetFullPathNameW(path, result, woutbufp, NULL);
        if (!result) {
            PyMem_RawFree(woutbufp);
            return -1;
        }
    }

    if (woutbufp != woutbuf) {
        *abspath_p = woutbufp;
        return 0;
    }

    *abspath_p = _PyMem_RawWcsdup(woutbufp);
    return 0;
}


/* 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 *abspath;

    if (_PyOS_getfullpathname(path->wide, &abspath) < 0) {
        return win32_error_object("GetFullPathNameW", path->object);
    }
    if (abspath == NULL) {
        return PyErr_NoMemory();
    }

    PyObject *str = PyUnicode_FromWideChar(abspath, -1);
    PyMem_RawFree(abspath);
    if (str == NULL) {
        return NULL;
    }
    if (PyBytes_Check(path->object)) {
        Py_SETREF(str, PyUnicode_EncodeFSDefault(str));
    }
    return str;
}


/*[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 (result && PyBytes_Check(path->object)) {
        Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
    }

cleanup:
    if (target_path != buf) {
        PyMem_Free(target_path);
    }
    CloseHandle(hFile);
    return result;
}

/*[clinic input]
os._findfirstfile
    path: path_t
    /
A function to get the real file name without accessing the file in Windows.
[clinic start generated code]*/

static PyObject *
os__findfirstfile_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=106dd3f0779c83dd input=0734dff70f60e1a8]*/
{
    PyObject *result;
    HANDLE hFindFile;
    WIN32_FIND_DATAW wFileData;
    WCHAR *wRealFileName;

    Py_BEGIN_ALLOW_THREADS
    hFindFile = FindFirstFileW(path->wide, &wFileData);
    Py_END_ALLOW_THREADS

    if (hFindFile == INVALID_HANDLE_VALUE) {
        path_error(path);
        return NULL;
    }

    wRealFileName = wFileData.cFileName;
    result = PyUnicode_FromWideChar(wRealFileName, -1);
    FindClose(hFindFile);
    return result;
}


/*[clinic input]
os._getvolumepathname

    path: path_t

A helper function for ismount on Win32.
[clinic start generated code]*/

static PyObject *
os__getvolumepathname_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=804c63fd13a1330b input=722b40565fa21552]*/
{
    PyObject *result;
    wchar_t *mountpath=NULL;
    size_t buflen;
    BOOL ret;

    /* Volume path should be shorter than entire path */
    buflen = Py_MAX(path->length, MAX_PATH);

    if (buflen > PY_DWORD_MAX) {
        PyErr_SetString(PyExc_OverflowError, "path too long");
        return NULL;
    }

    mountpath = PyMem_New(wchar_t, buflen);
    if (mountpath == NULL)
        return PyErr_NoMemory();

    Py_BEGIN_ALLOW_THREADS
    ret = GetVolumePathNameW(path->wide, mountpath,
                             Py_SAFE_DOWNCAST(buflen, size_t, DWORD));
    Py_END_ALLOW_THREADS

    if (!ret) {
        result = win32_error_object("_getvolumepathname", path->object);
        goto exit;
    }
    result = PyUnicode_FromWideChar(mountpath, -1);
    if (PyBytes_Check(path->object))
        Py_SETREF(result, PyUnicode_EncodeFSDefault(result));

exit:
    PyMem_Free(mountpath);
    return result;
}


/*[clinic input]
os._path_splitroot

    path: path_t,
    /

Removes everything after the root on Win32.
[clinic start generated code]*/

static PyObject *
os__path_splitroot_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=ab7f1a88b654581c input=42831e41f8458f6d]*/
{
    wchar_t *buffer;
    wchar_t *end;
    PyObject *result = NULL;
    HRESULT ret;

    buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1));
    if (!buffer) {
        return NULL;
    }
    wcscpy(buffer, path->wide);
    for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) {
        *p = L'\\';
    }

    Py_BEGIN_ALLOW_THREADS
    ret = PathCchSkipRoot(buffer, &end);
    Py_END_ALLOW_THREADS
    if (FAILED(ret)) {
        result = Py_BuildValue("sO", "", path->object);
    } else if (end != buffer) {
        size_t rootLen = (size_t)(end - buffer);
        result = Py_BuildValue("NN",
            PyUnicode_FromWideChar(path->wide, rootLen),
            PyUnicode_FromWideChar(path->wide + rootLen, -1)
        );
    } else {
        result = Py_BuildValue("Os", path->object, "");
    }
    PyMem_Free(buffer);

    return result;
}


#define PY_IFREG  1 // Regular file
#define PY_IFDIR  2 // Directory
#define PY_IFLNK  4 // Symlink
#define PY_IFMNT  8 // Mount Point (junction)
#define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction)
#define PY_IFRRP 32 // Regular Reparse Point

static inline BOOL
_testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType)
{
    switch (testedType) {
    case PY_IFREG:
        return diskDevice && attributes &&
               !(attributes & FILE_ATTRIBUTE_DIRECTORY);
    case PY_IFDIR:
        return attributes & FILE_ATTRIBUTE_DIRECTORY;
    case PY_IFLNK:
        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
               reparseTag == IO_REPARSE_TAG_SYMLINK;
    case PY_IFMNT:
        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
               reparseTag == IO_REPARSE_TAG_MOUNT_POINT;
    case PY_IFLRP:
        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
               IsReparseTagNameSurrogate(reparseTag);
    case PY_IFRRP:
        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
               reparseTag && !IsReparseTagNameSurrogate(reparseTag);
    }

    return FALSE;
}

static BOOL
_testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly)
{
    assert(testedType == PY_IFREG || testedType == PY_IFDIR ||
           testedType == PY_IFLNK || testedType == PY_IFMNT ||
           testedType == PY_IFLRP || testedType == PY_IFRRP);

    BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK;
    if (diskOnly && !diskDevice) {
        return FALSE;
    }
    if (testedType != PY_IFREG && testedType != PY_IFDIR) {
        FILE_ATTRIBUTE_TAG_INFO info;
        return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info,
                                            sizeof(info)) &&
               _testInfo(info.FileAttributes, info.ReparseTag, diskDevice,
                         testedType);
    }
    FILE_BASIC_INFO info;
    return GetFileInformationByHandleEx(hfile, FileBasicInfo, &info,
                                        sizeof(info)) &&
           _testInfo(info.FileAttributes, 0, diskDevice, testedType);
}

static BOOL
_testFileTypeByName(LPCWSTR path, int testedType)
{
    assert(testedType == PY_IFREG || testedType == PY_IFDIR ||
           testedType == PY_IFLNK || testedType == PY_IFMNT ||
           testedType == PY_IFLRP || testedType == PY_IFRRP);

    FILE_STAT_BASIC_INFORMATION info;
    if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info,
                                     sizeof(info)))
    {
        BOOL diskDevice = info.DeviceType == FILE_DEVICE_DISK ||
                          info.DeviceType == FILE_DEVICE_VIRTUAL_DISK ||
                          info.DeviceType == FILE_DEVICE_CD_ROM;
        BOOL result = _testInfo(info.FileAttributes, info.ReparseTag,
                                diskDevice, testedType);
        if (!result || (testedType != PY_IFREG && testedType != PY_IFDIR) ||
            !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
        {
            return result;
        }
    }
    else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(
                GetLastError()))
    {
        return FALSE;
    }

    DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
    if (testedType != PY_IFREG && testedType != PY_IFDIR) {
        flags |= FILE_FLAG_OPEN_REPARSE_POINT;
    }
    HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL,
                               OPEN_EXISTING, flags, NULL);
    if (hfile != INVALID_HANDLE_VALUE) {
        BOOL result = _testFileTypeByHandle(hfile, testedType, FALSE);
        CloseHandle(hfile);
        return result;
    }

    switch (GetLastError()) {
    case ERROR_ACCESS_DENIED:
    case ERROR_SHARING_VIOLATION:
    case ERROR_CANT_ACCESS_FILE:
    case ERROR_INVALID_PARAMETER:
        int rc;
        STRUCT_STAT st;
        if (testedType == PY_IFREG || testedType == PY_IFDIR) {
            rc = STAT(path, &st);
        }
        else {
            // PY_IFRRP is not generally supported in this case, except for
            // unhandled reparse points such as IO_REPARSE_TAG_APPEXECLINK.
            rc = LSTAT(path, &st);
        }
        if (!rc) {
            return _testInfo(st.st_file_attributes, st.st_reparse_tag,
                             st.st_mode & S_IFREG, testedType);
        }
    }

    return FALSE;
}


static BOOL
_testFileExistsByName(LPCWSTR path, BOOL followLinks)
{
    FILE_STAT_BASIC_INFORMATION info;
    if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info,
                                     sizeof(info)))
    {
        if (!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
            (!followLinks && IsReparseTagNameSurrogate(info.ReparseTag)))
        {
            return TRUE;
        }
    }
    else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(
                    GetLastError()))
    {
        return FALSE;
    }

    DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
    if (!followLinks) {
        flags |= FILE_FLAG_OPEN_REPARSE_POINT;
    }
    HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL,
                               OPEN_EXISTING, flags, NULL);
    if (hfile != INVALID_HANDLE_VALUE) {
        if (followLinks) {
            CloseHandle(hfile);
            return TRUE;
        }
        // Regular Reparse Points (PY_IFRRP) have to be traversed.
        BOOL result = _testFileTypeByHandle(hfile, PY_IFRRP, FALSE);
        CloseHandle(hfile);
        if (!result) {
            return TRUE;
        }
        hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
                            FILE_FLAG_BACKUP_SEMANTICS, NULL);
        if (hfile != INVALID_HANDLE_VALUE) {
            CloseHandle(hfile);
            return TRUE;
        }
    }

    switch (GetLastError()) {
    case ERROR_ACCESS_DENIED:
    case ERROR_SHARING_VIOLATION:
    case ERROR_CANT_ACCESS_FILE:
    case ERROR_INVALID_PARAMETER:
        STRUCT_STAT _st;
        return followLinks ? !STAT(path, &_st): !LSTAT(path, &_st);
    }

    return FALSE;
}


static BOOL
_testFileExists(path_t *path, BOOL followLinks)
{
    BOOL result = FALSE;
    if (path->value_error) {
        return FALSE;
    }

    Py_BEGIN_ALLOW_THREADS
    if (path->fd != -1) {
        HANDLE hfile = _Py_get_osfhandle_noraise(path->fd);
        if (hfile != INVALID_HANDLE_VALUE) {
            if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) {
                result = TRUE;
            }
        }
    }
    else if (path->wide) {
        result = _testFileExistsByName(path->wide, followLinks);
    }
    Py_END_ALLOW_THREADS

    return result;
}


static BOOL
_testFileType(path_t *path, int testedType)
{
    BOOL result = FALSE;
    if (path->value_error) {
        return FALSE;
    }

    Py_BEGIN_ALLOW_THREADS
    if (path->fd != -1) {
        HANDLE hfile = _Py_get_osfhandle_noraise(path->fd);
        if (hfile != INVALID_HANDLE_VALUE) {
            result = _testFileTypeByHandle(hfile, testedType, TRUE);
        }
    }
    else if (path->wide) {
        result = _testFileTypeByName(path->wide, testedType);
    }
    Py_END_ALLOW_THREADS

    return result;
}


/*[clinic input]
os._path_exists -> bool

    path: path_t(allow_fd=True, suppress_value_error=True)

Test whether a path exists.  Returns False for broken symbolic links.

[clinic start generated code]*/

static int
os__path_exists_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=8da13acf666e16ba input=142beabfc66783eb]*/
{
    return _testFileExists(path, TRUE);
}


/*[clinic input]
os._path_lexists -> bool

    path: path_t(allow_fd=True, suppress_value_error=True)

Test whether a path exists.  Returns True for broken symbolic links.

[clinic start generated code]*/

static int
os__path_lexists_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=e7240ed5fc45bff3 input=208205112a3cc1ed]*/
{
    return _testFileExists(path, FALSE);
}


/*[clinic input]
os._path_isdir -> bool

    path: path_t(allow_fd=True, suppress_value_error=True),
    /

Return true if the pathname refers to an existing directory.

[clinic start generated code]*/

static int
os__path_isdir_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=d5786196f9e2fa7a input=0d3fd790564d244b]*/
{
    return _testFileType(path, PY_IFDIR);
}


/*[clinic input]
os._path_isfile -> bool

    path: path_t(allow_fd=True, suppress_value_error=True)

Test whether a path is a regular file

[clinic start generated code]*/

static int
os__path_isfile_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=5c3073bc212b9863 input=4ac1fd350b30a39e]*/
{
    return _testFileType(path, PY_IFREG);
}


/*[clinic input]
os._path_islink -> bool

    path: path_t(allow_fd=True, suppress_value_error=True)

Test whether a path is a symbolic link

[clinic start generated code]*/

static int
os__path_islink_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=30da7bda8296adcc input=7510ce05b547debb]*/
{
    return _testFileType(path, PY_IFLNK);
}


/*[clinic input]
os._path_isjunction -> bool

    path: path_t(allow_fd=True, suppress_value_error=True)

Test whether a path is a junction

[clinic start generated code]*/

static int
os__path_isjunction_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=e1d17a9dd18a9945 input=7dcb8bc4e972fcaf]*/
{
    return _testFileType(path, PY_IFMNT);
}

#undef PY_IFREG
#undef PY_IFDIR
#undef PY_IFLNK
#undef PY_IFMNT
#undef PY_IFLRP
#undef PY_IFRRP

#endif /* MS_WINDOWS */


/*[clinic input]
os._path_splitroot_ex

    path: path_t(make_wide=True, nonstrict=True),
    /

Split a pathname into drive, root and tail.

The tail contains anything after the root.
[clinic start generated code]*/

static PyObject *
os__path_splitroot_ex_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=4b0072b6cdf4b611 input=4ac47b394d68bd21]*/
{
    Py_ssize_t drvsize, rootsize;
    PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL;

    const wchar_t *buffer = path->wide;
    _Py_skiproot(buffer, path->length, &drvsize, &rootsize);
    drv = PyUnicode_FromWideChar(buffer, drvsize);
    if (drv == NULL) {
        goto exit;
    }
    root = PyUnicode_FromWideChar(&buffer[drvsize], rootsize);
    if (root == NULL) {
        goto exit;
    }
    tail = PyUnicode_FromWideChar(&buffer[drvsize + rootsize],
                                  path->length - drvsize - rootsize);
    if (tail == NULL) {
        goto exit;
    }
    if (PyBytes_Check(path->object)) {
        Py_SETREF(drv, PyUnicode_EncodeFSDefault(drv));
        if (drv == NULL) {
            goto exit;
        }
        Py_SETREF(root, PyUnicode_EncodeFSDefault(root));
        if (root == NULL) {
            goto exit;
        }
        Py_SETREF(tail, PyUnicode_EncodeFSDefault(tail));
        if (tail == NULL) {
            goto exit;
        }
    }
    result = PyTuple_Pack(3, drv, root, tail);
exit:
    Py_XDECREF(drv);
    Py_XDECREF(root);
    Py_XDECREF(tail);
    return result;
}


/*[clinic input]
os._path_normpath

    path: path_t(make_wide=True, nonstrict=True)

Normalize path, eliminating double slashes, etc.
[clinic start generated code]*/

static PyObject *
os__path_normpath_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=d353e7ed9410c044 input=3d4ac23b06332dcb]*/
{
    PyObject *result;
    Py_ssize_t norm_len;
    wchar_t *norm_path = _Py_normpath_and_size((wchar_t *)path->wide,
                                               path->length, &norm_len);
    if (!norm_len) {
        result = PyUnicode_FromOrdinal('.');
    }
    else {
        result = PyUnicode_FromWideChar(norm_path, norm_len);
    }
    if (PyBytes_Check(path->object)) {
        Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
    }
    return result;
}

/*[clinic input]
os.mkdir

    path : path_t

    mode: int = 0o777

    *

    dir_fd : dir_fd(requires='mkdirat') = None

# "mkdir(path, mode=0o777, *, dir_fd=None)\n\n\

Create a directory.

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.

The mode argument is ignored on Windows. Where it is used, the current umask
value is first masked out.
[clinic start generated code]*/

static PyObject *
os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd)
/*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/
{
    int result;
#ifdef MS_WINDOWS
    int error = 0;
    SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr) };
    SECURITY_ATTRIBUTES *pSecAttr = NULL;
#endif
#ifdef HAVE_MKDIRAT
    int mkdirat_unavailable = 0;
#endif

    if (PySys_Audit("os.mkdir", "Oii", path->object, mode,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    // For API sets that don't support these APIs, we have no choice
    // but to silently create a directory with default ACL.
#if defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM)
    if (mode == 0700 /* 0o700 */) {
        ULONG sdSize;
        pSecAttr = &secAttr;
        // Set a discretionary ACL (D) that is protected (P) and includes
        // inheritable (OICI) entries that allow (A) full control (FA) to
        // SYSTEM (SY), Administrators (BA), and the owner (OW).
        if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
            L"D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;OW)",
            SDDL_REVISION_1,
            &secAttr.lpSecurityDescriptor,
            &sdSize
        )) {
            error = GetLastError();
        }
    }
#endif
    if (!error) {
        result = CreateDirectoryW(path->wide, pSecAttr);
        if (secAttr.lpSecurityDescriptor &&
            // uncommonly, LocalFree returns non-zero on error, but still uses
            // GetLastError() to see what the error code is
            LocalFree(secAttr.lpSecurityDescriptor)) {
            error = GetLastError();
        }
    }
    Py_END_ALLOW_THREADS

    if (error) {
        return PyErr_SetFromWindowsErr(error);
    }
    if (!result) {
        return path_error(path);
    }
#else
    Py_BEGIN_ALLOW_THREADS
#if HAVE_MKDIRAT
    if (dir_fd != DEFAULT_DIR_FD) {
      if (HAVE_MKDIRAT_RUNTIME) {
        result = mkdirat(dir_fd, path->narrow, mode);

      } else {
        mkdirat_unavailable = 1;
      }
    } else
#endif
#if defined(__WATCOMC__) && !defined(__QNX__)
        result = mkdir(path->narrow);
#else
        result = mkdir(path->narrow, mode);
#endif
    Py_END_ALLOW_THREADS

#if HAVE_MKDIRAT
    if (mkdirat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (result < 0)
        return path_error(path);
#endif /* MS_WINDOWS */
    Py_RETURN_NONE;
}


/* sys/resource.h is needed for at least: wait3(), wait4(), broken nice. */
#if defined(HAVE_SYS_RESOURCE_H)
#include <sys/resource.h>
#endif


#ifdef HAVE_NICE
/*[clinic input]
os.nice

    increment: int
    /

Add increment to the priority of process and return the new priority.
[clinic start generated code]*/

static PyObject *
os_nice_impl(PyObject *module, int increment)
/*[clinic end generated code: output=9dad8a9da8109943 input=864be2d402a21da2]*/
{
    int value;

    /* There are two flavours of 'nice': one that returns the new
       priority (as required by almost all standards out there) and the
       Linux/FreeBSD one, which returns '0' on success and advices
       the use of getpriority() to get the new priority.

       If we are of the nice family that returns the new priority, we
       need to clear errno before the call, and check if errno is filled
       before calling posix_error() on a returnvalue of -1, because the
       -1 may be the actual new priority! */

    errno = 0;
    value = nice(increment);
#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY)
    if (value == 0)
        value = getpriority(PRIO_PROCESS, 0);
#endif
    if (value == -1 && errno != 0)
        /* either nice() or getpriority() returned an error */
        return posix_error();
    return PyLong_FromLong((long) value);
}
#endif /* HAVE_NICE */


#ifdef HAVE_GETPRIORITY
/*[clinic input]
os.getpriority

    which: int
    who: int

Return program scheduling priority.
[clinic start generated code]*/

static PyObject *
os_getpriority_impl(PyObject *module, int which, int who)
/*[clinic end generated code: output=c41b7b63c7420228 input=9be615d40e2544ef]*/
{
    int retval;

    errno = 0;
    retval = getpriority(which, who);
    if (errno != 0)
        return posix_error();
    return PyLong_FromLong((long)retval);
}
#endif /* HAVE_GETPRIORITY */


#ifdef HAVE_SETPRIORITY
/*[clinic input]
os.setpriority

    which: int
    who: int
    priority: int

Set program scheduling priority.
[clinic start generated code]*/

static PyObject *
os_setpriority_impl(PyObject *module, int which, int who, int priority)
/*[clinic end generated code: output=3d910d95a7771eb2 input=710ccbf65b9dc513]*/
{
    int retval;

    retval = setpriority(which, who, priority);
    if (retval == -1)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETPRIORITY */


static PyObject *
internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is_replace)
{
    const char *function_name = is_replace ? "replace" : "rename";
    int dir_fd_specified;

#ifdef HAVE_RENAMEAT
    int renameat_unavailable = 0;
#endif

#ifdef MS_WINDOWS
    BOOL result;
    int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;
#else
    int result;
#endif

    dir_fd_specified = (src_dir_fd != DEFAULT_DIR_FD) ||
                       (dst_dir_fd != DEFAULT_DIR_FD);
#ifndef HAVE_RENAMEAT
    if (dir_fd_specified) {
        argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd");
        return NULL;
    }
#endif

    if (PySys_Audit("os.rename", "OOii", src->object, dst->object,
                    src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd,
                    dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    result = MoveFileExW(src->wide, dst->wide, flags);
    Py_END_ALLOW_THREADS

    if (!result)
        return path_error2(src, dst);

#else
    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_RENAMEAT
    if (dir_fd_specified) {
        if (HAVE_RENAMEAT_RUNTIME) {
            result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow);
        } else {
            renameat_unavailable = 1;
        }
    } else
#endif
    result = rename(src->narrow, dst->narrow);
    Py_END_ALLOW_THREADS


#ifdef HAVE_RENAMEAT
    if (renameat_unavailable) {
        argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd");
        return NULL;
    }
#endif

    if (result)
        return path_error2(src, dst);
#endif
    Py_RETURN_NONE;
}


/*[clinic input]
@permit_long_docstring_body
os.rename

    src : path_t
    dst : path_t
    *
    src_dir_fd : dir_fd = None
    dst_dir_fd : dir_fd = None

Rename a file or directory.

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.
src_dir_fd and dst_dir_fd, may not be implemented on your platform.
  If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_rename_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
               int dst_dir_fd)
/*[clinic end generated code: output=59e803072cf41230 input=11aae8c091162766]*/
{
    return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 0);
}


/*[clinic input]
@permit_long_docstring_body
os.replace = os.rename

Rename a file or directory, overwriting the destination.

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.
src_dir_fd and dst_dir_fd, may not be implemented on your platform.
  If they are unavailable, using them will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_replace_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
                int dst_dir_fd)
/*[clinic end generated code: output=1968c02e7857422b input=78d6c8087e90994c]*/
{
    return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 1);
}


/*[clinic input]
os.rmdir

    path: path_t
    *
    dir_fd: dir_fd(requires='unlinkat') = None

Remove a directory.

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_rmdir_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/
{
    int result;
#ifdef HAVE_UNLINKAT
    int unlinkat_unavailable = 0;
#endif

    if (PySys_Audit("os.rmdir", "Oi", path->object,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
    /* Windows, success=1, UNIX, success=0 */
    result = !RemoveDirectoryW(path->wide);
#else
#ifdef HAVE_UNLINKAT
    if (dir_fd != DEFAULT_DIR_FD) {
      if (HAVE_UNLINKAT_RUNTIME) {
        result = unlinkat(dir_fd, path->narrow, AT_REMOVEDIR);
      } else {
        unlinkat_unavailable = 1;
        result = -1;
      }
    } else
#endif
        result = rmdir(path->narrow);
#endif
    Py_END_ALLOW_THREADS

#ifdef HAVE_UNLINKAT
    if (unlinkat_unavailable) {
        argument_unavailable_error("rmdir", "dir_fd");
        return NULL;
    }
#endif

    if (result)
        return path_error(path);

    Py_RETURN_NONE;
}


#ifdef HAVE_SYSTEM
#ifdef MS_WINDOWS
/*[clinic input]
os.system -> long

    command: Py_UNICODE

Execute the command in a subshell.
[clinic start generated code]*/

static long
os_system_impl(PyObject *module, const wchar_t *command)
/*[clinic end generated code: output=dd528cbd5943a679 input=303f5ce97df606b0]*/
{
    long result;

    if (PySys_Audit("os.system", "(u)", command) < 0) {
        return -1;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
    result = _wsystem(command);
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    return result;
}
#else /* MS_WINDOWS */
/*[clinic input]
os.system -> long

    command: unicode_fs_encoded

Execute the command in a subshell.
[clinic start generated code]*/

static long
os_system_impl(PyObject *module, PyObject *command)
/*[clinic end generated code: output=290fc437dd4f33a0 input=47c6f24b6dc92881]*/
{
    long result;
    const char *bytes = PyBytes_AsString(command);

    if (PySys_Audit("os.system", "(O)", command) < 0) {
        return -1;
    }

    Py_BEGIN_ALLOW_THREADS
    result = system(bytes);
    Py_END_ALLOW_THREADS
    return result;
}
#endif
#endif /* HAVE_SYSTEM */


#ifdef HAVE_UMASK
/*[clinic input]
os.umask

    mask: int
    /

Set the current numeric umask and return the previous umask.
[clinic start generated code]*/

static PyObject *
os_umask_impl(PyObject *module, int mask)
/*[clinic end generated code: output=a2e33ce3bc1a6e33 input=ab6bfd9b24d8a7e8]*/
{
    int i = (int)umask(mask);
    if (i < 0)
        return posix_error();
    return PyLong_FromLong((long)i);
}
#endif

#ifdef MS_WINDOWS

/* override the default DeleteFileW behavior so that directory
symlinks can be removed with this function, the same as with
Unix symlinks */
BOOL WINAPI Py_DeleteFileW(LPCWSTR lpFileName)
{
    WIN32_FILE_ATTRIBUTE_DATA info;
    WIN32_FIND_DATAW find_data;
    HANDLE find_data_handle;
    int is_directory = 0;
    int is_link = 0;

    if (GetFileAttributesExW(lpFileName, GetFileExInfoStandard, &info)) {
        is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;

        /* Get WIN32_FIND_DATA structure for the path to determine if
           it is a symlink */
        if(is_directory &&
           info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
            find_data_handle = FindFirstFileW(lpFileName, &find_data);

            if(find_data_handle != INVALID_HANDLE_VALUE) {
                /* IO_REPARSE_TAG_SYMLINK if it is a symlink and
                   IO_REPARSE_TAG_MOUNT_POINT if it is a junction point. */
                is_link = find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK ||
                          find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT;
                FindClose(find_data_handle);
            }
        }
    }

    if (is_directory && is_link)
        return RemoveDirectoryW(lpFileName);

    return DeleteFileW(lpFileName);
}
#endif /* MS_WINDOWS */


/*[clinic input]
os.unlink

    path: path_t
    *
    dir_fd: dir_fd(requires='unlinkat')=None

Remove a file (same as remove()).

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.

[clinic start generated code]*/

static PyObject *
os_unlink_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/
{
    int result;
#ifdef HAVE_UNLINKAT
    int unlinkat_unavailable = 0;
#endif

    if (PySys_Audit("os.remove", "Oi", path->object,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
    /* Windows, success=1, UNIX, success=0 */
    result = !Py_DeleteFileW(path->wide);
#else
#ifdef HAVE_UNLINKAT
    if (dir_fd != DEFAULT_DIR_FD) {
      if (HAVE_UNLINKAT_RUNTIME) {

        result = unlinkat(dir_fd, path->narrow, 0);
      } else {
        unlinkat_unavailable = 1;
      }
    } else
#endif /* HAVE_UNLINKAT */
        result = unlink(path->narrow);
#endif
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS

#ifdef HAVE_UNLINKAT
    if (unlinkat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (result)
        return path_error(path);

    Py_RETURN_NONE;
}


/*[clinic input]
os.remove = os.unlink

Remove a file (same as unlink()).

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_remove_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=a8535b28f0068883 input=e05c5ab55cd30983]*/
{
    return os_unlink_impl(module, path, dir_fd);
}


static PyStructSequence_Field uname_result_fields[] = {
    {"sysname",    "operating system name"},
    {"nodename",   "name of machine on network (implementation-defined)"},
    {"release",    "operating system release"},
    {"version",    "operating system version"},
    {"machine",    "hardware identifier"},
    {NULL}
};

PyDoc_STRVAR(uname_result__doc__,
"uname_result: Result from os.uname().\n\n\
This object may be accessed either as a tuple of\n\
  (sysname, nodename, release, version, machine),\n\
or via the attributes sysname, nodename, release, version, and machine.\n\
\n\
See os.uname for more information.");

static PyStructSequence_Desc uname_result_desc = {
    MODNAME ".uname_result", /* name */
    uname_result__doc__, /* doc */
    uname_result_fields,
    5
};

#ifdef HAVE_UNAME
/*[clinic input]
os.uname

Return an object identifying the current operating system.

The object behaves like a named tuple with the following fields:
  (sysname, nodename, release, version, machine)

[clinic start generated code]*/

static PyObject *
os_uname_impl(PyObject *module)
/*[clinic end generated code: output=e6a49cf1a1508a19 input=e68bd246db3043ed]*/
{
    struct utsname u;
    int res;
    PyObject *value;

    Py_BEGIN_ALLOW_THREADS
    res = uname(&u);
    Py_END_ALLOW_THREADS
    if (res < 0)
        return posix_error();

    PyObject *UnameResultType = get_posix_state(module)->UnameResultType;
    value = PyStructSequence_New((PyTypeObject *)UnameResultType);
    if (value == NULL)
        return NULL;

#define SET(i, field) \
    { \
    PyObject *o = PyUnicode_DecodeFSDefault(field); \
    if (!o) { \
        Py_DECREF(value); \
        return NULL; \
    } \
    PyStructSequence_SET_ITEM(value, i, o); \
    } \

    SET(0, u.sysname);
    SET(1, u.nodename);
    SET(2, u.release);
    SET(3, u.version);
    SET(4, u.machine);

#undef SET

    return value;
}
#endif /* HAVE_UNAME */



typedef struct {
    int    now;
    time_t atime_s;
    long   atime_ns;
    time_t mtime_s;
    long   mtime_ns;
} utime_t;

/*
 * these macros assume that "ut" is a pointer to a utime_t
 * they also intentionally leak the declaration of a pointer named "time"
 */
#define UTIME_TO_TIMESPEC \
    struct timespec ts[2]; \
    struct timespec *time; \
    if (ut->now) \
        time = NULL; \
    else { \
        ts[0].tv_sec = ut->atime_s; \
        ts[0].tv_nsec = ut->atime_ns; \
        ts[1].tv_sec = ut->mtime_s; \
        ts[1].tv_nsec = ut->mtime_ns; \
        time = ts; \
    } \

#define UTIME_TO_TIMEVAL \
    struct timeval tv[2]; \
    struct timeval *time; \
    if (ut->now) \
        time = NULL; \
    else { \
        tv[0].tv_sec = ut->atime_s; \
        tv[0].tv_usec = ut->atime_ns / 1000; \
        tv[1].tv_sec = ut->mtime_s; \
        tv[1].tv_usec = ut->mtime_ns / 1000; \
        time = tv; \
    } \

#define UTIME_TO_UTIMBUF \
    struct utimbuf u; \
    struct utimbuf *time; \
    if (ut->now) \
        time = NULL; \
    else { \
        u.actime = ut->atime_s; \
        u.modtime = ut->mtime_s; \
        time = &u; \
    }

#define UTIME_TO_TIME_T \
    time_t timet[2]; \
    time_t *time; \
    if (ut->now) \
        time = NULL; \
    else { \
        timet[0] = ut->atime_s; \
        timet[1] = ut->mtime_s; \
        time = timet; \
    } \


#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT)

static int
utime_dir_fd(utime_t *ut, int dir_fd, const char *path, int follow_symlinks)
{
#if defined(__APPLE__) &&  defined(HAVE_UTIMENSAT)
    if (HAVE_UTIMENSAT_RUNTIME) {
        int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
        UTIME_TO_TIMESPEC;
        return utimensat(dir_fd, path, time, flags);
    }  else {
        errno = ENOSYS;
        return -1;
    }
#elif defined(HAVE_UTIMENSAT)
    int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
    UTIME_TO_TIMESPEC;
    return utimensat(dir_fd, path, time, flags);
#elif defined(HAVE_FUTIMESAT)
    UTIME_TO_TIMEVAL;
    /*
     * follow_symlinks will never be false here;
     * we only allow !follow_symlinks and dir_fd together
     * if we have utimensat()
     */
    assert(follow_symlinks);
    return futimesat(dir_fd, path, time);
#endif
}

    #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_converter
#else
    #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_unavailable
#endif

#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)

static int
utime_fd(utime_t *ut, int fd)
{
#ifdef HAVE_FUTIMENS

    if (HAVE_FUTIMENS_RUNTIME) {

    UTIME_TO_TIMESPEC;
    return futimens(fd, time);

    } else
#ifndef HAVE_FUTIMES
    {
        /* Not sure if this can happen */
        PyErr_SetString(
            PyExc_RuntimeError,
            "neither futimens nor futimes are supported"
            " on this system");
        return -1;
    }
#endif

#endif
#ifdef HAVE_FUTIMES
    {
    UTIME_TO_TIMEVAL;
    return futimes(fd, time);
    }
#endif
}

    #define PATH_UTIME_HAVE_FD 1
#else
    #define PATH_UTIME_HAVE_FD 0
#endif

#if defined(HAVE_UTIMENSAT) || defined(HAVE_LUTIMES)
#  define UTIME_HAVE_NOFOLLOW_SYMLINKS
#endif

#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS

static int
utime_nofollow_symlinks(utime_t *ut, const char *path)
{
#ifdef HAVE_UTIMENSAT
    if (HAVE_UTIMENSAT_RUNTIME) {
        UTIME_TO_TIMESPEC;
        return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW);
    } else
#ifndef HAVE_LUTIMES
    {
        /* Not sure if this can happen */
        PyErr_SetString(
            PyExc_RuntimeError,
            "neither utimensat nor lutimes are supported"
            " on this system");
        return -1;
    }
#endif
#endif

#ifdef HAVE_LUTIMES
    {
    UTIME_TO_TIMEVAL;
    return lutimes(path, time);
    }
#endif
}

#endif

#ifndef MS_WINDOWS

static int
utime_default(utime_t *ut, const char *path)
{
#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
    if (HAVE_UTIMENSAT_RUNTIME) {
        UTIME_TO_TIMESPEC;
        return utimensat(DEFAULT_DIR_FD, path, time, 0);
    } else {
        UTIME_TO_TIMEVAL;
        return utimes(path, time);
    }
#elif defined(HAVE_UTIMENSAT)
    UTIME_TO_TIMESPEC;
    return utimensat(DEFAULT_DIR_FD, path, time, 0);
#elif defined(HAVE_UTIMES)
    UTIME_TO_TIMEVAL;
    return utimes(path, time);
#elif defined(HAVE_UTIME_H)
    UTIME_TO_UTIMBUF;
    return utime(path, time);
#else
    UTIME_TO_TIME_T;
    return utime(path, time);
#endif
}

#endif

static int
split_py_long_to_s_and_ns(PyObject *module, PyObject *py_long, time_t *s, long *ns)
{
    int result = 0;
    PyObject *divmod;
    divmod = PyNumber_Divmod(py_long, get_posix_state(module)->billion);
    if (!divmod)
        goto exit;
    if (!PyTuple_Check(divmod) || PyTuple_GET_SIZE(divmod) != 2) {
        PyErr_Format(PyExc_TypeError,
                     "%.200s.__divmod__() must return a 2-tuple, not %.200s",
                     _PyType_Name(Py_TYPE(py_long)), _PyType_Name(Py_TYPE(divmod)));
        goto exit;
    }
    *s = _PyLong_AsTime_t(PyTuple_GET_ITEM(divmod, 0));
    if ((*s == -1) && PyErr_Occurred())
        goto exit;
    *ns = PyLong_AsLong(PyTuple_GET_ITEM(divmod, 1));
    if ((*ns == -1) && PyErr_Occurred())
        goto exit;

    result = 1;
exit:
    Py_XDECREF(divmod);
    return result;
}


/*[clinic input]
os.utime

    path: path_t(allow_fd='PATH_UTIME_HAVE_FD')
    times: object = None
    *
    ns: object = NULL
    dir_fd: dir_fd(requires='futimensat') = None
    follow_symlinks: bool=True

# "utime(path, times=None, *[, ns], dir_fd=None, follow_symlinks=True)\n\

Set the access and modified time of 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.

If times is not None, it must be a tuple (atime, mtime);
    atime and mtime should be expressed as float seconds since the epoch.
If ns is specified, it must be a tuple (atime_ns, mtime_ns);
    atime_ns and mtime_ns should be expressed as integer nanoseconds
    since the epoch.
If times is None and ns is unspecified, utime uses the current time.
Specifying tuples for both times and ns is an error.

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, utime 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 available on your platform.
  If they are unavailable, using them will raise a NotImplementedError.

[clinic start generated code]*/

static PyObject *
os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
              int dir_fd, int follow_symlinks)
/*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/
{
#ifdef MS_WINDOWS
    HANDLE hFile;
    FILETIME atime, mtime;
#else
    int result;
#endif

    utime_t utime;

    memset(&utime, 0, sizeof(utime_t));

    if (times != Py_None && ns) {
        PyErr_SetString(PyExc_ValueError,
                     "utime: you may specify either 'times'"
                     " or 'ns' but not both");
        return NULL;
    }

    if (times != Py_None) {
        time_t a_sec, m_sec;
        long a_nsec, m_nsec;
        if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) {
            PyErr_SetString(PyExc_TypeError,
                         "utime: 'times' must be either"
                         " a tuple of two numbers or None");
            return NULL;
        }
        utime.now = 0;
        if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0),
                                     &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 ||
            _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1),
                                     &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) {
            return NULL;
        }
        utime.atime_s = a_sec;
        utime.atime_ns = a_nsec;
        utime.mtime_s = m_sec;
        utime.mtime_ns = m_nsec;
    }
    else if (ns) {
        if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) {
            PyErr_SetString(PyExc_TypeError,
                         "utime: 'ns' must be a tuple of two ints");
            return NULL;
        }
        utime.now = 0;
        if (!split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 0),
                                      &utime.atime_s, &utime.atime_ns) ||
            !split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 1),
                                       &utime.mtime_s, &utime.mtime_ns)) {
            return NULL;
        }
    }
    else {
        /* times and ns are both None/unspecified. use "now". */
        utime.now = 1;
    }

#if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS)
    if (follow_symlinks_specified("utime", follow_symlinks))
        return NULL;
#endif

    if (path_and_dir_fd_invalid("utime", path, dir_fd) ||
        dir_fd_and_fd_invalid("utime", dir_fd, path->fd) ||
        fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks))
        return NULL;

#if !defined(HAVE_UTIMENSAT)
    if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) {
        PyErr_SetString(PyExc_ValueError,
                     "utime: cannot use dir_fd and follow_symlinks "
                     "together on this platform");
        return NULL;
    }
#endif

    if (PySys_Audit("os.utime", "OOOi", path->object, times, ns ? ns : Py_None,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    hFile = CreateFileW(path->wide, FILE_WRITE_ATTRIBUTES, 0,
                        NULL, OPEN_EXISTING,
                        FILE_FLAG_BACKUP_SEMANTICS, NULL);
    Py_END_ALLOW_THREADS
    if (hFile == INVALID_HANDLE_VALUE) {
        path_error(path);
        return NULL;
    }

    if (utime.now) {
        GetSystemTimeAsFileTime(&mtime);
        atime = mtime;
    }
    else {
        _Py_time_t_to_FILE_TIME(utime.atime_s, utime.atime_ns, &atime);
        _Py_time_t_to_FILE_TIME(utime.mtime_s, utime.mtime_ns, &mtime);
    }
    if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
        path_error(path);
        CloseHandle(hFile);
        return NULL;
    }
    CloseHandle(hFile);
#else /* MS_WINDOWS */
    Py_BEGIN_ALLOW_THREADS

#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS
    if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
        result = utime_nofollow_symlinks(&utime, path->narrow);
    else
#endif

#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT)
    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
        result = utime_dir_fd(&utime, dir_fd, path->narrow, follow_symlinks);

    } else
#endif

#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)
    if (path->fd != -1)
        result = utime_fd(&utime, path->fd);
    else
#endif

    result = utime_default(&utime, path->narrow);

    Py_END_ALLOW_THREADS

#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
    /* See utime_dir_fd implementation */
    if (result == -1 && errno == ENOSYS) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (result < 0) {
        path_error(path);
        return NULL;
    }

#endif /* MS_WINDOWS */

    Py_RETURN_NONE;
}

/* Process operations */


/*[clinic input]
os._exit

    status: int

Exit to the system with specified status, without normal exit processing.
[clinic start generated code]*/

static PyObject *
os__exit_impl(PyObject *module, int status)
/*[clinic end generated code: output=116e52d9c2260d54 input=5e6d57556b0c4a62]*/
{
    _exit(status);
    return NULL; /* Make gcc -Wall happy */
}

#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV)
#define EXECV_CHAR wchar_t
#else
#define EXECV_CHAR char
#endif

#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) || defined(HAVE_RTPSPAWN)
static void
free_string_array(EXECV_CHAR **array, Py_ssize_t count)
{
    Py_ssize_t i;
    for (i = 0; i < count; i++)
        PyMem_Free(array[i]);
    PyMem_Free(array);
}

static int
fsconvert_strdup(PyObject *o, EXECV_CHAR **out)
{
    Py_ssize_t size;
    PyObject *ub;
    int result = 0;
#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV)
    if (!PyUnicode_FSDecoder(o, &ub))
        return 0;
    *out = PyUnicode_AsWideCharString(ub, &size);
    if (*out)
        result = 1;
#else
    if (!PyUnicode_FSConverter(o, &ub))
        return 0;
    size = PyBytes_GET_SIZE(ub);
    *out = PyMem_Malloc(size + 1);
    if (*out) {
        memcpy(*out, PyBytes_AS_STRING(ub), size + 1);
        result = 1;
    } else
        PyErr_NoMemory();
#endif
    Py_DECREF(ub);
    return result;
}
#endif

#if defined(HAVE_EXECV) || defined (HAVE_FEXECVE) || defined(HAVE_RTPSPAWN)
static EXECV_CHAR**
parse_envlist(PyObject* env, Py_ssize_t *envc_ptr)
{
    Py_ssize_t i, pos, envc;
    PyObject *keys = NULL, *vals = NULL;
    PyObject *key = NULL, *val = NULL, *key2 = NULL, *val2 = NULL;
    EXECV_CHAR **envlist;

    i = PyMapping_Size(env);
    if (i < 0)
        return NULL;
    envlist = PyMem_NEW(EXECV_CHAR *, i + 1);
    if (envlist == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    envc = 0;
    keys = PyMapping_Keys(env);
    if (!keys)
        goto error;
    vals = PyMapping_Values(env);
    if (!vals)
        goto error;
    if (!PyList_Check(keys) || !PyList_Check(vals)) {
        PyErr_Format(PyExc_TypeError,
                     "env.keys() or env.values() is not a list");
        goto error;
    }

    for (pos = 0; pos < i; pos++) {
        // The 'key' and 'val' must be strong references because of
        // possible side-effects by PyUnicode_FS{Converter,Decoder}().
        key = PyList_GetItemRef(keys, pos);
        if (key == NULL) {
            goto error;
        }
        val = PyList_GetItemRef(vals, pos);
        if (val == NULL) {
            goto error;
        }

#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV)
        if (!PyUnicode_FSDecoder(key, &key2)) {
            goto error;
        }
        if (!PyUnicode_FSDecoder(val, &val2)) {
            goto error;
        }
        /* Search from index 1 because on Windows starting '=' is allowed for
           defining hidden environment variables. */
        if (PyUnicode_GET_LENGTH(key2) == 0 ||
            PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1)
        {
            PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
            goto error;
        }
        PyObject *keyval = PyUnicode_FromFormat("%U=%U", key2, val2);
#else
        if (!PyUnicode_FSConverter(key, &key2)) {
            goto error;
        }
        if (!PyUnicode_FSConverter(val, &val2)) {
            goto error;
        }
        if (PyBytes_GET_SIZE(key2) == 0 ||
            strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL)
        {
            PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
            goto error;
        }
        PyObject *keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2),
                                              PyBytes_AS_STRING(val2));
#endif
        if (!keyval) {
            goto error;
        }

        if (!fsconvert_strdup(keyval, &envlist[envc++])) {
            Py_DECREF(keyval);
            goto error;
        }

        Py_CLEAR(key);
        Py_CLEAR(val);
        Py_CLEAR(key2);
        Py_CLEAR(val2);
        Py_DECREF(keyval);
    }
    Py_DECREF(vals);
    Py_DECREF(keys);

    envlist[envc] = 0;
    *envc_ptr = envc;
    return envlist;

error:
    Py_XDECREF(key);
    Py_XDECREF(val);
    Py_XDECREF(key2);
    Py_XDECREF(val2);
    Py_XDECREF(keys);
    Py_XDECREF(vals);
    free_string_array(envlist, envc);
    return NULL;
}

static EXECV_CHAR**
parse_arglist(PyObject* argv, Py_ssize_t *argc)
{
    int i;
    EXECV_CHAR **argvlist = PyMem_NEW(EXECV_CHAR *, *argc+1);
    if (argvlist == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    for (i = 0; i < *argc; i++) {
        PyObject* item = PySequence_ITEM(argv, i);
        if (item == NULL)
            goto fail;
        if (!fsconvert_strdup(item, &argvlist[i])) {
            Py_DECREF(item);
            goto fail;
        }
        Py_DECREF(item);
    }
    argvlist[*argc] = NULL;
    return argvlist;
fail:
    *argc = i;
    free_string_array(argvlist, *argc);
    return NULL;
}

#endif


#ifdef HAVE_EXECV
/*[clinic input]
os.execv

    path: path_t
        Path of executable file.
    argv: object
        Tuple or list of strings.
    /

Execute an executable path with arguments, replacing current process.
[clinic start generated code]*/

static PyObject *
os_execv_impl(PyObject *module, path_t *path, PyObject *argv)
/*[clinic end generated code: output=3b52fec34cd0dafd input=9bac31efae07dac7]*/
{
    EXECV_CHAR **argvlist;
    Py_ssize_t argc;

    PyInterpreterState *interp = _PyInterpreterState_GET();
    if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) {
        PyErr_SetString(PyExc_RuntimeError,
                        "exec not supported for isolated subinterpreters");
        return NULL;
    }

    /* execv has two arguments: (path, argv), where
       argv is a list or tuple of strings. */

    if (!PyList_Check(argv) && !PyTuple_Check(argv)) {
        PyErr_SetString(PyExc_TypeError,
                        "execv() arg 2 must be a tuple or list");
        return NULL;
    }
    argc = PySequence_Size(argv);
    if (argc < 1) {
        PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty");
        return NULL;
    }

    argvlist = parse_arglist(argv, &argc);
    if (argvlist == NULL) {
        return NULL;
    }
    if (!argvlist[0][0]) {
        PyErr_SetString(PyExc_ValueError,
            "execv() arg 2 first element cannot be empty");
        free_string_array(argvlist, argc);
        return NULL;
    }

    if (PySys_Audit("os.exec", "OOO", path->object, argv, Py_None) < 0) {
        free_string_array(argvlist, argc);
        return NULL;
    }

    _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_WEXECV
    _wexecv(path->wide, argvlist);
#else
    execv(path->narrow, argvlist);
#endif
    _Py_END_SUPPRESS_IPH

    /* If we get here it's definitely an error */

    posix_error();
    free_string_array(argvlist, argc);
    return NULL;
}


/*[clinic input]
os.execve

    path: path_t(allow_fd='PATH_HAVE_FEXECVE')
        Path of executable file.
    argv: object
        Tuple or list of strings.
    env: object
        Dictionary of strings mapping to strings.

Execute an executable path with arguments, replacing current process.
[clinic start generated code]*/

static PyObject *
os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
/*[clinic end generated code: output=ff9fa8e4da8bde58 input=626804fa092606d9]*/
{
    EXECV_CHAR **argvlist = NULL;
    EXECV_CHAR **envlist;
    Py_ssize_t argc, envc;

    PyInterpreterState *interp = _PyInterpreterState_GET();
    if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) {
        PyErr_SetString(PyExc_RuntimeError,
                        "exec not supported for isolated subinterpreters");
        return NULL;
    }

    /* execve has three arguments: (path, argv, env), where
       argv is a list or tuple of strings and env is a dictionary
       like posix.environ. */

    if (!PyList_Check(argv) && !PyTuple_Check(argv)) {
        PyErr_SetString(PyExc_TypeError,
                        "execve: argv must be a tuple or list");
        goto fail_0;
    }
    argc = PySequence_Size(argv);
    if (argc < 1) {
        PyErr_SetString(PyExc_ValueError, "execve: argv must not be empty");
        return NULL;
    }

    if (!PyMapping_Check(env)) {
        PyErr_SetString(PyExc_TypeError,
                        "execve: environment must be a mapping object");
        goto fail_0;
    }

    argvlist = parse_arglist(argv, &argc);
    if (argvlist == NULL) {
        goto fail_0;
    }
    if (!argvlist[0][0]) {
        PyErr_SetString(PyExc_ValueError,
            "execve: argv first element cannot be empty");
        goto fail_0;
    }

    envlist = parse_envlist(env, &envc);
    if (envlist == NULL)
        goto fail_0;

    if (PySys_Audit("os.exec", "OOO", path->object, argv, env) < 0) {
        goto fail_1;
    }

    _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_FEXECVE
    if (path->fd > -1)
        fexecve(path->fd, argvlist, envlist);
    else
#endif
#ifdef HAVE_WEXECV
        _wexecve(path->wide, argvlist, envlist);
#else
        execve(path->narrow, argvlist, envlist);
#endif
    _Py_END_SUPPRESS_IPH

    /* If we get here it's definitely an error */

    posix_path_error(path);
  fail_1:
    free_string_array(envlist, envc);
  fail_0:
    if (argvlist)
        free_string_array(argvlist, argc);
    return NULL;
}

#endif /* HAVE_EXECV */

#ifdef HAVE_POSIX_SPAWN

enum posix_spawn_file_actions_identifier {
    POSIX_SPAWN_OPEN,
    POSIX_SPAWN_CLOSE,
    POSIX_SPAWN_DUP2
#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP
    ,POSIX_SPAWN_CLOSEFROM
#endif
};

#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
static int
convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res);
#endif

static int
parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpgroup,
                        int resetids, int setsid, PyObject *setsigmask,
                        PyObject *setsigdef, PyObject *scheduler,
                        posix_spawnattr_t *attrp)
{
    long all_flags = 0;

    errno = posix_spawnattr_init(attrp);
    if (errno) {
        posix_error();
        return -1;
    }

    if (setpgroup) {
        pid_t pgid = PyLong_AsPid(setpgroup);
        if (pgid == (pid_t)-1 && PyErr_Occurred()) {
            goto fail;
        }
        errno = posix_spawnattr_setpgroup(attrp, pgid);
        if (errno) {
            posix_error();
            goto fail;
        }
        all_flags |= POSIX_SPAWN_SETPGROUP;
    }

    if (resetids) {
        all_flags |= POSIX_SPAWN_RESETIDS;
    }

    if (setsid) {
#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
        if (HAVE_POSIX_SPAWN_SETSID_RUNTIME) {
#endif
#ifdef POSIX_SPAWN_SETSID
        all_flags |= POSIX_SPAWN_SETSID;
#elif defined(POSIX_SPAWN_SETSID_NP)
        all_flags |= POSIX_SPAWN_SETSID_NP;
#else
        argument_unavailable_error(func_name, "setsid");
        return -1;
#endif

#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
        } else {
            argument_unavailable_error(func_name, "setsid");
            return -1;
        }
#endif /* HAVE_POSIX_SPAWN_SETSID_RUNTIME */

    }

#ifdef HAVE_SIGSET_T
   if (setsigmask) {
        sigset_t set;
        if (!_Py_Sigset_Converter(setsigmask, &set)) {
            goto fail;
        }
        errno = posix_spawnattr_setsigmask(attrp, &set);
        if (errno) {
            posix_error();
            goto fail;
        }
        all_flags |= POSIX_SPAWN_SETSIGMASK;
    }

    if (setsigdef) {
        sigset_t set;
        if (!_Py_Sigset_Converter(setsigdef, &set)) {
            goto fail;
        }
        errno = posix_spawnattr_setsigdefault(attrp, &set);
        if (errno) {
            posix_error();
            goto fail;
        }
        all_flags |= POSIX_SPAWN_SETSIGDEF;
    }
#else
    if (setsigmask || setsigdef) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "sigset is not supported on this platform");
        goto fail;
    }
#endif

    if (scheduler) {
#ifdef POSIX_SPAWN_SETSCHEDULER
        PyObject *py_schedpolicy;
        PyObject *schedparam_obj;
        struct sched_param schedparam;

        if (!PyArg_ParseTuple(scheduler, "OO"
                        ";A scheduler tuple must have two elements",
                        &py_schedpolicy, &schedparam_obj)) {
            goto fail;
        }
        if (!convert_sched_param(module, schedparam_obj, &schedparam)) {
            goto fail;
        }
        if (py_schedpolicy != Py_None) {
            int schedpolicy = PyLong_AsInt(py_schedpolicy);

            if (schedpolicy == -1 && PyErr_Occurred()) {
                goto fail;
            }
            errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy);
            if (errno) {
                posix_error();
                goto fail;
            }
            all_flags |= POSIX_SPAWN_SETSCHEDULER;
        }
        errno = posix_spawnattr_setschedparam(attrp, &schedparam);
        if (errno) {
            posix_error();
            goto fail;
        }
        all_flags |= POSIX_SPAWN_SETSCHEDPARAM;
#else
        PyErr_SetString(PyExc_NotImplementedError,
                "The scheduler option is not supported in this system.");
        goto fail;
#endif
    }

    errno = posix_spawnattr_setflags(attrp, all_flags);
    if (errno) {
        posix_error();
        goto fail;
    }

    return 0;

fail:
    (void)posix_spawnattr_destroy(attrp);
    return -1;
}

static int
parse_file_actions(PyObject *file_actions,
                   posix_spawn_file_actions_t *file_actionsp,
                   PyObject *temp_buffer)
{
    PyObject *seq;
    PyObject *file_action = NULL;
    PyObject *tag_obj;

    seq = PySequence_Fast(file_actions,
                          "file_actions must be a sequence or None");
    if (seq == NULL) {
        return -1;
    }

    errno = posix_spawn_file_actions_init(file_actionsp);
    if (errno) {
        posix_error();
        Py_DECREF(seq);
        return -1;
    }

    for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) {
        file_action = PySequence_Fast_GET_ITEM(seq, i);
        Py_INCREF(file_action);
        if (!PyTuple_Check(file_action) || !PyTuple_GET_SIZE(file_action)) {
            PyErr_SetString(PyExc_TypeError,
                "Each file_actions element must be a non-empty tuple");
            goto fail;
        }
        long tag = PyLong_AsLong(PyTuple_GET_ITEM(file_action, 0));
        if (tag == -1 && PyErr_Occurred()) {
            goto fail;
        }

        /* Populate the file_actions object */
        switch (tag) {
            case POSIX_SPAWN_OPEN: {
                int fd, oflag;
                PyObject *path;
                unsigned long mode;
                if (!PyArg_ParseTuple(file_action, "OiO&ik"
                        ";A open file_action tuple must have 5 elements",
                        &tag_obj, &fd, PyUnicode_FSConverter, &path,
                        &oflag, &mode))
                {
                    goto fail;
                }
                if (PyList_Append(temp_buffer, path)) {
                    Py_DECREF(path);
                    goto fail;
                }
                errno = posix_spawn_file_actions_addopen(file_actionsp,
                        fd, PyBytes_AS_STRING(path), oflag, (mode_t)mode);
                if (errno) {
                    posix_error();
                    Py_DECREF(path);
                    goto fail;
                }
                Py_DECREF(path);
                break;
            }
            case POSIX_SPAWN_CLOSE: {
                int fd;
                if (!PyArg_ParseTuple(file_action, "Oi"
                        ";A close file_action tuple must have 2 elements",
                        &tag_obj, &fd))
                {
                    goto fail;
                }
                errno = posix_spawn_file_actions_addclose(file_actionsp, fd);
                if (errno) {
                    posix_error();
                    goto fail;
                }
                break;
            }
            case POSIX_SPAWN_DUP2: {
                int fd1, fd2;
                if (!PyArg_ParseTuple(file_action, "Oii"
                        ";A dup2 file_action tuple must have 3 elements",
                        &tag_obj, &fd1, &fd2))
                {
                    goto fail;
                }
                errno = posix_spawn_file_actions_adddup2(file_actionsp,
                                                         fd1, fd2);
                if (errno) {
                    posix_error();
                    goto fail;
                }
                break;
            }
#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP
            case POSIX_SPAWN_CLOSEFROM: {
                int fd;
                if (!PyArg_ParseTuple(file_action, "Oi"
                        ";A closefrom file_action tuple must have 2 elements",
                        &tag_obj, &fd))
                {
                    goto fail;
                }
                errno = posix_spawn_file_actions_addclosefrom_np(file_actionsp,
                                                                 fd);
                if (errno) {
                    posix_error();
                    goto fail;
                }
                break;
            }
#endif
            default: {
                PyErr_SetString(PyExc_TypeError,
                                "Unknown file_actions identifier");
                goto fail;
            }
        }
        Py_DECREF(file_action);
    }

    Py_DECREF(seq);
    return 0;

fail:
    Py_DECREF(seq);
    Py_DECREF(file_action);
    (void)posix_spawn_file_actions_destroy(file_actionsp);
    return -1;
}


static PyObject *
py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv,
               PyObject *env, PyObject *file_actions,
               PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask,
               PyObject *setsigdef, PyObject *scheduler)
{
    const char *func_name = use_posix_spawnp ? "posix_spawnp" : "posix_spawn";
    EXECV_CHAR **argvlist = NULL;
    EXECV_CHAR **envlist = NULL;
    posix_spawn_file_actions_t file_actions_buf;
    posix_spawn_file_actions_t *file_actionsp = NULL;
    posix_spawnattr_t attr;
    posix_spawnattr_t *attrp = NULL;
    Py_ssize_t argc, envc;
    PyObject *result = NULL;
    PyObject *temp_buffer = NULL;
    pid_t pid;
    int err_code;

    /* posix_spawn and posix_spawnp have three arguments: (path, argv, env), where
       argv is a list or tuple of strings and env is a dictionary
       like posix.environ. */

    if (!PyList_Check(argv) && !PyTuple_Check(argv)) {
        PyErr_Format(PyExc_TypeError,
                     "%s: argv must be a tuple or list", func_name);
        goto exit;
    }
    argc = PySequence_Size(argv);
    if (argc < 1) {
        PyErr_Format(PyExc_ValueError,
                     "%s: argv must not be empty", func_name);
        goto exit;
    }

    if (!PyMapping_Check(env) && env != Py_None) {
        PyErr_Format(PyExc_TypeError,
                     "%s: environment must be a mapping object or None", func_name);
        goto exit;
    }

    argvlist = parse_arglist(argv, &argc);
    if (argvlist == NULL) {
        goto exit;
    }
    if (!argvlist[0][0]) {
        PyErr_Format(PyExc_ValueError,
                     "%s: argv first element cannot be empty", func_name);
        goto exit;
    }

#ifdef USE_DARWIN_NS_GET_ENVIRON
    // There is no environ global in this situation.
    char **environ = NULL;
#endif

    if (env == Py_None) {
#ifdef USE_DARWIN_NS_GET_ENVIRON
        environ = *_NSGetEnviron();
#endif
        envlist = environ;
    } else {
        envlist = parse_envlist(env, &envc);
        if (envlist == NULL) {
            goto exit;
        }
    }

    if (file_actions != NULL && file_actions != Py_None) {
        /* There is a bug in old versions of glibc that makes some of the
         * helper functions for manipulating file actions not copy the provided
         * buffers. The problem is that posix_spawn_file_actions_addopen does not
         * copy the value of path for some old versions of glibc (<2.20).
         * The use of temp_buffer here is a workaround that keeps the
         * python objects that own the buffers alive until posix_spawn gets called.
         * Check https://bugs.python.org/issue33630 and
         * https://sourceware.org/bugzilla/show_bug.cgi?id=17048 for more info.*/
        temp_buffer = PyList_New(0);
        if (!temp_buffer) {
            goto exit;
        }
        if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer)) {
            goto exit;
        }
        file_actionsp = &file_actions_buf;
    }

    if (parse_posix_spawn_flags(module, func_name, setpgroup, resetids, setsid,
                                setsigmask, setsigdef, scheduler, &attr)) {
        goto exit;
    }
    attrp = &attr;

    if (PySys_Audit("os.posix_spawn", "OOO", path->object, argv, env) < 0) {
        goto exit;
    }

    _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_POSIX_SPAWNP
    if (use_posix_spawnp) {
        err_code = posix_spawnp(&pid, path->narrow,
                                file_actionsp, attrp, argvlist, envlist);
    }
    else
#endif /* HAVE_POSIX_SPAWNP */
    {
        err_code = posix_spawn(&pid, path->narrow,
                               file_actionsp, attrp, argvlist, envlist);
    }
    _Py_END_SUPPRESS_IPH

    if (err_code) {
        errno = err_code;
        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
        goto exit;
    }
#ifdef _Py_MEMORY_SANITIZER
    __msan_unpoison(&pid, sizeof(pid));
#endif
    result = PyLong_FromPid(pid);

exit:
    if (file_actionsp) {
        (void)posix_spawn_file_actions_destroy(file_actionsp);
    }
    if (attrp) {
        (void)posix_spawnattr_destroy(attrp);
    }
    if (envlist && envlist != environ) {
        free_string_array(envlist, envc);
    }
    if (argvlist) {
        free_string_array(argvlist, argc);
    }
    Py_XDECREF(temp_buffer);
    return result;
}


/*[clinic input]

os.posix_spawn
    path: path_t
        Path of executable file.
    argv: object
        Tuple or list of strings.
    env: object
        Dictionary of strings mapping to strings.
    /
    *
    file_actions: object(c_default='NULL') = ()
        A sequence of file action tuples.
    setpgroup: object = NULL
        The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
    resetids: bool = False
        If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.
    setsid: bool = False
        If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.
    setsigmask: object(c_default='NULL') = ()
        The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
    setsigdef: object(c_default='NULL') = ()
        The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.
    scheduler: object = NULL
        A tuple with the scheduler policy (optional) and parameters.

Execute the program specified by path in a new process.
[clinic start generated code]*/

static PyObject *
os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
                    PyObject *env, PyObject *file_actions,
                    PyObject *setpgroup, int resetids, int setsid,
                    PyObject *setsigmask, PyObject *setsigdef,
                    PyObject *scheduler)
/*[clinic end generated code: output=14a1098c566bc675 input=808aed1090d84e33]*/
{
    return py_posix_spawn(0, module, path, argv, env, file_actions,
                          setpgroup, resetids, setsid, setsigmask, setsigdef,
                          scheduler);
}
 #endif /* HAVE_POSIX_SPAWN */



#ifdef HAVE_POSIX_SPAWNP
/*[clinic input]

os.posix_spawnp
    path: path_t
        Path of executable file.
    argv: object
        Tuple or list of strings.
    env: object
        Dictionary of strings mapping to strings.
    /
    *
    file_actions: object(c_default='NULL') = ()
        A sequence of file action tuples.
    setpgroup: object = NULL
        The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
    resetids: bool = False
        If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.
    setsid: bool = False
        If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.
    setsigmask: object(c_default='NULL') = ()
        The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
    setsigdef: object(c_default='NULL') = ()
        The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.
    scheduler: object = NULL
        A tuple with the scheduler policy (optional) and parameters.

Execute the program specified by path in a new process.
[clinic start generated code]*/

static PyObject *
os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
                     PyObject *env, PyObject *file_actions,
                     PyObject *setpgroup, int resetids, int setsid,
                     PyObject *setsigmask, PyObject *setsigdef,
                     PyObject *scheduler)
/*[clinic end generated code: output=7b9aaefe3031238d input=9e89e616116752a1]*/
{
    return py_posix_spawn(1, module, path, argv, env, file_actions,
                          setpgroup, resetids, setsid, setsigmask, setsigdef,
                          scheduler);
}
#endif /* HAVE_POSIX_SPAWNP */

#ifdef HAVE_RTPSPAWN
static intptr_t
_rtp_spawn(int mode, const char *rtpFileName, const char *argv[],
               const char  *envp[])
{
     RTP_ID rtpid;
     int status;
     pid_t res;
     int async_err = 0;

     /* Set priority=100 and uStackSize=16 MiB (0x1000000) for new processes.
        uStackSize=0 cannot be used, the default stack size is too small for
        Python. */
     if (envp) {
         rtpid = rtpSpawn(rtpFileName, argv, envp,
                          100, 0x1000000, 0, VX_FP_TASK);
     }
     else {
         rtpid = rtpSpawn(rtpFileName, argv, (const char **)environ,
                          100, 0x1000000, 0, VX_FP_TASK);
     }
     if ((rtpid != RTP_ID_ERROR) && (mode == _P_WAIT)) {
         do {
             res = waitpid((pid_t)rtpid, &status, 0);
         } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

         if (res < 0)
             return RTP_ID_ERROR;
         return ((intptr_t)status);
     }
     return ((intptr_t)rtpid);
}
#endif

#if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV) || defined(HAVE_RTPSPAWN)
/*[clinic input]
os.spawnv

    mode: int
        Mode of process creation.
    path: path_t
        Path of executable file.
    argv: object
        Tuple or list of strings.
    /

Execute the program specified by path in a new process.
[clinic start generated code]*/

static PyObject *
os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv)
/*[clinic end generated code: output=71cd037a9d96b816 input=43224242303291be]*/
{
    EXECV_CHAR **argvlist;
    int i;
    Py_ssize_t argc;
    intptr_t spawnval;
    PyObject *(*getitem)(PyObject *, Py_ssize_t);

    /* spawnv has three arguments: (mode, path, argv), where
       argv is a list or tuple of strings. */

    if (PyList_Check(argv)) {
        argc = PyList_Size(argv);
        getitem = PyList_GetItem;
    }
    else if (PyTuple_Check(argv)) {
        argc = PyTuple_Size(argv);
        getitem = PyTuple_GetItem;
    }
    else {
        PyErr_SetString(PyExc_TypeError,
                        "spawnv() arg 2 must be a tuple or list");
        return NULL;
    }
    if (argc == 0) {
        PyErr_SetString(PyExc_ValueError,
            "spawnv() arg 2 cannot be empty");
        return NULL;
    }

    argvlist = PyMem_NEW(EXECV_CHAR *, argc+1);
    if (argvlist == NULL) {
        return PyErr_NoMemory();
    }
    for (i = 0; i < argc; i++) {
        if (!fsconvert_strdup((*getitem)(argv, i),
                              &argvlist[i])) {
            free_string_array(argvlist, i);
            PyErr_SetString(
                PyExc_TypeError,
                "spawnv() arg 2 must contain only strings");
            return NULL;
        }
        if (i == 0 && !argvlist[0][0]) {
            free_string_array(argvlist, i + 1);
            PyErr_SetString(
                PyExc_ValueError,
                "spawnv() arg 2 first element cannot be empty");
            return NULL;
        }
    }
    argvlist[argc] = NULL;

#if !defined(HAVE_RTPSPAWN)
    if (mode == _OLD_P_OVERLAY)
        mode = _P_OVERLAY;
#endif

    if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv,
                    Py_None) < 0) {
        free_string_array(argvlist, argc);
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_WSPAWNV
    spawnval = _wspawnv(mode, path->wide, argvlist);
#elif defined(HAVE_RTPSPAWN)
    spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist, NULL);
#else
    spawnval = _spawnv(mode, path->narrow, argvlist);
#endif
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS

    int saved_errno = errno;
    free_string_array(argvlist, argc);

    if (spawnval == -1) {
        errno = saved_errno;
        posix_error();
        return NULL;
    }
    return Py_BuildValue(_Py_PARSE_INTPTR, spawnval);
}

/*[clinic input]
os.spawnve

    mode: int
        Mode of process creation.
    path: path_t
        Path of executable file.
    argv: object
        Tuple or list of strings.
    env: object
        Dictionary of strings mapping to strings.
    /

Execute the program specified by path in a new process.
[clinic start generated code]*/

static PyObject *
os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv,
                PyObject *env)
/*[clinic end generated code: output=30fe85be56fe37ad input=3e40803ee7c4c586]*/
{
    EXECV_CHAR **argvlist;
    EXECV_CHAR **envlist;
    PyObject *res = NULL;
    Py_ssize_t argc, i, envc;
    intptr_t spawnval;
    PyObject *(*getitem)(PyObject *, Py_ssize_t);
    Py_ssize_t lastarg = 0;

    /* spawnve has four arguments: (mode, path, argv, env), where
       argv is a list or tuple of strings and env is a dictionary
       like posix.environ. */

    if (PyList_Check(argv)) {
        argc = PyList_Size(argv);
        getitem = PyList_GetItem;
    }
    else if (PyTuple_Check(argv)) {
        argc = PyTuple_Size(argv);
        getitem = PyTuple_GetItem;
    }
    else {
        PyErr_SetString(PyExc_TypeError,
                        "spawnve() arg 2 must be a tuple or list");
        goto fail_0;
    }
    if (argc == 0) {
        PyErr_SetString(PyExc_ValueError,
            "spawnve() arg 2 cannot be empty");
        goto fail_0;
    }
    if (!PyMapping_Check(env)) {
        PyErr_SetString(PyExc_TypeError,
                        "spawnve() arg 3 must be a mapping object");
        goto fail_0;
    }

    argvlist = PyMem_NEW(EXECV_CHAR *, argc+1);
    if (argvlist == NULL) {
        PyErr_NoMemory();
        goto fail_0;
    }
    for (i = 0; i < argc; i++) {
        if (!fsconvert_strdup((*getitem)(argv, i),
                              &argvlist[i]))
        {
            lastarg = i;
            goto fail_1;
        }
        if (i == 0 && !argvlist[0][0]) {
            lastarg = i + 1;
            PyErr_SetString(
                PyExc_ValueError,
                "spawnv() arg 2 first element cannot be empty");
            goto fail_1;
        }
    }
    lastarg = argc;
    argvlist[argc] = NULL;

    envlist = parse_envlist(env, &envc);
    if (envlist == NULL)
        goto fail_1;

#if !defined(HAVE_RTPSPAWN)
    if (mode == _OLD_P_OVERLAY)
        mode = _P_OVERLAY;
#endif

    if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv, env) < 0) {
        goto fail_2;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_WSPAWNV
    spawnval = _wspawnve(mode, path->wide, argvlist, envlist);
#elif defined(HAVE_RTPSPAWN)
    spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist,
                           (const char **)envlist);
#else
    spawnval = _spawnve(mode, path->narrow, argvlist, envlist);
#endif
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS

    if (spawnval == -1)
        (void) posix_error();
    else
        res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval);

  fail_2:
    while (--envc >= 0) {
        PyMem_Free(envlist[envc]);
    }
    PyMem_Free(envlist);
  fail_1:
    free_string_array(argvlist, lastarg);
  fail_0:
    return res;
}

#endif /* HAVE_SPAWNV */

#ifdef HAVE_FORK

/* Helper function to validate arguments.
   Returns 0 on success.  non-zero on failure with a TypeError raised.
   If obj is non-NULL it must be callable.  */
static int
check_null_or_callable(PyObject *obj, const char* obj_name)
{
    if (obj && !PyCallable_Check(obj)) {
        PyErr_Format(PyExc_TypeError, "'%s' must be callable, not %s",
                     obj_name, _PyType_Name(Py_TYPE(obj)));
        return -1;
    }
    return 0;
}

/*[clinic input]
os.register_at_fork

    *
    before: object=NULL
        A callable to be called in the parent before the fork() syscall.
    after_in_child: object=NULL
        A callable to be called in the child after fork().
    after_in_parent: object=NULL
        A callable to be called in the parent after fork().

Register callables to be called when forking a new process.

'before' callbacks are called in reverse order.
'after_in_child' and 'after_in_parent' callbacks are called in order.

[clinic start generated code]*/

static PyObject *
os_register_at_fork_impl(PyObject *module, PyObject *before,
                         PyObject *after_in_child, PyObject *after_in_parent)
/*[clinic end generated code: output=5398ac75e8e97625 input=cd1187aa85d2312e]*/
{
    PyInterpreterState *interp;

    if (!before && !after_in_child && !after_in_parent) {
        PyErr_SetString(PyExc_TypeError, "At least one argument is required.");
        return NULL;
    }
    if (check_null_or_callable(before, "before") ||
        check_null_or_callable(after_in_child, "after_in_child") ||
        check_null_or_callable(after_in_parent, "after_in_parent")) {
        return NULL;
    }
    interp = _PyInterpreterState_GET();

    if (register_at_forker(&interp->before_forkers, before)) {
        return NULL;
    }
    if (register_at_forker(&interp->after_forkers_child, after_in_child)) {
        return NULL;
    }
    if (register_at_forker(&interp->after_forkers_parent, after_in_parent)) {
        return NULL;
    }
    Py_RETURN_NONE;
}
#endif /* HAVE_FORK */

#if defined(HAVE_FORK1) || defined(HAVE_FORKPTY) || defined(HAVE_FORK)
// Common code to raise a warning if we detect there is more than one thread
// running in the process. Best effort, silent if unable to count threads.
// Constraint: Quick. Never overcounts. Never leaves an error set.
//
// This MUST only be called from the parent process after
// PyOS_AfterFork_Parent().
static int
warn_about_fork_with_threads(
    const char* name,  // Name of the API to use in the warning message.
    const Py_ssize_t num_os_threads  // Only trusted when >= 1.
)
{
    // It's not safe to issue the warning while the world is stopped, because
    // other threads might be holding locks that we need, which would deadlock.
    assert(!_PyRuntime.stoptheworld.world_stopped);

    Py_ssize_t num_python_threads = num_os_threads;
    if (num_python_threads <= 0) {
        // Fall back to just the number our threading module knows about.
        // An incomplete view of the world, but better than nothing.
        PyObject *threading = PyImport_GetModule(&_Py_ID(threading));
        if (!threading) {
            PyErr_Clear();
            return 0;
        }
        PyObject *threading_active =
                PyObject_GetAttr(threading, &_Py_ID(_active));
        if (!threading_active) {
            PyErr_Clear();
            Py_DECREF(threading);
            return 0;
        }
        PyObject *threading_limbo =
                PyObject_GetAttr(threading, &_Py_ID(_limbo));
        if (!threading_limbo) {
            PyErr_Clear();
            Py_DECREF(threading);
            Py_DECREF(threading_active);
            return 0;
        }
        Py_DECREF(threading);
        // Duplicating what threading.active_count() does but without holding
        // threading._active_limbo_lock so our count could be inaccurate if
        // these dicts are mid-update from another thread.  Not a big deal.
        // Worst case if someone replaced threading._active or threading._limbo
        // with non-dicts, we get -1 from *Length() below and undercount.
        // Nobody should, but we're best effort so we clear errors and move on.
        num_python_threads = (PyMapping_Length(threading_active)
                              + PyMapping_Length(threading_limbo));
        PyErr_Clear();
        Py_DECREF(threading_active);
        Py_DECREF(threading_limbo);
    }
    if (num_python_threads > 1) {
        return PyErr_WarnFormat(
                PyExc_DeprecationWarning, 1,
#ifdef HAVE_GETPID
                "This process (pid=%d) is multi-threaded, "
#else
                "This process is multi-threaded, "
#endif
                "use of %s() may lead to deadlocks in the child.",
#ifdef HAVE_GETPID
                getpid(),
#endif
                name);
    }
    return 0;
}

// If this returns <= 0, we were unable to successfully use any OS APIs.
// Returns a positive number of threads otherwise.
static Py_ssize_t get_number_of_os_threads(void)
{
    // TODO: Consider making an `os` module API to return the current number
    // of threads in the process. That'd presumably use this platform code but
    // raise an error rather than using the inaccurate fallback.
    Py_ssize_t num_python_threads = 0;
#if defined(__APPLE__) && defined(HAVE_GETPID)
    mach_port_t macos_self = mach_task_self();
    mach_port_t macos_task;
    if (task_for_pid(macos_self, getpid(), &macos_task) == KERN_SUCCESS) {
        thread_array_t macos_threads;
        mach_msg_type_number_t macos_n_threads;
        if (task_threads(macos_task, &macos_threads,
                         &macos_n_threads) == KERN_SUCCESS) {
            num_python_threads = macos_n_threads;
        }
    }
#elif defined(__linux__)
    // Linux /proc/self/stat 20th field is the number of threads.
    FILE* proc_stat = fopen("/proc/self/stat", "r");
    if (proc_stat) {
        size_t n;
        // Size chosen arbitrarily. ~60% more bytes than a 20th column index
        // observed on the author's workstation.
        char stat_line[160];
        n = fread(&stat_line, 1, 159, proc_stat);
        stat_line[n] = '\0';
        fclose(proc_stat);

        char *saveptr = NULL;
        char *field = strtok_r(stat_line, " ", &saveptr);
        unsigned int idx;
        for (idx = 19; idx && field; --idx) {
            field = strtok_r(NULL, " ", &saveptr);
        }
        if (idx == 0 && field) {  // found the 20th field
            num_python_threads = atoi(field);  // 0 on error
        }
    }
#endif
    return num_python_threads;
}
#endif  // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK

#ifdef HAVE_FORK1
/*[clinic input]
os.fork1

Fork a child process with a single multiplexed (i.e., not bound) thread.

Return 0 to child process and PID of child to parent process.
[clinic start generated code]*/

static PyObject *
os_fork1_impl(PyObject *module)
/*[clinic end generated code: output=0de8e67ce2a310bc input=12db02167893926e]*/
{
    pid_t pid;

    PyInterpreterState *interp = _PyInterpreterState_GET();
    if (_PyInterpreterState_GetFinalizing(interp) != NULL) {
        PyErr_SetString(PyExc_PythonFinalizationError,
                        "can't fork at interpreter shutdown");
        return NULL;
    }
    if (!_Py_IsMainInterpreter(interp)) {
        PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
        return NULL;
    }
    PyOS_BeforeFork();
    pid = fork1();
    int saved_errno = errno;
    if (pid == 0) {
        /* child: this clobbers and resets the import lock. */
        PyOS_AfterFork_Child();
    } else {
        // Called before AfterFork_Parent in case those hooks start threads.
        Py_ssize_t num_os_threads = get_number_of_os_threads();
        /* parent: release the import lock. */
        PyOS_AfterFork_Parent();
        // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
        if (warn_about_fork_with_threads("fork1", num_os_threads) < 0) {
            return NULL;
        }
    }
    if (pid == -1) {
        errno = saved_errno;
        return posix_error();
    }
    return PyLong_FromPid(pid);
}
#endif /* HAVE_FORK1 */


#ifdef HAVE_FORK
/*[clinic input]
os.fork

Fork a child process.

Return 0 to child process and PID of child to parent process.
[clinic start generated code]*/

static PyObject *
os_fork_impl(PyObject *module)
/*[clinic end generated code: output=3626c81f98985d49 input=13c956413110eeaa]*/
{
    pid_t pid;
    PyInterpreterState *interp = _PyInterpreterState_GET();
    if (_PyInterpreterState_GetFinalizing(interp) != NULL) {
        PyErr_SetString(PyExc_PythonFinalizationError,
                        "can't fork at interpreter shutdown");
        return NULL;
    }
    if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_FORK)) {
        PyErr_SetString(PyExc_RuntimeError,
                        "fork not supported for isolated subinterpreters");
        return NULL;
    }
    if (PySys_Audit("os.fork", NULL) < 0) {
        return NULL;
    }
    PyOS_BeforeFork();
    pid = fork();
    int saved_errno = errno;
    if (pid == 0) {
        /* child: this clobbers and resets the import lock. */
        PyOS_AfterFork_Child();
    } else {
        // Called before AfterFork_Parent in case those hooks start threads.
        Py_ssize_t num_os_threads = get_number_of_os_threads();
        /* parent: release the import lock. */
        PyOS_AfterFork_Parent();
        // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
        if (warn_about_fork_with_threads("fork", num_os_threads) < 0)
            return NULL;
    }
    if (pid == -1) {
        errno = saved_errno;
        return posix_error();
    }
    return PyLong_FromPid(pid);
}
#endif /* HAVE_FORK */


#ifdef HAVE_SCHED_H
#ifdef HAVE_SCHED_GET_PRIORITY_MAX
/*[clinic input]
os.sched_get_priority_max

    policy: int

Get the maximum scheduling priority for policy.
[clinic start generated code]*/

static PyObject *
os_sched_get_priority_max_impl(PyObject *module, int policy)
/*[clinic end generated code: output=9e465c6e43130521 input=2097b7998eca6874]*/
{
    /* make sure that errno is cleared before the call */
    errno = 0;
    int max = sched_get_priority_max(policy);
    if (max == -1 && errno)
        return posix_error();
    return PyLong_FromLong(max);
}


/*[clinic input]
os.sched_get_priority_min

    policy: int

Get the minimum scheduling priority for policy.
[clinic start generated code]*/

static PyObject *
os_sched_get_priority_min_impl(PyObject *module, int policy)
/*[clinic end generated code: output=7595c1138cc47a6d input=21bc8fa0d70983bf]*/
{
    /* make sure that errno is cleared before the call */
    errno = 0;
    int min = sched_get_priority_min(policy);
    if (min == -1 && errno)
        return posix_error();
    return PyLong_FromLong(min);
}
#endif /* HAVE_SCHED_GET_PRIORITY_MAX */


#ifdef HAVE_SCHED_SETSCHEDULER
/*[clinic input]
os.sched_getscheduler
    pid: pid_t
    /

Get the scheduling policy for the process identified by pid.

Passing 0 for pid returns the scheduling policy for the calling process.
[clinic start generated code]*/

static PyObject *
os_sched_getscheduler_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=dce4c0bd3f1b34c8 input=8d99dac505485ac8]*/
{
    int policy;

    policy = sched_getscheduler(pid);
    if (policy < 0)
        return posix_error();
    return PyLong_FromLong(policy);
}
#endif /* HAVE_SCHED_SETSCHEDULER */


#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
/*[clinic input]
class os.sched_param "PyObject *" "SchedParamType"

@classmethod
os.sched_param.__new__

    sched_priority: object
        A scheduling parameter.

Currently has only one field: sched_priority
[clinic start generated code]*/

static PyObject *
os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority)
/*[clinic end generated code: output=48f4067d60f48c13 input=eb42909a2c0e3e6c]*/
{
    PyObject *res;

    res = PyStructSequence_New(type);
    if (!res)
        return NULL;
    PyStructSequence_SET_ITEM(res, 0, Py_NewRef(sched_priority));
    return res;
}

static PyObject *
os_sched_param_reduce(PyObject *self, PyObject *Py_UNUSED(dummy))
{
    return Py_BuildValue("(O(O))", Py_TYPE(self), PyStructSequence_GetItem(self, 0));
}

static PyMethodDef os_sched_param_reduce_method = {
    "__reduce__", os_sched_param_reduce, METH_NOARGS | METH_COEXIST, NULL,
};

PyDoc_VAR(os_sched_param__doc__);

static PyStructSequence_Field sched_param_fields[] = {
    {"sched_priority", "the scheduling priority"},
    {0}
};

static PyStructSequence_Desc sched_param_desc = {
    MODNAME ".sched_param", /* name */
    os_sched_param__doc__, /* doc */
    sched_param_fields,
    1
};

static int
convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res)
{
    long priority;

    if (!Py_IS_TYPE(param, (PyTypeObject *)get_posix_state(module)->SchedParamType)) {
        PyErr_SetString(PyExc_TypeError, "must have a sched_param object");
        return 0;
    }
    priority = PyLong_AsLong(PyStructSequence_GET_ITEM(param, 0));
    if (priority == -1 && PyErr_Occurred())
        return 0;
    if (priority > INT_MAX || priority < INT_MIN) {
        PyErr_SetString(PyExc_OverflowError, "sched_priority out of range");
        return 0;
    }
    res->sched_priority = Py_SAFE_DOWNCAST(priority, long, int);
    return 1;
}
#endif /* defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) */


#ifdef HAVE_SCHED_SETSCHEDULER
/*[clinic input]
os.sched_setscheduler

    pid: pid_t
    policy: int
    param as param_obj: object
    /

Set the scheduling policy for the process identified by pid.

If pid is 0, the calling process is changed.
param is an instance of sched_param.
[clinic start generated code]*/

static PyObject *
os_sched_setscheduler_impl(PyObject *module, pid_t pid, int policy,
                           PyObject *param_obj)
/*[clinic end generated code: output=cde27faa55dc993e input=73013d731bd8fbe9]*/
{
    struct sched_param param;
    if (!convert_sched_param(module, param_obj, &param)) {
        return NULL;
    }

    /*
    ** sched_setscheduler() returns 0 in Linux, but the previous
    ** scheduling policy under Solaris/Illumos, and others.
    ** On error, -1 is returned in all Operating Systems.
    */
    if (sched_setscheduler(pid, policy, &param) == -1)
        return posix_error();
    Py_RETURN_NONE;
}
#endif  /* HAVE_SCHED_SETSCHEDULER*/


#ifdef HAVE_SCHED_SETPARAM
/*[clinic input]
os.sched_getparam
    pid: pid_t
    /

Returns scheduling parameters for the process identified by pid.

If pid is 0, returns parameters for the calling process.
Return value is an instance of sched_param.
[clinic start generated code]*/

static PyObject *
os_sched_getparam_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=b194e8708dcf2db8 input=18a1ef9c2efae296]*/
{
    struct sched_param param;
    PyObject *result;
    PyObject *priority;

    if (sched_getparam(pid, &param))
        return posix_error();
    PyObject *SchedParamType = get_posix_state(module)->SchedParamType;
    result = PyStructSequence_New((PyTypeObject *)SchedParamType);
    if (!result)
        return NULL;
    priority = PyLong_FromLong(param.sched_priority);
    if (!priority) {
        Py_DECREF(result);
        return NULL;
    }
    PyStructSequence_SET_ITEM(result, 0, priority);
    return result;
}


/*[clinic input]
os.sched_setparam
    pid: pid_t
    param as param_obj: object
    /

Set scheduling parameters for the process identified by pid.

If pid is 0, sets parameters for the calling process.
param should be an instance of sched_param.
[clinic start generated code]*/

static PyObject *
os_sched_setparam_impl(PyObject *module, pid_t pid, PyObject *param_obj)
/*[clinic end generated code: output=f19fe020a53741c1 input=27b98337c8b2dcc7]*/
{
    struct sched_param param;
    if (!convert_sched_param(module, param_obj, &param)) {
        return NULL;
    }

    if (sched_setparam(pid, &param))
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SCHED_SETPARAM */


#ifdef HAVE_SCHED_RR_GET_INTERVAL
/*[clinic input]
@permit_long_summary
os.sched_rr_get_interval -> double
    pid: pid_t
    /

Return the round-robin quantum for the process identified by pid, in seconds.

Value returned is a float.
[clinic start generated code]*/

static double
os_sched_rr_get_interval_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=7e2d935833ab47dc input=cab0b83586776b10]*/
{
    struct timespec interval;
    if (sched_rr_get_interval(pid, &interval)) {
        posix_error();
        return -1.0;
    }
#ifdef _Py_MEMORY_SANITIZER
    __msan_unpoison(&interval, sizeof(interval));
#endif
    return (double)interval.tv_sec + 1e-9*interval.tv_nsec;
}
#endif /* HAVE_SCHED_RR_GET_INTERVAL */


/*[clinic input]
os.sched_yield

Voluntarily relinquish the CPU.
[clinic start generated code]*/

static PyObject *
os_sched_yield_impl(PyObject *module)
/*[clinic end generated code: output=902323500f222cac input=e54d6f98189391d4]*/
{
    int result;
    Py_BEGIN_ALLOW_THREADS
    result = sched_yield();
    Py_END_ALLOW_THREADS
    if (result < 0) {
        return posix_error();
    }
    Py_RETURN_NONE;
}

#ifdef HAVE_SCHED_SETAFFINITY
/* The minimum number of CPUs allocated in a cpu_set_t */
static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT;

/*[clinic input]
os.sched_setaffinity
    pid: pid_t
    mask : object
    /

Set the CPU affinity of the process identified by pid to mask.

mask should be an iterable of integers identifying CPUs.
[clinic start generated code]*/

static PyObject *
os_sched_setaffinity_impl(PyObject *module, pid_t pid, PyObject *mask)
/*[clinic end generated code: output=882d7dd9a229335b input=a0791a597c7085ba]*/
{
    int ncpus;
    size_t setsize;
    cpu_set_t *cpu_set = NULL;
    PyObject *iterator = NULL, *item;

    iterator = PyObject_GetIter(mask);
    if (iterator == NULL)
        return NULL;

    ncpus = NCPUS_START;
    setsize = CPU_ALLOC_SIZE(ncpus);
    cpu_set = CPU_ALLOC(ncpus);
    if (cpu_set == NULL) {
        PyErr_NoMemory();
        goto error;
    }
    CPU_ZERO_S(setsize, cpu_set);

    while ((item = PyIter_Next(iterator))) {
        long cpu;
        if (!PyIndex_Check(item)) {
            PyErr_Format(PyExc_TypeError,
                        "expected an iterator of ints, "
                        "but iterator yielded %R",
                        Py_TYPE(item));
            Py_DECREF(item);
            goto error;
        }
        cpu = PyLong_AsLong(item);
        Py_DECREF(item);
        if (cpu < 0) {
            if (!PyErr_Occurred())
                PyErr_SetString(PyExc_ValueError, "negative CPU number");
            goto error;
        }
        if (cpu > INT_MAX - 1) {
            PyErr_SetString(PyExc_OverflowError, "CPU number too large");
            goto error;
        }
        if (cpu >= ncpus) {
            /* Grow CPU mask to fit the CPU number */
            int newncpus = ncpus;
            cpu_set_t *newmask;
            size_t newsetsize;
            while (newncpus <= cpu) {
                if (newncpus > INT_MAX / 2)
                    newncpus = cpu + 1;
                else
                    newncpus = newncpus * 2;
            }
            newmask = CPU_ALLOC(newncpus);
            if (newmask == NULL) {
                PyErr_NoMemory();
                goto error;
            }
            newsetsize = CPU_ALLOC_SIZE(newncpus);
            CPU_ZERO_S(newsetsize, newmask);
            memcpy(newmask, cpu_set, setsize);
            CPU_FREE(cpu_set);
            setsize = newsetsize;
            cpu_set = newmask;
            ncpus = newncpus;
        }
        CPU_SET_S(cpu, setsize, cpu_set);
    }
    if (PyErr_Occurred()) {
        goto error;
    }
    Py_CLEAR(iterator);

    if (sched_setaffinity(pid, setsize, cpu_set)) {
        posix_error();
        goto error;
    }
    CPU_FREE(cpu_set);
    Py_RETURN_NONE;

error:
    if (cpu_set)
        CPU_FREE(cpu_set);
    Py_XDECREF(iterator);
    return NULL;
}


/*[clinic input]
@permit_long_summary
os.sched_getaffinity
    pid: pid_t
    /

Return the affinity of the process identified by pid (or the current process if zero).

The affinity is returned as a set of CPU identifiers.
[clinic start generated code]*/

static PyObject *
os_sched_getaffinity_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=f726f2c193c17a4f input=cb79ff13579ef091]*/
{
    int ncpus = NCPUS_START;
    size_t setsize;
    cpu_set_t *mask;

    while (1) {
        setsize = CPU_ALLOC_SIZE(ncpus);
        mask = CPU_ALLOC(ncpus);
        if (mask == NULL) {
            return PyErr_NoMemory();
        }
        if (sched_getaffinity(pid, setsize, mask) == 0) {
            break;
        }
        CPU_FREE(mask);
        if (errno != EINVAL) {
            return posix_error();
        }
        if (ncpus > INT_MAX / 2) {
            PyErr_SetString(PyExc_OverflowError,
                            "could not allocate a large enough CPU set");
            return NULL;
        }
        ncpus *= 2;
    }

    PyObject *res = PySet_New(NULL);
    if (res == NULL) {
        goto error;
    }

    int cpu = 0;
    int count = CPU_COUNT_S(setsize, mask);
    for (; count; cpu++) {
        if (CPU_ISSET_S(cpu, setsize, mask)) {
            PyObject *cpu_num = PyLong_FromLong(cpu);
            --count;
            if (cpu_num == NULL) {
                goto error;
            }
            if (PySet_Add(res, cpu_num)) {
                Py_DECREF(cpu_num);
                goto error;
            }
            Py_DECREF(cpu_num);
        }
    }
    CPU_FREE(mask);
    return res;

error:
    if (mask) {
        CPU_FREE(mask);
    }
    Py_XDECREF(res);
    return NULL;
}
#endif /* HAVE_SCHED_SETAFFINITY */

#endif /* HAVE_SCHED_H */


#ifdef HAVE_POSIX_OPENPT
/*[clinic input]
os.posix_openpt -> int

    oflag: int
    /

Open and return a file descriptor for a master pseudo-terminal device.

Performs a posix_openpt() C function call. The oflag argument is used to
set file status flags and file access modes as specified in the manual page
of posix_openpt() of your system.
[clinic start generated code]*/

static int
os_posix_openpt_impl(PyObject *module, int oflag)
/*[clinic end generated code: output=ee0bc2624305fc79 input=0de33d0e29693caa]*/
{
    int fd;

#if defined(O_CLOEXEC)
    oflag |= O_CLOEXEC;
#endif

    fd = posix_openpt(oflag);
    if (fd == -1) {
        posix_error();
        return -1;
    }

    // Just in case, likely a no-op given O_CLOEXEC above.
    if (_Py_set_inheritable(fd, 0, NULL) < 0) {
        close(fd);
        return -1;
    }

    return fd;
}
#endif /* HAVE_POSIX_OPENPT */

#ifdef HAVE_GRANTPT
/*[clinic input]
os.grantpt

    fd: fildes
        File descriptor of a master pseudo-terminal device.
    /

Grant access to the slave pseudo-terminal device.

Performs a grantpt() C function call.
[clinic start generated code]*/

static PyObject *
os_grantpt_impl(PyObject *module, int fd)
/*[clinic end generated code: output=dfd580015cf548ab input=0668e3b96760e849]*/
{
    int ret;
    int saved_errno;
    PyOS_sighandler_t sig_saved;

    sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL);

    ret = grantpt(fd);
    if (ret == -1)
        saved_errno = errno;

    PyOS_setsig(SIGCHLD, sig_saved);

    if (ret == -1) {
        errno = saved_errno;
        return posix_error();
    }

    Py_RETURN_NONE;
}
#endif /* HAVE_GRANTPT */

#ifdef HAVE_UNLOCKPT
/*[clinic input]
os.unlockpt

    fd: fildes
        File descriptor of a master pseudo-terminal device.
    /

Unlock a pseudo-terminal master/slave pair.

Performs an unlockpt() C function call.
[clinic start generated code]*/

static PyObject *
os_unlockpt_impl(PyObject *module, int fd)
/*[clinic end generated code: output=e08d354dec12d30c input=de7ab1f59f69a2b4]*/
{
    if (unlockpt(fd) == -1)
        return posix_error();

    Py_RETURN_NONE;
}
#endif /* HAVE_UNLOCKPT */

#if defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R)
static PyObject *
py_ptsname(int fd)
{
    // POSIX manpage: Upon failure, ptsname() shall return a null pointer
    // and may set errno. Always initialize errno to avoid undefined behavior.
    errno = 0;
    char *name = ptsname(fd);
    if (name == NULL) {
        return posix_error();
    }
    return PyUnicode_DecodeFSDefault(name);
}

/*[clinic input]
os.ptsname

    fd: fildes
        File descriptor of a master pseudo-terminal device.
    /

Return the name of the slave pseudo-terminal device.

If the ptsname_r() C function is available, it is called;
otherwise, performs a ptsname() C function call.
[clinic start generated code]*/

static PyObject *
os_ptsname_impl(PyObject *module, int fd)
/*[clinic end generated code: output=ef300fadc5675872 input=1369ccc0546f3130]*/
{
#ifdef HAVE_PTSNAME_R
    int ret;
    char name[MAXPATHLEN+1];

    if (HAVE_PTSNAME_R_RUNTIME) {
        ret = ptsname_r(fd, name, sizeof(name));
    }
    else {
        // fallback to ptsname() if ptsname_r() is not available in runtime.
        return py_ptsname(fd);
    }
    if (ret != 0) {
        errno = ret;
        return posix_error();
    }

    return PyUnicode_DecodeFSDefault(name);
#else
    return py_ptsname(fd);
#endif /* HAVE_PTSNAME_R */
}
#endif /* defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R) */

/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */
#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX)
#  define DEV_PTY_FILE "/dev/ptc"
#  define HAVE_DEV_PTMX
#else
#  define DEV_PTY_FILE "/dev/ptmx"
#endif

#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX)
#ifdef HAVE_PTY_H
#include <pty.h>
#elif defined(HAVE_LIBUTIL_H)
#include <libutil.h>
#elif defined(HAVE_UTIL_H)
#include <util.h>
#endif /* HAVE_PTY_H */
#ifdef HAVE_UTMP_H
#include <utmp.h>
#endif /* HAVE_UTMP_H */
#ifdef HAVE_STROPTS_H
#include <stropts.h>
#endif
#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX) */


#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX)
/*[clinic input]
os.openpty

Open a pseudo-terminal.

Return a tuple of (master_fd, slave_fd) containing open file descriptors
for both the master and slave ends.
[clinic start generated code]*/

static PyObject *
os_openpty_impl(PyObject *module)
/*[clinic end generated code: output=98841ce5ec9cef3c input=f3d99fd99e762907]*/
{
    int master_fd = -1, slave_fd = -1;
#ifndef HAVE_OPENPTY
    char * slave_name;
#endif
#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY)
    PyOS_sighandler_t sig_saved;
#if defined(__sun) && defined(__SVR4)
    extern char *ptsname(int fildes);
#endif
#endif

#ifdef HAVE_OPENPTY
    if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0)
        goto posix_error;

    if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
        goto error;
    if (_Py_set_inheritable(slave_fd, 0, NULL) < 0)
        goto error;

#elif defined(HAVE__GETPTY)
    slave_name = _getpty(&master_fd, O_RDWR, 0666, 0);
    if (slave_name == NULL)
        goto posix_error;
    if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
        goto error;

    slave_fd = _Py_open(slave_name, O_RDWR);
    if (slave_fd < 0)
        goto error;

#else
    master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
    if (master_fd < 0)
        goto posix_error;

    sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL);

    /* change permission of slave */
    if (grantpt(master_fd) < 0) {
        int saved_errno = errno;
        PyOS_setsig(SIGCHLD, sig_saved);
        errno = saved_errno;
        goto posix_error;
    }

    /* unlock slave */
    if (unlockpt(master_fd) < 0) {
        int saved_errno = errno;
        PyOS_setsig(SIGCHLD, sig_saved);
        errno = saved_errno;
        goto posix_error;
    }

    PyOS_setsig(SIGCHLD, sig_saved);

    slave_name = ptsname(master_fd); /* get name of slave */
    if (slave_name == NULL)
        goto posix_error;

    slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
    if (slave_fd == -1)
        goto error;

    if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
        goto posix_error;

#if !defined(__CYGWIN__) && !defined(__ANDROID__) && !defined(HAVE_DEV_PTC)
    ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */
    ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */
#ifndef __hpux
    ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */
#endif /* __hpux */
#endif /* HAVE_CYGWIN */
#endif /* HAVE_OPENPTY */

    return Py_BuildValue("(ii)", master_fd, slave_fd);

posix_error:
    posix_error();
error:
    if (master_fd != -1)
        close(master_fd);
    if (slave_fd != -1)
        close(slave_fd);
    return NULL;
}
#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */


#if defined(HAVE_SETSID) && defined(TIOCSCTTY)
#define HAVE_FALLBACK_LOGIN_TTY 1
#endif /* defined(HAVE_SETSID) && defined(TIOCSCTTY) */

#if defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY)
/*[clinic input]
os.login_tty

    fd: fildes
    /

Prepare the tty of which fd is a file descriptor for a new login session.

Make the calling process a session leader; make the tty the
controlling tty, the stdin, the stdout, and the stderr of the
calling process; close fd.
[clinic start generated code]*/

static PyObject *
os_login_tty_impl(PyObject *module, int fd)
/*[clinic end generated code: output=495a79911b4cc1bc input=5f298565099903a2]*/
{
#ifdef HAVE_LOGIN_TTY
    if (login_tty(fd) == -1) {
        return posix_error();
    }
#else /* defined(HAVE_FALLBACK_LOGIN_TTY) */
    /* Establish a new session. */
    if (setsid() == -1) {
        return posix_error();
    }

    /* The tty becomes the controlling terminal. */
    if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) {
        return posix_error();
    }

    /* The tty becomes stdin/stdout/stderr */
    if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1) {
        return posix_error();
    }
    if (fd > 2) {
        close(fd);
    }
#endif /* HAVE_LOGIN_TTY */
    Py_RETURN_NONE;
}
#endif /* defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) */


#ifdef HAVE_FORKPTY
/*[clinic input]
os.forkpty

Fork a new process with a new pseudo-terminal as controlling tty.

Returns a tuple of (pid, master_fd).
Like fork(), return pid of 0 to the child process,
and pid of child to the parent process.
To both, return fd of newly opened pseudo-terminal.
The master_fd is non-inheritable.
[clinic start generated code]*/

static PyObject *
os_forkpty_impl(PyObject *module)
/*[clinic end generated code: output=60d0a5c7512e4087 input=24765e0f33275b3b]*/
{
    int master_fd = -1;
    pid_t pid;

    PyInterpreterState *interp = _PyInterpreterState_GET();
    if (_PyInterpreterState_GetFinalizing(interp) != NULL) {
        PyErr_SetString(PyExc_PythonFinalizationError,
                        "can't fork at interpreter shutdown");
        return NULL;
    }
    if (!_Py_IsMainInterpreter(interp)) {
        PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
        return NULL;
    }
    if (PySys_Audit("os.forkpty", NULL) < 0) {
        return NULL;
    }
    PyOS_BeforeFork();
    pid = forkpty(&master_fd, NULL, NULL, NULL);
    if (pid == 0) {
        /* child: this clobbers and resets the import lock. */
        PyOS_AfterFork_Child();
    } else {
        // Called before AfterFork_Parent in case those hooks start threads.
        Py_ssize_t num_os_threads = get_number_of_os_threads();
        /* parent: release the import lock. */
        PyOS_AfterFork_Parent();
        /* set O_CLOEXEC on master_fd */
        if (_Py_set_inheritable(master_fd, 0, NULL) < 0) {
            PyErr_FormatUnraisable("Exception ignored when setting master_fd "
                                   "non-inheritable in forkpty()");
        }

        // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
        if (warn_about_fork_with_threads("forkpty", num_os_threads) < 0)
            return NULL;
    }
    if (pid == -1) {
        return posix_error();
    }

    return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd);
}
#endif /* HAVE_FORKPTY */


#ifdef HAVE_GETEGID
/*[clinic input]
os.getegid

Return the current process's effective group id.
[clinic start generated code]*/

static PyObject *
os_getegid_impl(PyObject *module)
/*[clinic end generated code: output=67d9be7ac68898a2 input=1596f79ad1107d5d]*/
{
    return _PyLong_FromGid(getegid());
}
#endif /* HAVE_GETEGID */


#ifdef HAVE_GETEUID
/*[clinic input]
os.geteuid

Return the current process's effective user id.
[clinic start generated code]*/

static PyObject *
os_geteuid_impl(PyObject *module)
/*[clinic end generated code: output=ea1b60f0d6abb66e input=4644c662d3bd9f19]*/
{
    return _PyLong_FromUid(geteuid());
}
#endif /* HAVE_GETEUID */


#ifdef HAVE_GETGID
/*[clinic input]
os.getgid

Return the current process's group id.
[clinic start generated code]*/

static PyObject *
os_getgid_impl(PyObject *module)
/*[clinic end generated code: output=4f28ebc9d3e5dfcf input=58796344cd87c0f6]*/
{
    return _PyLong_FromGid(getgid());
}
#endif /* HAVE_GETGID */


#if defined(HAVE_GETPID)
/*[clinic input]
os.getpid

Return the current process id.
[clinic start generated code]*/

static PyObject *
os_getpid_impl(PyObject *module)
/*[clinic end generated code: output=9ea6fdac01ed2b3c input=5a9a00f0ab68aa00]*/
{
#if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)
    return PyLong_FromPid(getpid());
#else
    return PyLong_FromUnsignedLong(GetCurrentProcessId());
#endif
}
#endif /* defined(HAVE_GETPID) */

#ifdef NGROUPS_MAX
#define MAX_GROUPS NGROUPS_MAX
#else
    /* defined to be 16 on Solaris7, so this should be a small number */
#define MAX_GROUPS 64
#endif

#ifdef HAVE_GETGROUPLIST

#ifdef __APPLE__
/*[clinic input]
os.getgrouplist

    user: str
        username to lookup
    group as basegid: int
        base group id of the user
    /

Returns a list of groups to which a user belongs.
[clinic start generated code]*/

static PyObject *
os_getgrouplist_impl(PyObject *module, const char *user, int basegid)
/*[clinic end generated code: output=6e734697b8c26de0 input=f8d870374b09a490]*/
#else
/*[clinic input]
os.getgrouplist

    user: str
        username to lookup
    group as basegid: gid_t
        base group id of the user
    /

Returns a list of groups to which a user belongs.
[clinic start generated code]*/

static PyObject *
os_getgrouplist_impl(PyObject *module, const char *user, gid_t basegid)
/*[clinic end generated code: output=0ebd7fb70115575b input=cc61d5c20b08958d]*/
#endif
{
    int i, ngroups;
    PyObject *list;
#ifdef __APPLE__
    int *groups;
#else
    gid_t *groups;
#endif

    /*
     * NGROUPS_MAX is defined by POSIX.1 as the maximum
     * number of supplemental groups a users can belong to.
     * We have to increment it by one because
     * getgrouplist() returns both the supplemental groups
     * and the primary group, i.e. all of the groups the
     * user belongs to.
     */
    ngroups = 1 + MAX_GROUPS;

    while (1) {
#ifdef __APPLE__
        groups = PyMem_New(int, ngroups);
#else
        groups = PyMem_New(gid_t, ngroups);
#endif
        if (groups == NULL) {
            return PyErr_NoMemory();
        }

        int old_ngroups = ngroups;
        if (getgrouplist(user, basegid, groups, &ngroups) != -1) {
            /* Success */
            break;
        }

        /* getgrouplist() fails if the group list is too small */
        PyMem_Free(groups);

        if (ngroups > old_ngroups) {
            /* If the group list is too small, the glibc implementation of
               getgrouplist() sets ngroups to the total number of groups and
               returns -1. */
        }
        else {
            /* Double the group list size */
            if (ngroups > INT_MAX / 2) {
                return PyErr_NoMemory();
            }
            ngroups *= 2;
        }

        /* Retry getgrouplist() with a larger group list */
    }

#ifdef _Py_MEMORY_SANITIZER
    /* Clang memory sanitizer libc intercepts don't know getgrouplist. */
    __msan_unpoison(&ngroups, sizeof(ngroups));
    __msan_unpoison(groups, ngroups*sizeof(*groups));
#endif

    list = PyList_New(ngroups);
    if (list == NULL) {
        PyMem_Free(groups);
        return NULL;
    }

    for (i = 0; i < ngroups; i++) {
#ifdef __APPLE__
        PyObject *o = PyLong_FromUnsignedLong((unsigned long)groups[i]);
#else
        PyObject *o = _PyLong_FromGid(groups[i]);
#endif
        if (o == NULL) {
            Py_DECREF(list);
            PyMem_Free(groups);
            return NULL;
        }
        PyList_SET_ITEM(list, i, o);
    }

    PyMem_Free(groups);

    return list;
}
#endif /* HAVE_GETGROUPLIST */


#ifdef HAVE_GETGROUPS
/*[clinic input]
os.getgroups

Return list of supplemental group IDs for the process.
[clinic start generated code]*/

static PyObject *
os_getgroups_impl(PyObject *module)
/*[clinic end generated code: output=42b0c17758561b56 input=d3f109412e6a155c]*/
{
    // Call getgroups with length 0 to get the actual number of groups
    int n = getgroups(0, NULL);
    if (n < 0) {
        return posix_error();
    }

    if (n == 0) {
        return PyList_New(0);
    }

    gid_t *grouplist = PyMem_New(gid_t, n);
    if (grouplist == NULL) {
        return PyErr_NoMemory();
    }

    n = getgroups(n, grouplist);
    if (n == -1) {
        posix_error();
        PyMem_Free(grouplist);
        return NULL;
    }

    PyObject *result = PyList_New(n);
    if (result == NULL) {
        goto error;
    }

    for (int i = 0; i < n; ++i) {
        PyObject *group = _PyLong_FromGid(grouplist[i]);
        if (group == NULL) {
            goto error;
        }
        PyList_SET_ITEM(result, i, group);
    }
    PyMem_Free(grouplist);

    return result;

error:
    PyMem_Free(grouplist);
    Py_XDECREF(result);
    return NULL;
}
#endif /* HAVE_GETGROUPS */

#ifdef HAVE_INITGROUPS
#ifdef __APPLE__
/*[clinic input]
os.initgroups

    username as oname: unicode_fs_encoded
    gid: int
    /

Initialize the group access list.

Call the system initgroups() to initialize the group access list with all of
the groups of which the specified username is a member, plus the specified
group id.
[clinic start generated code]*/

static PyObject *
os_initgroups_impl(PyObject *module, PyObject *oname, int gid)
/*[clinic end generated code: output=7f074d30a425fd3a input=984e60c7fed88cb4]*/
#else
/*[clinic input]
os.initgroups

    username as oname: unicode_fs_encoded
    gid: gid_t
    /

Initialize the group access list.

Call the system initgroups() to initialize the group access list with all of
the groups of which the specified username is a member, plus the specified
group id.
[clinic start generated code]*/

static PyObject *
os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid)
/*[clinic end generated code: output=59341244521a9e3f input=17d8fbe2dea42ca4]*/
#endif
{
    const char *username = PyBytes_AS_STRING(oname);

    if (initgroups(username, gid) == -1)
        return PyErr_SetFromErrno(PyExc_OSError);

    Py_RETURN_NONE;
}
#endif /* HAVE_INITGROUPS */


#ifdef HAVE_GETPGID
/*[clinic input]
os.getpgid

    pid: pid_t

Call the system call getpgid(), and return the result.
[clinic start generated code]*/

static PyObject *
os_getpgid_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=1db95a97be205d18 input=39d710ae3baaf1c7]*/
{
    pid_t pgid = getpgid(pid);
    if (pgid < 0)
        return posix_error();
    return PyLong_FromPid(pgid);
}
#endif /* HAVE_GETPGID */


#ifdef HAVE_GETPGRP
/*[clinic input]
os.getpgrp

Return the current process group id.
[clinic start generated code]*/

static PyObject *
os_getpgrp_impl(PyObject *module)
/*[clinic end generated code: output=c4fc381e51103cf3 input=6846fb2bb9a3705e]*/
{
#ifdef GETPGRP_HAVE_ARG
    return PyLong_FromPid(getpgrp(0));
#else /* GETPGRP_HAVE_ARG */
    return PyLong_FromPid(getpgrp());
#endif /* GETPGRP_HAVE_ARG */
}
#endif /* HAVE_GETPGRP */


#ifdef HAVE_SETPGRP
/*[clinic input]
os.setpgrp

Make the current process the leader of its process group.
[clinic start generated code]*/

static PyObject *
os_setpgrp_impl(PyObject *module)
/*[clinic end generated code: output=2554735b0a60f0a0 input=1f0619fcb5731e7e]*/
{
#ifdef SETPGRP_HAVE_ARG
    if (setpgrp(0, 0) < 0)
#else /* SETPGRP_HAVE_ARG */
    if (setpgrp() < 0)
#endif /* SETPGRP_HAVE_ARG */
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETPGRP */

#ifdef HAVE_GETPPID

#ifdef MS_WINDOWS
#include <winternl.h>
#include <ProcessSnapshot.h>

// The structure definition in winternl.h may be incomplete.
// This structure is the full version from the MSDN documentation.
typedef struct _PROCESS_BASIC_INFORMATION_FULL {
    NTSTATUS ExitStatus;
    PVOID PebBaseAddress;
    ULONG_PTR AffinityMask;
    LONG BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION_FULL;

typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) (
    IN    HANDLE           ProcessHandle,
    IN    PROCESSINFOCLASS ProcessInformationClass,
    OUT   PVOID            ProcessInformation,
    IN    ULONG            ProcessInformationLength,
    OUT   PULONG           ReturnLength OPTIONAL);

// This function returns the process ID of the parent process.
// Returns 0 on failure.
static ULONG
win32_getppid_fast(void)
{
    NTSTATUS status;
    HMODULE ntdll;
    PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess;
    PROCESS_BASIC_INFORMATION_FULL basic_information;
    static ULONG cached_ppid = 0;

    if (cached_ppid) {
        // No need to query the kernel again.
        return cached_ppid;
    }

    ntdll = GetModuleHandleW(L"ntdll.dll");
    if (!ntdll) {
        return 0;
    }

    pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess");
    if (!pNtQueryInformationProcess) {
        return 0;
    }

    status = pNtQueryInformationProcess(GetCurrentProcess(),
                                        ProcessBasicInformation,
                                        &basic_information,
                                        sizeof(basic_information),
                                        NULL);

    if (!NT_SUCCESS(status)) {
        return 0;
    }

    // Perform sanity check on the parent process ID we received from NtQueryInformationProcess.
    // The check covers values which exceed the 32-bit range (if running on x64) as well as
    // zero and (ULONG) -1.

    if (basic_information.InheritedFromUniqueProcessId == 0 ||
        basic_information.InheritedFromUniqueProcessId >= ULONG_MAX)
    {
        return 0;
    }

    // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId
    // structure member contains a ULONG_PTR which represents the process ID of our parent
    // process. This process ID will be correctly returned even if the parent process has
    // exited or been terminated.

    cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId;
    return cached_ppid;
}

static PyObject*
win32_getppid(void)
{
    DWORD error;
    PyObject* result = NULL;
    HANDLE process = GetCurrentProcess();
    HPSS snapshot = NULL;
    ULONG pid;

    pid = win32_getppid_fast();
    if (pid != 0) {
        return PyLong_FromUnsignedLong(pid);
    }

    // If failure occurs in win32_getppid_fast(), fall back to using the PSS API.

    error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot);
    if (error != ERROR_SUCCESS) {
        return PyErr_SetFromWindowsErr(error);
    }

    PSS_PROCESS_INFORMATION info;
    error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info,
                             sizeof(info));
    if (error == ERROR_SUCCESS) {
        result = PyLong_FromUnsignedLong(info.ParentProcessId);
    }
    else {
        result = PyErr_SetFromWindowsErr(error);
    }

    PssFreeSnapshot(process, snapshot);
    return result;
}
#endif /*MS_WINDOWS*/


/*[clinic input]
os.getppid

Return the parent's process id.

If the parent process has already exited, Windows machines will still
return its id; others systems will return the id of the 'init' process (1).
[clinic start generated code]*/

static PyObject *
os_getppid_impl(PyObject *module)
/*[clinic end generated code: output=43b2a946a8c603b4 input=e637cb87539c030e]*/
{
#ifdef MS_WINDOWS
    return win32_getppid();
#else
    return PyLong_FromPid(getppid());
#endif
}
#endif /* HAVE_GETPPID */


#ifdef HAVE_GETLOGIN
/*[clinic input]
os.getlogin

Return the actual login name.
[clinic start generated code]*/

static PyObject *
os_getlogin_impl(PyObject *module)
/*[clinic end generated code: output=a32e66a7e5715dac input=2a21ab1e917163df]*/
{
    PyObject *result = NULL;
#ifdef MS_WINDOWS
    wchar_t user_name[UNLEN + 1];
    DWORD num_chars = Py_ARRAY_LENGTH(user_name);

    if (GetUserNameW(user_name, &num_chars)) {
        /* num_chars is the number of unicode chars plus null terminator */
        result = PyUnicode_FromWideChar(user_name, num_chars - 1);
    }
    else
        result = PyErr_SetFromWindowsErr(GetLastError());
#elif defined (HAVE_GETLOGIN_R)
# if defined (HAVE_MAXLOGNAME)
    char name[MAXLOGNAME + 1];
# elif defined (HAVE_UT_NAMESIZE)
    char name[UT_NAMESIZE + 1];
# else
    char name[256];
# endif
    int err = getlogin_r(name, sizeof(name));
    if (err) {
        int old_errno = errno;
        errno = err;
        posix_error();
        errno = old_errno;
    }
    else {
        result = PyUnicode_DecodeFSDefault(name);
    }
#else
    char *name;
    int old_errno = errno;

    errno = 0;
    name = getlogin();
    if (name == NULL) {
        if (errno)
            posix_error();
        else
            PyErr_SetString(PyExc_OSError, "unable to determine login name");
    }
    else
        result = PyUnicode_DecodeFSDefault(name);
    errno = old_errno;
#endif
    return result;
}
#endif /* HAVE_GETLOGIN */


#ifdef HAVE_GETUID
/*[clinic input]
os.getuid

Return the current process's user id.
[clinic start generated code]*/

static PyObject *
os_getuid_impl(PyObject *module)
/*[clinic end generated code: output=415c0b401ebed11a input=b53c8b35f110a516]*/
{
    return _PyLong_FromUid(getuid());
}
#endif /* HAVE_GETUID */


#ifdef MS_WINDOWS
#define HAVE_KILL
#endif /* MS_WINDOWS */

#ifdef HAVE_KILL
/*[clinic input]
os.kill

    pid: pid_t
    signal: Py_ssize_t
    /

Kill a process with a signal.
[clinic start generated code]*/

static PyObject *
os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal)
/*[clinic end generated code: output=8e346a6701c88568 input=61a36b86ca275ab9]*/
{
    if (PySys_Audit("os.kill", "in", pid, signal) < 0) {
        return NULL;
    }
#ifndef MS_WINDOWS
    if (kill(pid, (int)signal) == -1) {
        return posix_error();
    }

    // Check immediately if the signal was sent to the current process.
    // Don't micro-optimize pid == getpid(), since PyErr_SetString() check
    // is cheap.
    if (PyErr_CheckSignals()) {
        return NULL;
    }

    Py_RETURN_NONE;
#else /* !MS_WINDOWS */
    DWORD sig = (DWORD)signal;

#ifdef HAVE_WINDOWS_CONSOLE_IO
    /* Console processes which share a common console can be sent CTRL+C or
       CTRL+BREAK events, provided they handle said events. */
    if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) {
        if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) {
            return PyErr_SetFromWindowsErr(0);
        }
        Py_RETURN_NONE;
    }
#endif /* HAVE_WINDOWS_CONSOLE_IO */

    /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
       attempt to open and terminate the process. */
    HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
    if (handle == NULL) {
        return PyErr_SetFromWindowsErr(0);
    }

    BOOL res = TerminateProcess(handle, sig);
    CloseHandle(handle);
    if (res == 0) {
        return PyErr_SetFromWindowsErr(0);
    }

    Py_RETURN_NONE;
#endif /* !MS_WINDOWS */
}
#endif /* HAVE_KILL */


#ifdef HAVE_KILLPG
/*[clinic input]
os.killpg

    pgid: pid_t
    signal: int
    /

Kill a process group with a signal.
[clinic start generated code]*/

static PyObject *
os_killpg_impl(PyObject *module, pid_t pgid, int signal)
/*[clinic end generated code: output=6dbcd2f1fdf5fdba input=38b5449eb8faec19]*/
{
    if (PySys_Audit("os.killpg", "ii", pgid, signal) < 0) {
        return NULL;
    }
    /* XXX some man pages make the `pgid` parameter an int, others
       a pid_t. Since getpgrp() returns a pid_t, we assume killpg should
       take the same type. Moreover, pid_t is always at least as wide as
       int (else compilation of this module fails), which is safe. */
    if (killpg(pgid, signal) == -1)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_KILLPG */


#ifdef HAVE_PLOCK
#ifdef HAVE_SYS_LOCK_H
#include <sys/lock.h>
#endif

/*[clinic input]
os.plock
    op: int
    /

Lock program segments into memory.");
[clinic start generated code]*/

static PyObject *
os_plock_impl(PyObject *module, int op)
/*[clinic end generated code: output=81424167033b168e input=e6e5e348e1525f60]*/
{
    if (plock(op) == -1)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_PLOCK */


#ifdef HAVE_SETUID
/*[clinic input]
os.setuid

    uid: uid_t
    /

Set the current process's user id.
[clinic start generated code]*/

static PyObject *
os_setuid_impl(PyObject *module, uid_t uid)
/*[clinic end generated code: output=a0a41fd0d1ec555f input=c921a3285aa22256]*/
{
    if (setuid(uid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETUID */


#ifdef HAVE_SETEUID
/*[clinic input]
os.seteuid

    euid: uid_t
    /

Set the current process's effective user id.
[clinic start generated code]*/

static PyObject *
os_seteuid_impl(PyObject *module, uid_t euid)
/*[clinic end generated code: output=102e3ad98361519a input=ba93d927e4781aa9]*/
{
    if (seteuid(euid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETEUID */


#ifdef HAVE_SETEGID
/*[clinic input]
os.setegid

    egid: gid_t
    /

Set the current process's effective group id.
[clinic start generated code]*/

static PyObject *
os_setegid_impl(PyObject *module, gid_t egid)
/*[clinic end generated code: output=4e4b825a6a10258d input=4080526d0ccd6ce3]*/
{
    if (setegid(egid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETEGID */


#ifdef HAVE_SETREUID
/*[clinic input]
os.setreuid

    ruid: uid_t
    euid: uid_t
    /

Set the current process's real and effective user ids.
[clinic start generated code]*/

static PyObject *
os_setreuid_impl(PyObject *module, uid_t ruid, uid_t euid)
/*[clinic end generated code: output=62d991210006530a input=0ca8978de663880c]*/
{
    if (setreuid(ruid, euid) < 0) {
        return posix_error();
    } else {
        Py_RETURN_NONE;
    }
}
#endif /* HAVE_SETREUID */


#ifdef HAVE_SETREGID
/*[clinic input]
os.setregid

    rgid: gid_t
    egid: gid_t
    /

Set the current process's real and effective group ids.
[clinic start generated code]*/

static PyObject *
os_setregid_impl(PyObject *module, gid_t rgid, gid_t egid)
/*[clinic end generated code: output=aa803835cf5342f3 input=c59499f72846db78]*/
{
    if (setregid(rgid, egid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETREGID */


#ifdef HAVE_SETGID
/*[clinic input]
os.setgid
    gid: gid_t
    /

Set the current process's group id.
[clinic start generated code]*/

static PyObject *
os_setgid_impl(PyObject *module, gid_t gid)
/*[clinic end generated code: output=bdccd7403f6ad8c3 input=27d30c4059045dc6]*/
{
    if (setgid(gid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETGID */


#ifdef HAVE_SETGROUPS
/*[clinic input]
os.setgroups

    groups: object
    /

Set the groups of the current process to list.
[clinic start generated code]*/

static PyObject *
os_setgroups(PyObject *module, PyObject *groups)
/*[clinic end generated code: output=3fcb32aad58c5ecd input=fa742ca3daf85a7e]*/
{
    if (!PySequence_Check(groups)) {
        PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence");
        return NULL;
    }
    Py_ssize_t len = PySequence_Size(groups);
    if (len < 0) {
        return NULL;
    }
    if (len > MAX_GROUPS) {
        PyErr_SetString(PyExc_ValueError, "too many groups");
        return NULL;
    }

    gid_t *grouplist = PyMem_New(gid_t, len);
    if (grouplist == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    for (Py_ssize_t i = 0; i < len; i++) {
        PyObject *elem;
        elem = PySequence_GetItem(groups, i);
        if (!elem) {
            PyMem_Free(grouplist);
            return NULL;
        }
        if (!PyIndex_Check(elem)) {
            PyErr_SetString(PyExc_TypeError,
                            "groups must be integers");
            Py_DECREF(elem);
            PyMem_Free(grouplist);
            return NULL;
        } else {
            if (!_Py_Gid_Converter(elem, &grouplist[i])) {
                Py_DECREF(elem);
                PyMem_Free(grouplist);
                return NULL;
            }
        }
        Py_DECREF(elem);
    }

    if (setgroups(len, grouplist) < 0) {
        posix_error();
        PyMem_Free(grouplist);
        return NULL;
    }
    PyMem_Free(grouplist);
    Py_RETURN_NONE;
}
#endif /* HAVE_SETGROUPS */

#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
static PyObject *
wait_helper(PyObject *module, pid_t pid, int status, struct rusage *ru)
{
    PyObject *result;
    PyObject *struct_rusage;

    if (pid == -1)
        return posix_error();

    // If wait succeeded but no child was ready to report status, ru will not
    // have been populated.
    if (pid == 0) {
        memset(ru, 0, sizeof(*ru));
    }

    struct_rusage = PyImport_ImportModuleAttrString("resource", "struct_rusage");
    if (struct_rusage == NULL)
        return NULL;

    /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */
    result = PyStructSequence_New((PyTypeObject*) struct_rusage);
    Py_DECREF(struct_rusage);
    if (!result)
        return NULL;

    int pos = 0;

#ifndef doubletime
#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
#endif

#define SET_RESULT(CALL)                                     \
    do {                                                     \
        PyObject *item = (CALL);                             \
        if (item == NULL) {                                  \
            Py_DECREF(result);                               \
            return NULL;                                     \
        }                                                    \
        PyStructSequence_SET_ITEM(result, pos++, item);      \
    } while(0)

    SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_utime)));
    SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_stime)));
    SET_RESULT(PyLong_FromLong(ru->ru_maxrss));
    SET_RESULT(PyLong_FromLong(ru->ru_ixrss));
    SET_RESULT(PyLong_FromLong(ru->ru_idrss));
    SET_RESULT(PyLong_FromLong(ru->ru_isrss));
    SET_RESULT(PyLong_FromLong(ru->ru_minflt));
    SET_RESULT(PyLong_FromLong(ru->ru_majflt));
    SET_RESULT(PyLong_FromLong(ru->ru_nswap));
    SET_RESULT(PyLong_FromLong(ru->ru_inblock));
    SET_RESULT(PyLong_FromLong(ru->ru_oublock));
    SET_RESULT(PyLong_FromLong(ru->ru_msgsnd));
    SET_RESULT(PyLong_FromLong(ru->ru_msgrcv));
    SET_RESULT(PyLong_FromLong(ru->ru_nsignals));
    SET_RESULT(PyLong_FromLong(ru->ru_nvcsw));
    SET_RESULT(PyLong_FromLong(ru->ru_nivcsw));
#undef SET_RESULT

    return Py_BuildValue("NiN", PyLong_FromPid(pid), status, result);
}
#endif /* HAVE_WAIT3 || HAVE_WAIT4 */


#ifdef HAVE_WAIT3
/*[clinic input]
os.wait3

    options: int
Wait for completion of a child process.

Returns a tuple of information about the child process:
  (pid, status, rusage)
[clinic start generated code]*/

static PyObject *
os_wait3_impl(PyObject *module, int options)
/*[clinic end generated code: output=92c3224e6f28217a input=8ac4c56956b61710]*/
{
    pid_t pid;
    struct rusage ru;
    int async_err = 0;
    WAIT_TYPE status;
    WAIT_STATUS_INT(status) = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        pid = wait3(&status, options, &ru);
        Py_END_ALLOW_THREADS
    } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (pid < 0)
        return (!async_err) ? posix_error() : NULL;

    return wait_helper(module, pid, WAIT_STATUS_INT(status), &ru);
}
#endif /* HAVE_WAIT3 */


#ifdef HAVE_WAIT4
/*[clinic input]

os.wait4

    pid: pid_t
    options: int

Wait for completion of a specific child process.

Returns a tuple of information about the child process:
  (pid, status, rusage)
[clinic start generated code]*/

static PyObject *
os_wait4_impl(PyObject *module, pid_t pid, int options)
/*[clinic end generated code: output=66195aa507b35f70 input=d11deed0750600ba]*/
{
    pid_t res;
    struct rusage ru;
    int async_err = 0;
    WAIT_TYPE status;
    WAIT_STATUS_INT(status) = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        res = wait4(pid, &status, options, &ru);
        Py_END_ALLOW_THREADS
    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res < 0)
        return (!async_err) ? posix_error() : NULL;

    return wait_helper(module, res, WAIT_STATUS_INT(status), &ru);
}
#endif /* HAVE_WAIT4 */


#if defined(HAVE_WAITID)
/*[clinic input]
os.waitid

    idtype: idtype_t
        Must be one of be P_PID, P_PGID or P_ALL.
    id: id_t
        The id to wait on.
    options: int
        Constructed from the ORing of one or more of WEXITED, WSTOPPED
        or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT.
    /

Returns the result of waiting for a process or processes.

Returns either waitid_result or None if WNOHANG is specified and there are
no children in a waitable state.
[clinic start generated code]*/

static PyObject *
os_waitid_impl(PyObject *module, idtype_t idtype, id_t id, int options)
/*[clinic end generated code: output=5d2e1c0bde61f4d8 input=d8e7f76e052b7920]*/
{
    PyObject *result;
    int res;
    int async_err = 0;
    siginfo_t si;
    si.si_pid = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        res = waitid(idtype, id, &si, options);
        Py_END_ALLOW_THREADS
    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res < 0)
        return (!async_err) ? posix_error() : NULL;

    if (si.si_pid == 0)
        Py_RETURN_NONE;

    PyObject *WaitidResultType = get_posix_state(module)->WaitidResultType;
    result = PyStructSequence_New((PyTypeObject *)WaitidResultType);
    if (!result)
        return NULL;

    int pos = 0;

#define SET_RESULT(CALL)                                     \
    do {                                                     \
        PyObject *item = (CALL);                             \
        if (item == NULL) {                                  \
            Py_DECREF(result);                               \
            return NULL;                                     \
        }                                                    \
        PyStructSequence_SET_ITEM(result, pos++, item);      \
    } while(0)

    SET_RESULT(PyLong_FromPid(si.si_pid));
    SET_RESULT(_PyLong_FromUid(si.si_uid));
    SET_RESULT(PyLong_FromLong((long)(si.si_signo)));
    SET_RESULT(PyLong_FromLong((long)(si.si_status)));
    SET_RESULT(PyLong_FromLong((long)(si.si_code)));

#undef SET_RESULT

    return result;
}
#endif /* defined(HAVE_WAITID) */


#if defined(HAVE_WAITPID)
/*[clinic input]
os.waitpid
    pid: pid_t
    options: int
    /

Wait for completion of a given child process.

Returns a tuple of information regarding the child process:
    (pid, status)

The options argument is ignored on Windows.
[clinic start generated code]*/

static PyObject *
os_waitpid_impl(PyObject *module, pid_t pid, int options)
/*[clinic end generated code: output=5c37c06887a20270 input=0bf1666b8758fda3]*/
{
    pid_t res;
    int async_err = 0;
    WAIT_TYPE status;
    WAIT_STATUS_INT(status) = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        res = waitpid(pid, &status, options);
        Py_END_ALLOW_THREADS
    } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res < 0)
        return (!async_err) ? posix_error() : NULL;

    return Py_BuildValue("Ni", PyLong_FromPid(res), WAIT_STATUS_INT(status));
}
#elif defined(HAVE_CWAIT)
/* MS C has a variant of waitpid() that's usable for most purposes. */
/*[clinic input]
os.waitpid
    pid: intptr_t
    options: int
    /

Wait for completion of a given process.

Returns a tuple of information regarding the process:
    (pid, status << 8)

The options argument is ignored on Windows.
[clinic start generated code]*/

static PyObject *
os_waitpid_impl(PyObject *module, intptr_t pid, int options)
/*[clinic end generated code: output=be836b221271d538 input=40f2440c515410f8]*/
{
    int status;
    intptr_t res;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        res = _cwait(&status, pid, options);
        _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;

    unsigned long long ustatus = (unsigned int)status;

    /* shift the status left a byte so this is more like the POSIX waitpid */
    return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8);
}
#endif


#ifdef HAVE_WAIT
/*[clinic input]
os.wait

Wait for completion of a child process.

Returns a tuple of information about the child process:
    (pid, status)
[clinic start generated code]*/

static PyObject *
os_wait_impl(PyObject *module)
/*[clinic end generated code: output=6bc419ac32fb364b input=03b0182d4a4700ce]*/
{
    pid_t pid;
    int async_err = 0;
    WAIT_TYPE status;
    WAIT_STATUS_INT(status) = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        pid = wait(&status);
        Py_END_ALLOW_THREADS
    } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (pid < 0)
        return (!async_err) ? posix_error() : NULL;

    return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status));
}
#endif /* HAVE_WAIT */


// This system call always crashes on older Android versions.
#if defined(__linux__) && defined(__NR_pidfd_open) && \
    !(defined(__ANDROID__) && __ANDROID_API__ < 31)
/*[clinic input]
os.pidfd_open
  pid: pid_t
  flags: unsigned_int = 0

Return a file descriptor referring to the process *pid*.

The descriptor can be used to perform process management without races and
signals.
[clinic start generated code]*/

static PyObject *
os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
/*[clinic end generated code: output=5c7252698947dc41 input=c3fd99ce947ccfef]*/
{
    int fd = syscall(__NR_pidfd_open, pid, flags);
    if (fd < 0) {
        return posix_error();
    }
    return PyLong_FromLong(fd);
}
#endif


#ifdef HAVE_SETNS
/*[clinic input]
os.setns
  fd: fildes
    A file descriptor to a namespace.
  nstype: int = 0
    Type of namespace.

Move the calling thread into different namespaces.
[clinic start generated code]*/

static PyObject *
os_setns_impl(PyObject *module, int fd, int nstype)
/*[clinic end generated code: output=5dbd055bfb66ecd0 input=42787871226bf3ee]*/
{
    int res;

    Py_BEGIN_ALLOW_THREADS
    res = setns(fd, nstype);
    Py_END_ALLOW_THREADS

    if (res != 0) {
        return posix_error();
    }

    Py_RETURN_NONE;
}
#endif


#ifdef HAVE_UNSHARE
/*[clinic input]
os.unshare
  flags: int
    Namespaces to be unshared.

Disassociate parts of a process (or thread) execution context.
[clinic start generated code]*/

static PyObject *
os_unshare_impl(PyObject *module, int flags)
/*[clinic end generated code: output=1b3177906dd237ee input=9e065db3232b8b1b]*/
{
    int res;

    Py_BEGIN_ALLOW_THREADS
    res = unshare(flags);
    Py_END_ALLOW_THREADS

    if (res != 0) {
        return posix_error();
    }

    Py_RETURN_NONE;
}
#endif


#if defined(HAVE_READLINK) || defined(MS_WINDOWS)
/*[clinic input]
os.readlink

    path: path_t
    *
    dir_fd: dir_fd(requires='readlinkat') = None

Return a string representing the path to which the symbolic link points.

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.

dir_fd may not be implemented on your platform.  If it is unavailable,
using it will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_readlink_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=d21b732a2e814030 input=113c87e0db1ecaf2]*/
{
#if defined(HAVE_READLINK)
    char buffer[MAXPATHLEN+1];
    ssize_t length;
#ifdef HAVE_READLINKAT
    int readlinkat_unavailable = 0;
#endif

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_READLINKAT
    if (dir_fd != DEFAULT_DIR_FD) {
        if (HAVE_READLINKAT_RUNTIME) {
            length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN);
        } else {
            readlinkat_unavailable = 1;
        }
    } else
#endif
        length = readlink(path->narrow, buffer, MAXPATHLEN);
    Py_END_ALLOW_THREADS

#ifdef HAVE_READLINKAT
    if (readlinkat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (length < 0) {
        return path_error(path);
    }
    buffer[length] = '\0';

    if (PyUnicode_Check(path->object))
        return PyUnicode_DecodeFSDefaultAndSize(buffer, length);
    else
        return PyBytes_FromStringAndSize(buffer, length);
#elif defined(MS_WINDOWS)
    DWORD n_bytes_returned;
    DWORD io_result = 0;
    HANDLE reparse_point_handle;
    char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
    _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer;
    PyObject *result = NULL;

    /* First get a handle to the reparse point */
    Py_BEGIN_ALLOW_THREADS
    reparse_point_handle = CreateFileW(
        path->wide,
        0,
        0,
        0,
        OPEN_EXISTING,
        FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS,
        0);
    if (reparse_point_handle != INVALID_HANDLE_VALUE) {
        /* New call DeviceIoControl to read the reparse point */
        io_result = DeviceIoControl(
            reparse_point_handle,
            FSCTL_GET_REPARSE_POINT,
            0, 0, /* in buffer */
            target_buffer, sizeof(target_buffer),
            &n_bytes_returned,
            0 /* we're not using OVERLAPPED_IO */
            );
        CloseHandle(reparse_point_handle);
    }
    Py_END_ALLOW_THREADS

    if (io_result == 0) {
        return path_error(path);
    }

    wchar_t *name = NULL;
    Py_ssize_t nameLen = 0;
    if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK)
    {
        name = (wchar_t *)((char*)rdb->SymbolicLinkReparseBuffer.PathBuffer +
                           rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset);
        nameLen = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
    }
    else if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
    {
        name = (wchar_t *)((char*)rdb->MountPointReparseBuffer.PathBuffer +
                           rdb->MountPointReparseBuffer.SubstituteNameOffset);
        nameLen = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
    }
    else
    {
        PyErr_SetString(PyExc_ValueError, "not a symbolic link");
    }
    if (name) {
        if (nameLen > 4 && wcsncmp(name, L"\\??\\", 4) == 0) {
            /* Our buffer is mutable, so this is okay */
            name[1] = L'\\';
        }
        result = PyUnicode_FromWideChar(name, nameLen);
        if (result && PyBytes_Check(path->object)) {
            Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
        }
    }
    return result;
#endif
}
#endif /* defined(HAVE_READLINK) || defined(MS_WINDOWS) */

#if defined(MS_WINDOWS)

/* Remove the last portion of the path - return 0 on success */
static int
_dirnameW(WCHAR *path)
{
    WCHAR *ptr;
    size_t length = wcsnlen_s(path, MAX_PATH);
    if (length == MAX_PATH) {
        return -1;
    }

    /* walk the path from the end until a backslash is encountered */
    for(ptr = path + length; ptr != path; ptr--) {
        if (*ptr == L'\\' || *ptr == L'/') {
            break;
        }
    }
    *ptr = 0;
    return 0;
}

#endif

#ifdef HAVE_SYMLINK

#if defined(MS_WINDOWS)

/* Is this path absolute? */
static int
_is_absW(const WCHAR *path)
{
    return path[0] == L'\\' || path[0] == L'/' ||
        (path[0] && path[1] == L':');
}

/* join root and rest with a backslash - return 0 on success */
static int
_joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest)
{
    if (_is_absW(rest)) {
        return wcscpy_s(dest_path, MAX_PATH, rest);
    }

    if (wcscpy_s(dest_path, MAX_PATH, root)) {
        return -1;
    }

    if (dest_path[0] && wcscat_s(dest_path, MAX_PATH, L"\\")) {
        return -1;
    }

    return wcscat_s(dest_path, MAX_PATH, rest);
}

/* Return True if the path at src relative to dest is a directory */
static int
_check_dirW(LPCWSTR src, LPCWSTR dest)
{
    WIN32_FILE_ATTRIBUTE_DATA src_info;
    WCHAR dest_parent[MAX_PATH];
    WCHAR src_resolved[MAX_PATH] = L"";

    /* dest_parent = os.path.dirname(dest) */
    if (wcscpy_s(dest_parent, MAX_PATH, dest) ||
        _dirnameW(dest_parent)) {
        return 0;
    }
    /* src_resolved = os.path.join(dest_parent, src) */
    if (_joinW(src_resolved, dest_parent, src)) {
        return 0;
    }
    return (
        GetFileAttributesExW(src_resolved, GetFileExInfoStandard, &src_info)
        && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
    );
}
#endif


/*[clinic input]
os.symlink
    src: path_t
    dst: path_t
    target_is_directory: bool = False
    *
    dir_fd: dir_fd(requires='symlinkat')=None

# "symlink(src, dst, target_is_directory=False, *, dir_fd=None)\n\n\

Create a symbolic link pointing to src named dst.

target_is_directory is required on Windows if the target is to be
  interpreted as a directory.  (On Windows, symlink requires
  Windows 6.0 or greater, and raises a NotImplementedError otherwise.)
  target_is_directory is ignored on non-Windows platforms.

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.

[clinic start generated code]*/

static PyObject *
os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
                int target_is_directory, int dir_fd)
/*[clinic end generated code: output=08ca9f3f3cf960f6 input=e820ec4472547bc3]*/
{
#ifdef MS_WINDOWS
    DWORD result;
    DWORD flags = 0;

    /* Assumed true, set to false if detected to not be available. */
    static int windows_has_symlink_unprivileged_flag = TRUE;
#else
    int result;
#ifdef HAVE_SYMLINKAT
    int symlinkat_unavailable = 0;
#endif
#endif

    if (PySys_Audit("os.symlink", "OOi", src->object, dst->object,
                    dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
        return NULL;
    }

#ifdef MS_WINDOWS

    if (windows_has_symlink_unprivileged_flag) {
        /* Allow non-admin symlinks if system allows it. */
        flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
    /* if src is a directory, ensure flags==1 (target_is_directory bit) */
    if (target_is_directory || _check_dirW(src->wide, dst->wide)) {
        flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
    }

    result = CreateSymbolicLinkW(dst->wide, src->wide, flags);
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS

    if (windows_has_symlink_unprivileged_flag && !result &&
        ERROR_INVALID_PARAMETER == GetLastError()) {

        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        /* This error might be caused by
        SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE not being supported.
        Try again, and update windows_has_symlink_unprivileged_flag if we
        are successful this time.

        NOTE: There is a risk of a race condition here if there are other
        conditions than the flag causing ERROR_INVALID_PARAMETER, and
        another process (or thread) changes that condition in between our
        calls to CreateSymbolicLink.
        */
        flags &= ~(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);
        result = CreateSymbolicLinkW(dst->wide, src->wide, flags);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS

        if (result || ERROR_INVALID_PARAMETER != GetLastError()) {
            windows_has_symlink_unprivileged_flag = FALSE;
        }
    }

    if (!result)
        return path_error2(src, dst);

#else

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_SYMLINKAT
    if (dir_fd != DEFAULT_DIR_FD) {
        if (HAVE_SYMLINKAT_RUNTIME) {
            result = symlinkat(src->narrow, dir_fd, dst->narrow);
        } else {
            symlinkat_unavailable = 1;
        }
    } else
#endif
        result = symlink(src->narrow, dst->narrow);
    Py_END_ALLOW_THREADS

#ifdef HAVE_SYMLINKAT
    if (symlinkat_unavailable) {
          argument_unavailable_error(NULL, "dir_fd");
          return NULL;
    }
#endif

    if (result)
        return path_error2(src, dst);
#endif

    Py_RETURN_NONE;
}
#endif /* HAVE_SYMLINK */


static PyStructSequence_Field times_result_fields[] = {
    {"user",    "user time"},
    {"system",   "system time"},
    {"children_user",    "user time of children"},
    {"children_system",    "system time of children"},
    {"elapsed",    "elapsed time since an arbitrary point in the past"},
    {NULL}
};

PyDoc_STRVAR(times_result__doc__,
"times_result: Result from os.times().\n\n\
This object may be accessed either as a tuple of\n\
  (user, system, children_user, children_system, elapsed),\n\
or via the attributes user, system, children_user, children_system,\n\
and elapsed.\n\
\n\
See os.times for more information.");

static PyStructSequence_Desc times_result_desc = {
    MODNAME ".times_result", /* name */
    times_result__doc__, /* doc */
    times_result_fields,
    5
};

static PyObject *
build_times_result(PyObject *module, double user, double system,
    double children_user, double children_system,
    double elapsed)
{
    PyObject *TimesResultType = get_posix_state(module)->TimesResultType;
    PyObject *value = PyStructSequence_New((PyTypeObject *)TimesResultType);
    if (value == NULL)
        return NULL;

#define SET(i, field) \
    { \
    PyObject *o = PyFloat_FromDouble(field); \
    if (!o) { \
        Py_DECREF(value); \
        return NULL; \
    } \
    PyStructSequence_SET_ITEM(value, i, o); \
    } \

    SET(0, user);
    SET(1, system);
    SET(2, children_user);
    SET(3, children_system);
    SET(4, elapsed);

#undef SET

    return value;
}


/*[clinic input]
os.times

Return a collection containing process timing information.

The object returned behaves like a named tuple with these fields:
  (utime, stime, cutime, cstime, elapsed_time)
All fields are floating-point numbers.
[clinic start generated code]*/

static PyObject *
os_times_impl(PyObject *module)
/*[clinic end generated code: output=35f640503557d32a input=8dbfe33a2dcc3df3]*/
{
#ifdef MS_WINDOWS
    FILETIME create, exit, kernel, user;
    HANDLE hProc;
    hProc = GetCurrentProcess();
    GetProcessTimes(hProc, &create, &exit, &kernel, &user);
    /* The fields of a FILETIME structure are the hi and lo part
       of a 64-bit value expressed in 100 nanosecond units.
       1e7 is one second in such units; 1e-7 the inverse.
       429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
    */
    return build_times_result(module,
        (double)(user.dwHighDateTime*429.4967296 +
                 user.dwLowDateTime*1e-7),
        (double)(kernel.dwHighDateTime*429.4967296 +
                 kernel.dwLowDateTime*1e-7),
        (double)0,
        (double)0,
        (double)0);
#else /* MS_WINDOWS */
    _posixstate *state = get_posix_state(module);
    long ticks_per_second = state->ticks_per_second;

    struct tms process;
    clock_t elapsed;
    errno = 0;
    elapsed = times(&process);
    if (elapsed == (clock_t) -1) {
        return posix_error();
    }

    return build_times_result(module,
        (double)process.tms_utime / ticks_per_second,
        (double)process.tms_stime / ticks_per_second,
        (double)process.tms_cutime / ticks_per_second,
        (double)process.tms_cstime / ticks_per_second,
        (double)elapsed / ticks_per_second);
#endif /* MS_WINDOWS */
}


#if defined(HAVE_TIMERFD_CREATE)
#define ONE_SECOND_IN_NS (1000 * 1000 * 1000)
#define EXTRACT_NSEC(value)  (long)( ( (double)(value) - (time_t)(value) ) * 1e9)
#define CONVERT_SEC_AND_NSEC_TO_DOUBLE(sec, nsec) ( (double)(sec) + (double)(nsec) * 1e-9 )

static PyObject *
build_itimerspec(const struct itimerspec* curr_value)
{
    double _value = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_value.tv_sec,
                                                          curr_value->it_value.tv_nsec);
    PyObject *value = PyFloat_FromDouble(_value);
    if (value == NULL) {
        return NULL;
    }
    double _interval = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_interval.tv_sec,
                                                   curr_value->it_interval.tv_nsec);
    PyObject *interval = PyFloat_FromDouble(_interval);
    if (interval == NULL) {
        Py_DECREF(value);
        return NULL;
    }
    PyObject *tuple = PyTuple_Pack(2, value, interval);
    Py_DECREF(interval);
    Py_DECREF(value);
    return tuple;
}

static PyObject *
build_itimerspec_ns(const struct itimerspec* curr_value)
{
    PyTime_t value, interval;
    if (_PyTime_FromTimespec(&value, &curr_value->it_value) < 0) {
        return NULL;
    }
    if (_PyTime_FromTimespec(&interval, &curr_value->it_interval) < 0) {
        return NULL;
    }
    return Py_BuildValue("LL", value, interval);
}

/*[clinic input]
os.timerfd_create

    clockid: int
        A valid clock ID constant as timer file descriptor.

        time.CLOCK_REALTIME
        time.CLOCK_MONOTONIC
        time.CLOCK_BOOTTIME
    /
    *
    flags: int = 0
        0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC.

        os.TFD_NONBLOCK
            If *TFD_NONBLOCK* is set as a flag, read doesn't blocks.
            If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires.

        os.TFD_CLOEXEC
            If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag

Create and return a timer file descriptor.
[clinic start generated code]*/

static PyObject *
os_timerfd_create_impl(PyObject *module, int clockid, int flags)
/*[clinic end generated code: output=1caae80fb168004a input=64b7020c5ac0b8f4]*/

{
    int fd;
    Py_BEGIN_ALLOW_THREADS
    flags |= TFD_CLOEXEC;  // PEP 446: always create non-inheritable FD
    fd = timerfd_create(clockid, flags);
    Py_END_ALLOW_THREADS
    if (fd == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return PyLong_FromLong(fd);
}

/*[clinic input]
os.timerfd_settime

    fd: fildes
        A timer file descriptor.
    /
    *
    flags: int = 0
        0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.
    initial as initial_double: double = 0.0
        The initial expiration time, in seconds.
    interval as interval_double: double = 0.0
        The timer's interval, in seconds.

Alter a timer file descriptor's internal timer in seconds.
[clinic start generated code]*/

static PyObject *
os_timerfd_settime_impl(PyObject *module, int fd, int flags,
                        double initial_double, double interval_double)
/*[clinic end generated code: output=df4c1bce6859224e input=81d2c0d7e936e8a7]*/
{
    PyTime_t initial, interval;
    if (_PyTime_FromSecondsDouble(initial_double, _PyTime_ROUND_FLOOR,
                                  &initial) < 0) {
        return NULL;
    }
    if (_PyTime_FromSecondsDouble(interval_double, _PyTime_ROUND_FLOOR,
                                  &interval) < 0) {
        return NULL;
    }

    struct itimerspec new_value, old_value;
    if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) {
        PyErr_SetString(PyExc_ValueError, "invalid initial value");
        return NULL;
    }
    if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) {
        PyErr_SetString(PyExc_ValueError, "invalid interval value");
        return NULL;
    }

    int result;
    Py_BEGIN_ALLOW_THREADS
    result = timerfd_settime(fd, flags, &new_value, &old_value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return build_itimerspec(&old_value);
}


/*[clinic input]
os.timerfd_settime_ns

    fd: fildes
        A timer file descriptor.
    /
    *
    flags: int = 0
        0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.
    initial: long_long = 0
        initial expiration timing in seconds.
    interval: long_long = 0
        interval for the timer in seconds.

Alter a timer file descriptor's internal timer in nanoseconds.
[clinic start generated code]*/

static PyObject *
os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags,
                           long long initial, long long interval)
/*[clinic end generated code: output=6273ec7d7b4cc0b3 input=261e105d6e42f5bc]*/
{
    struct itimerspec new_value;
    struct itimerspec old_value;
    int result;
    if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) {
        PyErr_SetString(PyExc_ValueError, "invalid initial value");
        return NULL;
    }
    if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) {
        PyErr_SetString(PyExc_ValueError, "invalid interval value");
        return NULL;
    }
    Py_BEGIN_ALLOW_THREADS
    result = timerfd_settime(fd, flags, &new_value, &old_value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return build_itimerspec_ns(&old_value);
}

/*[clinic input]
@permit_long_summary
os.timerfd_gettime

    fd: fildes
        A timer file descriptor.
    /

Return a tuple of a timer file descriptor's (interval, next expiration) in float seconds.
[clinic start generated code]*/

static PyObject *
os_timerfd_gettime_impl(PyObject *module, int fd)
/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=05f7d568a4820dc6]*/
{
    struct itimerspec curr_value;
    int result;
    Py_BEGIN_ALLOW_THREADS
    result = timerfd_gettime(fd, &curr_value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return build_itimerspec(&curr_value);
}


/*[clinic input]
@permit_long_summary
os.timerfd_gettime_ns

    fd: fildes
        A timer file descriptor.
    /

Return a tuple of a timer file descriptor's (interval, next expiration) in nanoseconds.
[clinic start generated code]*/

static PyObject *
os_timerfd_gettime_ns_impl(PyObject *module, int fd)
/*[clinic end generated code: output=580633a4465f39fe input=d0de95b9782179c5]*/
{
    struct itimerspec curr_value;
    int result;
    Py_BEGIN_ALLOW_THREADS
    result = timerfd_gettime(fd, &curr_value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return build_itimerspec_ns(&curr_value);
}

#undef ONE_SECOND_IN_NS
#undef EXTRACT_NSEC

#endif  /* HAVE_TIMERFD_CREATE */

#ifdef HAVE_GETSID
/*[clinic input]
os.getsid

    pid: pid_t
    /

Call the system call getsid(pid) and return the result.
[clinic start generated code]*/

static PyObject *
os_getsid_impl(PyObject *module, pid_t pid)
/*[clinic end generated code: output=112deae56b306460 input=eeb2b923a30ce04e]*/
{
    int sid;
    sid = getsid(pid);
    if (sid < 0)
        return posix_error();
    return PyLong_FromLong((long)sid);
}
#endif /* HAVE_GETSID */


#ifdef HAVE_SETSID
/*[clinic input]
os.setsid

Call the system call setsid().
[clinic start generated code]*/

static PyObject *
os_setsid_impl(PyObject *module)
/*[clinic end generated code: output=e2ddedd517086d77 input=5fff45858e2f0776]*/
{
    if (setsid() < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETSID */


#ifdef HAVE_SETPGID
/*[clinic input]
os.setpgid

    pid: pid_t
    pgrp: pid_t
    /

Call the system call setpgid(pid, pgrp).
[clinic start generated code]*/

static PyObject *
os_setpgid_impl(PyObject *module, pid_t pid, pid_t pgrp)
/*[clinic end generated code: output=6461160319a43d6a input=fceb395eca572e1a]*/
{
    if (setpgid(pid, pgrp) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETPGID */


#ifdef HAVE_TCGETPGRP
/*[clinic input]
os.tcgetpgrp

    fd: int
    /

Return the process group associated with the terminal specified by fd.
[clinic start generated code]*/

static PyObject *
os_tcgetpgrp_impl(PyObject *module, int fd)
/*[clinic end generated code: output=f865e88be86c272b input=7f6c18eac10ada86]*/
{
    pid_t pgid = tcgetpgrp(fd);
    if (pgid < 0)
        return posix_error();
    return PyLong_FromPid(pgid);
}
#endif /* HAVE_TCGETPGRP */


#ifdef HAVE_TCSETPGRP
/*[clinic input]
os.tcsetpgrp

    fd: int
    pgid: pid_t
    /

Set the process group associated with the terminal specified by fd.
[clinic start generated code]*/

static PyObject *
os_tcsetpgrp_impl(PyObject *module, int fd, pid_t pgid)
/*[clinic end generated code: output=f1821a381b9daa39 input=5bdc997c6a619020]*/
{
    if (tcsetpgrp(fd, pgid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_TCSETPGRP */

/* Functions acting on file descriptors */

#ifdef O_CLOEXEC
extern int _Py_open_cloexec_works;
#endif


/*[clinic input]
os.open -> int
    path: path_t
    flags: int
    mode: int = 0o777
    *
    dir_fd: dir_fd(requires='openat') = None

# "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\

Open a file for low level IO.  Returns a file descriptor (integer).

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/

static int
os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd)
/*[clinic end generated code: output=abc7227888c8bc73 input=ad8623b29acd2934]*/
{
    int fd;
    int async_err = 0;
#ifdef HAVE_OPENAT
    int openat_unavailable = 0;
#endif

#ifdef O_CLOEXEC
    int *atomic_flag_works = &_Py_open_cloexec_works;
#elif !defined(MS_WINDOWS)
    int *atomic_flag_works = NULL;
#endif

#ifdef MS_WINDOWS
    flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
    flags |= O_CLOEXEC;
#endif

    if (PySys_Audit("open", "OOi", path->object, Py_None, flags) < 0) {
        return -1;
    }

    _Py_BEGIN_SUPPRESS_IPH
    do {
        Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
        fd = _wopen(path->wide, flags, mode);
#else
#ifdef HAVE_OPENAT
        if (dir_fd != DEFAULT_DIR_FD) {
            if (HAVE_OPENAT_RUNTIME) {
                fd = openat(dir_fd, path->narrow, flags, mode);

            } else {
                openat_unavailable = 1;
                fd = -1;
            }
        } else
#endif /* HAVE_OPENAT */
            fd = open(path->narrow, flags, mode);
#endif /* !MS_WINDOWS */
        Py_END_ALLOW_THREADS
    } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    _Py_END_SUPPRESS_IPH

#ifdef HAVE_OPENAT
    if (openat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return -1;
    }
#endif

    if (fd < 0) {
        if (!async_err)
            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
        return -1;
    }

#ifndef MS_WINDOWS
    if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) {
        close(fd);
        return -1;
    }
#endif

    return fd;
}


/*[clinic input]
os.close

    fd: int

Close a file descriptor.
[clinic start generated code]*/

static PyObject *
os_close_impl(PyObject *module, int fd)
/*[clinic end generated code: output=2fe4e93602822c14 input=2bc42451ca5c3223]*/
{
    int res;
    /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/
     * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
     * for more details.
     */
    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
    res = close(fd);
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    if (res < 0)
        return posix_error();
    Py_RETURN_NONE;
}

/*[clinic input]
os.closerange

    fd_low: int
    fd_high: int
    /

Closes all file descriptors in [fd_low, fd_high), ignoring errors.
[clinic start generated code]*/

static PyObject *
os_closerange_impl(PyObject *module, int fd_low, int fd_high)
/*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/
{
    Py_BEGIN_ALLOW_THREADS
    _Py_closerange(fd_low, fd_high - 1);
    Py_END_ALLOW_THREADS
    Py_RETURN_NONE;
}


/*[clinic input]
os.dup -> int

    fd: int
    /

Return a duplicate of a file descriptor.
[clinic start generated code]*/

static int
os_dup_impl(PyObject *module, int fd)
/*[clinic end generated code: output=486f4860636b2a9f input=6f10f7ea97f7852a]*/
{
    return _Py_dup(fd);
}

// dup2() is either provided by libc or dup2.c with AC_REPLACE_FUNCS().
// dup2.c provides working dup2() if and only if F_DUPFD is available.
#if (defined(HAVE_DUP3) || defined(F_DUPFD) || defined(MS_WINDOWS))
/*[clinic input]
os.dup2 -> int
    fd: int
    fd2: int
    inheritable: bool=True

Duplicate file descriptor.
[clinic start generated code]*/

static int
os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
/*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/
{
    int res = 0;
#if defined(HAVE_DUP3) && \
    !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC))
    /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */
    static int dup3_works = -1;
#endif

    /* dup2() can fail with EINTR if the target FD is already open, because it
     * then has to be closed. See os_close_impl() for why we don't handle EINTR
     * upon close(), and therefore below.
     */
#ifdef MS_WINDOWS
    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
    res = dup2(fd, fd2);
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    if (res < 0) {
        posix_error();
        return -1;
    }
    res = fd2; // msvcrt dup2 returns 0 on success.

    /* Character files like console cannot be make non-inheritable */
    if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
        close(fd2);
        return -1;
    }

#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)
    Py_BEGIN_ALLOW_THREADS
    if (!inheritable)
        res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
    else
        res = dup2(fd, fd2);
    Py_END_ALLOW_THREADS
    if (res < 0) {
        posix_error();
        return -1;
    }

#else

#ifdef HAVE_DUP3
    if (!inheritable && dup3_works != 0) {
        Py_BEGIN_ALLOW_THREADS
        res = dup3(fd, fd2, O_CLOEXEC);
        Py_END_ALLOW_THREADS
        if (res < 0) {
            if (dup3_works == -1)
                dup3_works = (errno != ENOSYS);
            if (dup3_works) {
                posix_error();
                return -1;
            }
        }
    }

    if (inheritable || dup3_works == 0)
    {
#endif
        Py_BEGIN_ALLOW_THREADS
        res = dup2(fd, fd2);
        Py_END_ALLOW_THREADS
        if (res < 0) {
            posix_error();
            return -1;
        }

        if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
            close(fd2);
            return -1;
        }
#ifdef HAVE_DUP3
    }
#endif

#endif

    return res;
}
#endif


#ifdef HAVE_LOCKF
/*[clinic input]
os.lockf

    fd: int
        An open file descriptor.
    command: int
        One of F_LOCK, F_TLOCK, F_ULOCK or F_TEST.
    length: Py_off_t
        The number of bytes to lock, starting at the current position.
    /

Apply, test or remove a POSIX lock on an open file descriptor.

[clinic start generated code]*/

static PyObject *
os_lockf_impl(PyObject *module, int fd, int command, Py_off_t length)
/*[clinic end generated code: output=af7051f3e7c29651 input=65da41d2106e9b79]*/
{
    int res;

    if (PySys_Audit("os.lockf", "iiL", fd, command, length) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    res = lockf(fd, command, length);
    Py_END_ALLOW_THREADS

    if (res < 0)
        return posix_error();

    Py_RETURN_NONE;
}
#endif /* HAVE_LOCKF */


/*[clinic input]
@permit_long_docstring_body
os.lseek -> Py_off_t

    fd: int
        An open file descriptor, as returned by os.open().
    position: Py_off_t
        Position, interpreted relative to 'whence'.
    whence as how: int
        The relative position to seek from. Valid values are:
        - SEEK_SET: seek from the start of the file.
        - SEEK_CUR: seek from the current file position.
        - SEEK_END: seek from the end of the file.
    /

Set the position of a file descriptor.  Return the new position.

The return value is the number of bytes relative to the beginning of the file.
[clinic start generated code]*/

static Py_off_t
os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how)
/*[clinic end generated code: output=971e1efb6b30bd2f input=4a3de549f07e1c40]*/
{
    Py_off_t result;

#ifdef SEEK_SET
    /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
    switch (how) {
        case 0: how = SEEK_SET; break;
        case 1: how = SEEK_CUR; break;
        case 2: how = SEEK_END; break;
    }
#endif /* SEEK_END */

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
    result = _lseeki64(fd, position, how);
#else
    result = lseek(fd, position, how);
#endif
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    if (result < 0)
        posix_error();

    return result;
}


/*[clinic input]
os.read
    fd: int
    length: Py_ssize_t
    /

Read from a file descriptor.  Returns a bytes object.
[clinic start generated code]*/

static PyObject *
os_read_impl(PyObject *module, int fd, Py_ssize_t length)
/*[clinic end generated code: output=dafbe9a5cddb987b input=1df2eaa27c0bf1d3]*/
{
    if (length < 0) {
        errno = EINVAL;
        return posix_error();
    }

    length = Py_MIN(length, _PY_READ_MAX);

    PyBytesWriter *writer = PyBytesWriter_Create(length);
    if (writer == NULL) {
        return NULL;
    }

    Py_ssize_t n = _Py_read(fd, PyBytesWriter_GetData(writer), length);
    if (n == -1) {
        PyBytesWriter_Discard(writer);
        return NULL;
    }

    return PyBytesWriter_FinishWithSize(writer, n);
}

/*[clinic input]
@permit_long_docstring_body
os.readinto -> Py_ssize_t
    fd: int
    buffer: Py_buffer(accept={rwbuffer})
    /

Read into a buffer object from a file descriptor.

The buffer should be mutable and bytes-like. On success, returns the number of
bytes read. Less bytes may be read than the size of the buffer. The underlying
system call will be retried when interrupted by a signal, unless the signal
handler raises an exception. Other errors will not be retried and an error will
be raised.

Returns 0 if *fd* is at end of file or if the provided *buffer* has length 0
(which can be used to check for errors without reading data). Never returns
negative.
[clinic start generated code]*/

static Py_ssize_t
os_readinto_impl(PyObject *module, int fd, Py_buffer *buffer)
/*[clinic end generated code: output=8091a3513c683a80 input=a770382bd3d32f9a]*/
{
    assert(buffer->len >= 0);
    Py_ssize_t result = _Py_read(fd, buffer->buf, buffer->len);
    /* Ensure negative is never returned without an error. Simplifies calling
        code. _Py_read should succeed, possibly reading 0 bytes, _or_ set an
        error. */
    assert(result >= 0 || (result == -1 && PyErr_Occurred()));
    return result;
}

#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \
                                || defined(__APPLE__))) \
    || defined(HAVE_READV) || defined(HAVE_PREADV) || defined (HAVE_PREADV2) \
    || defined(HAVE_WRITEV) || defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2)
static int
iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, Py_ssize_t cnt, int type)
{
    Py_ssize_t i, j;

    *iov = PyMem_New(struct iovec, cnt);
    if (*iov == NULL) {
        PyErr_NoMemory();
        return -1;
    }

    *buf = PyMem_New(Py_buffer, cnt);
    if (*buf == NULL) {
        PyMem_Free(*iov);
        PyErr_NoMemory();
        return -1;
    }

    for (i = 0; i < cnt; i++) {
        PyObject *item = PySequence_GetItem(seq, i);
        if (item == NULL)
            goto fail;
        if (PyObject_GetBuffer(item, &(*buf)[i], type) == -1) {
            Py_DECREF(item);
            goto fail;
        }
        Py_DECREF(item);
        (*iov)[i].iov_base = (*buf)[i].buf;
        (*iov)[i].iov_len = (*buf)[i].len;
    }
    return 0;

fail:
    PyMem_Free(*iov);
    for (j = 0; j < i; j++) {
        PyBuffer_Release(&(*buf)[j]);
    }
    PyMem_Free(*buf);
    return -1;
}

static void
iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt)
{
    int i;
    PyMem_Free(iov);
    for (i = 0; i < cnt; i++) {
        PyBuffer_Release(&buf[i]);
    }
    PyMem_Free(buf);
}
#endif


#ifdef HAVE_READV
/*[clinic input]
os.readv -> Py_ssize_t

    fd: int
    buffers: object
    /

Read from a file descriptor fd into an iterable of buffers.

The buffers should be mutable buffers accepting bytes.
readv will transfer data into each buffer until it is full
and then move on to the next buffer in the sequence to hold
the rest of the data.

readv returns the total number of bytes read,
which may be less than the total capacity of all the buffers.
[clinic start generated code]*/

static Py_ssize_t
os_readv_impl(PyObject *module, int fd, PyObject *buffers)
/*[clinic end generated code: output=792da062d3fcebdb input=e679eb5dbfa0357d]*/
{
    Py_ssize_t cnt, n;
    int async_err = 0;
    struct iovec *iov;
    Py_buffer *buf;

    if (!PySequence_Check(buffers)) {
        PyErr_SetString(PyExc_TypeError,
            "readv() arg 2 must be a sequence");
        return -1;
    }

    cnt = PySequence_Size(buffers);
    if (cnt < 0)
        return -1;

    if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0)
        return -1;

    do {
        Py_BEGIN_ALLOW_THREADS
        n = readv(fd, iov, cnt);
        Py_END_ALLOW_THREADS
    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    int saved_errno = errno;
    iov_cleanup(iov, buf, cnt);
    if (n < 0) {
        if (!async_err) {
            errno = saved_errno;
            posix_error();
        }
        return -1;
    }

    return n;
}
#endif /* HAVE_READV */


#ifdef HAVE_PREAD
/*[clinic input]
@permit_long_summary
os.pread

    fd: int
    length: Py_ssize_t
    offset: Py_off_t
    /

Read a number of bytes from a file descriptor starting at a particular offset.

Read length bytes from file descriptor fd, starting at offset bytes from
the beginning of the file.  The file offset remains unchanged.
[clinic start generated code]*/

static PyObject *
os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset)
/*[clinic end generated code: output=3f875c1eef82e32f input=5943beb009d3da04]*/
{
    Py_ssize_t n;
    int async_err = 0;

    if (length < 0) {
        errno = EINVAL;
        return posix_error();
    }
    PyBytesWriter *writer = PyBytesWriter_Create(length);
    if (writer == NULL) {
        return NULL;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        n = pread(fd, PyBytesWriter_GetData(writer), length, offset);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    if (n < 0) {
        if (!async_err) {
            posix_error();
        }
        PyBytesWriter_Discard(writer);
        return NULL;
    }
    return PyBytesWriter_FinishWithSize(writer, n);
}
#endif /* HAVE_PREAD */

#if defined(HAVE_PREADV) || defined (HAVE_PREADV2)
/*[clinic input]
@permit_long_docstring_body
os.preadv -> Py_ssize_t

    fd: int
    buffers: object
    offset: Py_off_t
    flags: int = 0
    /

Reads from a file descriptor into a number of mutable bytes-like objects.

Combines the functionality of readv() and pread(). As readv(), it will
transfer data into each buffer until it is full and then move on to the next
buffer in the sequence to hold the rest of the data. Its fourth argument,
specifies the file offset at which the input operation is to be performed. It
will return the total number of bytes read (which can be less than the total
capacity of all the objects).

The flags argument contains a bitwise OR of zero or more of the following flags:

- RWF_HIPRI
- RWF_NOWAIT
- RWF_DONTCACHE

Using non-zero flags requires Linux 4.6 or newer.
[clinic start generated code]*/

static Py_ssize_t
os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
               int flags)
/*[clinic end generated code: output=26fc9c6e58e7ada5 input=34fb3b9ca06f7ba7]*/
{
    Py_ssize_t cnt, n;
    int async_err = 0;
    struct iovec *iov;
    Py_buffer *buf;

    if (!PySequence_Check(buffers)) {
        PyErr_SetString(PyExc_TypeError,
            "preadv2() arg 2 must be a sequence");
        return -1;
    }

    cnt = PySequence_Size(buffers);
    if (cnt < 0) {
        return -1;
    }

#ifndef HAVE_PREADV2
    if(flags != 0) {
        argument_unavailable_error("preadv2", "flags");
        return -1;
    }
#endif

    if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) {
        return -1;
    }
#ifdef HAVE_PREADV2
    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        n = preadv2(fd, iov, cnt, offset, flags);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
#else
    do {
#if defined(__APPLE__) && defined(__clang__)
/* This entire function will be removed from the module dict when the API
 * is not available.
 */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
#endif
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        n = preadv(fd, iov, cnt, offset);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif

#endif

    int saved_errno = errno;
    iov_cleanup(iov, buf, cnt);
    if (n < 0) {
        if (!async_err) {
            errno = saved_errno;
            posix_error();
        }
        return -1;
    }

    return n;
}
#endif /* HAVE_PREADV */


/*[clinic input]
os.write -> Py_ssize_t

    fd: int
    data: Py_buffer
    /

Write a bytes object to a file descriptor.
[clinic start generated code]*/

static Py_ssize_t
os_write_impl(PyObject *module, int fd, Py_buffer *data)
/*[clinic end generated code: output=e4ef5bc904b58ef9 input=3207e28963234f3c]*/
{
    return _Py_write(fd, data->buf, data->len);
}

#ifdef HAVE_SENDFILE
#ifdef __APPLE__
/*[clinic input]
os.sendfile

    out_fd: int
    in_fd: int
    offset: Py_off_t
    count as sbytes: Py_off_t
    headers: object(c_default="NULL") = ()
    trailers: object(c_default="NULL") = ()
    flags: int = 0

Copy count bytes from file descriptor in_fd to file descriptor out_fd.
[clinic start generated code]*/

static PyObject *
os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset,
                 Py_off_t sbytes, PyObject *headers, PyObject *trailers,
                 int flags)
/*[clinic end generated code: output=81c4bcd143f5c82b input=b0d72579d4c69afa]*/
#elif defined(__FreeBSD__) || defined(__DragonFly__)
/*[clinic input]
os.sendfile

    out_fd: int
    in_fd: int
    offset: Py_off_t
    count: Py_ssize_t(allow_negative=False)
    headers: object(c_default="NULL") = ()
    trailers: object(c_default="NULL") = ()
    flags: int = 0

Copy count bytes from file descriptor in_fd to file descriptor out_fd.
[clinic start generated code]*/

static PyObject *
os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset,
                 Py_ssize_t count, PyObject *headers, PyObject *trailers,
                 int flags)
/*[clinic end generated code: output=329ea009bdd55afc input=dcb026b94effa922]*/
#else
/*[clinic input]
os.sendfile

    out_fd: int
    in_fd: int
    offset as offobj: object
    count: Py_ssize_t(allow_negative=False)

Copy count bytes from file descriptor in_fd to file descriptor out_fd.
[clinic start generated code]*/

static PyObject *
os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj,
                 Py_ssize_t count)
/*[clinic end generated code: output=ae81216e40f167d8 input=424df0949059ea5b]*/
#endif
{
    Py_ssize_t ret;
    int async_err = 0;

#ifdef __APPLE__
    if(sbytes < 0) {
        PyErr_SetString(PyExc_ValueError,
                        "count cannot be negative");
        return NULL;
    }
#else
    assert(count >= 0);
#endif

#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
#ifndef __APPLE__
    off_t sbytes;
#endif
    Py_buffer *hbuf, *tbuf;
    struct sf_hdtr sf;

    sf.headers = NULL;
    sf.trailers = NULL;

    if (headers != NULL) {
        if (!PySequence_Check(headers)) {
            PyErr_SetString(PyExc_TypeError,
                "sendfile() headers must be a sequence");
            return NULL;
        } else {
            Py_ssize_t i = PySequence_Size(headers);
            if (i < 0)
                return NULL;
            if (i > INT_MAX) {
                PyErr_SetString(PyExc_OverflowError,
                    "sendfile() header is too large");
                return NULL;
            }
            if (i > 0) {
                sf.hdr_cnt = (int)i;
                if (iov_setup(&(sf.headers), &hbuf,
                              headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0)
                    return NULL;
#ifdef __APPLE__
                for (i = 0; i < sf.hdr_cnt; i++) {
                    Py_ssize_t blen = sf.headers[i].iov_len;
# define OFF_T_MAX 0x7fffffffffffffff
                    if (sbytes >= OFF_T_MAX - blen) {
                        PyErr_SetString(PyExc_OverflowError,
                            "sendfile() header is too large");
                        return NULL;
                    }
                    sbytes += blen;
                }
#endif
            }
        }
    }
    if (trailers != NULL) {
        if (!PySequence_Check(trailers)) {
            PyErr_SetString(PyExc_TypeError,
                "sendfile() trailers must be a sequence");
            return NULL;
        } else {
            Py_ssize_t i = PySequence_Size(trailers);
            if (i < 0)
                return NULL;
            if (i > INT_MAX) {
                PyErr_SetString(PyExc_OverflowError,
                    "sendfile() trailer is too large");
                return NULL;
            }
            if (i > 0) {
                sf.trl_cnt = (int)i;
                if (iov_setup(&(sf.trailers), &tbuf,
                              trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0)
                    return NULL;
            }
        }
    }

    _Py_BEGIN_SUPPRESS_IPH
    do {
        Py_BEGIN_ALLOW_THREADS
#ifdef __APPLE__
        ret = sendfile(in_fd, out_fd, offset, &sbytes, &sf, flags);
#else
        ret = sendfile(in_fd, out_fd, offset, count, &sf, &sbytes, flags);
#endif
        Py_END_ALLOW_THREADS
    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    _Py_END_SUPPRESS_IPH

    int saved_errno = errno;
    if (sf.headers != NULL)
        iov_cleanup(sf.headers, hbuf, sf.hdr_cnt);
    if (sf.trailers != NULL)
        iov_cleanup(sf.trailers, tbuf, sf.trl_cnt);

    if (ret < 0) {
        if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) {
            if (sbytes != 0) {
                // some data has been sent
                goto done;
            }
            // no data has been sent; upper application is supposed
            // to retry on EAGAIN or EBUSY
        }
        if (!async_err) {
            errno = saved_errno;
            posix_error();
        }
        return NULL;
    }
    goto done;

done:
    #if !defined(HAVE_LARGEFILE_SUPPORT)
        return PyLong_FromLong(sbytes);
    #else
        return PyLong_FromLongLong(sbytes);
    #endif

#else
#ifdef __linux__
    if (offobj == Py_None) {
        do {
            Py_BEGIN_ALLOW_THREADS
            ret = sendfile(out_fd, in_fd, NULL, count);
            Py_END_ALLOW_THREADS
        } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
        if (ret < 0)
            return (!async_err) ? posix_error() : NULL;
        return PyLong_FromSsize_t(ret);
    }
#endif
    off_t offset;
    if (!Py_off_t_converter(offobj, &offset))
        return NULL;

#if defined(__sun) && defined(__SVR4)
    // On Solaris, sendfile raises EINVAL rather than returning 0
    // when the offset is equal or bigger than the in_fd size.
    struct stat st;

    do {
        Py_BEGIN_ALLOW_THREADS
        ret = fstat(in_fd, &st);
        Py_END_ALLOW_THREADS
    } while (ret != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (ret < 0)
        return (!async_err) ? posix_error() : NULL;

    if (offset >= st.st_size) {
        return PyLong_FromLong(0);
    }

    // On illumos specifically sendfile() may perform a partial write but
    // return -1/an error (in one confirmed case the destination socket
    // had a 5 second timeout set and errno was EAGAIN) and it's on the client
    // code to check if the offset parameter was modified by sendfile().
    //
    // We need this variable to track said change.
    off_t original_offset = offset;
#endif

    do {
        Py_BEGIN_ALLOW_THREADS
        ret = sendfile(out_fd, in_fd, &offset, count);
#if defined(__sun) && defined(__SVR4)
        // This handles illumos-specific sendfile() partial write behavior,
        // see a comment above for more details.
        if (ret < 0 && offset != original_offset) {
            ret = offset - original_offset;
        }
#endif
        Py_END_ALLOW_THREADS
    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (ret < 0)
        return (!async_err) ? posix_error() : NULL;
    return PyLong_FromSsize_t(ret);
#endif
}
#endif /* HAVE_SENDFILE */


#if defined(__APPLE__)
/*[clinic input]
os._fcopyfile

    in_fd: int
    out_fd: int
    flags: int
    /

Efficiently copy content or metadata of 2 regular file descriptors (macOS).
[clinic start generated code]*/

static PyObject *
os__fcopyfile_impl(PyObject *module, int in_fd, int out_fd, int flags)
/*[clinic end generated code: output=c9d1a35a992e401b input=1e34638a86948795]*/
{
    int ret;

    Py_BEGIN_ALLOW_THREADS
    ret = fcopyfile(in_fd, out_fd, NULL, flags);
    Py_END_ALLOW_THREADS
    if (ret < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif


/*[clinic input]
os.fstat

    fd : int

Perform a stat system call on the given file descriptor.

Like stat(), but for an open file descriptor.
Equivalent to os.stat(fd).
[clinic start generated code]*/

static PyObject *
os_fstat_impl(PyObject *module, int fd)
/*[clinic end generated code: output=efc038cb5f654492 input=27e0e0ebbe5600c9]*/
{
    STRUCT_STAT st;
    int res;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        res = FSTAT(fd, &st);
        Py_END_ALLOW_THREADS
    } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    if (res != 0) {
#ifdef MS_WINDOWS
        return PyErr_SetFromWindowsErr(0);
#else
        return (!async_err) ? posix_error() : NULL;
#endif
    }

    return _pystat_fromstructstat(module, &st);
}


/*[clinic input]
os.isatty -> bool
    fd: int
    /

Return True if the fd is connected to a terminal.

Return True if the file descriptor is an open file descriptor
connected to the slave end of a terminal.
[clinic start generated code]*/

static int
os_isatty_impl(PyObject *module, int fd)
/*[clinic end generated code: output=6a48c8b4e644ca00 input=08ce94aa1eaf7b5e]*/
{
    int return_value;
    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
    return_value = isatty(fd);
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    return return_value;
}


#ifdef HAVE_PIPE
/*[clinic input]
os.pipe

Create a pipe.

Returns a tuple of two file descriptors:
  (read_fd, write_fd)
[clinic start generated code]*/

static PyObject *
os_pipe_impl(PyObject *module)
/*[clinic end generated code: output=ff9b76255793b440 input=02535e8c8fa6c4d4]*/
{
    int fds[2];
#ifdef MS_WINDOWS
    HANDLE read, write;
    SECURITY_ATTRIBUTES attr;
    BOOL ok;
#else
    int res;
#endif

#ifdef MS_WINDOWS
    attr.nLength = sizeof(attr);
    attr.lpSecurityDescriptor = NULL;
    attr.bInheritHandle = FALSE;

    Py_BEGIN_ALLOW_THREADS
    ok = CreatePipe(&read, &write, &attr, 0);
    if (ok) {
        fds[0] = _Py_open_osfhandle_noraise(read, _O_RDONLY | _O_NOINHERIT);
        fds[1] = _Py_open_osfhandle_noraise(write, _O_WRONLY | _O_NOINHERIT);
        if (fds[0] == -1 || fds[1] == -1) {
            CloseHandle(read);
            CloseHandle(write);
            ok = 0;
        }
    }
    Py_END_ALLOW_THREADS

    if (!ok)
        return PyErr_SetFromWindowsErr(0);
#else

#ifdef HAVE_PIPE2
    Py_BEGIN_ALLOW_THREADS
    res = pipe2(fds, O_CLOEXEC);
    Py_END_ALLOW_THREADS

    if (res != 0 && errno == ENOSYS)
    {
#endif
        Py_BEGIN_ALLOW_THREADS
        res = pipe(fds);
        Py_END_ALLOW_THREADS

        if (res == 0) {
            if (_Py_set_inheritable(fds[0], 0, NULL) < 0) {
                close(fds[0]);
                close(fds[1]);
                return NULL;
            }
            if (_Py_set_inheritable(fds[1], 0, NULL) < 0) {
                close(fds[0]);
                close(fds[1]);
                return NULL;
            }
        }
#ifdef HAVE_PIPE2
    }
#endif

    if (res != 0)
        return PyErr_SetFromErrno(PyExc_OSError);
#endif /* !MS_WINDOWS */
    return Py_BuildValue("(ii)", fds[0], fds[1]);
}
#endif  /* HAVE_PIPE */


#ifdef HAVE_PIPE2
/*[clinic input]
os.pipe2

    flags: int
    /

Create a pipe with flags set atomically.

Returns a tuple of two file descriptors:
  (read_fd, write_fd)

flags can be constructed by ORing together one or more of these values:
O_NONBLOCK, O_CLOEXEC.
[clinic start generated code]*/

static PyObject *
os_pipe2_impl(PyObject *module, int flags)
/*[clinic end generated code: output=25751fb43a45540f input=f261b6e7e63c6817]*/
{
    int fds[2];
    int res;

    res = pipe2(fds, flags);
    if (res != 0)
        return posix_error();
    return Py_BuildValue("(ii)", fds[0], fds[1]);
}
#endif /* HAVE_PIPE2 */


#ifdef HAVE_WRITEV
/*[clinic input]
os.writev -> Py_ssize_t
    fd: int
    buffers: object
    /

Iterate over buffers, and write the contents of each to a file descriptor.

Returns the total number of bytes written.
buffers must be a sequence of bytes-like objects.
[clinic start generated code]*/

static Py_ssize_t
os_writev_impl(PyObject *module, int fd, PyObject *buffers)
/*[clinic end generated code: output=56565cfac3aac15b input=5b8d17fe4189d2fe]*/
{
    Py_ssize_t cnt;
    Py_ssize_t result;
    int async_err = 0;
    struct iovec *iov;
    Py_buffer *buf;

    if (!PySequence_Check(buffers)) {
        PyErr_SetString(PyExc_TypeError,
            "writev() arg 2 must be a sequence");
        return -1;
    }
    cnt = PySequence_Size(buffers);
    if (cnt < 0)
        return -1;

    if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) {
        return -1;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        result = writev(fd, iov, cnt);
        Py_END_ALLOW_THREADS
    } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    if (result < 0 && !async_err)
        posix_error();

    iov_cleanup(iov, buf, cnt);
    return result;
}
#endif /* HAVE_WRITEV */


#ifdef HAVE_PWRITE
/*[clinic input]
os.pwrite -> Py_ssize_t

    fd: int
    buffer: Py_buffer
    offset: Py_off_t
    /

Write bytes to a file descriptor starting at a particular offset.

Write buffer to fd, starting at offset bytes from the beginning of
the file.  Returns the number of bytes written.  Does not change the
current file offset.
[clinic start generated code]*/

static Py_ssize_t
os_pwrite_impl(PyObject *module, int fd, Py_buffer *buffer, Py_off_t offset)
/*[clinic end generated code: output=c74da630758ee925 input=614acbc7e5a0339a]*/
{
    Py_ssize_t size;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    if (size < 0 && !async_err)
        posix_error();
    return size;
}
#endif /* HAVE_PWRITE */

#if defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2)
/*[clinic input]
@permit_long_summary
@permit_long_docstring_body
os.pwritev -> Py_ssize_t

    fd: int
    buffers: object
    offset: Py_off_t
    flags: int = 0
    /

Writes the contents of bytes-like objects to a file descriptor at a given offset.

Combines the functionality of writev() and pwrite(). All buffers must be a sequence
of bytes-like objects. Buffers are processed in array order. Entire contents of first
buffer is written before proceeding to second, and so on. The operating system may
set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used.
This function writes the contents of each object to the file descriptor and returns
the total number of bytes written.

The flags argument contains a bitwise OR of zero or more of the following flags:

- RWF_DSYNC
- RWF_SYNC
- RWF_APPEND
- RWF_DONTCACHE
- RWF_ATOMIC

Using non-zero flags requires Linux 4.7 or newer.
[clinic start generated code]*/

static Py_ssize_t
os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
                int flags)
/*[clinic end generated code: output=e3dd3e9d11a6a5c7 input=7de72245873f56bf]*/
{
    Py_ssize_t cnt;
    Py_ssize_t result;
    int async_err = 0;
    struct iovec *iov;
    Py_buffer *buf;

    if (!PySequence_Check(buffers)) {
        PyErr_SetString(PyExc_TypeError,
            "pwritev() arg 2 must be a sequence");
        return -1;
    }

    cnt = PySequence_Size(buffers);
    if (cnt < 0) {
        return -1;
    }

#ifndef HAVE_PWRITEV2
    if(flags != 0) {
        argument_unavailable_error("pwritev2", "flags");
        return -1;
    }
#endif

    if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) {
        return -1;
    }
#ifdef HAVE_PWRITEV2
    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        result = pwritev2(fd, iov, cnt, offset, flags);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
#else

#if defined(__APPLE__) && defined(__clang__)
/* This entire function will be removed from the module dict when the API
 * is not available.
 */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
#endif
    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
        result = pwritev(fd, iov, cnt, offset);
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic pop
#endif

#endif

    if (result < 0) {
        if (!async_err) {
            posix_error();
        }
        result = -1;
    }
    iov_cleanup(iov, buf, cnt);

    return result;
}
#endif /* HAVE_PWRITEV */

#ifdef HAVE_COPY_FILE_RANGE
/*[clinic input]

os.copy_file_range
    src: int
        Source file descriptor.
    dst: int
        Destination file descriptor.
    count: Py_ssize_t(allow_negative=False)
        Number of bytes to copy.
    offset_src: object = None
        Starting offset in src.
    offset_dst: object = None
        Starting offset in dst.

Copy count bytes from one file descriptor to another.

If offset_src is None, then src is read from the current position;
respectively for offset_dst.
[clinic start generated code]*/

static PyObject *
os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
                        PyObject *offset_src, PyObject *offset_dst)
/*[clinic end generated code: output=1a91713a1d99fc7a input=08dacb760869b87c]*/
{
    off_t offset_src_val, offset_dst_val;
    off_t *p_offset_src = NULL;
    off_t *p_offset_dst = NULL;
    Py_ssize_t ret;
    int async_err = 0;
    /* The flags argument is provided to allow
     * for future extensions and currently must be to 0. */
    int flags = 0;


    if (offset_src != Py_None) {
        if (!Py_off_t_converter(offset_src, &offset_src_val)) {
            return NULL;
        }
        p_offset_src = &offset_src_val;
    }

    if (offset_dst != Py_None) {
        if (!Py_off_t_converter(offset_dst, &offset_dst_val)) {
            return NULL;
        }
        p_offset_dst = &offset_dst_val;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags);
        Py_END_ALLOW_THREADS
    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    if (ret < 0) {
        return (!async_err) ? posix_error() : NULL;
    }

    return PyLong_FromSsize_t(ret);
}
#endif /* HAVE_COPY_FILE_RANGE*/

#if (defined(HAVE_SPLICE) && !defined(_AIX))
/*[clinic input]

os.splice
    src: int
        Source file descriptor.
    dst: int
        Destination file descriptor.
    count: Py_ssize_t(allow_negative=False)
        Number of bytes to copy.
    offset_src: object = None
        Starting offset in src.
    offset_dst: object = None
        Starting offset in dst.
    flags: unsigned_int = 0
        Flags to modify the semantics of the call.

Transfer count bytes from one pipe to a descriptor or vice versa.

If offset_src is None, then src is read from the current position;
respectively for offset_dst. The offset associated to the file
descriptor that refers to a pipe must be None.
[clinic start generated code]*/

static PyObject *
os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count,
               PyObject *offset_src, PyObject *offset_dst,
               unsigned int flags)
/*[clinic end generated code: output=d0386f25a8519dc5 input=034852a7b2e7af35]*/
{
    off_t offset_src_val, offset_dst_val;
    off_t *p_offset_src = NULL;
    off_t *p_offset_dst = NULL;
    Py_ssize_t ret;
    int async_err = 0;


    if (offset_src != Py_None) {
        if (!Py_off_t_converter(offset_src, &offset_src_val)) {
            return NULL;
        }
        p_offset_src = &offset_src_val;
    }

    if (offset_dst != Py_None) {
        if (!Py_off_t_converter(offset_dst, &offset_dst_val)) {
            return NULL;
        }
        p_offset_dst = &offset_dst_val;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        ret = splice(src, p_offset_src, dst, p_offset_dst, count, flags);
        Py_END_ALLOW_THREADS
    } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

    if (ret < 0) {
        return (!async_err) ? posix_error() : NULL;
    }

    return PyLong_FromSsize_t(ret);
}
#endif /* HAVE_SPLICE*/

#ifdef HAVE_MKFIFO
/*[clinic input]
os.mkfifo

    path: path_t
    mode: int=0o666
    *
    dir_fd: dir_fd(requires='mkfifoat')=None

Create a "fifo" (a POSIX named pipe).

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd)
/*[clinic end generated code: output=ce41cfad0e68c940 input=73032e98a36e0e19]*/
{
    int result;
    int async_err = 0;
#ifdef HAVE_MKFIFOAT
    int mkfifoat_unavailable = 0;
#endif

    do {
        Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_MKFIFOAT
        if (dir_fd != DEFAULT_DIR_FD) {
            if (HAVE_MKFIFOAT_RUNTIME) {
                result = mkfifoat(dir_fd, path->narrow, mode);

            } else {
                mkfifoat_unavailable = 1;
                result = 0;
            }
        } else
#endif
            result = mkfifo(path->narrow, mode);
        Py_END_ALLOW_THREADS
    } while (result != 0 && errno == EINTR &&
             !(async_err = PyErr_CheckSignals()));

#ifdef HAVE_MKFIFOAT
    if (mkfifoat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif

    if (result != 0)
        return (!async_err) ? posix_error() : NULL;

    Py_RETURN_NONE;
}
#endif /* HAVE_MKFIFO */


#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)
/*[clinic input]
@permit_long_docstring_body
os.mknod

    path: path_t
    mode: int=0o600
    device: dev_t=0
    *
    dir_fd: dir_fd(requires='mknodat')=None

Create a node in the file system.

Create a node in the file system (file, device special file or named pipe)
at path.  mode specifies both the permissions to use and the
type of node to be created, being combined (bitwise OR) with one of
S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO.  If S_IFCHR or S_IFBLK is set on mode,
device defines the newly created device special file (probably using
os.makedev()).  Otherwise device is ignored.

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.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/

static PyObject *
os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
              int dir_fd)
/*[clinic end generated code: output=92e55d3ca8917461 input=7121c4723d22545b]*/
{
    int result;
    int async_err = 0;
#ifdef HAVE_MKNODAT
    int mknodat_unavailable = 0;
#endif

    do {
        Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_MKNODAT
        if (dir_fd != DEFAULT_DIR_FD) {
            if (HAVE_MKNODAT_RUNTIME) {
                result = mknodat(dir_fd, path->narrow, mode, device);

            } else {
                mknodat_unavailable = 1;
                result = 0;
            }
        } else
#endif
            result = mknod(path->narrow, mode, device);
        Py_END_ALLOW_THREADS
    } while (result != 0 && errno == EINTR &&
             !(async_err = PyErr_CheckSignals()));
#ifdef HAVE_MKNODAT
    if (mknodat_unavailable) {
        argument_unavailable_error(NULL, "dir_fd");
        return NULL;
    }
#endif
    if (result != 0)
        return (!async_err) ? posix_error() : NULL;

    Py_RETURN_NONE;
}
#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */


#ifdef HAVE_DEVICE_MACROS
static PyObject *
major_minor_conv(unsigned int value)
{
#ifdef NODEV
    if (value == (unsigned int)NODEV) {
        return PyLong_FromLong((int)NODEV);
    }
#endif
    return PyLong_FromUnsignedLong(value);
}

static int
major_minor_check(dev_t value)
{
#ifdef NODEV
    if (value == NODEV) {
        return 1;
    }
#endif
    return (dev_t)(unsigned int)value == value;
}

/*[clinic input]
os.major

    device: dev_t
    /

Extracts a device major number from a raw device number.
[clinic start generated code]*/

static PyObject *
os_major_impl(PyObject *module, dev_t device)
/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/
{
    return major_minor_conv(major(device));
}


/*[clinic input]
os.minor

    device: dev_t
    /

Extracts a device minor number from a raw device number.
[clinic start generated code]*/

static PyObject *
os_minor_impl(PyObject *module, dev_t device)
/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/
{
    return major_minor_conv(minor(device));
}


/*[clinic input]
os.makedev -> dev_t

    major: dev_t
    minor: dev_t
    /

Composes a raw device number from the major and minor device numbers.
[clinic start generated code]*/

static dev_t
os_makedev_impl(PyObject *module, dev_t major, dev_t minor)
/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/
{
    if (!major_minor_check(major) || !major_minor_check(minor)) {
        PyErr_SetString(PyExc_OverflowError,
                        "Python int too large to convert to C unsigned int");
        return (dev_t)-1;
    }
    return makedev(major, minor);
}
#endif /* HAVE_DEVICE_MACROS */


#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
/*[clinic input]
os.ftruncate

    fd: int
    length: Py_off_t
    /

Truncate a file, specified by file descriptor, to a specific length.
[clinic start generated code]*/

static PyObject *
os_ftruncate_impl(PyObject *module, int fd, Py_off_t length)
/*[clinic end generated code: output=fba15523721be7e4 input=63b43641e52818f2]*/
{
    int result;
    int async_err = 0;

    if (PySys_Audit("os.truncate", "in", fd, length) < 0) {
        return NULL;
    }

    do {
        Py_BEGIN_ALLOW_THREADS
        _Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
        result = _chsize_s(fd, length);
#else
        result = ftruncate(fd, length);
#endif
        _Py_END_SUPPRESS_IPH
        Py_END_ALLOW_THREADS
    } while (result != 0 && errno == EINTR &&
             !(async_err = PyErr_CheckSignals()));
    if (result != 0)
        return (!async_err) ? posix_error() : NULL;
    Py_RETURN_NONE;
}
#endif /* HAVE_FTRUNCATE || MS_WINDOWS */


#if defined HAVE_TRUNCATE || defined MS_WINDOWS
/*[clinic input]
os.truncate
    path: path_t(allow_fd='PATH_HAVE_FTRUNCATE')
    length: Py_off_t

Truncate a file, specified by path, to a specific length.

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_truncate_impl(PyObject *module, path_t *path, Py_off_t length)
/*[clinic end generated code: output=43009c8df5c0a12b input=77229cf0b50a9b77]*/
{
    int result;
#ifdef MS_WINDOWS
    int fd;
#endif

    if (path->fd != -1)
        return os_ftruncate_impl(module, path->fd, length);

    if (PySys_Audit("os.truncate", "On", path->object, length) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    _Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
    fd = _wopen(path->wide, _O_WRONLY | _O_BINARY | _O_NOINHERIT);
    if (fd < 0)
        result = -1;
    else {
        result = _chsize_s(fd, length);
        close(fd);
        if (result < 0)
            errno = result;
    }
#else
    result = truncate(path->narrow, length);
#endif
    _Py_END_SUPPRESS_IPH
    Py_END_ALLOW_THREADS
    if (result < 0)
        return posix_path_error(path);

    Py_RETURN_NONE;
}
#endif /* HAVE_TRUNCATE || MS_WINDOWS */


/* Issue #22396: On 32-bit AIX platform, the prototypes of os.posix_fadvise()
   and os.posix_fallocate() in system headers are wrong if _LARGE_FILES is
   defined, which is the case in Python on AIX. AIX bug report:
   http://www-01.ibm.com/support/docview.wss?uid=isg1IV56170 */
#if defined(_AIX) && defined(_LARGE_FILES) && !defined(__64BIT__)
#  define POSIX_FADVISE_AIX_BUG
#endif


/* GH-111804: Due to posix_fallocate() not having consistent semantics across
   OSs, support was dropped in WASI preview2. */
#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && \
    !defined(__wasi__)
/*[clinic input]
os.posix_fallocate

    fd: int
    offset: Py_off_t
    length: Py_off_t
    /

Ensure a file has allocated at least a particular number of bytes on disk.

Ensure that the file specified by fd encompasses a range of bytes
starting at offset bytes from the beginning and continuing for length bytes.
[clinic start generated code]*/

static PyObject *
os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset,
                        Py_off_t length)
/*[clinic end generated code: output=73f107139564aa9d input=d7a2ef0ab2ca52fb]*/
{
    int result;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        result = posix_fallocate(fd, offset, length);
        Py_END_ALLOW_THREADS
    } while (result == EINTR && !(async_err = PyErr_CheckSignals()));

    if (result == 0)
        Py_RETURN_NONE;

    if (async_err)
        return NULL;

    errno = result;
    return posix_error();
}
#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG && !defined(__wasi__) */


#if defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG)
/*[clinic input]
os.posix_fadvise

    fd: int
    offset: Py_off_t
    length: Py_off_t
    advice: int
    /

Announce an intention to access data in a specific pattern.

Announce an intention to access data in a specific pattern, thus allowing
the kernel to make optimizations.
The advice applies to the region of the file specified by fd starting at
offset and continuing for length bytes.
advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL,
POSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEED, or
POSIX_FADV_DONTNEED.
[clinic start generated code]*/

static PyObject *
os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset,
                      Py_off_t length, int advice)
/*[clinic end generated code: output=412ef4aa70c98642 input=0fbe554edc2f04b5]*/
{
    int result;
    int async_err = 0;

    do {
        Py_BEGIN_ALLOW_THREADS
        result = posix_fadvise(fd, offset, length, advice);
        Py_END_ALLOW_THREADS
    } while (result == EINTR && !(async_err = PyErr_CheckSignals()));

    if (result == 0)
        Py_RETURN_NONE;

    if (async_err)
        return NULL;

    errno = result;
    return posix_error();
}
#endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */


#ifdef MS_WINDOWS
static PyObject*
win32_putenv(PyObject *name, PyObject *value)
{
    /* Search from index 1 because on Windows starting '=' is allowed for
       defining hidden environment variables. */
    if (PyUnicode_GET_LENGTH(name) == 0 ||
        PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1)
    {
        PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
        return NULL;
    }
    PyObject *unicode;
    if (value != NULL) {
        unicode = PyUnicode_FromFormat("%U=%U", name, value);
    }
    else {
        unicode = PyUnicode_FromFormat("%U=", name);
    }
    if (unicode == NULL) {
        return NULL;
    }

    Py_ssize_t size;
    wchar_t *env = PyUnicode_AsWideCharString(unicode, &size);
    Py_DECREF(unicode);

    if (env == NULL) {
        return NULL;
    }
    if (size > _MAX_ENV) {
        PyErr_Format(PyExc_ValueError,
                     "the environment variable is longer than %u characters",
                     _MAX_ENV);
        PyMem_Free(env);
        return NULL;
    }
    if (wcslen(env) != (size_t)size) {
        PyErr_SetString(PyExc_ValueError,
                        "embedded null character");
        PyMem_Free(env);
        return NULL;
    }

    /* _wputenv() and SetEnvironmentVariableW() update the environment in the
       Process Environment Block (PEB). _wputenv() also updates CRT 'environ'
       and '_wenviron' variables, whereas SetEnvironmentVariableW() does not.

       Prefer _wputenv() to be compatible with C libraries using CRT
       variables and CRT functions using these variables (ex: getenv()). */
    int err = _wputenv(env);

    if (err) {
        posix_error();
        PyMem_Free(env);
        return NULL;
    }
    PyMem_Free(env);

    Py_RETURN_NONE;
}
#endif


#ifdef MS_WINDOWS
/*[clinic input]
os.putenv

    name: unicode
    value: unicode
    /

Change or add an environment variable.
[clinic start generated code]*/

static PyObject *
os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/
{
    if (PySys_Audit("os.putenv", "OO", name, value) < 0) {
        return NULL;
    }
    return win32_putenv(name, value);
}
#else
/*[clinic input]
os.putenv

    name: unicode_fs_encoded
    value: unicode_fs_encoded
    /

Change or add an environment variable.
[clinic start generated code]*/

static PyObject *
os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
/*[clinic end generated code: output=d29a567d6b2327d2 input=84fcd30f873c8c45]*/
{
    const char *name_string = PyBytes_AS_STRING(name);
    const char *value_string = PyBytes_AS_STRING(value);

    if (strchr(name_string, '=') != NULL) {
        PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
        return NULL;
    }

    if (PySys_Audit("os.putenv", "OO", name, value) < 0) {
        return NULL;
    }

    if (setenv(name_string, value_string, 1)) {
        return posix_error();
    }
    Py_RETURN_NONE;
}
#endif  /* !defined(MS_WINDOWS) */


#ifdef MS_WINDOWS
/*[clinic input]
os.unsetenv
    name: unicode
    /

Delete an environment variable.
[clinic start generated code]*/

static PyObject *
os_unsetenv_impl(PyObject *module, PyObject *name)
/*[clinic end generated code: output=54c4137ab1834f02 input=4d6a1747cc526d2f]*/
{
    if (PySys_Audit("os.unsetenv", "(O)", name) < 0) {
        return NULL;
    }
    return win32_putenv(name, NULL);
}
#else
/*[clinic input]
os.unsetenv
    name: unicode_fs_encoded
    /

Delete an environment variable.
[clinic start generated code]*/

static PyObject *
os_unsetenv_impl(PyObject *module, PyObject *name)
/*[clinic end generated code: output=54c4137ab1834f02 input=78ff12e505ade80a]*/
{
    if (PySys_Audit("os.unsetenv", "(O)", name) < 0) {
        return NULL;
    }
#ifdef HAVE_BROKEN_UNSETENV
    unsetenv(PyBytes_AS_STRING(name));
#else
    int err = unsetenv(PyBytes_AS_STRING(name));
    if (err) {
        return posix_error();
    }
#endif

    Py_RETURN_NONE;
}
#endif /* !MS_WINDOWS */


#ifdef HAVE_CLEARENV
/*[clinic input]
os._clearenv
[clinic start generated code]*/

static PyObject *
os__clearenv_impl(PyObject *module)
/*[clinic end generated code: output=2d6705d62c014b51 input=47d2fa7f323c43ca]*/
{
    errno = 0;
    int err = clearenv();
    if (err) {
        return posix_error();
    }
    Py_RETURN_NONE;
}
#endif


/*[clinic input]
os.strerror

    code: int
    /

Translate an error code to a message string.
[clinic start generated code]*/

static PyObject *
os_strerror_impl(PyObject *module, int code)
/*[clinic end generated code: output=baebf09fa02a78f2 input=75a8673d97915a91]*/
{
    char *message = strerror(code);
    if (message == NULL) {
        PyErr_SetString(PyExc_ValueError,
                        "strerror() argument out of range");
        return NULL;
    }
    return PyUnicode_DecodeLocale(message, "surrogateescape");
}


#ifdef HAVE_SYS_WAIT_H
#ifdef WCOREDUMP
/*[clinic input]
os.WCOREDUMP -> bool

    status: int
    /

Return True if the process returning status was dumped to a core file.
[clinic start generated code]*/

static int
os_WCOREDUMP_impl(PyObject *module, int status)
/*[clinic end generated code: output=1a584b147b16bd18 input=8b05e7ab38528d04]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WCOREDUMP(wait_status);
}
#endif /* WCOREDUMP */


#ifdef WIFCONTINUED
/*[clinic input]
os.WIFCONTINUED -> bool

    status: int

Return True if a particular process was continued from a job control stop.

Return True if the process returning status was continued from a
job control stop.
[clinic start generated code]*/

static int
os_WIFCONTINUED_impl(PyObject *module, int status)
/*[clinic end generated code: output=1e35295d844364bd input=e777e7d38eb25bd9]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WIFCONTINUED(wait_status);
}
#endif /* WIFCONTINUED */


#ifdef WIFSTOPPED
/*[clinic input]
os.WIFSTOPPED -> bool

    status: int

Return True if the process returning status was stopped.
[clinic start generated code]*/

static int
os_WIFSTOPPED_impl(PyObject *module, int status)
/*[clinic end generated code: output=fdb57122a5c9b4cb input=043cb7f1289ef904]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WIFSTOPPED(wait_status);
}
#endif /* WIFSTOPPED */


#ifdef WIFSIGNALED
/*[clinic input]
os.WIFSIGNALED -> bool

    status: int

Return True if the process returning status was terminated by a signal.
[clinic start generated code]*/

static int
os_WIFSIGNALED_impl(PyObject *module, int status)
/*[clinic end generated code: output=d1dde4dcc819a5f5 input=d55ba7cc9ce5dc43]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WIFSIGNALED(wait_status);
}
#endif /* WIFSIGNALED */


#ifdef WIFEXITED
/*[clinic input]
@permit_long_summary
os.WIFEXITED -> bool

    status: int

Return True if the process returning status exited via the exit() system call.
[clinic start generated code]*/

static int
os_WIFEXITED_impl(PyObject *module, int status)
/*[clinic end generated code: output=01c09d6ebfeea397 input=8c24a82148709b30]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WIFEXITED(wait_status);
}
#endif /* WIFEXITED */


#ifdef WEXITSTATUS
/*[clinic input]
os.WEXITSTATUS -> int

    status: int

Return the process return code from status.
[clinic start generated code]*/

static int
os_WEXITSTATUS_impl(PyObject *module, int status)
/*[clinic end generated code: output=6e3efbba11f6488d input=e1fb4944e377585b]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WEXITSTATUS(wait_status);
}
#endif /* WEXITSTATUS */


#ifdef WTERMSIG
/*[clinic input]
@permit_long_summary
os.WTERMSIG -> int

    status: int

Return the signal that terminated the process that provided the status value.
[clinic start generated code]*/

static int
os_WTERMSIG_impl(PyObject *module, int status)
/*[clinic end generated code: output=172f7dfc8dcfc3ad input=89072f6cbf3f8050]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WTERMSIG(wait_status);
}
#endif /* WTERMSIG */


#ifdef WSTOPSIG
/*[clinic input]
os.WSTOPSIG -> int

    status: int

Return the signal that stopped the process that provided the status value.
[clinic start generated code]*/

static int
os_WSTOPSIG_impl(PyObject *module, int status)
/*[clinic end generated code: output=0ab7586396f5d82b input=46ebf1d1b293c5c1]*/
{
    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    return WSTOPSIG(wait_status);
}
#endif /* WSTOPSIG */
#endif /* HAVE_SYS_WAIT_H */


#if defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H)
#ifdef _SCO_DS
/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the
   needed definitions in sys/statvfs.h */
#define _SVID3
#endif
#include <sys/statvfs.h>

#ifdef __APPLE__
/* On macOS struct statvfs uses 32-bit integers for block counts,
 * resulting in overflow when filesystems are larger than 4TB. Therefore
 * os.statvfs is implemented in terms of statfs(2).
 */

static PyObject*
_pystatvfs_fromstructstatfs(PyObject *module, struct statfs st) {
    PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType;
    PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType);
    if (v == NULL) {
        return NULL;
    }

    long flags = 0;
    if (st.f_flags & MNT_RDONLY) {
        flags |= ST_RDONLY;
    }
    if (st.f_flags & MNT_NOSUID) {
        flags |= ST_NOSUID;
    }

    _Static_assert(sizeof(st.f_blocks) == sizeof(long long), "assuming large file");

#define SET_ITEM(SEQ, INDEX, EXPR)                       \
    do {                                                 \
        PyObject *obj = (EXPR);                          \
        if (obj == NULL) {                               \
            Py_DECREF((SEQ));                            \
            return NULL;                                 \
        }                                                \
        PyStructSequence_SET_ITEM((SEQ), (INDEX), obj);  \
    } while (0)

    SET_ITEM(v, 0, PyLong_FromLong((long) st.f_iosize));
    SET_ITEM(v, 1, PyLong_FromLong((long) st.f_bsize));
    SET_ITEM(v, 2, PyLong_FromLongLong((long long) st.f_blocks));
    SET_ITEM(v, 3, PyLong_FromLongLong((long long) st.f_bfree));
    SET_ITEM(v, 4, PyLong_FromLongLong((long long) st.f_bavail));
    SET_ITEM(v, 5, PyLong_FromLongLong((long long) st.f_files));
    SET_ITEM(v, 6, PyLong_FromLongLong((long long) st.f_ffree));
    SET_ITEM(v, 7, PyLong_FromLongLong((long long) st.f_ffree));
    SET_ITEM(v, 8, PyLong_FromLong((long) flags));

    SET_ITEM(v, 9, PyLong_FromLong((long) NAME_MAX));
    SET_ITEM(v, 10, PyLong_FromUnsignedLong(st.f_fsid.val[0]));

#undef SET_ITEM

    return v;
}

#else



static PyObject*
_pystatvfs_fromstructstatvfs(PyObject *module, struct statvfs st) {
    PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType;
    PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType);
    if (v == NULL)
        return NULL;

    int pos = 0;

#define SET_RESULT(CALL)                                     \
    do {                                                     \
        PyObject *item = (CALL);                             \
        if (item == NULL) {                                  \
            Py_DECREF(v);                                    \
            return NULL;                                     \
        }                                                    \
        PyStructSequence_SET_ITEM(v, pos++, item);           \
    } while(0)

#if !defined(HAVE_LARGEFILE_SUPPORT)
    SET_RESULT(PyLong_FromLong((long) st.f_bsize));
    SET_RESULT(PyLong_FromLong((long) st.f_frsize));
    SET_RESULT(PyLong_FromLong((long) st.f_blocks));
    SET_RESULT(PyLong_FromLong((long) st.f_bfree));
    SET_RESULT(PyLong_FromLong((long) st.f_bavail));
    SET_RESULT(PyLong_FromLong((long) st.f_files));
    SET_RESULT(PyLong_FromLong((long) st.f_ffree));
    SET_RESULT(PyLong_FromLong((long) st.f_favail));
    SET_RESULT(PyLong_FromLong((long) st.f_flag));
    SET_RESULT(PyLong_FromLong((long) st.f_namemax));
#else
    SET_RESULT(PyLong_FromLong((long) st.f_bsize));
    SET_RESULT(PyLong_FromLong((long) st.f_frsize));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_blocks));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_bfree));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_bavail));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_files));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_ffree));
    SET_RESULT(PyLong_FromLongLong((long long) st.f_favail));
    SET_RESULT(PyLong_FromLong((long) st.f_flag));
    SET_RESULT(PyLong_FromLong((long) st.f_namemax));
#endif
/* The _ALL_SOURCE feature test macro defines f_fsid as a structure
 * (issue #32390). */
#if defined(_AIX) && defined(_ALL_SOURCE)
    SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid.val[0]));
#else
    SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid));
#endif

#undef SET_RESULT

    return v;
}

#endif


/*[clinic input]
os.fstatvfs
    fd: int
    /

Perform an fstatvfs system call on the given fd.

Equivalent to statvfs(fd).
[clinic start generated code]*/

static PyObject *
os_fstatvfs_impl(PyObject *module, int fd)
/*[clinic end generated code: output=53547cf0cc55e6c5 input=d8122243ac50975e]*/
{
    int result;
    int async_err = 0;
#ifdef __APPLE__
    struct statfs st;
    /* On macOS os.fstatvfs is implemented using fstatfs(2) because
     * the former uses 32-bit values for block counts.
     */
    do {
        Py_BEGIN_ALLOW_THREADS
        result = fstatfs(fd, &st);
        Py_END_ALLOW_THREADS
    } while (result != 0 && errno == EINTR &&
             !(async_err = PyErr_CheckSignals()));
    if (result != 0)
        return (!async_err) ? posix_error() : NULL;

    return _pystatvfs_fromstructstatfs(module, st);
#else
    struct statvfs st;

    do {
        Py_BEGIN_ALLOW_THREADS
        result = fstatvfs(fd, &st);
        Py_END_ALLOW_THREADS
    } while (result != 0 && errno == EINTR &&
             !(async_err = PyErr_CheckSignals()));
    if (result != 0)
        return (!async_err) ? posix_error() : NULL;

    return _pystatvfs_fromstructstatvfs(module, st);
#endif
}
#endif /* defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) */


#if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H)
#include <sys/statvfs.h>
/*[clinic input]
os.statvfs

    path: path_t(allow_fd='PATH_HAVE_FSTATVFS')

Perform a statvfs system call on the given 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_statvfs_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=87106dd1beb8556e input=3f5c35791c669bd9]*/
{
    int result;

#ifdef __APPLE__
    /* On macOS os.statvfs is implemented using statfs(2)/fstatfs(2) because
     * the former uses 32-bit values for block counts.
     */
    struct statfs st;

    Py_BEGIN_ALLOW_THREADS
    if (path->fd != -1) {
        result = fstatfs(path->fd, &st);
    }
    else
        result = statfs(path->narrow, &st);
    Py_END_ALLOW_THREADS

    if (result) {
        return path_error(path);
    }

    return _pystatvfs_fromstructstatfs(module, st);

#else
    struct statvfs st;

    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FSTATVFS
    if (path->fd != -1) {
        result = fstatvfs(path->fd, &st);
    }
    else
#endif
        result = statvfs(path->narrow, &st);
    Py_END_ALLOW_THREADS

    if (result) {
        return path_error(path);
    }

    return _pystatvfs_fromstructstatvfs(module, st);
#endif
}
#endif /* defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) */


#ifdef MS_WINDOWS
/*[clinic input]
os._getdiskusage

    path: path_t

Return disk usage statistics about the given path as a (total, free) tuple.
[clinic start generated code]*/

static PyObject *
os__getdiskusage_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=3bd3991f5e5c5dfb input=6af8d1b7781cc042]*/
{
    BOOL retval;
    ULARGE_INTEGER _, total, free;
    DWORD err = 0;

    Py_BEGIN_ALLOW_THREADS
    retval = GetDiskFreeSpaceExW(path->wide, &_, &total, &free);
    Py_END_ALLOW_THREADS
    if (retval == 0) {
        if (GetLastError() == ERROR_DIRECTORY) {
            wchar_t *dir_path = NULL;

            dir_path = PyMem_New(wchar_t, path->length + 1);
            if (dir_path == NULL) {
                return PyErr_NoMemory();
            }

            wcscpy_s(dir_path, path->length + 1, path->wide);

            if (_dirnameW(dir_path) != -1) {
                Py_BEGIN_ALLOW_THREADS
                retval = GetDiskFreeSpaceExW(dir_path, &_, &total, &free);
                Py_END_ALLOW_THREADS
            }
            /* Record the last error in case it's modified by PyMem_Free. */
            err = GetLastError();
            PyMem_Free(dir_path);
            if (retval) {
                goto success;
            }
        }
        return PyErr_SetFromWindowsErr(err);
    }

success:
    return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart);
}
#endif /* MS_WINDOWS */


/* This is used for fpathconf(), pathconf(), confstr() and sysconf().
 * It maps strings representing configuration variable names to
 * integer values, allowing those functions to be called with the
 * magic names instead of polluting the module's namespace with tons of
 * rarely-used constants.  There are three separate tables that use
 * these definitions.
 *
 * This code is always included, even if none of the interfaces that
 * need it are included.  The #if hackery needed to avoid it would be
 * sufficiently pervasive that it's not worth the loss of readability.
 */
struct constdef {
    const char *name;
    int value;
};

static int
conv_confname(PyObject *module, PyObject *arg, int *valuep, const char *tablename)
{
    if (PyUnicode_Check(arg)) {
        PyObject *table = PyObject_GetAttrString(module, tablename);
        if (table == NULL) {
            return 0;
        }

        arg = PyObject_GetItem(table, arg);
        Py_DECREF(table);
        if (arg == NULL) {
            PyErr_SetString(
                PyExc_ValueError, "unrecognized configuration name");
            return 0;
        }
    } else {
        Py_INCREF(arg);  // Match the Py_DECREF below.
    }

    int success = 0;
    if (!PyIndex_Check(arg)) {
        PyErr_SetString(PyExc_TypeError,
            "configuration names must be strings or integers");
    } else {
        int value = PyLong_AsInt(arg);
        if (!(value == -1 && PyErr_Occurred())) {
            *valuep = value;
            success = 1;
        }
    }
    Py_DECREF(arg);
    return success;
}


#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF)
static struct constdef  posix_constants_pathconf[] = {
#ifdef _PC_ABI_AIO_XFER_MAX
    {"PC_ABI_AIO_XFER_MAX",     _PC_ABI_AIO_XFER_MAX},
#endif
#ifdef _PC_ABI_ASYNC_IO
    {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO},
#endif
#ifdef _PC_ASYNC_IO
    {"PC_ASYNC_IO",     _PC_ASYNC_IO},
#endif
#ifdef _PC_CHOWN_RESTRICTED
    {"PC_CHOWN_RESTRICTED",     _PC_CHOWN_RESTRICTED},
#endif
#ifdef _PC_FILESIZEBITS
    {"PC_FILESIZEBITS", _PC_FILESIZEBITS},
#endif
#ifdef _PC_LAST
    {"PC_LAST", _PC_LAST},
#endif
#ifdef _PC_LINK_MAX
    {"PC_LINK_MAX",     _PC_LINK_MAX},
#endif
#ifdef _PC_MAX_CANON
    {"PC_MAX_CANON",    _PC_MAX_CANON},
#endif
#ifdef _PC_MAX_INPUT
    {"PC_MAX_INPUT",    _PC_MAX_INPUT},
#endif
#ifdef _PC_NAME_MAX
    {"PC_NAME_MAX",     _PC_NAME_MAX},
#endif
#ifdef _PC_NO_TRUNC
    {"PC_NO_TRUNC",     _PC_NO_TRUNC},
#endif
#ifdef _PC_PATH_MAX
    {"PC_PATH_MAX",     _PC_PATH_MAX},
#endif
#ifdef _PC_PIPE_BUF
    {"PC_PIPE_BUF",     _PC_PIPE_BUF},
#endif
#ifdef _PC_PRIO_IO
    {"PC_PRIO_IO",      _PC_PRIO_IO},
#endif
#ifdef _PC_SOCK_MAXBUF
    {"PC_SOCK_MAXBUF",  _PC_SOCK_MAXBUF},
#endif
#ifdef _PC_SYNC_IO
    {"PC_SYNC_IO",      _PC_SYNC_IO},
#endif
#ifdef _PC_VDISABLE
    {"PC_VDISABLE",     _PC_VDISABLE},
#endif
#ifdef _PC_ACL_ENABLED
    {"PC_ACL_ENABLED",  _PC_ACL_ENABLED},
#endif
#ifdef _PC_MIN_HOLE_SIZE
    {"PC_MIN_HOLE_SIZE",    _PC_MIN_HOLE_SIZE},
#endif
#ifdef _PC_ALLOC_SIZE_MIN
    {"PC_ALLOC_SIZE_MIN",   _PC_ALLOC_SIZE_MIN},
#endif
#ifdef _PC_REC_INCR_XFER_SIZE
    {"PC_REC_INCR_XFER_SIZE",   _PC_REC_INCR_XFER_SIZE},
#endif
#ifdef _PC_REC_MAX_XFER_SIZE
    {"PC_REC_MAX_XFER_SIZE",    _PC_REC_MAX_XFER_SIZE},
#endif
#ifdef _PC_REC_MIN_XFER_SIZE
    {"PC_REC_MIN_XFER_SIZE",    _PC_REC_MIN_XFER_SIZE},
#endif
#ifdef _PC_REC_XFER_ALIGN
    {"PC_REC_XFER_ALIGN",   _PC_REC_XFER_ALIGN},
#endif
#ifdef _PC_SYMLINK_MAX
    {"PC_SYMLINK_MAX",  _PC_SYMLINK_MAX},
#endif
#ifdef _PC_XATTR_ENABLED
    {"PC_XATTR_ENABLED",    _PC_XATTR_ENABLED},
#endif
#ifdef _PC_XATTR_EXISTS
    {"PC_XATTR_EXISTS", _PC_XATTR_EXISTS},
#endif
#ifdef _PC_TIMESTAMP_RESOLUTION
    {"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION},
#endif
};
#endif


#ifdef HAVE_FPATHCONF
/*[clinic input]
os.fpathconf -> long

    fd: fildes
    name: confname(table="pathconf_names")
    /

Return the configuration limit name for the file descriptor fd.

If there is no limit, return -1.
[clinic start generated code]*/

static long
os_fpathconf_impl(PyObject *module, int fd, int name)
/*[clinic end generated code: output=d5b7042425fc3e21 input=023d44589c9ed6aa]*/
{
    long limit;

    errno = 0;
    limit = fpathconf(fd, name);
    if (limit == -1 && errno != 0)
        posix_error();

    return limit;
}
#endif /* HAVE_FPATHCONF */


#ifdef HAVE_PATHCONF
/*[clinic input]
os.pathconf -> long
    path: path_t(allow_fd='PATH_HAVE_FPATHCONF')
    name: confname(table="pathconf_names")

Return the configuration limit name for the file or directory path.

If there is no limit, return -1.
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 long
os_pathconf_impl(PyObject *module, path_t *path, int name)
/*[clinic end generated code: output=5bedee35b293a089 input=6f6072f57b10c787]*/
{
    long limit;

    errno = 0;
#ifdef HAVE_FPATHCONF
    if (path->fd != -1)
        limit = fpathconf(path->fd, name);
    else
#endif
        limit = pathconf(path->narrow, name);
    if (limit == -1 && errno != 0) {
        if (errno == EINVAL)
            /* could be a path or name problem */
            posix_error();
        else
            path_error(path);
    }

    return limit;
}
#endif /* HAVE_PATHCONF */

#ifdef HAVE_CONFSTR
static struct constdef posix_constants_confstr[] = {
#ifdef _CS_ARCHITECTURE
    {"CS_ARCHITECTURE", _CS_ARCHITECTURE},
#endif
#ifdef _CS_GNU_LIBC_VERSION
    {"CS_GNU_LIBC_VERSION",     _CS_GNU_LIBC_VERSION},
#endif
#ifdef _CS_GNU_LIBPTHREAD_VERSION
    {"CS_GNU_LIBPTHREAD_VERSION",       _CS_GNU_LIBPTHREAD_VERSION},
#endif
#ifdef _CS_HOSTNAME
    {"CS_HOSTNAME",     _CS_HOSTNAME},
#endif
#ifdef _CS_HW_PROVIDER
    {"CS_HW_PROVIDER",  _CS_HW_PROVIDER},
#endif
#ifdef _CS_HW_SERIAL
    {"CS_HW_SERIAL",    _CS_HW_SERIAL},
#endif
#ifdef _CS_INITTAB_NAME
    {"CS_INITTAB_NAME", _CS_INITTAB_NAME},
#endif
#ifdef _CS_LFS64_CFLAGS
    {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS},
#endif
#ifdef _CS_LFS64_LDFLAGS
    {"CS_LFS64_LDFLAGS",        _CS_LFS64_LDFLAGS},
#endif
#ifdef _CS_LFS64_LIBS
    {"CS_LFS64_LIBS",   _CS_LFS64_LIBS},
#endif
#ifdef _CS_LFS64_LINTFLAGS
    {"CS_LFS64_LINTFLAGS",      _CS_LFS64_LINTFLAGS},
#endif
#ifdef _CS_LFS_CFLAGS
    {"CS_LFS_CFLAGS",   _CS_LFS_CFLAGS},
#endif
#ifdef _CS_LFS_LDFLAGS
    {"CS_LFS_LDFLAGS",  _CS_LFS_LDFLAGS},
#endif
#ifdef _CS_LFS_LIBS
    {"CS_LFS_LIBS",     _CS_LFS_LIBS},
#endif
#ifdef _CS_LFS_LINTFLAGS
    {"CS_LFS_LINTFLAGS",        _CS_LFS_LINTFLAGS},
#endif
#ifdef _CS_MACHINE
    {"CS_MACHINE",      _CS_MACHINE},
#endif
#ifdef _CS_PATH
    {"CS_PATH", _CS_PATH},
#endif
#ifdef _CS_RELEASE
    {"CS_RELEASE",      _CS_RELEASE},
#endif
#ifdef _CS_SRPC_DOMAIN
    {"CS_SRPC_DOMAIN",  _CS_SRPC_DOMAIN},
#endif
#ifdef _CS_SYSNAME
    {"CS_SYSNAME",      _CS_SYSNAME},
#endif
#ifdef _CS_VERSION
    {"CS_VERSION",      _CS_VERSION},
#endif
#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS
    {"CS_XBS5_ILP32_OFF32_CFLAGS",      _CS_XBS5_ILP32_OFF32_CFLAGS},
#endif
#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS
    {"CS_XBS5_ILP32_OFF32_LDFLAGS",     _CS_XBS5_ILP32_OFF32_LDFLAGS},
#endif
#ifdef _CS_XBS5_ILP32_OFF32_LIBS
    {"CS_XBS5_ILP32_OFF32_LIBS",        _CS_XBS5_ILP32_OFF32_LIBS},
#endif
#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS
    {"CS_XBS5_ILP32_OFF32_LINTFLAGS",   _CS_XBS5_ILP32_OFF32_LINTFLAGS},
#endif
#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS
    {"CS_XBS5_ILP32_OFFBIG_CFLAGS",     _CS_XBS5_ILP32_OFFBIG_CFLAGS},
#endif
#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS
    {"CS_XBS5_ILP32_OFFBIG_LDFLAGS",    _CS_XBS5_ILP32_OFFBIG_LDFLAGS},
#endif
#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS
    {"CS_XBS5_ILP32_OFFBIG_LIBS",       _CS_XBS5_ILP32_OFFBIG_LIBS},
#endif
#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS
    {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS",  _CS_XBS5_ILP32_OFFBIG_LINTFLAGS},
#endif
#ifdef _CS_XBS5_LP64_OFF64_CFLAGS
    {"CS_XBS5_LP64_OFF64_CFLAGS",       _CS_XBS5_LP64_OFF64_CFLAGS},
#endif
#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS
    {"CS_XBS5_LP64_OFF64_LDFLAGS",      _CS_XBS5_LP64_OFF64_LDFLAGS},
#endif
#ifdef _CS_XBS5_LP64_OFF64_LIBS
    {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS},
#endif
#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS
    {"CS_XBS5_LP64_OFF64_LINTFLAGS",    _CS_XBS5_LP64_OFF64_LINTFLAGS},
#endif
#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS
    {"CS_XBS5_LPBIG_OFFBIG_CFLAGS",     _CS_XBS5_LPBIG_OFFBIG_CFLAGS},
#endif
#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS
    {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS",    _CS_XBS5_LPBIG_OFFBIG_LDFLAGS},
#endif
#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS
    {"CS_XBS5_LPBIG_OFFBIG_LIBS",       _CS_XBS5_LPBIG_OFFBIG_LIBS},
#endif
#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS
    {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS",  _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS},
#endif
#ifdef _MIPS_CS_AVAIL_PROCESSORS
    {"MIPS_CS_AVAIL_PROCESSORS",        _MIPS_CS_AVAIL_PROCESSORS},
#endif
#ifdef _MIPS_CS_BASE
    {"MIPS_CS_BASE",    _MIPS_CS_BASE},
#endif
#ifdef _MIPS_CS_HOSTID
    {"MIPS_CS_HOSTID",  _MIPS_CS_HOSTID},
#endif
#ifdef _MIPS_CS_HW_NAME
    {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME},
#endif
#ifdef _MIPS_CS_NUM_PROCESSORS
    {"MIPS_CS_NUM_PROCESSORS",  _MIPS_CS_NUM_PROCESSORS},
#endif
#ifdef _MIPS_CS_OSREL_MAJ
    {"MIPS_CS_OSREL_MAJ",       _MIPS_CS_OSREL_MAJ},
#endif
#ifdef _MIPS_CS_OSREL_MIN
    {"MIPS_CS_OSREL_MIN",       _MIPS_CS_OSREL_MIN},
#endif
#ifdef _MIPS_CS_OSREL_PATCH
    {"MIPS_CS_OSREL_PATCH",     _MIPS_CS_OSREL_PATCH},
#endif
#ifdef _MIPS_CS_OS_NAME
    {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME},
#endif
#ifdef _MIPS_CS_OS_PROVIDER
    {"MIPS_CS_OS_PROVIDER",     _MIPS_CS_OS_PROVIDER},
#endif
#ifdef _MIPS_CS_PROCESSORS
    {"MIPS_CS_PROCESSORS",      _MIPS_CS_PROCESSORS},
#endif
#ifdef _MIPS_CS_SERIAL
    {"MIPS_CS_SERIAL",  _MIPS_CS_SERIAL},
#endif
#ifdef _MIPS_CS_VENDOR
    {"MIPS_CS_VENDOR",  _MIPS_CS_VENDOR},
#endif
};


/*[clinic input]
os.confstr

    name: confname(table="confstr_names")
    /

Return a string-valued system configuration variable.
[clinic start generated code]*/

static PyObject *
os_confstr_impl(PyObject *module, int name)
/*[clinic end generated code: output=bfb0b1b1e49b9383 input=4c6ffca2837ec959]*/
{
    PyObject *result = NULL;
    char buffer[255];
    size_t len;

    errno = 0;
    len = confstr(name, buffer, sizeof(buffer));
    if (len == 0) {
        if (errno) {
            posix_error();
            return NULL;
        }
        else {
            Py_RETURN_NONE;
        }
    }

    if (len >= sizeof(buffer)) {
        size_t len2;
        char *buf = PyMem_Malloc(len);
        if (buf == NULL)
            return PyErr_NoMemory();
        len2 = confstr(name, buf, len);
        assert(len == len2);
        result = PyUnicode_DecodeFSDefaultAndSize(buf, len2-1);
        PyMem_Free(buf);
    }
    else
        result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
    return result;
}
#endif /* HAVE_CONFSTR */


#ifdef HAVE_SYSCONF
static struct constdef posix_constants_sysconf[] = {
#ifdef _SC_2_CHAR_TERM
    {"SC_2_CHAR_TERM",  _SC_2_CHAR_TERM},
#endif
#ifdef _SC_2_C_BIND
    {"SC_2_C_BIND",     _SC_2_C_BIND},
#endif
#ifdef _SC_2_C_DEV
    {"SC_2_C_DEV",      _SC_2_C_DEV},
#endif
#ifdef _SC_2_C_VERSION
    {"SC_2_C_VERSION",  _SC_2_C_VERSION},
#endif
#ifdef _SC_2_FORT_DEV
    {"SC_2_FORT_DEV",   _SC_2_FORT_DEV},
#endif
#ifdef _SC_2_FORT_RUN
    {"SC_2_FORT_RUN",   _SC_2_FORT_RUN},
#endif
#ifdef _SC_2_LOCALEDEF
    {"SC_2_LOCALEDEF",  _SC_2_LOCALEDEF},
#endif
#ifdef _SC_2_SW_DEV
    {"SC_2_SW_DEV",     _SC_2_SW_DEV},
#endif
#ifdef _SC_2_UPE
    {"SC_2_UPE",        _SC_2_UPE},
#endif
#ifdef _SC_2_VERSION
    {"SC_2_VERSION",    _SC_2_VERSION},
#endif
#ifdef _SC_ABI_ASYNCHRONOUS_IO
    {"SC_ABI_ASYNCHRONOUS_IO",  _SC_ABI_ASYNCHRONOUS_IO},
#endif
#ifdef _SC_ACL
    {"SC_ACL",  _SC_ACL},
#endif
#ifdef _SC_AIO_LISTIO_MAX
    {"SC_AIO_LISTIO_MAX",       _SC_AIO_LISTIO_MAX},
#endif
#ifdef _SC_AIO_MAX
    {"SC_AIO_MAX",      _SC_AIO_MAX},
#endif
#ifdef _SC_AIO_PRIO_DELTA_MAX
    {"SC_AIO_PRIO_DELTA_MAX",   _SC_AIO_PRIO_DELTA_MAX},
#endif
#ifdef _SC_ARG_MAX
    {"SC_ARG_MAX",      _SC_ARG_MAX},
#endif
#ifdef _SC_ASYNCHRONOUS_IO
    {"SC_ASYNCHRONOUS_IO",      _SC_ASYNCHRONOUS_IO},
#endif
#ifdef _SC_ATEXIT_MAX
    {"SC_ATEXIT_MAX",   _SC_ATEXIT_MAX},
#endif
#ifdef _SC_AUDIT
    {"SC_AUDIT",        _SC_AUDIT},
#endif
#ifdef _SC_AVPHYS_PAGES
    {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES},
#endif
#ifdef _SC_BC_BASE_MAX
    {"SC_BC_BASE_MAX",  _SC_BC_BASE_MAX},
#endif
#ifdef _SC_BC_DIM_MAX
    {"SC_BC_DIM_MAX",   _SC_BC_DIM_MAX},
#endif
#ifdef _SC_BC_SCALE_MAX
    {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX},
#endif
#ifdef _SC_BC_STRING_MAX
    {"SC_BC_STRING_MAX",        _SC_BC_STRING_MAX},
#endif
#ifdef _SC_CAP
    {"SC_CAP",  _SC_CAP},
#endif
#ifdef _SC_CHARCLASS_NAME_MAX
    {"SC_CHARCLASS_NAME_MAX",   _SC_CHARCLASS_NAME_MAX},
#endif
#ifdef _SC_CHAR_BIT
    {"SC_CHAR_BIT",     _SC_CHAR_BIT},
#endif
#ifdef _SC_CHAR_MAX
    {"SC_CHAR_MAX",     _SC_CHAR_MAX},
#endif
#ifdef _SC_CHAR_MIN
    {"SC_CHAR_MIN",     _SC_CHAR_MIN},
#endif
#ifdef _SC_CHILD_MAX
    {"SC_CHILD_MAX",    _SC_CHILD_MAX},
#endif
#ifdef _SC_CLK_TCK
    {"SC_CLK_TCK",      _SC_CLK_TCK},
#endif
#ifdef _SC_COHER_BLKSZ
    {"SC_COHER_BLKSZ",  _SC_COHER_BLKSZ},
#endif
#ifdef _SC_COLL_WEIGHTS_MAX
    {"SC_COLL_WEIGHTS_MAX",     _SC_COLL_WEIGHTS_MAX},
#endif
#ifdef _SC_DCACHE_ASSOC
    {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC},
#endif
#ifdef _SC_DCACHE_BLKSZ
    {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ},
#endif
#ifdef _SC_DCACHE_LINESZ
    {"SC_DCACHE_LINESZ",        _SC_DCACHE_LINESZ},
#endif
#ifdef _SC_DCACHE_SZ
    {"SC_DCACHE_SZ",    _SC_DCACHE_SZ},
#endif
#ifdef _SC_DCACHE_TBLKSZ
    {"SC_DCACHE_TBLKSZ",        _SC_DCACHE_TBLKSZ},
#endif
#ifdef _SC_DELAYTIMER_MAX
    {"SC_DELAYTIMER_MAX",       _SC_DELAYTIMER_MAX},
#endif
#ifdef _SC_EQUIV_CLASS_MAX
    {"SC_EQUIV_CLASS_MAX",      _SC_EQUIV_CLASS_MAX},
#endif
#ifdef _SC_EXPR_NEST_MAX
    {"SC_EXPR_NEST_MAX",        _SC_EXPR_NEST_MAX},
#endif
#ifdef _SC_FSYNC
    {"SC_FSYNC",        _SC_FSYNC},
#endif
#ifdef _SC_GETGR_R_SIZE_MAX
    {"SC_GETGR_R_SIZE_MAX",     _SC_GETGR_R_SIZE_MAX},
#endif
#ifdef _SC_GETPW_R_SIZE_MAX
    {"SC_GETPW_R_SIZE_MAX",     _SC_GETPW_R_SIZE_MAX},
#endif
#ifdef _SC_ICACHE_ASSOC
    {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC},
#endif
#ifdef _SC_ICACHE_BLKSZ
    {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ},
#endif
#ifdef _SC_ICACHE_LINESZ
    {"SC_ICACHE_LINESZ",        _SC_ICACHE_LINESZ},
#endif
#ifdef _SC_ICACHE_SZ
    {"SC_ICACHE_SZ",    _SC_ICACHE_SZ},
#endif
#ifdef _SC_INF
    {"SC_INF",  _SC_INF},
#endif
#ifdef _SC_INT_MAX
    {"SC_INT_MAX",      _SC_INT_MAX},
#endif
#ifdef _SC_INT_MIN
    {"SC_INT_MIN",      _SC_INT_MIN},
#endif
#ifdef _SC_IOV_MAX
    {"SC_IOV_MAX",      _SC_IOV_MAX},
#endif
#ifdef _SC_IP_SECOPTS
    {"SC_IP_SECOPTS",   _SC_IP_SECOPTS},
#endif
#ifdef _SC_JOB_CONTROL
    {"SC_JOB_CONTROL",  _SC_JOB_CONTROL},
#endif
#ifdef _SC_KERN_POINTERS
    {"SC_KERN_POINTERS",        _SC_KERN_POINTERS},
#endif
#ifdef _SC_KERN_SIM
    {"SC_KERN_SIM",     _SC_KERN_SIM},
#endif
#ifdef _SC_LINE_MAX
    {"SC_LINE_MAX",     _SC_LINE_MAX},
#endif
#ifdef _SC_LOGIN_NAME_MAX
    {"SC_LOGIN_NAME_MAX",       _SC_LOGIN_NAME_MAX},
#endif
#ifdef _SC_LOGNAME_MAX
    {"SC_LOGNAME_MAX",  _SC_LOGNAME_MAX},
#endif
#ifdef _SC_LONG_BIT
    {"SC_LONG_BIT",     _SC_LONG_BIT},
#endif
#ifdef _SC_MAC
    {"SC_MAC",  _SC_MAC},
#endif
#ifdef _SC_MAPPED_FILES
    {"SC_MAPPED_FILES", _SC_MAPPED_FILES},
#endif
#ifdef _SC_MAXPID
    {"SC_MAXPID",       _SC_MAXPID},
#endif
#ifdef _SC_MB_LEN_MAX
    {"SC_MB_LEN_MAX",   _SC_MB_LEN_MAX},
#endif
#ifdef _SC_MEMLOCK
    {"SC_MEMLOCK",      _SC_MEMLOCK},
#endif
#ifdef _SC_MEMLOCK_RANGE
    {"SC_MEMLOCK_RANGE",        _SC_MEMLOCK_RANGE},
#endif
#ifdef _SC_MEMORY_PROTECTION
    {"SC_MEMORY_PROTECTION",    _SC_MEMORY_PROTECTION},
#endif
#ifdef _SC_MESSAGE_PASSING
    {"SC_MESSAGE_PASSING",      _SC_MESSAGE_PASSING},
#endif
#ifdef _SC_MMAP_FIXED_ALIGNMENT
    {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT},
#endif
#ifdef _SC_MQ_OPEN_MAX
    {"SC_MQ_OPEN_MAX",  _SC_MQ_OPEN_MAX},
#endif
#ifdef _SC_MQ_PRIO_MAX
    {"SC_MQ_PRIO_MAX",  _SC_MQ_PRIO_MAX},
#endif
#ifdef _SC_NACLS_MAX
    {"SC_NACLS_MAX",    _SC_NACLS_MAX},
#endif
#ifdef _SC_NGROUPS_MAX
    {"SC_NGROUPS_MAX",  _SC_NGROUPS_MAX},
#endif
#ifdef _SC_NL_ARGMAX
    {"SC_NL_ARGMAX",    _SC_NL_ARGMAX},
#endif
#ifdef _SC_NL_LANGMAX
    {"SC_NL_LANGMAX",   _SC_NL_LANGMAX},
#endif
#ifdef _SC_NL_MSGMAX
    {"SC_NL_MSGMAX",    _SC_NL_MSGMAX},
#endif
#ifdef _SC_NL_NMAX
    {"SC_NL_NMAX",      _SC_NL_NMAX},
#endif
#ifdef _SC_NL_SETMAX
    {"SC_NL_SETMAX",    _SC_NL_SETMAX},
#endif
#ifdef _SC_NL_TEXTMAX
    {"SC_NL_TEXTMAX",   _SC_NL_TEXTMAX},
#endif
#ifdef _SC_NPROCESSORS_CONF
    {"SC_NPROCESSORS_CONF",     _SC_NPROCESSORS_CONF},
#endif
#ifdef _SC_NPROCESSORS_ONLN
    {"SC_NPROCESSORS_ONLN",     _SC_NPROCESSORS_ONLN},
#endif
#ifdef _SC_NPROC_CONF
    {"SC_NPROC_CONF",   _SC_NPROC_CONF},
#endif
#ifdef _SC_NPROC_ONLN
    {"SC_NPROC_ONLN",   _SC_NPROC_ONLN},
#endif
#ifdef _SC_NZERO
    {"SC_NZERO",        _SC_NZERO},
#endif
#ifdef _SC_OPEN_MAX
    {"SC_OPEN_MAX",     _SC_OPEN_MAX},
#endif
#ifdef _SC_PAGESIZE
    {"SC_PAGESIZE",     _SC_PAGESIZE},
#endif
#ifdef _SC_PAGE_SIZE
    {"SC_PAGE_SIZE",    _SC_PAGE_SIZE},
#endif
#ifdef _SC_AIX_REALMEM
    {"SC_AIX_REALMEM", _SC_AIX_REALMEM},
#endif
#ifdef _SC_PASS_MAX
    {"SC_PASS_MAX",     _SC_PASS_MAX},
#endif
#ifdef _SC_PHYS_PAGES
    {"SC_PHYS_PAGES",   _SC_PHYS_PAGES},
#endif
#ifdef _SC_PII
    {"SC_PII",  _SC_PII},
#endif
#ifdef _SC_PII_INTERNET
    {"SC_PII_INTERNET", _SC_PII_INTERNET},
#endif
#ifdef _SC_PII_INTERNET_DGRAM
    {"SC_PII_INTERNET_DGRAM",   _SC_PII_INTERNET_DGRAM},
#endif
#ifdef _SC_PII_INTERNET_STREAM
    {"SC_PII_INTERNET_STREAM",  _SC_PII_INTERNET_STREAM},
#endif
#ifdef _SC_PII_OSI
    {"SC_PII_OSI",      _SC_PII_OSI},
#endif
#ifdef _SC_PII_OSI_CLTS
    {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS},
#endif
#ifdef _SC_PII_OSI_COTS
    {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS},
#endif
#ifdef _SC_PII_OSI_M
    {"SC_PII_OSI_M",    _SC_PII_OSI_M},
#endif
#ifdef _SC_PII_SOCKET
    {"SC_PII_SOCKET",   _SC_PII_SOCKET},
#endif
#ifdef _SC_PII_XTI
    {"SC_PII_XTI",      _SC_PII_XTI},
#endif
#ifdef _SC_POLL
    {"SC_POLL", _SC_POLL},
#endif
#ifdef _SC_PRIORITIZED_IO
    {"SC_PRIORITIZED_IO",       _SC_PRIORITIZED_IO},
#endif
#ifdef _SC_PRIORITY_SCHEDULING
    {"SC_PRIORITY_SCHEDULING",  _SC_PRIORITY_SCHEDULING},
#endif
#ifdef _SC_REALTIME_SIGNALS
    {"SC_REALTIME_SIGNALS",     _SC_REALTIME_SIGNALS},
#endif
#ifdef _SC_RE_DUP_MAX
    {"SC_RE_DUP_MAX",   _SC_RE_DUP_MAX},
#endif
#ifdef _SC_RTSIG_MAX
    {"SC_RTSIG_MAX",    _SC_RTSIG_MAX},
#endif
#ifdef _SC_SAVED_IDS
    {"SC_SAVED_IDS",    _SC_SAVED_IDS},
#endif
#ifdef _SC_SCHAR_MAX
    {"SC_SCHAR_MAX",    _SC_SCHAR_MAX},
#endif
#ifdef _SC_SCHAR_MIN
    {"SC_SCHAR_MIN",    _SC_SCHAR_MIN},
#endif
#ifdef _SC_SELECT
    {"SC_SELECT",       _SC_SELECT},
#endif
#ifdef _SC_SEMAPHORES
    {"SC_SEMAPHORES",   _SC_SEMAPHORES},
#endif
#ifdef _SC_SEM_NSEMS_MAX
    {"SC_SEM_NSEMS_MAX",        _SC_SEM_NSEMS_MAX},
#endif
#ifdef _SC_SEM_VALUE_MAX
    {"SC_SEM_VALUE_MAX",        _SC_SEM_VALUE_MAX},
#endif
#ifdef _SC_SHARED_MEMORY_OBJECTS
    {"SC_SHARED_MEMORY_OBJECTS",        _SC_SHARED_MEMORY_OBJECTS},
#endif
#ifdef _SC_SHRT_MAX
    {"SC_SHRT_MAX",     _SC_SHRT_MAX},
#endif
#ifdef _SC_SHRT_MIN
    {"SC_SHRT_MIN",     _SC_SHRT_MIN},
#endif
#ifdef _SC_SIGQUEUE_MAX
    {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX},
#endif
#ifdef _SC_SIGRT_MAX
    {"SC_SIGRT_MAX",    _SC_SIGRT_MAX},
#endif
#ifdef _SC_SIGRT_MIN
    {"SC_SIGRT_MIN",    _SC_SIGRT_MIN},
#endif
#ifdef _SC_SOFTPOWER
    {"SC_SOFTPOWER",    _SC_SOFTPOWER},
#endif
#ifdef _SC_SPLIT_CACHE
    {"SC_SPLIT_CACHE",  _SC_SPLIT_CACHE},
#endif
#ifdef _SC_SSIZE_MAX
    {"SC_SSIZE_MAX",    _SC_SSIZE_MAX},
#endif
#ifdef _SC_STACK_PROT
    {"SC_STACK_PROT",   _SC_STACK_PROT},
#endif
#ifdef _SC_STREAM_MAX
    {"SC_STREAM_MAX",   _SC_STREAM_MAX},
#endif
#ifdef _SC_SYNCHRONIZED_IO
    {"SC_SYNCHRONIZED_IO",      _SC_SYNCHRONIZED_IO},
#endif
#ifdef _SC_THREADS
    {"SC_THREADS",      _SC_THREADS},
#endif
#ifdef _SC_THREAD_ATTR_STACKADDR
    {"SC_THREAD_ATTR_STACKADDR",        _SC_THREAD_ATTR_STACKADDR},
#endif
#ifdef _SC_THREAD_ATTR_STACKSIZE
    {"SC_THREAD_ATTR_STACKSIZE",        _SC_THREAD_ATTR_STACKSIZE},
#endif
#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS
    {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS},
#endif
#ifdef _SC_THREAD_KEYS_MAX
    {"SC_THREAD_KEYS_MAX",      _SC_THREAD_KEYS_MAX},
#endif
#ifdef _SC_THREAD_PRIORITY_SCHEDULING
    {"SC_THREAD_PRIORITY_SCHEDULING",   _SC_THREAD_PRIORITY_SCHEDULING},
#endif
#ifdef _SC_THREAD_PRIO_INHERIT
    {"SC_THREAD_PRIO_INHERIT",  _SC_THREAD_PRIO_INHERIT},
#endif
#ifdef _SC_THREAD_PRIO_PROTECT
    {"SC_THREAD_PRIO_PROTECT",  _SC_THREAD_PRIO_PROTECT},
#endif
#ifdef _SC_THREAD_PROCESS_SHARED
    {"SC_THREAD_PROCESS_SHARED",        _SC_THREAD_PROCESS_SHARED},
#endif
#ifdef _SC_THREAD_SAFE_FUNCTIONS
    {"SC_THREAD_SAFE_FUNCTIONS",        _SC_THREAD_SAFE_FUNCTIONS},
#endif
#ifdef _SC_THREAD_STACK_MIN
    {"SC_THREAD_STACK_MIN",     _SC_THREAD_STACK_MIN},
#endif
#ifdef _SC_THREAD_THREADS_MAX
    {"SC_THREAD_THREADS_MAX",   _SC_THREAD_THREADS_MAX},
#endif
#ifdef _SC_TIMERS
    {"SC_TIMERS",       _SC_TIMERS},
#endif
#ifdef _SC_TIMER_MAX
    {"SC_TIMER_MAX",    _SC_TIMER_MAX},
#endif
#ifdef _SC_TTY_NAME_MAX
    {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX},
#endif
#ifdef _SC_TZNAME_MAX
    {"SC_TZNAME_MAX",   _SC_TZNAME_MAX},
#endif
#ifdef _SC_T_IOV_MAX
    {"SC_T_IOV_MAX",    _SC_T_IOV_MAX},
#endif
#ifdef _SC_UCHAR_MAX
    {"SC_UCHAR_MAX",    _SC_UCHAR_MAX},
#endif
#ifdef _SC_UINT_MAX
    {"SC_UINT_MAX",     _SC_UINT_MAX},
#endif
#ifdef _SC_UIO_MAXIOV
    {"SC_UIO_MAXIOV",   _SC_UIO_MAXIOV},
#endif
#ifdef _SC_ULONG_MAX
    {"SC_ULONG_MAX",    _SC_ULONG_MAX},
#endif
#ifdef _SC_USHRT_MAX
    {"SC_USHRT_MAX",    _SC_USHRT_MAX},
#endif
#ifdef _SC_VERSION
    {"SC_VERSION",      _SC_VERSION},
#endif
#ifdef _SC_WORD_BIT
    {"SC_WORD_BIT",     _SC_WORD_BIT},
#endif
#ifdef _SC_XBS5_ILP32_OFF32
    {"SC_XBS5_ILP32_OFF32",     _SC_XBS5_ILP32_OFF32},
#endif
#ifdef _SC_XBS5_ILP32_OFFBIG
    {"SC_XBS5_ILP32_OFFBIG",    _SC_XBS5_ILP32_OFFBIG},
#endif
#ifdef _SC_XBS5_LP64_OFF64
    {"SC_XBS5_LP64_OFF64",      _SC_XBS5_LP64_OFF64},
#endif
#ifdef _SC_XBS5_LPBIG_OFFBIG
    {"SC_XBS5_LPBIG_OFFBIG",    _SC_XBS5_LPBIG_OFFBIG},
#endif
#ifdef _SC_XOPEN_CRYPT
    {"SC_XOPEN_CRYPT",  _SC_XOPEN_CRYPT},
#endif
#ifdef _SC_XOPEN_ENH_I18N
    {"SC_XOPEN_ENH_I18N",       _SC_XOPEN_ENH_I18N},
#endif
#ifdef _SC_XOPEN_LEGACY
    {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY},
#endif
#ifdef _SC_XOPEN_REALTIME
    {"SC_XOPEN_REALTIME",       _SC_XOPEN_REALTIME},
#endif
#ifdef _SC_XOPEN_REALTIME_THREADS
    {"SC_XOPEN_REALTIME_THREADS",       _SC_XOPEN_REALTIME_THREADS},
#endif
#ifdef _SC_XOPEN_SHM
    {"SC_XOPEN_SHM",    _SC_XOPEN_SHM},
#endif
#ifdef _SC_XOPEN_UNIX
    {"SC_XOPEN_UNIX",   _SC_XOPEN_UNIX},
#endif
#ifdef _SC_XOPEN_VERSION
    {"SC_XOPEN_VERSION",        _SC_XOPEN_VERSION},
#endif
#ifdef _SC_XOPEN_XCU_VERSION
    {"SC_XOPEN_XCU_VERSION",    _SC_XOPEN_XCU_VERSION},
#endif
#ifdef _SC_XOPEN_XPG2
    {"SC_XOPEN_XPG2",   _SC_XOPEN_XPG2},
#endif
#ifdef _SC_XOPEN_XPG3
    {"SC_XOPEN_XPG3",   _SC_XOPEN_XPG3},
#endif
#ifdef _SC_XOPEN_XPG4
    {"SC_XOPEN_XPG4",   _SC_XOPEN_XPG4},
#endif
#ifdef _SC_MINSIGSTKSZ
    {"SC_MINSIGSTKSZ",   _SC_MINSIGSTKSZ},
#endif
};


/*[clinic input]
os.sysconf -> long
    name: confname(table="sysconf_names")
    /

Return an integer-valued system configuration variable.
[clinic start generated code]*/

static long
os_sysconf_impl(PyObject *module, int name)
/*[clinic end generated code: output=3662f945fc0cc756 input=930b8f23b5d15086]*/
{
    long value;

    errno = 0;
    value = sysconf(name);
    if (value == -1 && errno != 0)
        posix_error();
    return value;
}
#endif /* HAVE_SYSCONF */


static int
setup_confname_table(struct constdef *table, size_t tablesize,
                     const char *tablename, PyObject *module)
{
    PyObject *d = PyDict_New();
    if (d == NULL)
        return -1;

    for (size_t i=0; i < tablesize; ++i) {
        PyObject *o = PyLong_FromLong(table[i].value);
        if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) {
            Py_XDECREF(o);
            Py_DECREF(d);
            return -1;
        }
        Py_DECREF(o);
    }
    return PyModule_Add(module, tablename, d);
}

/* Return -1 on failure, 0 on success. */
static int
setup_confname_tables(PyObject *module)
{
#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF)
    if (setup_confname_table(posix_constants_pathconf,
                             sizeof(posix_constants_pathconf)
                               / sizeof(struct constdef),
                             "pathconf_names", module))
        return -1;
#endif
#ifdef HAVE_CONFSTR
    if (setup_confname_table(posix_constants_confstr,
                             sizeof(posix_constants_confstr)
                               / sizeof(struct constdef),
                             "confstr_names", module))
        return -1;
#endif
#ifdef HAVE_SYSCONF
    if (setup_confname_table(posix_constants_sysconf,
                             sizeof(posix_constants_sysconf)
                               / sizeof(struct constdef),
                             "sysconf_names", module))
        return -1;
#endif
    return 0;
}


/*[clinic input]
os.abort

Abort the interpreter immediately.

This function 'dumps core' or otherwise fails in the hardest way possible
on the hosting operating system.  This function never returns.
[clinic start generated code]*/

static PyObject *
os_abort_impl(PyObject *module)
/*[clinic end generated code: output=dcf52586dad2467c input=cf2c7d98bc504047]*/
{
    abort();
    /*NOTREACHED*/
#ifndef __clang__
    /* Issue #28152: abort() is declared with __attribute__((__noreturn__)).
       GCC emits a warning without "return NULL;" (compiler bug?), but Clang
       is smarter and emits a warning on the return. */
    Py_FatalError("abort() called from Python code didn't abort!");
    return NULL;
#endif
}

#ifdef MS_WINDOWS
/* Grab ShellExecute dynamically from shell32 */
static int has_ShellExecute = -1;
static HINSTANCE (CALLBACK *Py_ShellExecuteW)(HWND, LPCWSTR, LPCWSTR, LPCWSTR,
                                              LPCWSTR, INT);
static int
check_ShellExecute(void)
{
    HINSTANCE hShell32;

    /* only recheck */
    if (-1 == has_ShellExecute) {
        Py_BEGIN_ALLOW_THREADS
        /* Security note: this call is not vulnerable to "DLL hijacking".
           SHELL32 is part of "KnownDLLs" and so Windows always load
           the system SHELL32.DLL, even if there is another SHELL32.DLL
           in the DLL search path. */
        hShell32 = LoadLibraryW(L"SHELL32");
        if (hShell32) {
            *(FARPROC*)&Py_ShellExecuteW = GetProcAddress(hShell32,
                                            "ShellExecuteW");
            has_ShellExecute = Py_ShellExecuteW != NULL;
        } else {
            has_ShellExecute = 0;
        }
        Py_END_ALLOW_THREADS
    }
    return has_ShellExecute;
}


/*[clinic input]
os.startfile
    filepath: path_t
    operation: Py_UNICODE = NULL
    arguments: Py_UNICODE = NULL
    cwd: path_t(nullable=True) = None
    show_cmd: int = 1

Start a file with its associated application.

When "operation" is not specified or "open", this acts like
double-clicking the file in Explorer, or giving the file name as an
argument to the DOS "start" command: the file is opened with whatever
application (if any) its extension is associated.
When another "operation" is given, it specifies what should be done with
the file.  A typical operation is "print".

"arguments" is passed to the application, but should be omitted if the
file is a document.

"cwd" is the working directory for the operation. If "filepath" is
relative, it will be resolved against this directory. This argument
should usually be an absolute path.

"show_cmd" can be used to override the recommended visibility option.
See the Windows ShellExecute documentation for values.

startfile returns as soon as the associated application is launched.
There is no option to wait for the application to close, and no way
to retrieve the application's exit status.

The filepath is relative to the current directory.  If you want to use
an absolute path, make sure the first character is not a slash ("/");
the underlying Win32 ShellExecute function doesn't work if it is.
[clinic start generated code]*/

static PyObject *
os_startfile_impl(PyObject *module, path_t *filepath,
                  const wchar_t *operation, const wchar_t *arguments,
                  path_t *cwd, int show_cmd)
/*[clinic end generated code: output=1c6f2f3340e31ffa input=8248997b80669622]*/
{
    HINSTANCE rc;

    if(!check_ShellExecute()) {
        /* If the OS doesn't have ShellExecute, return a
           NotImplementedError. */
        return PyErr_Format(PyExc_NotImplementedError,
            "startfile not available on this platform");
    }

    if (PySys_Audit("os.startfile", "Ou", filepath->object, operation) < 0) {
        return NULL;
    }
    if (PySys_Audit("os.startfile/2", "OuuOi", filepath->object, operation,
                    arguments, cwd->object ? cwd->object : Py_None,
                    show_cmd) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    rc = Py_ShellExecuteW((HWND)0, operation, filepath->wide,
                          arguments, cwd->wide, show_cmd);
    Py_END_ALLOW_THREADS

    if (rc <= (HINSTANCE)32) {
        win32_error_object("startfile", filepath->object);
        return NULL;
    }
    Py_RETURN_NONE;
}
#endif /* MS_WINDOWS */


#ifdef HAVE_GETLOADAVG
/*[clinic input]
os.getloadavg

Return average recent system load information.

Return the number of processes in the system run queue averaged over
the last 1, 5, and 15 minutes as a tuple of three floats.
Raises OSError if the load average was unobtainable.
[clinic start generated code]*/

static PyObject *
os_getloadavg_impl(PyObject *module)
/*[clinic end generated code: output=9ad3a11bfb4f4bd2 input=3d6d826b76d8a34e]*/
{
    double loadavg[3];
    if (getloadavg(loadavg, 3)!=3) {
        PyErr_SetString(PyExc_OSError, "Load averages are unobtainable");
        return NULL;
    } else
        return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]);
}
#endif /* HAVE_GETLOADAVG */


/*[clinic input]
os.device_encoding
    fd: int

Return a string describing the encoding of a terminal's file descriptor.

The file descriptor must be attached to a terminal.
If the device is not a terminal, return None.
[clinic start generated code]*/

static PyObject *
os_device_encoding_impl(PyObject *module, int fd)
/*[clinic end generated code: output=e0d294bbab7e8c2b input=9e1d4a42b66df312]*/
{
    return _Py_device_encoding(fd);
}


#ifdef HAVE_SETRESUID
/*[clinic input]
os.setresuid

    ruid: uid_t
    euid: uid_t
    suid: uid_t
    /

Set the current process's real, effective, and saved user ids.
[clinic start generated code]*/

static PyObject *
os_setresuid_impl(PyObject *module, uid_t ruid, uid_t euid, uid_t suid)
/*[clinic end generated code: output=834a641e15373e97 input=9e33cb79a82792f3]*/
{
    if (setresuid(ruid, euid, suid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETRESUID */


#ifdef HAVE_SETRESGID
/*[clinic input]
os.setresgid

    rgid: gid_t
    egid: gid_t
    sgid: gid_t
    /

Set the current process's real, effective, and saved group ids.
[clinic start generated code]*/

static PyObject *
os_setresgid_impl(PyObject *module, gid_t rgid, gid_t egid, gid_t sgid)
/*[clinic end generated code: output=6aa402f3d2e514a9 input=33e9e0785ef426b1]*/
{
    if (setresgid(rgid, egid, sgid) < 0)
        return posix_error();
    Py_RETURN_NONE;
}
#endif /* HAVE_SETRESGID */


#ifdef HAVE_GETRESUID
/*[clinic input]
os.getresuid

Return a tuple of the current process's real, effective, and saved user ids.
[clinic start generated code]*/

static PyObject *
os_getresuid_impl(PyObject *module)
/*[clinic end generated code: output=8e0becff5dece5bf input=41ccfa8e1f6517ad]*/
{
    uid_t ruid, euid, suid;
    if (getresuid(&ruid, &euid, &suid) < 0)
        return posix_error();
    return Py_BuildValue("(NNN)", _PyLong_FromUid(ruid),
                                  _PyLong_FromUid(euid),
                                  _PyLong_FromUid(suid));
}
#endif /* HAVE_GETRESUID */


#ifdef HAVE_GETRESGID
/*[clinic input]
@permit_long_summary
os.getresgid

Return a tuple of the current process's real, effective, and saved group ids.
[clinic start generated code]*/

static PyObject *
os_getresgid_impl(PyObject *module)
/*[clinic end generated code: output=2719c4bfcf27fb9f input=ad9adadc86fbdb17]*/
{
    gid_t rgid, egid, sgid;
    if (getresgid(&rgid, &egid, &sgid) < 0)
        return posix_error();
    return Py_BuildValue("(NNN)", _PyLong_FromGid(rgid),
                                  _PyLong_FromGid(egid),
                                  _PyLong_FromGid(sgid));
}
#endif /* HAVE_GETRESGID */


#ifdef USE_XATTRS
/*[clinic input]
os.getxattr

    path: path_t(allow_fd=True)
    attribute: path_t
    *
    follow_symlinks: bool = True

Return the value of extended attribute attribute on path.

path may be either a string, a path-like object, or an open file descriptor.
If follow_symlinks is False, and the last element of the path is a symbolic
  link, getxattr will examine the symbolic link itself instead of the file
  the link points to.

[clinic start generated code]*/

static PyObject *
os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute,
                 int follow_symlinks)
/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/
{
    if (fd_and_follow_symlinks_invalid("getxattr", path->fd, follow_symlinks))
        return NULL;

    if (PySys_Audit("os.getxattr", "OO", path->object, attribute->object) < 0) {
        return NULL;
    }

    for (Py_ssize_t i = 0; ; i++) {
        ssize_t result;
        static const Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0};
        Py_ssize_t buffer_size = buffer_sizes[i];
        if (!buffer_size) {
            path_error(path);
            return NULL;
        }
        PyBytesWriter *writer = PyBytesWriter_Create(buffer_size);
        if (writer == NULL) {
            return NULL;
        }
        void *ptr = PyBytesWriter_GetData(writer);

        Py_BEGIN_ALLOW_THREADS;
        if (path->fd >= 0)
            result = fgetxattr(path->fd, attribute->narrow, ptr, buffer_size);
        else if (follow_symlinks)
            result = getxattr(path->narrow, attribute->narrow, ptr, buffer_size);
        else
            result = lgetxattr(path->narrow, attribute->narrow, ptr, buffer_size);
        Py_END_ALLOW_THREADS;

        if (result < 0) {
            PyBytesWriter_Discard(writer);
            if (errno == ERANGE) {
                continue;
            }
            path_error(path);
            return NULL;
        }

        return PyBytesWriter_FinishWithSize(writer, result);
    }
}


/*[clinic input]
@permit_long_docstring_body
os.setxattr

    path: path_t(allow_fd=True)
    attribute: path_t
    value: Py_buffer
    flags: int = 0
    *
    follow_symlinks: bool = True

Set extended attribute attribute on path to value.

path may be either a string, a path-like object,  or an open file descriptor.
If follow_symlinks is False, and the last element of the path is a symbolic
  link, setxattr will modify the symbolic link itself instead of the file
  the link points to.

[clinic start generated code]*/

static PyObject *
os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute,
                 Py_buffer *value, int flags, int follow_symlinks)
/*[clinic end generated code: output=98b83f63fdde26bb input=4098e6f68699f3d7]*/
{
    ssize_t result;

    if (fd_and_follow_symlinks_invalid("setxattr", path->fd, follow_symlinks))
        return NULL;

    if (PySys_Audit("os.setxattr", "OOy#i", path->object, attribute->object,
                    value->buf, value->len, flags) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS;
    if (path->fd > -1)
        result = fsetxattr(path->fd, attribute->narrow,
                           value->buf, value->len, flags);
    else if (follow_symlinks)
        result = setxattr(path->narrow, attribute->narrow,
                           value->buf, value->len, flags);
    else
        result = lsetxattr(path->narrow, attribute->narrow,
                           value->buf, value->len, flags);
    Py_END_ALLOW_THREADS;

    if (result) {
        path_error(path);
        return NULL;
    }

    Py_RETURN_NONE;
}


/*[clinic input]
os.removexattr

    path: path_t(allow_fd=True)
    attribute: path_t
    *
    follow_symlinks: bool = True

Remove extended attribute attribute on path.

path may be either a string, a path-like object, or an open file descriptor.
If follow_symlinks is False, and the last element of the path is a symbolic
  link, removexattr will modify the symbolic link itself instead of the file
  the link points to.

[clinic start generated code]*/

static PyObject *
os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute,
                    int follow_symlinks)
/*[clinic end generated code: output=521a51817980cda6 input=3d9a7d36fe2f7c4e]*/
{
    ssize_t result;

    if (fd_and_follow_symlinks_invalid("removexattr", path->fd, follow_symlinks))
        return NULL;

    if (PySys_Audit("os.removexattr", "OO", path->object, attribute->object) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS;
    if (path->fd > -1)
        result = fremovexattr(path->fd, attribute->narrow);
    else if (follow_symlinks)
        result = removexattr(path->narrow, attribute->narrow);
    else
        result = lremovexattr(path->narrow, attribute->narrow);
    Py_END_ALLOW_THREADS;

    if (result) {
        return path_error(path);
    }

    Py_RETURN_NONE;
}


/*[clinic input]
@permit_long_docstring_body
os.listxattr

    path: path_t(allow_fd=True, nullable=True) = None
    *
    follow_symlinks: bool = True

Return a list of extended attributes on path.

path may be either None, a string, a path-like object, or an open file descriptor.
if path is None, listxattr will examine the current directory.
If follow_symlinks is False, and the last element of the path is a symbolic
  link, listxattr will examine the symbolic link itself instead of the file
  the link points to.
[clinic start generated code]*/

static PyObject *
os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks)
/*[clinic end generated code: output=bebdb4e2ad0ce435 input=48aa9ac8be47dea1]*/
{
    Py_ssize_t i;
    PyObject *result = NULL;
    const char *name;
    char *buffer = NULL;

    if (fd_and_follow_symlinks_invalid("listxattr", path->fd, follow_symlinks))
        goto exit;

    if (PySys_Audit("os.listxattr", "(O)",
                    path->object ? path->object : Py_None) < 0) {
        return NULL;
    }

    name = path->narrow ? path->narrow : ".";

    for (i = 0; ; i++) {
        const char *start, *trace, *end;
        ssize_t length;
        static const Py_ssize_t buffer_sizes[] = { 256, XATTR_LIST_MAX, 0 };
        Py_ssize_t buffer_size = buffer_sizes[i];
        if (!buffer_size) {
            /* ERANGE */
            path_error(path);
            break;
        }
        buffer = PyMem_Malloc(buffer_size);
        if (!buffer) {
            PyErr_NoMemory();
            break;
        }

        Py_BEGIN_ALLOW_THREADS;
        if (path->fd > -1)
            length = flistxattr(path->fd, buffer, buffer_size);
        else if (follow_symlinks)
            length = listxattr(name, buffer, buffer_size);
        else
            length = llistxattr(name, buffer, buffer_size);
        Py_END_ALLOW_THREADS;

        if (length < 0) {
            if (errno == ERANGE) {
                PyMem_Free(buffer);
                buffer = NULL;
                continue;
            }
            path_error(path);
            break;
        }

        result = PyList_New(0);
        if (!result) {
            goto exit;
        }

        end = buffer + length;
        for (trace = start = buffer; trace != end; trace++) {
            if (!*trace) {
                int error;
                PyObject *attribute = PyUnicode_DecodeFSDefaultAndSize(start,
                                                                 trace - start);
                if (!attribute) {
                    Py_SETREF(result, NULL);
                    goto exit;
                }
                error = PyList_Append(result, attribute);
                Py_DECREF(attribute);
                if (error) {
                    Py_SETREF(result, NULL);
                    goto exit;
                }
                start = trace + 1;
            }
        }
    break;
    }
exit:
    if (buffer)
        PyMem_Free(buffer);
    return result;
}
#endif /* USE_XATTRS */


/*[clinic input]
@permit_long_summary
os.urandom

    size: Py_ssize_t(allow_negative=False)
    /

Return a bytes object containing random bytes suitable for cryptographic use.
[clinic start generated code]*/

static PyObject *
os_urandom_impl(PyObject *module, Py_ssize_t size)
/*[clinic end generated code: output=42c5cca9d18068e9 input=58a0def87dbc2c22]*/
{
    if (size < 0) {
        return PyErr_Format(PyExc_ValueError,
                            "negative argument not allowed");
    }

    PyBytesWriter *writer = PyBytesWriter_Create(size);
    if (writer == NULL) {
        return NULL;
    }

    int result = _PyOS_URandom(PyBytesWriter_GetData(writer), size);
    if (result == -1) {
        PyBytesWriter_Discard(writer);
        return NULL;
    }
    return PyBytesWriter_Finish(writer);
}

#ifdef HAVE_MEMFD_CREATE
/*[clinic input]
os.memfd_create

    name: unicode_fs_encoded
    flags: unsigned_int(bitwise=True, c_default="MFD_CLOEXEC") = MFD_CLOEXEC

[clinic start generated code]*/

static PyObject *
os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
/*[clinic end generated code: output=6681ede983bdb9a6 input=cd0eb092cfac474b]*/
{
    int fd;
    const char *bytes = PyBytes_AS_STRING(name);
    Py_BEGIN_ALLOW_THREADS
    fd = memfd_create(bytes, flags);
    Py_END_ALLOW_THREADS
    if (fd == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return PyLong_FromLong(fd);
}
#endif

#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
/*[clinic input]
os.eventfd

    initval: unsigned_int
    flags: int(c_default="EFD_CLOEXEC") = EFD_CLOEXEC

Creates and returns an event notification file descriptor.
[clinic start generated code]*/

static PyObject *
os_eventfd_impl(PyObject *module, unsigned int initval, int flags)
/*[clinic end generated code: output=ce9c9bbd1446f2de input=66203e3c50c4028b]*/

{
    /* initval is limited to uint32_t, internal counter is uint64_t */
    int fd;
    Py_BEGIN_ALLOW_THREADS
    fd = eventfd(initval, flags);
    Py_END_ALLOW_THREADS
    if (fd == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return PyLong_FromLong(fd);
}

/*[clinic input]
os.eventfd_read

    fd: fildes

Read eventfd value
[clinic start generated code]*/

static PyObject *
os_eventfd_read_impl(PyObject *module, int fd)
/*[clinic end generated code: output=8f2c7b59a3521fd1 input=110f8b57fa596afe]*/
{
    eventfd_t value;
    int result;
    Py_BEGIN_ALLOW_THREADS
    result = eventfd_read(fd, &value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    return PyLong_FromUnsignedLongLong(value);
}

/*[clinic input]
os.eventfd_write

    fd: fildes
    value: unsigned_long_long

Write eventfd value.
[clinic start generated code]*/

static PyObject *
os_eventfd_write_impl(PyObject *module, int fd, unsigned long long value)
/*[clinic end generated code: output=bebd9040bbf987f5 input=156de8555be5a949]*/
{
    int result;
    Py_BEGIN_ALLOW_THREADS
    result = eventfd_write(fd, value);
    Py_END_ALLOW_THREADS
    if (result == -1) {
        return PyErr_SetFromErrno(PyExc_OSError);
    }
    Py_RETURN_NONE;
}
#endif  /* HAVE_EVENTFD && EFD_CLOEXEC */

/* Terminal size querying */

PyDoc_STRVAR(TerminalSize_docstring,
    "A tuple of (columns, lines) for holding terminal window size");

static PyStructSequence_Field TerminalSize_fields[] = {
    {"columns", "width of the terminal window in characters"},
    {"lines", "height of the terminal window in characters"},
    {NULL, NULL}
};

static PyStructSequence_Desc TerminalSize_desc = {
    "os.terminal_size",
    TerminalSize_docstring,
    TerminalSize_fields,
    2,
};

#if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL)
/*[clinic input]
os.get_terminal_size

    fd: int(c_default="fileno(stdout)", py_default="<unrepresentable>") = -1
    /

Return the size of the terminal window as (columns, lines).

The optional argument fd (default standard output) specifies
which file descriptor should be queried.

If the file descriptor is not connected to a terminal, an OSError
is thrown.

This function will only be defined if an implementation is
available for this system.

shutil.get_terminal_size is the high-level function which should
normally be used, os.get_terminal_size is the low-level implementation.
[clinic start generated code]*/

static PyObject *
os_get_terminal_size_impl(PyObject *module, int fd)
/*[clinic end generated code: output=fbab93acef980508 input=ead5679b82ddb920]*/
{
    int columns, lines;
    PyObject *termsize;

    /* Under some conditions stdout may not be connected and
     * fileno(stdout) may point to an invalid file descriptor. For example
     * GUI apps don't have valid standard streams by default.
     *
     * If this happens, and the optional fd argument is not present,
     * the ioctl below will fail returning EBADF. This is what we want.
     */

#ifdef TERMSIZE_USE_IOCTL
    {
        struct winsize w;
        if (ioctl(fd, TIOCGWINSZ, &w))
            return PyErr_SetFromErrno(PyExc_OSError);
        columns = w.ws_col;
        lines = w.ws_row;
    }
#endif /* TERMSIZE_USE_IOCTL */

#ifdef TERMSIZE_USE_CONIO
    {
        HANDLE handle;
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        handle = _Py_get_osfhandle(fd);
        if (handle == INVALID_HANDLE_VALUE)
            return NULL;

        if (!GetConsoleScreenBufferInfo(handle, &csbi))
            return PyErr_SetFromWindowsErr(0);

        columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    }
#endif /* TERMSIZE_USE_CONIO */

    PyObject *TerminalSizeType = get_posix_state(module)->TerminalSizeType;
    termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType);
    if (termsize == NULL)
        return NULL;

    int pos = 0;

#define SET_TERMSIZE(CALL)                                   \
    do {                                                     \
        PyObject *item = (CALL);                             \
        if (item == NULL) {                                  \
            Py_DECREF(termsize);                             \
            return NULL;                                     \
        }                                                    \
        PyStructSequence_SET_ITEM(termsize, pos++, item);    \
    } while(0)

    SET_TERMSIZE(PyLong_FromLong(columns));
    SET_TERMSIZE(PyLong_FromLong(lines));
#undef SET_TERMSIZE

    return termsize;
}
#endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */

/*[clinic input]
os.cpu_count

Return the number of logical CPUs in the system.

Return None if indeterminable.
[clinic start generated code]*/

static PyObject *
os_cpu_count_impl(PyObject *module)
/*[clinic end generated code: output=5fc29463c3936a9c input=ba2f6f8980a0e2eb]*/
{
    const PyConfig *config = _Py_GetConfig();
    if (config->cpu_count > 0) {
        return PyLong_FromLong(config->cpu_count);
    }

    int ncpu = 0;
#ifdef MS_WINDOWS
# ifdef MS_WINDOWS_DESKTOP
    ncpu = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
# else
    ncpu = 0;
# endif

#elif defined(__hpux)
    ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL);

#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
    ncpu = sysconf(_SC_NPROCESSORS_ONLN);

#elif defined(__VXWORKS__)
    ncpu = _Py_popcount32(vxCpuEnabledGet());

#elif defined(__DragonFly__) || \
      defined(__OpenBSD__)   || \
      defined(__FreeBSD__)   || \
      defined(__NetBSD__)    || \
      defined(__APPLE__)
    ncpu = 0;
    size_t len = sizeof(ncpu);
    int mib[2] = {CTL_HW, HW_NCPU};
    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) != 0) {
        ncpu = 0;
    }
#endif

    if (ncpu < 1) {
        Py_RETURN_NONE;
    }
    return PyLong_FromLong(ncpu);
}


/*[clinic input]
os.get_inheritable -> bool

    fd: int
    /

Get the close-on-exe flag of the specified file descriptor.
[clinic start generated code]*/

static int
os_get_inheritable_impl(PyObject *module, int fd)
/*[clinic end generated code: output=0445e20e149aa5b8 input=89ac008dc9ab6b95]*/
{
    int return_value;
    _Py_BEGIN_SUPPRESS_IPH
    return_value = _Py_get_inheritable(fd);
    _Py_END_SUPPRESS_IPH
    return return_value;
}


/*[clinic input]
os.set_inheritable
    fd: int
    inheritable: int
    /

Set the inheritable flag of the specified file descriptor.
[clinic start generated code]*/

static PyObject *
os_set_inheritable_impl(PyObject *module, int fd, int inheritable)
/*[clinic end generated code: output=f1b1918a2f3c38c2 input=9ceaead87a1e2402]*/
{
    int result;

    _Py_BEGIN_SUPPRESS_IPH
    result = _Py_set_inheritable(fd, inheritable, NULL);
    _Py_END_SUPPRESS_IPH
    if (result < 0)
        return NULL;
    Py_RETURN_NONE;
}


#ifdef MS_WINDOWS
#ifndef HANDLE_FLAG_INHERIT
#define HANDLE_FLAG_INHERIT 0x00000001
#endif

/*[clinic input]
os.get_handle_inheritable -> bool
    handle: intptr_t
    /

Get the close-on-exe flag of the specified file descriptor.
[clinic start generated code]*/

static int
os_get_handle_inheritable_impl(PyObject *module, intptr_t handle)
/*[clinic end generated code: output=36be5afca6ea84d8 input=cfe99f9c05c70ad1]*/
{
    DWORD flags;

    if (!GetHandleInformation((HANDLE)handle, &flags)) {
        PyErr_SetFromWindowsErr(0);
        return -1;
    }

    return flags & HANDLE_FLAG_INHERIT;
}


/*[clinic input]
os.set_handle_inheritable
    handle: intptr_t
    inheritable: bool
    /

Set the inheritable flag of the specified handle.
[clinic start generated code]*/

static PyObject *
os_set_handle_inheritable_impl(PyObject *module, intptr_t handle,
                               int inheritable)
/*[clinic end generated code: output=021d74fe6c96baa3 input=7a7641390d8364fc]*/
{
    DWORD flags = inheritable ? HANDLE_FLAG_INHERIT : 0;
    if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) {
        PyErr_SetFromWindowsErr(0);
        return NULL;
    }
    Py_RETURN_NONE;
}
#endif /* MS_WINDOWS */

/*[clinic input]
os.get_blocking -> bool
    fd: int
    /

Get the blocking mode of the file descriptor.

Return False if the O_NONBLOCK flag is set, True if the flag is cleared.
[clinic start generated code]*/

static int
os_get_blocking_impl(PyObject *module, int fd)
/*[clinic end generated code: output=336a12ad76a61482 input=f4afb59d51560179]*/
{
    int blocking;

    _Py_BEGIN_SUPPRESS_IPH
    blocking = _Py_get_blocking(fd);
    _Py_END_SUPPRESS_IPH
    return blocking;
}

/*[clinic input]
os.set_blocking
    fd: int
    blocking: bool
    /

Set the blocking mode of the specified file descriptor.

Set the O_NONBLOCK flag if blocking is False,
clear the O_NONBLOCK flag otherwise.
[clinic start generated code]*/

static PyObject *
os_set_blocking_impl(PyObject *module, int fd, int blocking)
/*[clinic end generated code: output=384eb43aa0762a9d input=7e9dfc9b14804dd4]*/
{
    int result;

    _Py_BEGIN_SUPPRESS_IPH
    result = _Py_set_blocking(fd, blocking);
    _Py_END_SUPPRESS_IPH
    if (result < 0)
        return NULL;
    Py_RETURN_NONE;
}


/*[clinic input]
class os.DirEntry "DirEntry *" "DirEntryType"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c18c7a448247980]*/

typedef struct {
    PyObject_HEAD
    PyObject *name;
    PyObject *path;
    PyObject *stat;
    PyObject *lstat;
#ifdef MS_WINDOWS
    struct _Py_stat_struct win32_lstat;
    uint64_t win32_file_index;
    uint64_t win32_file_index_high;
    int got_file_index;
#else /* POSIX */
#ifdef HAVE_DIRENT_D_TYPE
    unsigned char d_type;
#endif
    ino_t d_ino;
    int dir_fd;
#endif
} DirEntry;

#define DirEntry_CAST(op)   ((DirEntry *)(op))

static void
DirEntry_dealloc(PyObject *op)
{
    DirEntry *entry = DirEntry_CAST(op);
    PyTypeObject *tp = Py_TYPE(entry);
    Py_XDECREF(entry->name);
    Py_XDECREF(entry->path);
    Py_XDECREF(entry->stat);
    Py_XDECREF(entry->lstat);
    freefunc free_func = PyType_GetSlot(tp, Py_tp_free);
    free_func(entry);
    Py_DECREF(tp);
}

/* Forward reference */
static int
DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self,
                   int follow_symlinks, unsigned short mode_bits);

/*[clinic input]
os.DirEntry.is_symlink -> bool
    defining_class: defining_class
    /

Return True if the entry is a symbolic link; cached per entry.
[clinic start generated code]*/

static int
os_DirEntry_is_symlink_impl(DirEntry *self, PyTypeObject *defining_class)
/*[clinic end generated code: output=293096d589b6d47c input=e9acc5ee4d511113]*/
{
#ifdef MS_WINDOWS
    return (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK;
#elif defined(HAVE_DIRENT_D_TYPE)
    /* POSIX */
    if (self->d_type != DT_UNKNOWN)
        return self->d_type == DT_LNK;
    else
        return DirEntry_test_mode(defining_class, self, 0, S_IFLNK);
#else
    /* POSIX without d_type */
    return DirEntry_test_mode(defining_class, self, 0, S_IFLNK);
#endif
}

/*[clinic input]
os.DirEntry.is_junction -> bool

Return True if the entry is a junction; cached per entry.
[clinic start generated code]*/

static int
os_DirEntry_is_junction_impl(DirEntry *self)
/*[clinic end generated code: output=97f64d5d99eeccb5 input=4fc8e701eea118a1]*/
{
#ifdef MS_WINDOWS
    return self->win32_lstat.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT;
#else
    return 0;
#endif
}

static PyObject *
DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks)
{
    int result;
    STRUCT_STAT st;
    PyObject *ub;

#ifdef MS_WINDOWS
    if (!PyUnicode_FSDecoder(self->path, &ub))
        return NULL;
    wchar_t *path = PyUnicode_AsWideCharString(ub, NULL);
    Py_DECREF(ub);
#else /* POSIX */
    if (!PyUnicode_FSConverter(self->path, &ub))
        return NULL;
    const char *path = PyBytes_AS_STRING(ub);
    if (self->dir_fd != DEFAULT_DIR_FD) {
#ifdef HAVE_FSTATAT
      if (HAVE_FSTATAT_RUNTIME) {
        Py_BEGIN_ALLOW_THREADS
        result = fstatat(self->dir_fd, path, &st,
                         follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
        Py_END_ALLOW_THREADS
      } else

#endif /* HAVE_FSTATAT */
      {
        Py_DECREF(ub);
        PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat");
        return NULL;
      }
    }
    else
#endif
    {
        Py_BEGIN_ALLOW_THREADS
        if (follow_symlinks) {
            result = STAT(path, &st);
        }
        else {
            result = LSTAT(path, &st);
        }
        Py_END_ALLOW_THREADS
    }

    int saved_errno = errno;
#if defined(MS_WINDOWS)
    PyMem_Free(path);
#else
    Py_DECREF(ub);
#endif

    if (result != 0) {
        errno = saved_errno;
        path_object_error(self->path);
        return NULL;
    }

    return _pystat_fromstructstat(module, &st);
}

static PyObject *
DirEntry_get_lstat(PyTypeObject *defining_class, DirEntry *self)
{
    if (!self->lstat) {
        PyObject *module = PyType_GetModule(defining_class);
#ifdef MS_WINDOWS
        self->lstat = _pystat_fromstructstat(module, &self->win32_lstat);
#else /* POSIX */
        self->lstat = DirEntry_fetch_stat(module, self, 0);
#endif
    }
    return Py_XNewRef(self->lstat);
}

/*[clinic input]
os.DirEntry.stat
    defining_class: defining_class
    /
    *
    follow_symlinks: bool = True

Return stat_result object for the entry; cached per entry.
[clinic start generated code]*/

static PyObject *
os_DirEntry_stat_impl(DirEntry *self, PyTypeObject *defining_class,
                      int follow_symlinks)
/*[clinic end generated code: output=23f803e19c3e780e input=e816273c4e67ee98]*/
{
    if (!follow_symlinks) {
        return DirEntry_get_lstat(defining_class, self);
    }

    if (!self->stat) {
        int result = os_DirEntry_is_symlink_impl(self, defining_class);
        if (result == -1) {
            return NULL;
        }
        if (result) {
            PyObject *module = PyType_GetModule(defining_class);
            self->stat = DirEntry_fetch_stat(module, self, 1);
        }
        else {
            self->stat = DirEntry_get_lstat(defining_class, self);
        }
    }

    return Py_XNewRef(self->stat);
}

/* Set exception and return -1 on error, 0 for False, 1 for True */
static int
DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self,
                   int follow_symlinks, unsigned short mode_bits)
{
    PyObject *stat = NULL;
    PyObject *st_mode = NULL;
    long mode;
    int result;
#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE)
    int is_symlink;
    int need_stat;
#endif
#ifdef MS_WINDOWS
    unsigned long dir_bits;
#endif

#ifdef MS_WINDOWS
    is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK;
    need_stat = follow_symlinks && is_symlink;
#elif defined(HAVE_DIRENT_D_TYPE)
    is_symlink = self->d_type == DT_LNK;
    need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink);
#endif

#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE)
    if (need_stat) {
#endif
        stat = os_DirEntry_stat_impl(self, defining_class, follow_symlinks);
        if (!stat) {
            if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) {
                /* If file doesn't exist (anymore), then return False
                   (i.e., say it's not a file/directory) */
                PyErr_Clear();
                return 0;
            }
            goto error;
        }
        _posixstate* state = get_posix_state(PyType_GetModule(defining_class));
        st_mode = PyObject_GetAttr(stat, state->st_mode);
        if (!st_mode)
            goto error;

        mode = PyLong_AsLong(st_mode);
        if (mode == -1 && PyErr_Occurred())
            goto error;
        Py_CLEAR(st_mode);
        Py_CLEAR(stat);
        result = (mode & S_IFMT) == mode_bits;
#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE)
    }
    else if (is_symlink) {
        assert(mode_bits != S_IFLNK);
        result = 0;
    }
    else {
        assert(mode_bits == S_IFDIR || mode_bits == S_IFREG);
#ifdef MS_WINDOWS
        dir_bits = self->win32_lstat.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY;
        if (mode_bits == S_IFDIR)
            result = dir_bits != 0;
        else
            result = dir_bits == 0;
#else /* POSIX */
        if (mode_bits == S_IFDIR)
            result = self->d_type == DT_DIR;
        else
            result = self->d_type == DT_REG;
#endif
    }
#endif

    return result;

error:
    Py_XDECREF(st_mode);
    Py_XDECREF(stat);
    return -1;
}

/*[clinic input]
os.DirEntry.is_dir -> bool
    defining_class: defining_class
    /
    *
    follow_symlinks: bool = True

Return True if the entry is a directory; cached per entry.
[clinic start generated code]*/

static int
os_DirEntry_is_dir_impl(DirEntry *self, PyTypeObject *defining_class,
                        int follow_symlinks)
/*[clinic end generated code: output=0cd453b9c0987fdf input=1a4ffd6dec9920cb]*/
{
    return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFDIR);
}

/*[clinic input]
os.DirEntry.is_file -> bool
    defining_class: defining_class
    /
    *
    follow_symlinks: bool = True

Return True if the entry is a file; cached per entry.
[clinic start generated code]*/

static int
os_DirEntry_is_file_impl(DirEntry *self, PyTypeObject *defining_class,
                         int follow_symlinks)
/*[clinic end generated code: output=f7c277ab5ba80908 input=0a64c5a12e802e3b]*/
{
    return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFREG);
}

/*[clinic input]
os.DirEntry.inode

Return inode of the entry; cached per entry.
[clinic start generated code]*/

static PyObject *
os_DirEntry_inode_impl(DirEntry *self)
/*[clinic end generated code: output=156bb3a72162440e input=3ee7b872ae8649f0]*/
{
#ifdef MS_WINDOWS
    if (!self->got_file_index) {
        PyObject *unicode;
        STRUCT_STAT stat;
        int result;

        if (!PyUnicode_FSDecoder(self->path, &unicode))
            return NULL;
        wchar_t *path = PyUnicode_AsWideCharString(unicode, NULL);
        Py_DECREF(unicode);
        result = LSTAT(path, &stat);

        int saved_errno = errno;
        PyMem_Free(path);

        if (result != 0) {
            errno = saved_errno;
            return path_object_error(self->path);
        }

        self->win32_file_index = stat.st_ino;
        self->win32_file_index_high = stat.st_ino_high;
        self->got_file_index = 1;
    }
    return _pystat_l128_from_l64_l64(self->win32_file_index, self->win32_file_index_high);
#else /* POSIX */
    static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino),
                  "DirEntry.d_ino is larger than unsigned long long");
    return PyLong_FromUnsignedLongLong(self->d_ino);
#endif
}

static PyObject *
DirEntry_repr(PyObject *op)
{
    DirEntry *self = DirEntry_CAST(op);
    return PyUnicode_FromFormat("<DirEntry %R>", self->name);
}

/*[clinic input]
os.DirEntry.__fspath__

Returns the path for the entry.
[clinic start generated code]*/

static PyObject *
os_DirEntry___fspath___impl(DirEntry *self)
/*[clinic end generated code: output=6dd7f7ef752e6f4f input=3c49d0cf38df4fac]*/
{
    return Py_NewRef(self->path);
}

static PyMemberDef DirEntry_members[] = {
    {"name", Py_T_OBJECT_EX, offsetof(DirEntry, name), Py_READONLY,
     "the entry's base filename, relative to scandir() \"path\" argument"},
    {"path", Py_T_OBJECT_EX, offsetof(DirEntry, path), Py_READONLY,
     "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)"},
    {NULL}
};

#include "clinic/posixmodule.c.h"

static PyMethodDef DirEntry_methods[] = {
    OS_DIRENTRY_IS_DIR_METHODDEF
    OS_DIRENTRY_IS_FILE_METHODDEF
    OS_DIRENTRY_IS_SYMLINK_METHODDEF
    OS_DIRENTRY_IS_JUNCTION_METHODDEF
    OS_DIRENTRY_STAT_METHODDEF
    OS_DIRENTRY_INODE_METHODDEF
    OS_DIRENTRY___FSPATH___METHODDEF
    {"__class_getitem__",       Py_GenericAlias,
    METH_O|METH_CLASS,          PyDoc_STR("See PEP 585")},
    {NULL}
};

static PyType_Slot DirEntryType_slots[] = {
    {Py_tp_dealloc, DirEntry_dealloc},
    {Py_tp_repr, DirEntry_repr},
    {Py_tp_methods, DirEntry_methods},
    {Py_tp_members, DirEntry_members},
    {0, 0},
};

static PyType_Spec DirEntryType_spec = {
    .name = MODNAME ".DirEntry",
    .basicsize = sizeof(DirEntry),
    .flags = (
        Py_TPFLAGS_DEFAULT
        | Py_TPFLAGS_DISALLOW_INSTANTIATION
        | Py_TPFLAGS_IMMUTABLETYPE
    ),
    .slots = DirEntryType_slots
};


#ifdef MS_WINDOWS

static wchar_t *
join_path_filenameW(const wchar_t *path_wide, const wchar_t *filename)
{
    Py_ssize_t path_len;
    Py_ssize_t size;
    wchar_t *result;
    wchar_t ch;

    if (!path_wide) { /* Default arg: "." */
        path_wide = L".";
        path_len = 1;
    }
    else {
        path_len = wcslen(path_wide);
    }

    /* The +1's are for the path separator and the NUL */
    size = path_len + 1 + wcslen(filename) + 1;
    result = PyMem_New(wchar_t, size);
    if (!result) {
        PyErr_NoMemory();
        return NULL;
    }
    wcscpy(result, path_wide);
    if (path_len > 0) {
        ch = result[path_len - 1];
        if (ch != SEP && ch != ALTSEP && ch != L':')
            result[path_len++] = SEP;
        wcscpy(result + path_len, filename);
    }
    return result;
}

static PyObject *
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
{
    DirEntry *entry;
    BY_HANDLE_FILE_INFORMATION file_info;
    ULONG reparse_tag;
    wchar_t *joined_path;

    PyObject *DirEntryType = get_posix_state(module)->DirEntryType;
    entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
    if (!entry)
        return NULL;
    entry->name = NULL;
    entry->path = NULL;
    entry->stat = NULL;
    entry->lstat = NULL;
    entry->got_file_index = 0;

    entry->name = PyUnicode_FromWideChar(dataW->cFileName, -1);
    if (!entry->name)
        goto error;
    int return_bytes = path->wide && PyBytes_Check(path->object);
    if (return_bytes) {
        Py_SETREF(entry->name, PyUnicode_EncodeFSDefault(entry->name));
        if (!entry->name)
            goto error;
    }

    joined_path = join_path_filenameW(path->wide, dataW->cFileName);
    if (!joined_path)
        goto error;

    entry->path = PyUnicode_FromWideChar(joined_path, -1);
    PyMem_Free(joined_path);
    if (!entry->path)
        goto error;
    if (return_bytes) {
        Py_SETREF(entry->path, PyUnicode_EncodeFSDefault(entry->path));
        if (!entry->path)
            goto error;
    }

    find_data_to_file_info(dataW, &file_info, &reparse_tag);
    _Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);

    /* ctime is only deprecated from 3.12, so we copy birthtime across */
    entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime;
    entry->win32_lstat.st_ctime_nsec = entry->win32_lstat.st_birthtime_nsec;

    return (PyObject *)entry;

error:
    Py_DECREF(entry);
    return NULL;
}

#else /* POSIX */

static char *
join_path_filename(const char *path_narrow, const char* filename, Py_ssize_t filename_len)
{
    Py_ssize_t path_len;
    Py_ssize_t size;
    char *result;

    if (!path_narrow) { /* Default arg: "." */
        path_narrow = ".";
        path_len = 1;
    }
    else {
        path_len = strlen(path_narrow);
    }

    if (filename_len == -1)
        filename_len = strlen(filename);

    /* The +1's are for the path separator and the NUL */
    size = path_len + 1 + filename_len + 1;
    result = PyMem_New(char, size);
    if (!result) {
        PyErr_NoMemory();
        return NULL;
    }
    strcpy(result, path_narrow);
    if (path_len > 0 && result[path_len - 1] != '/')
        result[path_len++] = '/';
    strcpy(result + path_len, filename);
    return result;
}

static PyObject *
DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name,
                         Py_ssize_t name_len, ino_t d_ino
#ifdef HAVE_DIRENT_D_TYPE
                         , unsigned char d_type
#endif
                         )
{
    DirEntry *entry;
    char *joined_path;

    PyObject *DirEntryType = get_posix_state(module)->DirEntryType;
    entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
    if (!entry)
        return NULL;
    entry->name = NULL;
    entry->path = NULL;
    entry->stat = NULL;
    entry->lstat = NULL;

    if (path->fd != -1) {
        entry->dir_fd = path->fd;
        joined_path = NULL;
    }
    else {
        entry->dir_fd = DEFAULT_DIR_FD;
        joined_path = join_path_filename(path->narrow, name, name_len);
        if (!joined_path)
            goto error;
    }

    if (!path->narrow || !PyBytes_Check(path->object)) {
        entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len);
        if (joined_path)
            entry->path = PyUnicode_DecodeFSDefault(joined_path);
    }
    else {
        entry->name = PyBytes_FromStringAndSize(name, name_len);
        if (joined_path)
            entry->path = PyBytes_FromString(joined_path);
    }
    PyMem_Free(joined_path);
    if (!entry->name)
        goto error;

    if (path->fd != -1) {
        entry->path = Py_NewRef(entry->name);
    }
    else if (!entry->path)
        goto error;

#ifdef HAVE_DIRENT_D_TYPE
    entry->d_type = d_type;
#endif
    entry->d_ino = d_ino;

    return (PyObject *)entry;

error:
    Py_XDECREF(entry);
    return NULL;
}

#endif


typedef struct {
    PyObject_HEAD
    path_t path;
#ifdef MS_WINDOWS
    HANDLE handle;
    WIN32_FIND_DATAW file_data;
    int first_time;
#else /* POSIX */
    DIR *dirp;
#endif
#ifdef HAVE_FDOPENDIR
    int fd;
#endif
} ScandirIterator;

#define ScandirIterator_CAST(op)    ((ScandirIterator *)(op))

#ifdef MS_WINDOWS

static int
ScandirIterator_is_closed(ScandirIterator *iterator)
{
    return iterator->handle == INVALID_HANDLE_VALUE;
}

static void
ScandirIterator_closedir(ScandirIterator *iterator)
{
    HANDLE handle = iterator->handle;

    if (handle == INVALID_HANDLE_VALUE)
        return;

    iterator->handle = INVALID_HANDLE_VALUE;
    Py_BEGIN_ALLOW_THREADS
    FindClose(handle);
    Py_END_ALLOW_THREADS
}

static PyObject *
ScandirIterator_iternext(PyObject *op)
{
    ScandirIterator *iterator = ScandirIterator_CAST(op);
    WIN32_FIND_DATAW *file_data = &iterator->file_data;
    BOOL success;
    PyObject *entry;

    /* Happens if the iterator is iterated twice, or closed explicitly */
    if (iterator->handle == INVALID_HANDLE_VALUE)
        return NULL;

    while (1) {
        if (!iterator->first_time) {
            Py_BEGIN_ALLOW_THREADS
            success = FindNextFileW(iterator->handle, file_data);
            Py_END_ALLOW_THREADS
            if (!success) {
                /* Error or no more files */
                if (GetLastError() != ERROR_NO_MORE_FILES)
                    path_error(&iterator->path);
                break;
            }
        }
        iterator->first_time = 0;

        /* Skip over . and .. */
        if (wcscmp(file_data->cFileName, L".") != 0 &&
            wcscmp(file_data->cFileName, L"..") != 0)
        {
            PyObject *module = PyType_GetModule(Py_TYPE(iterator));
            entry = DirEntry_from_find_data(module, &iterator->path, file_data);
            if (!entry)
                break;
            return entry;
        }

        /* Loop till we get a non-dot directory or finish iterating */
    }

    /* Error or no more files */
    ScandirIterator_closedir(iterator);
    return NULL;
}

#else /* POSIX */

static int
ScandirIterator_is_closed(ScandirIterator *iterator)
{
    return !iterator->dirp;
}

static void
ScandirIterator_closedir(ScandirIterator *iterator)
{
    DIR *dirp = iterator->dirp;

    if (!dirp)
        return;

    iterator->dirp = NULL;
    Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FDOPENDIR
    if (iterator->path.fd != -1)
        rewinddir(dirp);
#endif
    closedir(dirp);
    Py_END_ALLOW_THREADS
    return;
}

static PyObject *
ScandirIterator_iternext(PyObject *op)
{
    ScandirIterator *iterator = ScandirIterator_CAST(op);
    struct dirent *direntp;
    Py_ssize_t name_len;
    int is_dot;
    PyObject *entry;

    /* Happens if the iterator is iterated twice, or closed explicitly */
    if (!iterator->dirp)
        return NULL;

    while (1) {
        errno = 0;
        Py_BEGIN_ALLOW_THREADS
        direntp = readdir(iterator->dirp);
        Py_END_ALLOW_THREADS

        if (!direntp) {
            /* Error or no more files */
            if (errno != 0)
                path_error(&iterator->path);
            break;
        }

        /* Skip over . and .. */
        name_len = NAMLEN(direntp);
        is_dot = direntp->d_name[0] == '.' &&
                 (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2));
        if (!is_dot) {
            PyObject *module = PyType_GetModule(Py_TYPE(iterator));
            entry = DirEntry_from_posix_info(module,
                                             &iterator->path, direntp->d_name,
                                             name_len, direntp->d_ino
#ifdef HAVE_DIRENT_D_TYPE
                                             , direntp->d_type
#endif
                                            );
            if (!entry)
                break;
            return entry;
        }

        /* Loop till we get a non-dot directory or finish iterating */
    }

    /* Error or no more files */
    ScandirIterator_closedir(iterator);
    return NULL;
}

#endif

static PyObject *
ScandirIterator_close(PyObject *op, PyObject *Py_UNUSED(dummy))
{
    ScandirIterator *self = ScandirIterator_CAST(op);
    ScandirIterator_closedir(self);
    Py_RETURN_NONE;
}

static PyObject *
ScandirIterator_enter(PyObject *self, PyObject *Py_UNUSED(dummy))
{
    return Py_NewRef(self);
}

static PyObject *
ScandirIterator_exit(PyObject *op, PyObject *Py_UNUSED(args))
{
    ScandirIterator *self = ScandirIterator_CAST(op);
    ScandirIterator_closedir(self);
    Py_RETURN_NONE;
}

static void
ScandirIterator_finalize(PyObject *op)
{
    ScandirIterator *iterator = ScandirIterator_CAST(op);
    /* Save the current exception, if any. */
    PyObject *exc = PyErr_GetRaisedException();

    if (!ScandirIterator_is_closed(iterator)) {
        ScandirIterator_closedir(iterator);

        if (PyErr_ResourceWarning(op, 1,
                                  "unclosed scandir iterator %R", iterator))
        {
            /* Spurious errors can appear at shutdown */
            if (PyErr_ExceptionMatches(PyExc_Warning)) {
                PyErr_FormatUnraisable("Exception ignored while finalizing "
                                       "scandir iterator %R", iterator);
            }
        }
    }

    path_cleanup(&iterator->path);

    /* Restore the saved exception. */
    PyErr_SetRaisedException(exc);
}

static void
ScandirIterator_dealloc(PyObject *op)
{
    PyTypeObject *tp = Py_TYPE(op);
    if (PyObject_CallFinalizerFromDealloc(op) < 0)
        return;

    freefunc free_func = PyType_GetSlot(tp, Py_tp_free);
    free_func(op);
    Py_DECREF(tp);
}

static PyMethodDef ScandirIterator_methods[] = {
    {"__enter__", ScandirIterator_enter, METH_NOARGS},
    {"__exit__", ScandirIterator_exit, METH_VARARGS},
    {"close", ScandirIterator_close, METH_NOARGS},
    {NULL}
};

static PyType_Slot ScandirIteratorType_slots[] = {
    {Py_tp_dealloc, ScandirIterator_dealloc},
    {Py_tp_finalize, ScandirIterator_finalize},
    {Py_tp_iter, PyObject_SelfIter},
    {Py_tp_iternext, ScandirIterator_iternext},
    {Py_tp_methods, ScandirIterator_methods},
    {0, 0},
};

static PyType_Spec ScandirIteratorType_spec = {
    .name = MODNAME ".ScandirIterator",
    .basicsize = sizeof(ScandirIterator),
    // bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since
    // PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance.
    .flags = (
        Py_TPFLAGS_DEFAULT
        | Py_TPFLAGS_HAVE_FINALIZE
        | Py_TPFLAGS_DISALLOW_INSTANTIATION
        | Py_TPFLAGS_IMMUTABLETYPE
    ),
    .slots = ScandirIteratorType_slots
};

/*[clinic input]
os.scandir

    path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None

Return an iterator of DirEntry objects for given path.

path can be specified as either str, bytes, or a path-like object.  If path
is bytes, the names of yielded DirEntry objects will also be bytes; in
all other circumstances they will be str.

If path is None, uses the path='.'.
[clinic start generated code]*/

static PyObject *
os_scandir_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/
{
    ScandirIterator *iterator;
#ifdef MS_WINDOWS
    wchar_t *path_strW;
#else
    const char *path_str;
#ifdef HAVE_FDOPENDIR
    int fd = -1;
#endif
#endif

    if (PySys_Audit("os.scandir", "O",
                    path->object ? path->object : Py_None) < 0) {
        return NULL;
    }

    PyObject *ScandirIteratorType = get_posix_state(module)->ScandirIteratorType;
    iterator = PyObject_New(ScandirIterator, (PyTypeObject *)ScandirIteratorType);
    if (!iterator)
        return NULL;

#ifdef MS_WINDOWS
    iterator->handle = INVALID_HANDLE_VALUE;
#else
    iterator->dirp = NULL;
#endif

    /* Move the ownership to iterator->path */
    memcpy(&iterator->path, path, sizeof(path_t));
    memset(path, 0, sizeof(path_t));

#ifdef MS_WINDOWS
    iterator->first_time = 1;

    path_strW = join_path_filenameW(iterator->path.wide, L"*.*");
    if (!path_strW)
        goto error;

    Py_BEGIN_ALLOW_THREADS
    iterator->handle = FindFirstFileW(path_strW, &iterator->file_data);
    Py_END_ALLOW_THREADS

    if (iterator->handle == INVALID_HANDLE_VALUE) {
        path_error(&iterator->path);
        PyMem_Free(path_strW);
        goto error;
    }
    PyMem_Free(path_strW);
#else /* POSIX */
    errno = 0;
#ifdef HAVE_FDOPENDIR
    if (iterator->path.fd != -1) {
      if (HAVE_FDOPENDIR_RUNTIME) {
        /* closedir() closes the FD, so we duplicate it */
        fd = _Py_dup(iterator->path.fd);
        if (fd == -1)
            goto error;

        Py_BEGIN_ALLOW_THREADS
        iterator->dirp = fdopendir(fd);
        Py_END_ALLOW_THREADS
      } else {
        PyErr_SetString(PyExc_TypeError,
            "scandir: path should be string, bytes, os.PathLike or None, not int");
        return NULL;
      }
    }
    else
#endif
    {
        if (iterator->path.narrow)
            path_str = iterator->path.narrow;
        else
            path_str = ".";

        Py_BEGIN_ALLOW_THREADS
        iterator->dirp = opendir(path_str);
        Py_END_ALLOW_THREADS
    }

    if (!iterator->dirp) {
        path_error(&iterator->path);
#ifdef HAVE_FDOPENDIR
        if (fd != -1) {
            Py_BEGIN_ALLOW_THREADS
            close(fd);
            Py_END_ALLOW_THREADS
        }
#endif
        goto error;
    }
#endif

    return (PyObject *)iterator;

error:
    Py_DECREF(iterator);
    return NULL;
}

/*
    Return the file system path representation of the object.

    If the object is str or bytes, then allow it to pass through with
    an incremented refcount. If the object defines __fspath__(), then
    return the result of that method. All other types raise a TypeError.
*/
PyObject *
PyOS_FSPath(PyObject *path)
{
    /* For error message reasons, this function is manually inlined in
       path_converter(). */
    PyObject *func = NULL;
    PyObject *path_repr = NULL;

    if (PyUnicode_Check(path) || PyBytes_Check(path)) {
        return Py_NewRef(path);
    }

    func = _PyObject_LookupSpecial(path, &_Py_ID(__fspath__));
    if ((NULL == func) || (func == Py_None)) {
        return PyErr_Format(PyExc_TypeError,
                            "expected str, bytes or os.PathLike object, "
                            "not %.200s",
                            _PyType_Name(Py_TYPE(path)));
    }

    path_repr = _PyObject_CallNoArgs(func);
    Py_DECREF(func);
    if (NULL == path_repr) {
        return NULL;
    }

    if (!(PyUnicode_Check(path_repr) || PyBytes_Check(path_repr))) {
        PyErr_Format(PyExc_TypeError,
                     "expected %.200s.__fspath__() to return str or bytes, "
                     "not %.200s", _PyType_Name(Py_TYPE(path)),
                     _PyType_Name(Py_TYPE(path_repr)));
        Py_DECREF(path_repr);
        return NULL;
    }

    return path_repr;
}

/*[clinic input]
@permit_long_docstring_body
os.fspath

    path: object

Return the file system path representation of the object.

If the object is str or bytes, then allow it to pass through as-is. If the
object defines __fspath__(), then return the result of that method. All other
types raise a TypeError.
[clinic start generated code]*/

static PyObject *
os_fspath_impl(PyObject *module, PyObject *path)
/*[clinic end generated code: output=c3c3b78ecff2914f input=f608743e60a3211e]*/
{
    return PyOS_FSPath(path);
}

#ifdef HAVE_GETRANDOM_SYSCALL
/*[clinic input]
os.getrandom

    size: Py_ssize_t
    flags: int=0

Obtain a series of random bytes.
[clinic start generated code]*/

static PyObject *
os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
/*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/
{
    if (size < 0) {
        errno = EINVAL;
        return posix_error();
    }

    PyBytesWriter *writer = PyBytesWriter_Create(size);
    if (writer == NULL) {
        return NULL;
    }
    void *data = PyBytesWriter_GetData(writer);

    Py_ssize_t n;
    while (1) {
        n = syscall(SYS_getrandom, data, size, flags);
        if (n < 0 && errno == EINTR) {
            if (PyErr_CheckSignals() < 0) {
                goto error;
            }

            /* getrandom() was interrupted by a signal: retry */
            continue;
        }
        break;
    }

    if (n < 0) {
        PyErr_SetFromErrno(PyExc_OSError);
        goto error;
    }

    return PyBytesWriter_FinishWithSize(writer, n);

error:
    PyBytesWriter_Discard(writer);
    return NULL;
}
#endif   /* HAVE_GETRANDOM_SYSCALL */

#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM)

/* bpo-36085: Helper functions for managing DLL search directories
 * on win32
 */

/*[clinic input]
os._add_dll_directory

    path: path_t

Add a path to the DLL search path.

This search path is used when resolving dependencies for imported
extension modules (the module itself is resolved through sys.path),
and also by ctypes.

Returns an opaque value that may be passed to os.remove_dll_directory
to remove this directory from the search path.
[clinic start generated code]*/

static PyObject *
os__add_dll_directory_impl(PyObject *module, path_t *path)
/*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/
{
    DLL_DIRECTORY_COOKIE cookie = 0;
    DWORD err = 0;

    if (PySys_Audit("os.add_dll_directory", "(O)", path->object) < 0) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    if (!(cookie = AddDllDirectory(path->wide))) {
        err = GetLastError();
    }
    Py_END_ALLOW_THREADS

    if (err) {
        return win32_error_object_err("add_dll_directory",
                                      path->object, err);
    }

    return PyCapsule_New(cookie, "DLL directory cookie", NULL);
}

/*[clinic input]
os._remove_dll_directory

    cookie: object

Removes a path from the DLL search path.

The parameter is an opaque value that was returned from
os.add_dll_directory. You can only remove directories that you added
yourself.
[clinic start generated code]*/

static PyObject *
os__remove_dll_directory_impl(PyObject *module, PyObject *cookie)
/*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/
{
    DLL_DIRECTORY_COOKIE cookieValue;
    DWORD err = 0;

    if (!PyCapsule_IsValid(cookie, "DLL directory cookie")) {
        PyErr_SetString(PyExc_TypeError,
            "Provided cookie was not returned from os.add_dll_directory");
        return NULL;
    }

    cookieValue = (DLL_DIRECTORY_COOKIE)PyCapsule_GetPointer(
        cookie, "DLL directory cookie");

    Py_BEGIN_ALLOW_THREADS
    if (!RemoveDllDirectory(cookieValue)) {
        err = GetLastError();
    }
    Py_END_ALLOW_THREADS

    if (err) {
        return win32_error_object_err("remove_dll_directory",
                                      NULL, err);
    }

    if (PyCapsule_SetName(cookie, NULL)) {
        return NULL;
    }

    Py_RETURN_NONE;
}

#endif /* MS_WINDOWS_APP || MS_WINDOWS_SYSTEM */


/* Only check if WIFEXITED is available: expect that it comes
   with WEXITSTATUS, WIFSIGNALED, etc.

   os.waitstatus_to_exitcode() is implemented in C and not in Python, so
   subprocess can safely call it during late Python finalization without
   risking that used os attributes were set to None by finalize_modules(). */
#if defined(WIFEXITED) || defined(MS_WINDOWS)
/*[clinic input]
os.waitstatus_to_exitcode

    status as status_obj: object

Convert a wait status to an exit code.

On Unix:

* If WIFEXITED(status) is true, return WEXITSTATUS(status).
* If WIFSIGNALED(status) is true, return -WTERMSIG(status).
* Otherwise, raise a ValueError.

On Windows, return status shifted right by 8 bits.

On Unix, if the process is being traced or if waitpid() was called with
WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true.
This function must not be called if WIFSTOPPED(status) is true.
[clinic start generated code]*/

static PyObject *
os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj)
/*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/
{
#ifndef MS_WINDOWS
    int status = PyLong_AsInt(status_obj);
    if (status == -1 && PyErr_Occurred()) {
        return NULL;
    }

    WAIT_TYPE wait_status;
    WAIT_STATUS_INT(wait_status) = status;
    int exitcode;
    if (WIFEXITED(wait_status)) {
        exitcode = WEXITSTATUS(wait_status);
        /* Sanity check to provide warranty on the function behavior.
           It should not occur in practice */
        if (exitcode < 0) {
            PyErr_Format(PyExc_ValueError, "invalid WEXITSTATUS: %i", exitcode);
            return NULL;
        }
    }
    else if (WIFSIGNALED(wait_status)) {
        int signum = WTERMSIG(wait_status);
        /* Sanity check to provide warranty on the function behavior.
           It should not occurs in practice */
        if (signum <= 0) {
            PyErr_Format(PyExc_ValueError, "invalid WTERMSIG: %i", signum);
            return NULL;
        }
        exitcode = -signum;
    } else if (WIFSTOPPED(wait_status)) {
        /* Status only received if the process is being traced
           or if waitpid() was called with WUNTRACED option. */
        int signum = WSTOPSIG(wait_status);
        PyErr_Format(PyExc_ValueError,
                     "process stopped by delivery of signal %i",
                     signum);
        return NULL;
    }
    else {
        PyErr_Format(PyExc_ValueError, "invalid wait status: %i", status);
        return NULL;
    }
    return PyLong_FromLong(exitcode);
#else
    /* Windows implementation: see os.waitpid() implementation
       which uses _cwait(). */
    unsigned long long status = PyLong_AsUnsignedLongLong(status_obj);
    if (status == (unsigned long long)-1 && PyErr_Occurred()) {
        return NULL;
    }

    unsigned long long exitcode = (status >> 8);
    /* ExitProcess() accepts an UINT type:
       reject exit code which doesn't fit in an UINT */
    if (exitcode > UINT_MAX) {
        PyErr_Format(PyExc_ValueError, "invalid exit code: %llu", exitcode);
        return NULL;
    }
    return PyLong_FromUnsignedLong((unsigned long)exitcode);
#endif
}
#endif

#if defined(MS_WINDOWS)
/*[clinic input]
os._supports_virtual_terminal

Checks if virtual terminal is supported in windows
[clinic start generated code]*/

static PyObject *
os__supports_virtual_terminal_impl(PyObject *module)
/*[clinic end generated code: output=bd0556a6d9d99fe6 input=0752c98e5d321542]*/
{
#ifdef HAVE_WINDOWS_CONSOLE_IO
    DWORD mode = 0;
    HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
    if (!GetConsoleMode(handle, &mode)) {
        Py_RETURN_FALSE;
    }
    return PyBool_FromLong(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING);
#else
    Py_RETURN_FALSE;
#endif /* HAVE_WINDOWS_CONSOLE_IO */
}
#endif

/*[clinic input]
os._inputhook

Calls PyOS_InputHook dropping the GIL first
[clinic start generated code]*/

static PyObject *
os__inputhook_impl(PyObject *module)
/*[clinic end generated code: output=525aca4ef3c6149f input=b5018fa1ec3aa440]*/
{
     int result = 0;
     if (PyOS_InputHook) {
         Py_BEGIN_ALLOW_THREADS;
         result = PyOS_InputHook();
         Py_END_ALLOW_THREADS;
     }
     return PyLong_FromLong(result);
}

/*[clinic input]
os._is_inputhook_installed

Checks if PyOS_InputHook is set
[clinic start generated code]*/

static PyObject *
os__is_inputhook_installed_impl(PyObject *module)
/*[clinic end generated code: output=3b3eab4f672c689a input=757820f79f48820c]*/
{
    return PyBool_FromLong(PyOS_InputHook != NULL);
}

/*[clinic input]
os._create_environ

Create the environment dictionary.
[clinic start generated code]*/

static PyObject *
os__create_environ_impl(PyObject *module)
/*[clinic end generated code: output=19d9039ab14f8ad4 input=a4c05686b34635e8]*/
{
    return convertenviron();
}


#ifdef __EMSCRIPTEN__
/*[clinic input]
os._emscripten_debugger

Create a breakpoint for the JavaScript debugger. Emscripten only.
[clinic start generated code]*/

static PyObject *
os__emscripten_debugger_impl(PyObject *module)
/*[clinic end generated code: output=ad47dc3bf0661343 input=d814b1877fb6083a]*/
{
    emscripten_debugger();
    Py_RETURN_NONE;
}

EM_JS(void, emscripten_log_impl_js, (const char* arg), {
    console.warn(UTF8ToString(arg));
});

/*[clinic input]
os._emscripten_log
   arg: str

Log something to the JS console. Emscripten only.
[clinic start generated code]*/

static PyObject *
os__emscripten_log_impl(PyObject *module, const char *arg)
/*[clinic end generated code: output=9749e5e293c42784 input=350aa1f70bc1e905]*/
{
    emscripten_log_impl_js(arg);
    Py_RETURN_NONE;
}
#endif /* __EMSCRIPTEN__ */


static PyMethodDef posix_methods[] = {
    OS_STAT_METHODDEF
    OS_STATX_METHODDEF
    OS_ACCESS_METHODDEF
    OS_TTYNAME_METHODDEF
    OS_CHDIR_METHODDEF
    OS_CHFLAGS_METHODDEF
    OS_CHMOD_METHODDEF
    OS_FCHMOD_METHODDEF
    OS_LCHMOD_METHODDEF
    OS_CHOWN_METHODDEF
    OS_FCHOWN_METHODDEF
    OS_LCHOWN_METHODDEF
    OS_LCHFLAGS_METHODDEF
    OS_CHROOT_METHODDEF
    OS_CTERMID_METHODDEF
    OS_GETCWD_METHODDEF
    OS_GETCWDB_METHODDEF
    OS_LINK_METHODDEF
    OS_LISTDIR_METHODDEF
    OS_LISTDRIVES_METHODDEF
    OS_LISTMOUNTS_METHODDEF
    OS_LISTVOLUMES_METHODDEF
    OS_LSTAT_METHODDEF
    OS_MKDIR_METHODDEF
    OS_NICE_METHODDEF
    OS_GETPRIORITY_METHODDEF
    OS_SETPRIORITY_METHODDEF
    OS_POSIX_SPAWN_METHODDEF
    OS_POSIX_SPAWNP_METHODDEF
    OS_READLINK_METHODDEF
    OS_COPY_FILE_RANGE_METHODDEF
    OS_SPLICE_METHODDEF
    OS_RENAME_METHODDEF
    OS_REPLACE_METHODDEF
    OS_RMDIR_METHODDEF
    OS_SYMLINK_METHODDEF
    OS_SYSTEM_METHODDEF
    OS_UMASK_METHODDEF
    OS_UNAME_METHODDEF
    OS_UNLINK_METHODDEF
    OS_REMOVE_METHODDEF
    OS_UTIME_METHODDEF
    OS_TIMES_METHODDEF
    OS__EXIT_METHODDEF
    OS__FCOPYFILE_METHODDEF
    OS_EXECV_METHODDEF
    OS_EXECVE_METHODDEF
    OS_SPAWNV_METHODDEF
    OS_SPAWNVE_METHODDEF
    OS_FORK1_METHODDEF
    OS_FORK_METHODDEF
    OS_REGISTER_AT_FORK_METHODDEF
    OS_SCHED_GET_PRIORITY_MAX_METHODDEF
    OS_SCHED_GET_PRIORITY_MIN_METHODDEF
    OS_SCHED_GETPARAM_METHODDEF
    OS_SCHED_GETSCHEDULER_METHODDEF
    OS_SCHED_RR_GET_INTERVAL_METHODDEF
    OS_SCHED_SETPARAM_METHODDEF
    OS_SCHED_SETSCHEDULER_METHODDEF
    OS_SCHED_YIELD_METHODDEF
    OS_SCHED_SETAFFINITY_METHODDEF
    OS_SCHED_GETAFFINITY_METHODDEF
    OS_POSIX_OPENPT_METHODDEF
    OS_GRANTPT_METHODDEF
    OS_UNLOCKPT_METHODDEF
    OS_PTSNAME_METHODDEF
    OS_OPENPTY_METHODDEF
    OS_LOGIN_TTY_METHODDEF
    OS_FORKPTY_METHODDEF
    OS_GETEGID_METHODDEF
    OS_GETEUID_METHODDEF
    OS_GETGID_METHODDEF
    OS_GETGROUPLIST_METHODDEF
    OS_GETGROUPS_METHODDEF
    OS_GETPID_METHODDEF
    OS_GETPGRP_METHODDEF
    OS_GETPPID_METHODDEF
    OS_GETUID_METHODDEF
    OS_GETLOGIN_METHODDEF
    OS_KILL_METHODDEF
    OS_KILLPG_METHODDEF
    OS_PLOCK_METHODDEF
    OS_STARTFILE_METHODDEF
    OS_SETUID_METHODDEF
    OS_SETEUID_METHODDEF
    OS_SETREUID_METHODDEF
    OS_SETGID_METHODDEF
    OS_SETEGID_METHODDEF
    OS_SETREGID_METHODDEF
    OS_SETGROUPS_METHODDEF
    OS_INITGROUPS_METHODDEF
    OS_GETPGID_METHODDEF
    OS_SETPGRP_METHODDEF
    OS_WAIT_METHODDEF
    OS_WAIT3_METHODDEF
    OS_WAIT4_METHODDEF
    OS_WAITID_METHODDEF
    OS_WAITPID_METHODDEF
    OS_PIDFD_OPEN_METHODDEF
    OS_GETSID_METHODDEF
    OS_SETSID_METHODDEF
    OS_SETPGID_METHODDEF
    OS_TCGETPGRP_METHODDEF
    OS_TCSETPGRP_METHODDEF
    OS_OPEN_METHODDEF
    OS_CLOSE_METHODDEF
    OS_CLOSERANGE_METHODDEF
    OS_DEVICE_ENCODING_METHODDEF
    OS_DUP_METHODDEF
    OS_DUP2_METHODDEF
    OS_LOCKF_METHODDEF
    OS_LSEEK_METHODDEF
    OS_READ_METHODDEF
    OS_READINTO_METHODDEF
    OS_READV_METHODDEF
    OS_PREAD_METHODDEF
    OS_PREADV_METHODDEF
    OS_WRITE_METHODDEF
    OS_WRITEV_METHODDEF
    OS_PWRITE_METHODDEF
    OS_PWRITEV_METHODDEF
    OS_SENDFILE_METHODDEF
    OS_FSTAT_METHODDEF
    OS_ISATTY_METHODDEF
    OS_PIPE_METHODDEF
    OS_PIPE2_METHODDEF
    OS_MKFIFO_METHODDEF
    OS_MKNOD_METHODDEF
    OS_MAJOR_METHODDEF
    OS_MINOR_METHODDEF
    OS_MAKEDEV_METHODDEF
    OS_FTRUNCATE_METHODDEF
    OS_TRUNCATE_METHODDEF
    OS_POSIX_FALLOCATE_METHODDEF
    OS_POSIX_FADVISE_METHODDEF
    OS_PUTENV_METHODDEF
    OS_UNSETENV_METHODDEF
    OS__CLEARENV_METHODDEF
    OS_STRERROR_METHODDEF
    OS_FCHDIR_METHODDEF
    OS_FSYNC_METHODDEF
    OS_SYNC_METHODDEF
    OS_FDATASYNC_METHODDEF
    OS_WCOREDUMP_METHODDEF
    OS_WIFCONTINUED_METHODDEF
    OS_WIFSTOPPED_METHODDEF
    OS_WIFSIGNALED_METHODDEF
    OS_WIFEXITED_METHODDEF
    OS_WEXITSTATUS_METHODDEF
    OS_WTERMSIG_METHODDEF
    OS_WSTOPSIG_METHODDEF
    OS_FSTATVFS_METHODDEF
    OS_STATVFS_METHODDEF
    OS_CONFSTR_METHODDEF
    OS_SYSCONF_METHODDEF
    OS_FPATHCONF_METHODDEF
    OS_PATHCONF_METHODDEF
    OS_ABORT_METHODDEF
    OS__GETFULLPATHNAME_METHODDEF
    OS__GETDISKUSAGE_METHODDEF
    OS__GETFINALPATHNAME_METHODDEF
    OS__FINDFIRSTFILE_METHODDEF
    OS__GETVOLUMEPATHNAME_METHODDEF
    OS__PATH_SPLITROOT_METHODDEF
    OS__PATH_SPLITROOT_EX_METHODDEF
    OS__PATH_NORMPATH_METHODDEF
    OS_GETLOADAVG_METHODDEF
    OS_URANDOM_METHODDEF
    OS_SETRESUID_METHODDEF
    OS_SETRESGID_METHODDEF
    OS_GETRESUID_METHODDEF
    OS_GETRESGID_METHODDEF

    OS_GETXATTR_METHODDEF
    OS_SETXATTR_METHODDEF
    OS_REMOVEXATTR_METHODDEF
    OS_LISTXATTR_METHODDEF

    OS_GET_TERMINAL_SIZE_METHODDEF
    OS_CPU_COUNT_METHODDEF
    OS_GET_INHERITABLE_METHODDEF
    OS_SET_INHERITABLE_METHODDEF
    OS_GET_HANDLE_INHERITABLE_METHODDEF
    OS_SET_HANDLE_INHERITABLE_METHODDEF
    OS_GET_BLOCKING_METHODDEF
    OS_SET_BLOCKING_METHODDEF
    OS_SCANDIR_METHODDEF
    OS_FSPATH_METHODDEF
    OS_GETRANDOM_METHODDEF
    OS_MEMFD_CREATE_METHODDEF
    OS_EVENTFD_METHODDEF
    OS_EVENTFD_READ_METHODDEF
    OS_EVENTFD_WRITE_METHODDEF
    OS__ADD_DLL_DIRECTORY_METHODDEF
    OS__REMOVE_DLL_DIRECTORY_METHODDEF
    OS_WAITSTATUS_TO_EXITCODE_METHODDEF
    OS_SETNS_METHODDEF
    OS_UNSHARE_METHODDEF
    OS_TIMERFD_CREATE_METHODDEF
    OS_TIMERFD_SETTIME_METHODDEF
    OS_TIMERFD_SETTIME_NS_METHODDEF
    OS_TIMERFD_GETTIME_METHODDEF
    OS_TIMERFD_GETTIME_NS_METHODDEF

    OS__PATH_ISDEVDRIVE_METHODDEF
    OS__PATH_ISDIR_METHODDEF
    OS__PATH_ISFILE_METHODDEF
    OS__PATH_ISLINK_METHODDEF
    OS__PATH_ISJUNCTION_METHODDEF
    OS__PATH_EXISTS_METHODDEF
    OS__PATH_LEXISTS_METHODDEF

    OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
    OS__INPUTHOOK_METHODDEF
    OS__IS_INPUTHOOK_INSTALLED_METHODDEF
    OS__CREATE_ENVIRON_METHODDEF
    OS__EMSCRIPTEN_DEBUGGER_METHODDEF
    OS__EMSCRIPTEN_LOG_METHODDEF
    {NULL,              NULL}            /* Sentinel */
};

static int
all_ins(PyObject *m)
{
#ifdef F_OK
    if (PyModule_AddIntMacro(m, F_OK)) return -1;
#endif
#ifdef R_OK
    if (PyModule_AddIntMacro(m, R_OK)) return -1;
#endif
#ifdef W_OK
    if (PyModule_AddIntMacro(m, W_OK)) return -1;
#endif
#ifdef X_OK
    if (PyModule_AddIntMacro(m, X_OK)) return -1;
#endif
#ifdef NGROUPS_MAX
    if (PyModule_AddIntMacro(m, NGROUPS_MAX)) return -1;
#endif
#ifdef TMP_MAX
    if (PyModule_AddIntMacro(m, TMP_MAX)) return -1;
#endif
#ifdef WCONTINUED
    if (PyModule_AddIntMacro(m, WCONTINUED)) return -1;
#endif
#ifdef WNOHANG
    if (PyModule_AddIntMacro(m, WNOHANG)) return -1;
#endif
#ifdef WUNTRACED
    if (PyModule_AddIntMacro(m, WUNTRACED)) return -1;
#endif
#ifdef O_RDONLY
    if (PyModule_AddIntMacro(m, O_RDONLY)) return -1;
#endif
#ifdef O_WRONLY
    if (PyModule_AddIntMacro(m, O_WRONLY)) return -1;
#endif
#ifdef O_RDWR
    if (PyModule_AddIntMacro(m, O_RDWR)) return -1;
#endif
#ifdef O_NDELAY
    if (PyModule_AddIntMacro(m, O_NDELAY)) return -1;
#endif
#ifdef O_NONBLOCK
    if (PyModule_AddIntMacro(m, O_NONBLOCK)) return -1;
#endif
#ifdef O_APPEND
    if (PyModule_AddIntMacro(m, O_APPEND)) return -1;
#endif
#ifdef O_DSYNC
    if (PyModule_AddIntMacro(m, O_DSYNC)) return -1;
#endif
#ifdef O_RSYNC
    if (PyModule_AddIntMacro(m, O_RSYNC)) return -1;
#endif
#ifdef O_SYNC
    if (PyModule_AddIntMacro(m, O_SYNC)) return -1;
#endif
#ifdef O_NOCTTY
    if (PyModule_AddIntMacro(m, O_NOCTTY)) return -1;
#endif
#ifdef O_CREAT
    if (PyModule_AddIntMacro(m, O_CREAT)) return -1;
#endif
#ifdef O_EXCL
    if (PyModule_AddIntMacro(m, O_EXCL)) return -1;
#endif
#ifdef O_TRUNC
    if (PyModule_AddIntMacro(m, O_TRUNC)) return -1;
#endif
#ifdef O_BINARY
    if (PyModule_AddIntMacro(m, O_BINARY)) return -1;
#endif
#ifdef O_TEXT
    if (PyModule_AddIntMacro(m, O_TEXT)) return -1;
#endif
#ifdef O_XATTR
    if (PyModule_AddIntMacro(m, O_XATTR)) return -1;
#endif
#ifdef O_LARGEFILE
    if (PyModule_AddIntMacro(m, O_LARGEFILE)) return -1;
#endif
#ifndef __GNU__
#ifdef O_SHLOCK
    if (PyModule_AddIntMacro(m, O_SHLOCK)) return -1;
#endif
#ifdef O_EXLOCK
    if (PyModule_AddIntMacro(m, O_EXLOCK)) return -1;
#endif
#endif
#ifdef O_EXEC
    if (PyModule_AddIntMacro(m, O_EXEC)) return -1;
#endif
#ifdef O_SEARCH
    if (PyModule_AddIntMacro(m, O_SEARCH)) return -1;
#endif
#ifdef O_PATH
    if (PyModule_AddIntMacro(m, O_PATH)) return -1;
#endif
#ifdef O_TTY_INIT
    if (PyModule_AddIntMacro(m, O_TTY_INIT)) return -1;
#endif
#ifdef O_TMPFILE
    if (PyModule_AddIntMacro(m, O_TMPFILE)) return -1;
#endif
#ifdef PRIO_PROCESS
    if (PyModule_AddIntMacro(m, PRIO_PROCESS)) return -1;
#endif
#ifdef PRIO_PGRP
    if (PyModule_AddIntMacro(m, PRIO_PGRP)) return -1;
#endif
#ifdef PRIO_USER
    if (PyModule_AddIntMacro(m, PRIO_USER)) return -1;
#endif
#ifdef PRIO_DARWIN_THREAD
    if (PyModule_AddIntMacro(m, PRIO_DARWIN_THREAD)) return -1;
#endif
#ifdef PRIO_DARWIN_PROCESS
    if (PyModule_AddIntMacro(m, PRIO_DARWIN_PROCESS)) return -1;
#endif
#ifdef PRIO_DARWIN_BG
    if (PyModule_AddIntMacro(m, PRIO_DARWIN_BG)) return -1;
#endif
#ifdef PRIO_DARWIN_NONUI
    if (PyModule_AddIntMacro(m, PRIO_DARWIN_NONUI)) return -1;
#endif
#ifdef O_CLOEXEC
    if (PyModule_AddIntMacro(m, O_CLOEXEC)) return -1;
#endif
#ifdef O_ACCMODE
    if (PyModule_AddIntMacro(m, O_ACCMODE)) return -1;
#endif
#ifdef O_EVTONLY
    if (PyModule_AddIntMacro(m, O_EVTONLY)) return -1;
#endif
#ifdef O_FSYNC
    if (PyModule_AddIntMacro(m, O_FSYNC)) return -1;
#endif
#ifdef O_SYMLINK
    if (PyModule_AddIntMacro(m, O_SYMLINK)) return -1;
#endif

#ifdef SEEK_HOLE
    if (PyModule_AddIntMacro(m, SEEK_HOLE)) return -1;
#endif
#ifdef SEEK_DATA
    if (PyModule_AddIntMacro(m, SEEK_DATA)) return -1;
#endif

/* MS Windows */
#ifdef O_NOINHERIT
    /* Don't inherit in child processes. */
    if (PyModule_AddIntMacro(m, O_NOINHERIT)) return -1;
#endif
#ifdef _O_SHORT_LIVED
    /* Optimize for short life (keep in memory). */
    /* MS forgot to define this one with a non-underscore form too. */
    if (PyModule_AddIntConstant(m, "O_SHORT_LIVED", _O_SHORT_LIVED)) return -1;
#endif
#ifdef O_TEMPORARY
    /* Automatically delete when last handle is closed. */
    if (PyModule_AddIntMacro(m, O_TEMPORARY)) return -1;
#endif
#ifdef O_RANDOM
    /* Optimize for random access. */
    if (PyModule_AddIntMacro(m, O_RANDOM)) return -1;
#endif
#ifdef O_SEQUENTIAL
    /* Optimize for sequential access. */
    if (PyModule_AddIntMacro(m, O_SEQUENTIAL)) return -1;
#endif

/* GNU extensions. */
#ifdef O_ASYNC
    /* Send a SIGIO signal whenever input or output
       becomes available on file descriptor */
    if (PyModule_AddIntMacro(m, O_ASYNC)) return -1;
#endif
#ifdef O_DIRECT
    /* Direct disk access. */
    if (PyModule_AddIntMacro(m, O_DIRECT)) return -1;
#endif
#ifdef O_DIRECTORY
    /* Must be a directory.      */
    if (PyModule_AddIntMacro(m, O_DIRECTORY)) return -1;
#endif
#ifdef O_NOFOLLOW
    /* Do not follow links.      */
    if (PyModule_AddIntMacro(m, O_NOFOLLOW)) return -1;
#endif
#ifdef O_NOFOLLOW_ANY
    if (PyModule_AddIntMacro(m, O_NOFOLLOW_ANY)) return -1;
#endif
#ifdef O_NOLINKS
    /* Fails if link count of the named file is greater than 1 */
    if (PyModule_AddIntMacro(m, O_NOLINKS)) return -1;
#endif
#ifdef O_NOATIME
    /* Do not update the access time. */
    if (PyModule_AddIntMacro(m, O_NOATIME)) return -1;
#endif

    /* These come from sysexits.h */
#ifdef EX_OK
    if (PyModule_AddIntMacro(m, EX_OK)) return -1;
#endif /* EX_OK */
#ifdef EX_USAGE
    if (PyModule_AddIntMacro(m, EX_USAGE)) return -1;
#endif /* EX_USAGE */
#ifdef EX_DATAERR
    if (PyModule_AddIntMacro(m, EX_DATAERR)) return -1;
#endif /* EX_DATAERR */
#ifdef EX_NOINPUT
    if (PyModule_AddIntMacro(m, EX_NOINPUT)) return -1;
#endif /* EX_NOINPUT */
#ifdef EX_NOUSER
    if (PyModule_AddIntMacro(m, EX_NOUSER)) return -1;
#endif /* EX_NOUSER */
#ifdef EX_NOHOST
    if (PyModule_AddIntMacro(m, EX_NOHOST)) return -1;
#endif /* EX_NOHOST */
#ifdef EX_UNAVAILABLE
    if (PyModule_AddIntMacro(m, EX_UNAVAILABLE)) return -1;
#endif /* EX_UNAVAILABLE */
#ifdef EX_SOFTWARE
    if (PyModule_AddIntMacro(m, EX_SOFTWARE)) return -1;
#endif /* EX_SOFTWARE */
#ifdef EX_OSERR
    if (PyModule_AddIntMacro(m, EX_OSERR)) return -1;
#endif /* EX_OSERR */
#ifdef EX_OSFILE
    if (PyModule_AddIntMacro(m, EX_OSFILE)) return -1;
#endif /* EX_OSFILE */
#ifdef EX_CANTCREAT
    if (PyModule_AddIntMacro(m, EX_CANTCREAT)) return -1;
#endif /* EX_CANTCREAT */
#ifdef EX_IOERR
    if (PyModule_AddIntMacro(m, EX_IOERR)) return -1;
#endif /* EX_IOERR */
#ifdef EX_TEMPFAIL
    if (PyModule_AddIntMacro(m, EX_TEMPFAIL)) return -1;
#endif /* EX_TEMPFAIL */
#ifdef EX_PROTOCOL
    if (PyModule_AddIntMacro(m, EX_PROTOCOL)) return -1;
#endif /* EX_PROTOCOL */
#ifdef EX_NOPERM
    if (PyModule_AddIntMacro(m, EX_NOPERM)) return -1;
#endif /* EX_NOPERM */
#ifdef EX_CONFIG
    if (PyModule_AddIntMacro(m, EX_CONFIG)) return -1;
#endif /* EX_CONFIG */
#ifdef EX_NOTFOUND
    if (PyModule_AddIntMacro(m, EX_NOTFOUND)) return -1;
#endif /* EX_NOTFOUND */

    /* statvfs */
#ifdef ST_RDONLY
    if (PyModule_AddIntMacro(m, ST_RDONLY)) return -1;
#endif /* ST_RDONLY */
#ifdef ST_NOSUID
    if (PyModule_AddIntMacro(m, ST_NOSUID)) return -1;
#endif /* ST_NOSUID */

       /* GNU extensions */
#ifdef ST_NODEV
    if (PyModule_AddIntMacro(m, ST_NODEV)) return -1;
#endif /* ST_NODEV */
#ifdef ST_NOEXEC
    if (PyModule_AddIntMacro(m, ST_NOEXEC)) return -1;
#endif /* ST_NOEXEC */
#ifdef ST_SYNCHRONOUS
    if (PyModule_AddIntMacro(m, ST_SYNCHRONOUS)) return -1;
#endif /* ST_SYNCHRONOUS */
#ifdef ST_MANDLOCK
    if (PyModule_AddIntMacro(m, ST_MANDLOCK)) return -1;
#endif /* ST_MANDLOCK */
#ifdef ST_WRITE
    if (PyModule_AddIntMacro(m, ST_WRITE)) return -1;
#endif /* ST_WRITE */
#ifdef ST_APPEND
    if (PyModule_AddIntMacro(m, ST_APPEND)) return -1;
#endif /* ST_APPEND */
#ifdef ST_NOATIME
    if (PyModule_AddIntMacro(m, ST_NOATIME)) return -1;
#endif /* ST_NOATIME */
#ifdef ST_NODIRATIME
    if (PyModule_AddIntMacro(m, ST_NODIRATIME)) return -1;
#endif /* ST_NODIRATIME */
#ifdef ST_RELATIME
    if (PyModule_AddIntMacro(m, ST_RELATIME)) return -1;
#endif /* ST_RELATIME */

    /* FreeBSD sendfile() constants */
#ifdef SF_NODISKIO
    if (PyModule_AddIntMacro(m, SF_NODISKIO)) return -1;
#endif
    /* is obsolete since the 11.x release */
#ifdef SF_MNOWAIT
    if (PyModule_AddIntMacro(m, SF_MNOWAIT)) return -1;
#endif
#ifdef SF_SYNC
    if (PyModule_AddIntMacro(m, SF_SYNC)) return -1;
#endif
#ifdef SF_NOCACHE
    if (PyModule_AddIntMacro(m, SF_NOCACHE)) return -1;
#endif

#ifdef TFD_NONBLOCK
    if (PyModule_AddIntMacro(m, TFD_NONBLOCK)) return -1;
#endif
#ifdef TFD_CLOEXEC
    if (PyModule_AddIntMacro(m, TFD_CLOEXEC)) return -1;
#endif
#ifdef TFD_TIMER_ABSTIME
    if (PyModule_AddIntMacro(m, TFD_TIMER_ABSTIME)) return -1;
#endif
#ifdef TFD_TIMER_CANCEL_ON_SET
    if (PyModule_AddIntMacro(m, TFD_TIMER_CANCEL_ON_SET)) return -1;
#endif

    /* constants for posix_fadvise */
#ifdef POSIX_FADV_NORMAL
    if (PyModule_AddIntMacro(m, POSIX_FADV_NORMAL)) return -1;
#endif
#ifdef POSIX_FADV_SEQUENTIAL
    if (PyModule_AddIntMacro(m, POSIX_FADV_SEQUENTIAL)) return -1;
#endif
#ifdef POSIX_FADV_RANDOM
    if (PyModule_AddIntMacro(m, POSIX_FADV_RANDOM)) return -1;
#endif
#ifdef POSIX_FADV_NOREUSE
    if (PyModule_AddIntMacro(m, POSIX_FADV_NOREUSE)) return -1;
#endif
#ifdef POSIX_FADV_WILLNEED
    if (PyModule_AddIntMacro(m, POSIX_FADV_WILLNEED)) return -1;
#endif
#ifdef POSIX_FADV_DONTNEED
    if (PyModule_AddIntMacro(m, POSIX_FADV_DONTNEED)) return -1;
#endif

    /* constants for waitid */
#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_WAITID)
    if (PyModule_AddIntMacro(m, P_PID)) return -1;
    if (PyModule_AddIntMacro(m, P_PGID)) return -1;
    if (PyModule_AddIntMacro(m, P_ALL)) return -1;
#ifdef P_PIDFD
    if (PyModule_AddIntMacro(m, P_PIDFD)) return -1;
#endif
#ifdef PIDFD_NONBLOCK
    if (PyModule_AddIntMacro(m, PIDFD_NONBLOCK)) return -1;
#endif
#endif
#ifdef WEXITED
    if (PyModule_AddIntMacro(m, WEXITED)) return -1;
#endif
#ifdef WNOWAIT
    if (PyModule_AddIntMacro(m, WNOWAIT)) return -1;
#endif
#ifdef WSTOPPED
    if (PyModule_AddIntMacro(m, WSTOPPED)) return -1;
#endif
#ifdef CLD_EXITED
    if (PyModule_AddIntMacro(m, CLD_EXITED)) return -1;
#endif
#ifdef CLD_KILLED
    if (PyModule_AddIntMacro(m, CLD_KILLED)) return -1;
#endif
#ifdef CLD_DUMPED
    if (PyModule_AddIntMacro(m, CLD_DUMPED)) return -1;
#endif
#ifdef CLD_TRAPPED
    if (PyModule_AddIntMacro(m, CLD_TRAPPED)) return -1;
#endif
#ifdef CLD_STOPPED
    if (PyModule_AddIntMacro(m, CLD_STOPPED)) return -1;
#endif
#ifdef CLD_CONTINUED
    if (PyModule_AddIntMacro(m, CLD_CONTINUED)) return -1;
#endif

    /* constants for lockf */
#ifdef F_LOCK
    if (PyModule_AddIntMacro(m, F_LOCK)) return -1;
#endif
#ifdef F_TLOCK
    if (PyModule_AddIntMacro(m, F_TLOCK)) return -1;
#endif
#ifdef F_ULOCK
    if (PyModule_AddIntMacro(m, F_ULOCK)) return -1;
#endif
#ifdef F_TEST
    if (PyModule_AddIntMacro(m, F_TEST)) return -1;
#endif

#ifdef RWF_DSYNC
    if (PyModule_AddIntConstant(m, "RWF_DSYNC", RWF_DSYNC)) return -1;
#endif
#ifdef RWF_HIPRI
    if (PyModule_AddIntConstant(m, "RWF_HIPRI", RWF_HIPRI)) return -1;
#endif
#ifdef RWF_SYNC
    if (PyModule_AddIntConstant(m, "RWF_SYNC", RWF_SYNC)) return -1;
#endif
#ifdef RWF_NOWAIT
    if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1;
#endif
#ifdef RWF_DONTCACHE
    if (PyModule_AddIntConstant(m, "RWF_DONTCACHE", RWF_DONTCACHE)) return -1;
#endif
#ifdef RWF_ATOMIC
    if (PyModule_AddIntConstant(m, "RWF_ATOMIC", RWF_ATOMIC)) return -1;
#endif
#ifdef RWF_APPEND
    if (PyModule_AddIntConstant(m, "RWF_APPEND", RWF_APPEND)) return -1;
#endif

/* constants for splice */
#if defined(HAVE_SPLICE) && defined(__linux__)
    if (PyModule_AddIntConstant(m, "SPLICE_F_MOVE", SPLICE_F_MOVE)) return -1;
    if (PyModule_AddIntConstant(m, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK)) return -1;
    if (PyModule_AddIntConstant(m, "SPLICE_F_MORE", SPLICE_F_MORE)) return -1;
#endif

/* constants for posix_spawn */
#ifdef HAVE_POSIX_SPAWN
    if (PyModule_AddIntConstant(m, "POSIX_SPAWN_OPEN", POSIX_SPAWN_OPEN)) return -1;
    if (PyModule_AddIntConstant(m, "POSIX_SPAWN_CLOSE", POSIX_SPAWN_CLOSE)) return -1;
    if (PyModule_AddIntConstant(m, "POSIX_SPAWN_DUP2", POSIX_SPAWN_DUP2)) return -1;
#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP
    if (PyModule_AddIntMacro(m, POSIX_SPAWN_CLOSEFROM)) return -1;
#endif
#endif

#if defined(HAVE_SPAWNV) || defined (HAVE_RTPSPAWN)
    if (PyModule_AddIntConstant(m, "P_WAIT", _P_WAIT)) return -1;
    if (PyModule_AddIntConstant(m, "P_NOWAIT", _P_NOWAIT)) return -1;
    if (PyModule_AddIntConstant(m, "P_NOWAITO", _P_NOWAITO)) return -1;
#endif
#ifdef HAVE_SPAWNV
    if (PyModule_AddIntConstant(m, "P_OVERLAY", _OLD_P_OVERLAY)) return -1;
    if (PyModule_AddIntConstant(m, "P_DETACH", _P_DETACH)) return -1;
#endif

#ifdef HAVE_SCHED_H
#ifdef SCHED_OTHER
    if (PyModule_AddIntMacro(m, SCHED_OTHER)) return -1;
#endif
#ifdef SCHED_DEADLINE
    if (PyModule_AddIntMacro(m, SCHED_DEADLINE)) return -1;
#endif
#ifdef SCHED_FIFO
    if (PyModule_AddIntMacro(m, SCHED_FIFO)) return -1;
#endif
#ifdef SCHED_NORMAL
    if (PyModule_AddIntMacro(m, SCHED_NORMAL)) return -1;
#endif
#ifdef SCHED_RR
    if (PyModule_AddIntMacro(m, SCHED_RR)) return -1;
#endif
#ifdef SCHED_SPORADIC
    if (PyModule_AddIntMacro(m, SCHED_SPORADIC)) return -1;
#endif
#ifdef SCHED_BATCH
    if (PyModule_AddIntMacro(m, SCHED_BATCH)) return -1;
#endif
#ifdef SCHED_IDLE
    if (PyModule_AddIntMacro(m, SCHED_IDLE)) return -1;
#endif
#ifdef SCHED_RESET_ON_FORK
    if (PyModule_AddIntMacro(m, SCHED_RESET_ON_FORK)) return -1;
#endif
#ifdef SCHED_SYS
    if (PyModule_AddIntMacro(m, SCHED_SYS)) return -1;
#endif
#ifdef SCHED_IA
    if (PyModule_AddIntMacro(m, SCHED_IA)) return -1;
#endif
#ifdef SCHED_FSS
    if (PyModule_AddIntMacro(m, SCHED_FSS)) return -1;
#endif
#ifdef SCHED_FX
    if (PyModule_AddIntConstant(m, "SCHED_FX", SCHED_FSS)) return -1;
#endif

/* constants for namespaces */
#if defined(HAVE_SETNS) || defined(HAVE_UNSHARE)
#ifdef CLONE_FS
    if (PyModule_AddIntMacro(m, CLONE_FS)) return -1;
#endif
#ifdef CLONE_FILES
    if (PyModule_AddIntMacro(m, CLONE_FILES)) return -1;
#endif
#ifdef CLONE_NEWNS
    if (PyModule_AddIntMacro(m, CLONE_NEWNS)) return -1;
#endif
#ifdef CLONE_NEWCGROUP
    if (PyModule_AddIntMacro(m, CLONE_NEWCGROUP)) return -1;
#endif
#ifdef CLONE_NEWUTS
    if (PyModule_AddIntMacro(m, CLONE_NEWUTS)) return -1;
#endif
#ifdef CLONE_NEWIPC
    if (PyModule_AddIntMacro(m, CLONE_NEWIPC)) return -1;
#endif
#ifdef CLONE_NEWUSER
    if (PyModule_AddIntMacro(m, CLONE_NEWUSER)) return -1;
#endif
#ifdef CLONE_NEWPID
    if (PyModule_AddIntMacro(m, CLONE_NEWPID)) return -1;
#endif
#ifdef CLONE_NEWNET
    if (PyModule_AddIntMacro(m, CLONE_NEWNET)) return -1;
#endif
#ifdef CLONE_NEWTIME
    if (PyModule_AddIntMacro(m, CLONE_NEWTIME)) return -1;
#endif
#ifdef CLONE_SYSVSEM
    if (PyModule_AddIntMacro(m, CLONE_SYSVSEM)) return -1;
#endif
#ifdef CLONE_THREAD
    if (PyModule_AddIntMacro(m, CLONE_THREAD)) return -1;
#endif
#ifdef CLONE_SIGHAND
    if (PyModule_AddIntMacro(m, CLONE_SIGHAND)) return -1;
#endif
#ifdef CLONE_VM
    if (PyModule_AddIntMacro(m, CLONE_VM)) return -1;
#endif
#endif

#endif

#ifdef USE_XATTRS
    if (PyModule_AddIntMacro(m, XATTR_CREATE)) return -1;
    if (PyModule_AddIntMacro(m, XATTR_REPLACE)) return -1;
    if (PyModule_AddIntMacro(m, XATTR_SIZE_MAX)) return -1;
#endif

#if HAVE_DECL_RTLD_LAZY
    if (PyModule_AddIntMacro(m, RTLD_LAZY)) return -1;
#endif
#if HAVE_DECL_RTLD_NOW
    if (PyModule_AddIntMacro(m, RTLD_NOW)) return -1;
#endif
#if HAVE_DECL_RTLD_GLOBAL
    if (PyModule_AddIntMacro(m, RTLD_GLOBAL)) return -1;
#endif
#if HAVE_DECL_RTLD_LOCAL
    if (PyModule_AddIntMacro(m, RTLD_LOCAL)) return -1;
#endif
#if HAVE_DECL_RTLD_NODELETE
    if (PyModule_AddIntMacro(m, RTLD_NODELETE)) return -1;
#endif
#if HAVE_DECL_RTLD_NOLOAD
    if (PyModule_AddIntMacro(m, RTLD_NOLOAD)) return -1;
#endif
#if HAVE_DECL_RTLD_DEEPBIND
    if (PyModule_AddIntMacro(m, RTLD_DEEPBIND)) return -1;
#endif
#if HAVE_DECL_RTLD_MEMBER
    if (PyModule_AddIntMacro(m, RTLD_MEMBER)) return -1;
#endif

#ifdef HAVE_GETRANDOM_SYSCALL
    if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
    if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
#endif
#ifdef HAVE_MEMFD_CREATE
    if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1;
    if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1;
#ifdef MFD_HUGETLB
    if (PyModule_AddIntMacro(m, MFD_HUGETLB)) return -1;
#endif
#ifdef MFD_HUGE_SHIFT
    if (PyModule_AddIntMacro(m, MFD_HUGE_SHIFT)) return -1;
#endif
#ifdef MFD_HUGE_MASK
    if (PyModule_AddIntMacro(m, MFD_HUGE_MASK)) return -1;
#endif
#ifdef MFD_HUGE_64KB
    if (PyModule_AddIntMacro(m, MFD_HUGE_64KB)) return -1;
#endif
#ifdef MFD_HUGE_512KB
    if (PyModule_AddIntMacro(m, MFD_HUGE_512KB)) return -1;
#endif
#ifdef MFD_HUGE_1MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_1MB)) return -1;
#endif
#ifdef MFD_HUGE_2MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_2MB)) return -1;
#endif
#ifdef MFD_HUGE_8MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_8MB)) return -1;
#endif
#ifdef MFD_HUGE_16MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_16MB)) return -1;
#endif
#ifdef MFD_HUGE_32MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_32MB)) return -1;
#endif
#ifdef MFD_HUGE_256MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_256MB)) return -1;
#endif
#ifdef MFD_HUGE_512MB
    if (PyModule_AddIntMacro(m, MFD_HUGE_512MB)) return -1;
#endif
#ifdef MFD_HUGE_1GB
    if (PyModule_AddIntMacro(m, MFD_HUGE_1GB)) return -1;
#endif
#ifdef MFD_HUGE_2GB
    if (PyModule_AddIntMacro(m, MFD_HUGE_2GB)) return -1;
#endif
#ifdef MFD_HUGE_16GB
    if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1;
#endif
#endif /* HAVE_MEMFD_CREATE */

#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
    if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1;
#ifdef EFD_NONBLOCK
    if (PyModule_AddIntMacro(m, EFD_NONBLOCK)) return -1;
#endif
#ifdef EFD_SEMAPHORE
    if (PyModule_AddIntMacro(m, EFD_SEMAPHORE)) return -1;
#endif
#endif  /* HAVE_EVENTFD && EFD_CLOEXEC */

#ifdef NODEV
    if (PyModule_Add(m, "NODEV", _PyLong_FromDev(NODEV))) return -1;
#endif

#ifdef AT_NO_AUTOMOUNT
    if (PyModule_AddIntMacro(m, AT_NO_AUTOMOUNT)) return -1;
#endif

#ifdef HAVE_STATX
    if (PyModule_AddIntMacro(m, STATX_TYPE)) return -1;
    if (PyModule_AddIntMacro(m, STATX_MODE)) return -1;
    if (PyModule_AddIntMacro(m, STATX_NLINK)) return -1;
    if (PyModule_AddIntMacro(m, STATX_UID)) return -1;
    if (PyModule_AddIntMacro(m, STATX_GID)) return -1;
    if (PyModule_AddIntMacro(m, STATX_ATIME)) return -1;
    if (PyModule_AddIntMacro(m, STATX_MTIME)) return -1;
    if (PyModule_AddIntMacro(m, STATX_CTIME)) return -1;
    if (PyModule_AddIntMacro(m, STATX_INO)) return -1;
    if (PyModule_AddIntMacro(m, STATX_SIZE)) return -1;
    if (PyModule_AddIntMacro(m, STATX_BLOCKS)) return -1;
    if (PyModule_AddIntMacro(m, STATX_BASIC_STATS)) return -1;
    if (PyModule_AddIntMacro(m, STATX_BTIME)) return -1;
#ifdef STATX_MNT_ID
    if (PyModule_AddIntMacro(m, STATX_MNT_ID)) return -1;
#endif
#ifdef STATX_DIOALIGN
    if (PyModule_AddIntMacro(m, STATX_DIOALIGN)) return -1;
#endif
#ifdef STATX_MNT_ID_UNIQUE
    if (PyModule_AddIntMacro(m, STATX_MNT_ID_UNIQUE)) return -1;
#endif
#ifdef STATX_SUBVOL
    if (PyModule_AddIntMacro(m, STATX_SUBVOL)) return -1;
#endif
#ifdef STATX_WRITE_ATOMIC
    if (PyModule_AddIntMacro(m, STATX_WRITE_ATOMIC)) return -1;
#endif
#ifdef STATX_DIO_READ_ALIGN
    if (PyModule_AddIntMacro(m, STATX_DIO_READ_ALIGN)) return -1;
#endif
    /* STATX_ALL intentionally omitted because it is deprecated */
    if (PyModule_AddIntMacro(m, AT_STATX_SYNC_AS_STAT)) return -1;
    if (PyModule_AddIntMacro(m, AT_STATX_FORCE_SYNC)) return -1;
    if (PyModule_AddIntMacro(m, AT_STATX_DONT_SYNC)) return -1;
    /* STATX_ATTR_* constants are in the stat module */
#endif /* HAVE_STATX */

#if defined(__APPLE__)
    if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1;
    if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1;
    if (PyModule_AddIntConstant(m, "_COPYFILE_ACL", COPYFILE_ACL)) return -1;
    if (PyModule_AddIntConstant(m, "_COPYFILE_XATTR", COPYFILE_XATTR)) return -1;
#endif

#ifdef MS_WINDOWS
    if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) return -1;
    if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_APPLICATION_DIR", LOAD_LIBRARY_SEARCH_APPLICATION_DIR)) return -1;
    if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_SYSTEM32", LOAD_LIBRARY_SEARCH_SYSTEM32)) return -1;
    if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_USER_DIRS", LOAD_LIBRARY_SEARCH_USER_DIRS)) return -1;
    if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR", LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR)) return -1;
#endif

    return 0;
}



#define PROBE(name, test) \
   static int name(void)  \
   {                      \
      if (test) {        \
          return 1;       \
      } else {            \
          return 0;       \
      }                   \
   }

#ifdef HAVE_FSTATAT
PROBE(probe_fstatat, HAVE_FSTATAT_RUNTIME)
#endif

#ifdef HAVE_FACCESSAT
PROBE(probe_faccessat, HAVE_FACCESSAT_RUNTIME)
#endif

#ifdef HAVE_FCHMODAT
PROBE(probe_fchmodat, HAVE_FCHMODAT_RUNTIME)
#endif

#ifdef HAVE_FCHOWNAT
PROBE(probe_fchownat, HAVE_FCHOWNAT_RUNTIME)
#endif

#ifdef HAVE_LINKAT
PROBE(probe_linkat, HAVE_LINKAT_RUNTIME)
#endif

#ifdef HAVE_FDOPENDIR
PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
#endif

#ifdef HAVE_MKDIRAT
PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
#endif

#ifdef HAVE_MKFIFOAT
PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME)
#endif

#ifdef HAVE_MKNODAT
PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME)
#endif

#ifdef HAVE_RENAMEAT
PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
#endif

#ifdef HAVE_UNLINKAT
PROBE(probe_unlinkat, HAVE_UNLINKAT_RUNTIME)
#endif

#ifdef HAVE_OPENAT
PROBE(probe_openat, HAVE_OPENAT_RUNTIME)
#endif

#ifdef HAVE_READLINKAT
PROBE(probe_readlinkat, HAVE_READLINKAT_RUNTIME)
#endif

#ifdef HAVE_SYMLINKAT
PROBE(probe_symlinkat, HAVE_SYMLINKAT_RUNTIME)
#endif

#ifdef HAVE_FUTIMENS
PROBE(probe_futimens, HAVE_FUTIMENS_RUNTIME)
#endif

#ifdef HAVE_UTIMENSAT
PROBE(probe_utimensat, HAVE_UTIMENSAT_RUNTIME)
#endif

#ifdef HAVE_PTSNAME_R
PROBE(probe_ptsname_r, HAVE_PTSNAME_R_RUNTIME)
#endif



static const struct have_function {
    const char * const label;
    int (*probe)(void);
} have_functions[] = {

#ifdef HAVE_EVENTFD
    {"HAVE_EVENTFD", NULL},
#endif

#ifdef HAVE_TIMERFD_CREATE
    {"HAVE_TIMERFD_CREATE", NULL},
#endif

#ifdef HAVE_FACCESSAT
    { "HAVE_FACCESSAT", probe_faccessat },
#endif

#ifdef HAVE_FCHDIR
    { "HAVE_FCHDIR", NULL },
#endif

#ifdef HAVE_FCHMOD
    { "HAVE_FCHMOD", NULL },
#endif

#ifdef HAVE_FCHMODAT
    { "HAVE_FCHMODAT", probe_fchmodat },
#endif

#ifdef HAVE_FCHOWN
    { "HAVE_FCHOWN", NULL },
#endif

#ifdef HAVE_FCHOWNAT
    { "HAVE_FCHOWNAT", probe_fchownat },
#endif

#ifdef HAVE_FEXECVE
    { "HAVE_FEXECVE", NULL },
#endif

#ifdef HAVE_FDOPENDIR
    { "HAVE_FDOPENDIR", probe_fdopendir },
#endif

#ifdef HAVE_FPATHCONF
    { "HAVE_FPATHCONF", NULL },
#endif

#ifdef HAVE_FSTATAT
    { "HAVE_FSTATAT", probe_fstatat },
#endif

#ifdef HAVE_FSTATVFS
    { "HAVE_FSTATVFS", NULL },
#endif

#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
    { "HAVE_FTRUNCATE", NULL },
#endif

#ifdef HAVE_FUTIMENS
    { "HAVE_FUTIMENS", probe_futimens },
#endif

#ifdef HAVE_FUTIMES
    { "HAVE_FUTIMES", NULL },
#endif

#ifdef HAVE_FUTIMESAT
    { "HAVE_FUTIMESAT", NULL },
#endif

#ifdef HAVE_LINKAT
    { "HAVE_LINKAT", probe_linkat },
#endif

#ifdef HAVE_LCHFLAGS
    { "HAVE_LCHFLAGS", NULL },
#endif

#ifdef HAVE_LCHMOD
    { "HAVE_LCHMOD", NULL },
#endif

#ifdef HAVE_LCHOWN
    { "HAVE_LCHOWN", NULL },
#endif

#ifdef HAVE_LSTAT
    { "HAVE_LSTAT", NULL },
#endif

#ifdef HAVE_LUTIMES
    { "HAVE_LUTIMES", NULL },
#endif

#ifdef HAVE_MEMFD_CREATE
    { "HAVE_MEMFD_CREATE", NULL },
#endif

#ifdef HAVE_MKDIRAT
    { "HAVE_MKDIRAT", probe_mkdirat },
#endif

#ifdef HAVE_MKFIFOAT
    { "HAVE_MKFIFOAT", probe_mkfifoat },
#endif

#ifdef HAVE_MKNODAT
    { "HAVE_MKNODAT", probe_mknodat },
#endif

#ifdef HAVE_OPENAT
    { "HAVE_OPENAT", probe_openat },
#endif

#ifdef HAVE_READLINKAT
    { "HAVE_READLINKAT", probe_readlinkat },
#endif

#ifdef HAVE_RENAMEAT
    { "HAVE_RENAMEAT", probe_renameat },
#endif

#ifdef HAVE_SYMLINKAT
    { "HAVE_SYMLINKAT", probe_symlinkat },
#endif

#ifdef HAVE_UNLINKAT
    { "HAVE_UNLINKAT", probe_unlinkat },
#endif

#ifdef HAVE_UTIMENSAT
    { "HAVE_UTIMENSAT", probe_utimensat },
#endif

#ifdef HAVE_PTSNAME_R
    { "HAVE_PTSNAME_R", probe_ptsname_r },
#endif

#ifdef MS_WINDOWS
    { "MS_WINDOWS", NULL },
#endif

    { NULL, NULL }
};


static int
posixmodule_exec(PyObject *m)
{
    _posixstate *state = get_posix_state(m);

#if defined(HAVE_PWRITEV)
    if (HAVE_PWRITEV_RUNTIME) {} else {
        PyObject* dct = PyModule_GetDict(m);

        if (dct == NULL) {
            return -1;
        }

        if (PyDict_PopString(dct, "pwritev", NULL) < 0) {
            return -1;
        }
        if (PyDict_PopString(dct, "preadv", NULL) < 0) {
            return -1;
        }
    }
#endif

#ifdef HAVE_STATX
    if (statx == NULL) {
        PyObject* dct = PyModule_GetDict(m);
        if (dct == NULL) {
            return -1;
        }
        if (PyDict_PopString(dct, "statx", NULL) < 0) {
            return -1;
        }
    }
    else {
        state->StatxResultType = PyType_FromModuleAndSpec(m, &pystatx_result_spec, NULL);
        if (PyModule_AddObjectRef(m, "statx_result", state->StatxResultType) < 0) {
            return -1;
        }
    }
#endif

    /* Initialize environ dictionary */
    if (PyModule_Add(m, "environ", convertenviron()) != 0) {
        return -1;
    }

    if (all_ins(m))
        return -1;

    if (setup_confname_tables(m))
        return -1;

    if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) {
        return -1;
    }

#if defined(HAVE_WAITID)
    state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc);
    if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) {
        return -1;
    }
#endif

    stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
    stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
    stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
    state->StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc);
    if (PyModule_AddObjectRef(m, "stat_result", state->StatResultType) < 0) {
        return -1;
    }
    state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new;
    ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new;

    state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc);
    if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) {
        return -1;
    }

#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
    state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc);
    if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) {
        return -1;
    }
    ((PyTypeObject *)state->SchedParamType)->tp_new = os_sched_param;
    if (_PyType_AddMethod((PyTypeObject *)state->SchedParamType,
                          &os_sched_param_reduce_method) < 0)
    {
        return -1;
    }
    PyType_Modified((PyTypeObject *)state->SchedParamType);
#endif

    /* initialize TerminalSize_info */
    state->TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc);
    if (PyModule_AddObjectRef(m, "terminal_size", state->TerminalSizeType) < 0) {
        return -1;
    }

    /* initialize scandir types */
    PyObject *ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL);
    if (ScandirIteratorType == NULL) {
        return -1;
    }
    state->ScandirIteratorType = ScandirIteratorType;

    state->DirEntryType = PyType_FromModuleAndSpec(m, &DirEntryType_spec, NULL);
    if (PyModule_AddObjectRef(m, "DirEntry", state->DirEntryType) < 0) {
        return -1;
    }

    state->TimesResultType = (PyObject *)PyStructSequence_NewType(&times_result_desc);
    if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) {
        return -1;
    }

    state->UnameResultType = (PyObject *)PyStructSequence_NewType(&uname_result_desc);
    if (PyModule_AddObjectRef(m, "uname_result", state->UnameResultType) < 0) {
        return -1;
    }

    if ((state->billion = PyLong_FromLong(1000000000)) == NULL)
        return -1;
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
    state->struct_rusage = PyUnicode_InternFromString("struct_rusage");
    if (state->struct_rusage == NULL)
        return -1;
#endif
    state->st_mode = PyUnicode_InternFromString("st_mode");
    if (state->st_mode == NULL)
        return -1;

    /* suppress "function not used" warnings */
    {
    int ignored;
    fd_specified("", -1);
    follow_symlinks_specified("", 1);
    dir_fd_and_follow_symlinks_invalid("chmod", DEFAULT_DIR_FD, 1);
    dir_fd_converter(Py_None, &ignored);
    dir_fd_unavailable(Py_None, &ignored);
    }

    /*
     * provide list of locally available functions
     * so os.py can populate support_* lists
     */
    PyObject *list = PyList_New(0);
    if (!list) {
        return -1;
    }
    for (const struct have_function *trace = have_functions; trace->label; trace++) {
        PyObject *unicode;
        if (trace->probe && !trace->probe()) continue;
        unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL);
        if (!unicode)
            return -1;
        if (PyList_Append(list, unicode))
            return -1;
        Py_DECREF(unicode);
    }

#ifndef MS_WINDOWS
    if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) {
        PyErr_SetString(PyExc_RuntimeError,
                        "cannot read ticks_per_second");
        return -1;
    }
    assert(state->ticks_per_second >= 1);
#endif

    return PyModule_Add(m, "_have_functions", list);
}


static PyModuleDef_Slot posixmodile_slots[] = {
    {Py_mod_exec, posixmodule_exec},
    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
    {Py_mod_gil, Py_MOD_GIL_NOT_USED},
    {0, NULL}
};

static struct PyModuleDef posixmodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = MODNAME,
    .m_doc = posix__doc__,
    .m_size = sizeof(_posixstate),
    .m_methods = posix_methods,
    .m_slots = posixmodile_slots,
    .m_traverse = _posix_traverse,
    .m_clear = _posix_clear,
    .m_free = _posix_free,
};

PyMODINIT_FUNC
INITFUNC(void)
{
    return PyModuleDef_Init(&posixmodule);
}
