10000 gh-113191: Add support of os.fchmod() on Windows (GH-113192) · python/cpython@1f06bae · GitHub
[go: up one dir, main page]

Skip to content

Commit 1f06bae

Browse files
gh-113191: Add support of os.fchmod() on Windows (GH-113192)
Also support a file descriptor in os.chmod().
1 parent 53330f1 commit 1f06bae

File tree

7 files changed

+82
-32
lines changed

7 files changed

+82
-32
lines changed

Doc/library/os.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,11 +1001,14 @@ as internal buffering of data.
10011001

10021002
.. audit-event:: os.chmod path,mode,dir_fd os.fchmod
10031003

1004-
.. availability:: Unix.
1004+
.. availability:: Unix, Windows.
10051005

10061006
The function is limited on Emscripten and WASI, see
10071007
:ref:`wasm-availability` for more information.
10081008

1009+
.. versionchanged:: 3.13
1010+
Added support on Windows.
1011+
10091012

10101013
.. function:: fchown(fd, uid, gid)
10111014

@@ -2077,7 +2080,8 @@ features:
20772080
Accepts a :term:`path-like object`.
20 10000 782081

20792082
.. versionchanged:: 3.13
2080-
Added support for the *follow_symlinks* argument on Windows.
2083+
Added support for a file descriptor and the *follow_symlinks* argument
2084+
on Windows.
20812085

20822086

20832087
.. function:: chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True)

Doc/whatsnew/3.13.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ os
289289
``False`` on Windows.
290290
(Contributed by Serhiy Storchaka in :gh:`59616`)
291291

292+
* Add support of :func:`os.fchmod` and a file descriptor
293+
in :func:`os.chmod` on Windows.
294+
(Contributed by Serhiy Storchaka in :gh:`113191`)
295+
292296
* :func:`os.posix_spawn` now accepts ``env=None``, which makes the newly spawned
293297
process use the current process environment.
294298
(Contributed by Jakub Kulik in :gh:`113119`.)

Lib/os.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def _add(str, fn):
131131
_set = set()
132132
_add("HAVE_FCHDIR", "chdir")
133133
_add("HAVE_FCHMOD", "chmod")
134+
_add("MS_WINDOWS", "chmod")
134135
_add("HAVE_FCHOWN", "chown")
135136
_add("HAVE_FDOPENDIR", "listdir")
136137
_add("HAVE_FDOPENDIR", "scandir")

Lib/test/test_posix.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -936,25 +936,26 @@ def test_utime(self):
936936
posix.utime(os_helper.TESTFN, (now, now))
937937

938938
def check_chmod(self, chmod_func, target, **kwargs):
939+
closefd = not isinstance(target, int)
939940
mode = os.stat(target).st_mode
940941
try:
941942
new_mode = mode & ~(stat.S_IWOTH | stat.S_IWGRP | stat.S_IWUSR)
942943
chmod_func(target, new_mode, **kwargs)
943944
self.assertEqual(os.stat(target).st_mode, new_mode)
944945
if stat.S_ISREG(mode):
945946
try:
946-
with open(target, 'wb+'):
947+
with open(target, 'wb+', closefd=closefd):
947948
pass
948949
except PermissionError:
949950
pass
950951
new_mode = mode | (stat.S_IWOTH | stat.S_IWGRP | stat.S_IWUSR)
951952
chmod_func(target, new_mode, **kwargs)
952953
self.assertEqual(os.stat(target).st_mode, new_mode)
953954
if stat.S_ISREG(mode):
954-
with open(target, 'wb+'):
955+
with open(target, 'wb+', closefd=closefd):
955956
pass
956957
finally:
957-
posix.chmod(target, mode)
958+
chmod_func(target, mode)
958959

959960
@os_helper.skip_unless_working_chmod
960961
def test_chmod_file(self):
@@ -971,6 +972,12 @@ def test_chmod_dir(self):
971972
target = self.tempdir()
972973
self.check_chmod(posix.chmod, target)
973974

975+
@os_helper.skip_unless_working_chmod
976+
def test_fchmod_file(self):
977+
with open(os_helper.TESTFN, 'wb+') as f:
978+
self.check_chmod(posix.fchmod, f.fileno())
979+
self.check_chmod(posix.chmod, f.fileno())
980+
974981
@unittest.skipUnless(hasattr(posix, 'lchmod'), 'test needs os.lchmod()')
975982
def test_lchmod_file(self):
976983
self.check_chmod(posix.lchmod, os_helper.TESTFN)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support of :func:`os.fchmod` and a file descriptor in :func:`os.chmod`
2+
on Windows.

Modules/clinic/posixmodule.c.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,6 +2855,8 @@ FTRUNCATE
28552855
#ifdef MS_WINDOWS
28562856
#undef PATH_HAVE_FTRUNCATE
28572857
#define PATH_HAVE_FTRUNCATE 1
2858+
#undef PATH_HAVE_FCHMOD
2859+
#define PATH_HAVE_FCHMOD 1
28582860
#endif
28592861

28602862
/*[python input]
@@ -3332,7 +3334,38 @@ win32_lchmod(LPCWSTR path, int mode)
33323334
}
33333335
return SetFileAttributesW(path, attr);
33343336
}
3335-
#endif
3337+
3338+
static int
3339+
win32_hchmod(HANDLE hfile, int mode)
3340+
{
3341+
FILE_BASIC_INFO info;
3342+
if (!GetFileInformationByHandleEx(hfile, FileBasicInfo,
3343+
&info, sizeof(info)))
3344+
{
3345+
return 0;
3346+
}
3347+
if (mode & _S_IWRITE) {
3348+
info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
3349+
}
3350+
else {
3351+
info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
3352+
}
3353+
return SetFileInformationByHandle(hfile, FileBasicInfo,
3354+
&info, sizeof(info));
3355+
}
3356+
3357+
static int
3358+
win32_fchmod(int fd, int mode)
3359+
{
3360+
HANDLE hfile = _Py_get_osfhandle_noraise(fd);
3361+
if (hfile == INVALID_HANDLE_VALUE) {
3362+
SetLastError(ERROR_INVALID_HANDLE);
3363+
return 0;
3364+
}
3365+
return win32_hchmod(hfile, mode);
3366+
}
3367+
3368+
#endif /* MS_WINDOWS */
33363369

33373370
/*[clinic input]
33383371
os.chmod
@@ -3395,27 +3428,16 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
33953428
#ifdef MS_WINDOWS
33963429
result = 0;
33973430
Py_BEGIN_ALLOW_THREADS
3398-
if (follow_symlinks) {
3399-
HANDLE hfile;
3400-
FILE_BASIC_INFO info;
3401-
3402-
hfile = CreateFileW(path->wide,
3403-
FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
3404-
0, NULL,
3405-
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
3431+
if (path->fd != -1) {
3432+
result = win32_fchmod(path->fd, mode);
3433+
}
3434+
else if (follow_symlinks) {
3435+
HANDLE hfile = CreateFileW(path->wide,
3436+
FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
3437+
0, NULL,
3438+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
34063439
if (hfile != INVALID_HANDLE_VALUE) {
3407-
if (GetFileInformationByHandleEx(hfile, FileBasicInfo,
3408-
&info, sizeof(info)))
3409-
{
3410-
if (mode & _S_IWRITE) {
3411-
info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
3412-
}
3413-
else {
3414-
info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
3415-
}
3416-
result = SetFileInformationByHandle(hfile, FileBasicInfo,
3417-
&info, sizeof(info));
3418-
}
3440+
result = win32_hchmod(hfile, mode);
34193441
(void)CloseHandle(hfile);
34203442
}
34213443
}
@@ -3511,7 +3533,7 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
35113533
}
35123534

35133535

3514-
#ifdef HAVE_FCHMOD
3536+
#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS)
35153537
/*[clinic input]
35163538
os.fchmod
35173539
@@ -3533,23 +3555,33 @@ os_fchmod_impl(PyObject *module, int fd, int mode)
35333555
/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/
35343556
{
35353557
int res;
3536-
int async_err = 0;
35373558

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

3563+
#ifdef MS_WINDOWS
3564+
res = 0;
3565+
Py_BEGIN_ALLOW_THREADS
3566+
res = win32_fchmod(fd, mode);
3567+
Py_END_ALLOW_THREADS
3568+
if (!res) {
3569+
return PyErr_SetFromWindowsErr(0);
3570+
}
3571+
#else /* MS_WINDOWS */
3572+
int async_err = 0;
35423573
do {
35433574
Py_BEGIN_ALLOW_THREADS
35443575
res = fchmod(fd, mode);
35453576
Py_END_ALLOW_THREADS
35463577
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
35473578
if (res != 0)
35483579
return (!async_err) ? posix_error() : NULL;
3580+
#endif /* MS_WINDOWS */
35493581

35503582
Py_RETURN_NONE;
35513583
}
3552-
#endif /* HAVE_FCHMOD */
3584+
#endif /* HAVE_FCHMOD || MS_WINDOWS */
35533585

35543586

35553587
#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS)

0 commit comments

Comments
 (0)
0