mirror of https://github.com/python/cpython
gh-113191: Add support of os.fchmod() on Windows (GH-113192)
Also support a file descriptor in os.chmod().
This commit is contained in:
parent
53330f1677
commit
1f06baeabd
|
@ -1001,11 +1001,14 @@ as internal buffering of data.
|
|||
|
||||
.. audit-event:: os.chmod path,mode,dir_fd os.fchmod
|
||||
|
||||
.. availability:: Unix.
|
||||
.. availability:: Unix, Windows.
|
||||
|
||||
The function is limited on Emscripten and WASI, see
|
||||
:ref:`wasm-availability` for more information.
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
Added support on Windows.
|
||||
|
||||
|
||||
.. function:: fchown(fd, uid, gid)
|
||||
|
||||
|
@ -2077,7 +2080,8 @@ features:
|
|||
Accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
Added support for the *follow_symlinks* argument on Windows.
|
||||
Added support for a file descriptor and the *follow_symlinks* argument
|
||||
on Windows.
|
||||
|
||||
|
||||
.. function:: chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True)
|
||||
|
|
|
@ -289,6 +289,10 @@ os
|
|||
``False`` on Windows.
|
||||
(Contributed by Serhiy Storchaka in :gh:`59616`)
|
||||
|
||||
* Add support of :func:`os.fchmod` and a file descriptor
|
||||
in :func:`os.chmod` on Windows.
|
||||
(Contributed by Serhiy Storchaka in :gh:`113191`)
|
||||
|
||||
* :func:`os.posix_spawn` now accepts ``env=None``, which makes the newly spawned
|
||||
process use the current process environment.
|
||||
(Contributed by Jakub Kulik in :gh:`113119`.)
|
||||
|
|
|
@ -131,6 +131,7 @@ if _exists("_have_functions"):
|
|||
_set = set()
|
||||
_add("HAVE_FCHDIR", "chdir")
|
||||
_add("HAVE_FCHMOD", "chmod")
|
||||
_add("MS_WINDOWS", "chmod")
|
||||
_add("HAVE_FCHOWN", "chown")
|
||||
_add("HAVE_FDOPENDIR", "listdir")
|
||||
_add("HAVE_FDOPENDIR", "scandir")
|
||||
|
|
|
@ -936,6 +936,7 @@ class PosixTester(unittest.TestCase):
|
|||
posix.utime(os_helper.TESTFN, (now, now))
|
||||
|
||||
def check_chmod(self, chmod_func, target, **kwargs):
|
||||
closefd = not isinstance(target, int)
|
||||
mode = os.stat(target).st_mode
|
||||
try:
|
||||
new_mode = mode & ~(stat.S_IWOTH | stat.S_IWGRP | stat.S_IWUSR)
|
||||
|
@ -943,7 +944,7 @@ class PosixTester(unittest.TestCase):
|
|||
self.assertEqual(os.stat(target).st_mode, new_mode)
|
||||
if stat.S_ISREG(mode):
|
||||
try:
|
||||
with open(target, 'wb+'):
|
||||
with open(target, 'wb+', closefd=closefd):
|
||||
pass
|
||||
except PermissionError:
|
||||
pass
|
||||
|
@ -951,10 +952,10 @@ class PosixTester(unittest.TestCase):
|
|||
chmod_func(target, new_mode, **kwargs)
|
||||
self.assertEqual(os.stat(target).st_mode, new_mode)
|
||||
if stat.S_ISREG(mode):
|
||||
with open(target, 'wb+'):
|
||||
with open(target, 'wb+', closefd=closefd):
|
||||
pass
|
||||
finally:
|
||||
posix.chmod(target, mode)
|
||||
chmod_func(target, mode)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_chmod_file(self):
|
||||
|
@ -971,6 +972,12 @@ class PosixTester(unittest.TestCase):
|
|||
target = self.tempdir()
|
||||
self.check_chmod(posix.chmod, target)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_fchmod_file(self):
|
||||
with open(os_helper.TESTFN, 'wb+') as f:
|
||||
self.check_chmod(posix.fchmod, f.fileno())
|
||||
self.check_chmod(posix.chmod, f.fileno())
|
||||
|
||||
@unittest.skipUnless(hasattr(posix, 'lchmod'), 'test needs os.lchmod()')
|
||||
def test_lchmod_file(self):
|
||||
self.check_chmod(posix.lchmod, os_helper.TESTFN)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add support of :func:`os.fchmod` and a file descriptor in :func:`os.chmod`
|
||||
on Windows.
|
|
@ -601,7 +601,7 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
#if defined(HAVE_FCHMOD)
|
||||
#if (defined(HAVE_FCHMOD) || defined(MS_WINDOWS))
|
||||
|
||||
PyDoc_STRVAR(os_fchmod__doc__,
|
||||
"fchmod($module, /, fd, mode)\n"
|
||||
|
@ -676,7 +676,7 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
#endif /* defined(HAVE_FCHMOD) */
|
||||
#endif /* (defined(HAVE_FCHMOD) || defined(MS_WINDOWS)) */
|
||||
|
||||
#if (defined(HAVE_LCHMOD) || defined(MS_WINDOWS))
|
||||
|
||||
|
@ -12422,4 +12422,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|||
#ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
|
||||
#define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
|
||||
#endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */
|
||||
/*[clinic end generated code: output=1be15e60a553b40d input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=b82391c4f58231b6 input=a9049054013a1b77]*/
|
||||
|
|
|
@ -2855,6 +2855,8 @@ FTRUNCATE
|
|||
#ifdef MS_WINDOWS
|
||||
#undef PATH_HAVE_FTRUNCATE
|
||||
#define PATH_HAVE_FTRUNCATE 1
|
||||
#undef PATH_HAVE_FCHMOD
|
||||
#define PATH_HAVE_FCHMOD 1
|
||||
#endif
|
||||
|
||||
/*[python input]
|
||||
|
@ -3332,7 +3334,38 @@ win32_lchmod(LPCWSTR path, int mode)
|
|||
}
|
||||
return SetFileAttributesW(path, attr);
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
|
@ -3395,27 +3428,16 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
|
|||
#ifdef MS_WINDOWS
|
||||
result = 0;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (follow_symlinks) {
|
||||
HANDLE hfile;
|
||||
FILE_BASIC_INFO info;
|
||||
|
||||
hfile = CreateFileW(path->wide,
|
||||
FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
|
||||
0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
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) {
|
||||
if (GetFileInformationByHandleEx(hfile, FileBasicInfo,
|
||||
&info, sizeof(info)))
|
||||
{
|
||||
if (mode & _S_IWRITE) {
|
||||
info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
else {
|
||||
info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
result = SetFileInformationByHandle(hfile, FileBasicInfo,
|
||||
&info, sizeof(info));
|
||||
}
|
||||
result = win32_hchmod(hfile, mode);
|
||||
(void)CloseHandle(hfile);
|
||||
}
|
||||
}
|
||||
|
@ -3511,7 +3533,7 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
|
|||
}
|
||||
|
||||
|
||||
#ifdef HAVE_FCHMOD
|
||||
#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS)
|
||||
/*[clinic input]
|
||||
os.fchmod
|
||||
|
||||
|
@ -3533,12 +3555,21 @@ os_fchmod_impl(PyObject *module, int fd, int mode)
|
|||
/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/
|
||||
{
|
||||
int res;
|
||||
int async_err = 0;
|
||||
|
||||
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);
|
||||
|
@ -3546,10 +3577,11 @@ os_fchmod_impl(PyObject *module, int fd, int mode)
|
|||
} 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 */
|
||||
#endif /* HAVE_FCHMOD || MS_WINDOWS */
|
||||
|
||||
|
||||
#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS)
|
||||
|
|
Loading…
Reference in New Issue