@@ -3309,6 +3309,29 @@ os_fchdir_impl(PyObject *module, int fd)
33093309}
33103310#endif /* HAVE_FCHDIR */
33113311
3312+ #ifdef MS_WINDOWS
3313+ # define CHMOD_DEFAULT_FOLLOW_SYMLINKS 0
3314+ #else
3315+ # define CHMOD_DEFAULT_FOLLOW_SYMLINKS 1
3316+ #endif
3317+
3318+ #ifdef MS_WINDOWS
3319+ static int
3320+ win32_lchmod (LPCWSTR path , int mode )
3321+ {
3322+ DWORD attr = GetFileAttributesW (path );
3323+ if (attr == INVALID_FILE_ATTRIBUTES ) {
3324+ return 0 ;
3325+ }
3326+ if (mode & _S_IWRITE ) {
3327+ attr &= ~FILE_ATTRIBUTE_READONLY ;
3328+ }
3329+ else {
3330+ attr |= FILE_ATTRIBUTE_READONLY ;
3331+ }
3332+ return SetFileAttributesW (path , attr );
3333+ }
3334+ #endif
33123335
33133336/*[clinic input]
33143337os.chmod
@@ -3331,7 +3354,8 @@ os.chmod
33313354 and path should be relative; path will then be relative to that
33323355 directory.
33333356
3334- follow_symlinks: bool = True
3357+ follow_symlinks: bool(c_default="CHMOD_DEFAULT_FOLLOW_SYMLINKS", \
3358+ py_default="(os.name != 'nt')") = CHMOD_DEFAULT_FOLLOW_SYMLINKS
33353359 If False, and the last element of the path is a symbolic link,
33363360 chmod will modify the symbolic link itself instead of the file
33373361 the link points to.
@@ -3348,20 +3372,16 @@ dir_fd and follow_symlinks may not be implemented on your platform.
33483372static PyObject *
33493373os_chmod_impl (PyObject * module , path_t * path , int mode , int dir_fd ,
33503374 int follow_symlinks )
3351- /*[clinic end generated code: output=5cf6a94915cc7bff input=674a14bc998de09d ]*/
3375+ /*[clinic end generated code: output=5cf6a94915cc7bff input=fcf115d174b9f3d8 ]*/
33523376{
33533377 int result ;
33543378
3355- #ifdef MS_WINDOWS
3356- DWORD attr ;
3357- #endif
3358-
33593379#ifdef HAVE_FCHMODAT
33603380 int fchmodat_nofollow_unsupported = 0 ;
33613381 int fchmodat_unsupported = 0 ;
33623382#endif
33633383
3364- #if !(defined(HAVE_FCHMODAT ) || defined(HAVE_LCHMOD ))
3384+ #if !(defined(HAVE_FCHMODAT ) || defined(HAVE_LCHMOD ) || defined( MS_WINDOWS ) )
33653385 if (follow_symlinks_specified ("chmod" , follow_symlinks ))
33663386 return NULL ;
33673387#endif
@@ -3372,19 +3392,36 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
33723392 }
33733393
33743394#ifdef MS_WINDOWS
3395+ result = 0 ;
33753396 Py_BEGIN_ALLOW_THREADS
3376- attr = GetFileAttributesW (path -> wide );
3377- if (attr == INVALID_FILE_ATTRIBUTES )
3378- result = 0 ;
3397+ if (follow_symlinks ) {
3398+ HANDLE hfile ;
3399+ FILE_BASIC_INFO info ;
3400+
3401+ hfile = CreateFileW (path -> wide ,
3402+ FILE_READ_ATTRIBUTES |FILE_WRITE_ATTRIBUTES ,
3403+ 0 , NULL ,
3404+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
3405+ if (hfile != INVALID_HANDLE_VALUE ) {
3406+ if (GetFileInformationByHandleEx (hfile , FileBasicInfo ,
3407+ & info , sizeof (info )))
3408+ {
3409+ if (mode & _S_IWRITE ) {
3410+ info .FileAttributes &= ~FILE_ATTRIBUTE_READONLY ;
3411+ }
3412+ else {
3413+ info .FileAttributes |= FILE_ATTRIBUTE_READONLY ;
3414+ }
3415+ result = SetFileInformationByHandle (hfile , FileBasicInfo ,
3416+ & info , sizeof (info ));
3417+ }
3418+ (void )CloseHandle (hfile );
3419+ }
3420+ }
33793421 else {
3380- if (mode & _S_IWRITE )
3381- attr &= ~FILE_ATTRIBUTE_READONLY ;
3382- else
3383- attr |= FILE_ATTRIBUTE_READONLY ;
3384- result = SetFileAttributesW (path -> wide , attr );
3422+ result = win32_lchmod (path -> wide , mode );
33853423 }
33863424 Py_END_ALLOW_THREADS
3387-
33883425 if (!result ) {
33893426 return path_error (path );
33903427 }
@@ -3514,7 +3551,7 @@ os_fchmod_impl(PyObject *module, int fd, int mode)
35143551#endif /* HAVE_FCHMOD */
35153552
35163553
3517- #ifdef HAVE_LCHMOD
3554+ #if defined( HAVE_LCHMOD ) || defined( MS_WINDOWS )
35183555/*[clinic input]
35193556os.lchmod
35203557
@@ -3535,16 +3572,26 @@ os_lchmod_impl(PyObject *module, path_t *path, int mode)
35353572 if (PySys_Audit ("os.chmod" , "Oii" , path -> object , mode , -1 ) < 0 ) {
35363573 return NULL ;
35373574 }
3575+ #ifdef MS_WINDOWS
3576+ Py_BEGIN_ALLOW_THREADS
3577+ res = win32_lchmod (path -> wide , mode );
3578+ Py_END_ALLOW_THREADS
3579+ if (!res ) {
3580+ path_error (path );
3581+ return NULL ;
3582+ }
3583+ #else /* MS_WINDOWS */
35383584 Py_BEGIN_ALLOW_THREADS
35393585 res = lchmod (path -> narrow , mode );
35403586 Py_END_ALLOW_THREADS
35413587 if (res < 0 ) {
35423588 path_error (path );
35433589 return NULL ;
35443590 }
3591+ #endif /* MS_WINDOWS */
35453592 Py_RETURN_NONE ;
35463593}
3547- #endif /* HAVE_LCHMOD */
3594+ #endif /* HAVE_LCHMOD || MS_WINDOWS */
35483595
35493596
35503597#ifdef HAVE_CHFLAGS
0 commit comments