Issue #23668: Adds support for os.truncate and os.ftruncate on Windows
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 5108fc7..bb3e9b9 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -839,9 +839,7 @@
 fileio_truncate(fileio *self, PyObject *args)
 {
     PyObject *posobj = NULL; /* the new size wanted by the user */
-#ifndef MS_WINDOWS
     Py_off_t pos;
-#endif
     int ret;
     int fd;
 
@@ -864,52 +862,6 @@
         Py_INCREF(posobj);
     }
 
-#ifdef MS_WINDOWS
-    /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
-       so don't even try using it. */
-    {
-        PyObject *oldposobj, *tempposobj;
-        HANDLE hFile;
-
-        /* we save the file pointer position */
-        oldposobj = portable_lseek(fd, NULL, 1);
-        if (oldposobj == NULL) {
-            Py_DECREF(posobj);
-            return NULL;
-        }
-
-        /* we then move to the truncation position */
-        tempposobj = portable_lseek(fd, posobj, 0);
-        if (tempposobj == NULL) {
-            Py_DECREF(oldposobj);
-            Py_DECREF(posobj);
-            return NULL;
-        }
-        Py_DECREF(tempposobj);
-
-        /* Truncate.  Note that this may grow the file! */
-        Py_BEGIN_ALLOW_THREADS
-        errno = 0;
-        hFile = (HANDLE)_get_osfhandle(fd);
-        ret = hFile == (HANDLE)-1; /* testing for INVALID_HANDLE value */
-        if (ret == 0) {
-            ret = SetEndOfFile(hFile) == 0;
-            if (ret)
-                errno = EACCES;
-        }
-        Py_END_ALLOW_THREADS
-
-        /* we restore the file pointer position in any case */
-        tempposobj = portable_lseek(fd, oldposobj, 0);
-        Py_DECREF(oldposobj);
-        if (tempposobj == NULL) {
-            Py_DECREF(posobj);
-            return NULL;
-        }
-        Py_DECREF(tempposobj);
-    }
-#else
-
 #if defined(HAVE_LARGEFILE_SUPPORT)
     pos = PyLong_AsLongLong(posobj);
 #else
@@ -922,11 +874,13 @@
 
     Py_BEGIN_ALLOW_THREADS
     errno = 0;
+#ifdef MS_WINDOWS
+    ret = _chsize_s(fd, pos);
+#else
     ret = ftruncate(fd, pos);
+#endif
     Py_END_ALLOW_THREADS
 
-#endif /* !MS_WINDOWS */
-
     if (ret != 0) {
         Py_DECREF(posobj);
         PyErr_SetFromErrno(PyExc_IOError);
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 7da1ab0..724acc6 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -2315,6 +2315,10 @@
 #endif
 /*[python end generated code: output=4bd4f6f7d41267f1 input=80b4c890b6774ea5]*/
 
+#ifdef MS_WINDOWS
+    #undef PATH_HAVE_FTRUNCATE
+    #define PATH_HAVE_FTRUNCATE 1
+#endif
 
 /*[python input]
 
@@ -8753,7 +8757,7 @@
 #endif /* HAVE_DEVICE_MACROS */
 
 
-#ifdef HAVE_FTRUNCATE
+#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
 /*[clinic input]
 os.ftruncate
 
@@ -8771,9 +8775,16 @@
     int result;
     int async_err = 0;
 
+    if (!_PyVerify_fd(fd))
+        return posix_error();
+
     do {
         Py_BEGIN_ALLOW_THREADS
+#ifdef MS_WINDOWS
+        result = _chsize_s(fd, length);
+#else
         result = ftruncate(fd, length);
+#endif
         Py_END_ALLOW_THREADS
     } while (result != 0 && errno == EINTR &&
              !(async_err = PyErr_CheckSignals()));
@@ -8781,10 +8792,10 @@
         return (!async_err) ? posix_error() : NULL;
     Py_RETURN_NONE;
 }
-#endif /* HAVE_FTRUNCATE */
+#endif /* HAVE_FTRUNCATE || MS_WINDOWS */
 
 
-#ifdef HAVE_TRUNCATE
+#if defined HAVE_TRUNCATE || defined MS_WINDOWS
 /*[clinic input]
 os.truncate
     path: path_t(allow_fd='PATH_HAVE_FTRUNCATE')
@@ -8801,21 +8812,37 @@
 /*[clinic end generated code: output=f60a9e08370e9e2e input=77229cf0b50a9b77]*/
 {
     int result;
+#ifdef MS_WINDOWS
+    int fd;
+#endif
+
+    if (path->fd != -1)
+        return os_ftruncate_impl(module, path->fd, length);
 
     Py_BEGIN_ALLOW_THREADS
-#ifdef HAVE_FTRUNCATE
-    if (path->fd != -1)
-        result = ftruncate(path->fd, length);
+#ifdef MS_WINDOWS
+    if (path->wide)
+        fd = _wopen(path->wide, _O_WRONLY | _O_BINARY | _O_NOINHERIT);
     else
+        fd = _open(path->narrow, _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
-        result = truncate(path->narrow, length);
     Py_END_ALLOW_THREADS
     if (result < 0)
         return path_error(path);
 
     Py_RETURN_NONE;
 }
-#endif /* HAVE_TRUNCATE */
+#endif /* HAVE_TRUNCATE || MS_WINDOWS */
 
 
 /* Issue #22396: On 32-bit AIX platform, the prototypes of os.posix_fadvise()
@@ -12771,7 +12798,7 @@
     "HAVE_FSTATVFS",
 #endif
 
-#ifdef HAVE_FTRUNCATE
+#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
     "HAVE_FTRUNCATE",
 #endif