pythonwindowsfilefile-permissionshidden-files

Making file hidden in Windows using Python blocks file writing


I am trying to make a hidden file in Windows using Python, but after I make it hidden it becomes impossible to write. However, reading this file is possible.

import ctypes

with open("test.txt", "r") as f: print('test')

ctypes.windll.kernel32.SetFileAttributesW("test.txt", 0x02) # adding h (hidden) attribute to a file

with open("test.txt", "r") as f: print('test') # works
with open("test.txt", "w") as f: print('test') # error

Error:

Traceback (most recent call last):
  File "C:\Users\user\Documents\client\test.py", line 8, in <module>
    with open("test.txt", "w") as f: print('test') # error
PermissionError: [Errno 13] Permission denied: 'test.txt'

Using 0x22 (which is a attribute and h attribute) instead of 0x02 doesnt't help. The same error occurs. After making the file visible again via attrib -H test.txt it becomes possible to open it in write mode. However, as far as I know, hidden files should be writable in Windows.

OS: Windows 11

Python version: Python 3.10.2


Solution

  • I haven't seen specific Python documentation on this behavior, but believe it is because at the implementation level, 'w' eventually is calling The Windows API CreateFileW which has this note:

    If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.

    Opening for writing mode='w' would eventually use CREATE_ALWAYS with FILE_ATTRIBUTE_NORMAL and would return access denied; whereas opening for reading and updating mode='r+' or appending mode='a' would work because OPEN_EXISTING would be used.

    Demonstrated below with Python:

    import os
    import ctypes
    
    FILE_ATTRIBUTE_HIDDEN = 0x2
    FILE_ATTRIBUTE_NORMAL = 0x80
    
    # Ensure file is deleted first for script repeatability.
    try:
        ctypes.windll.kernel32.SetFileAttributesW("test.txt", FILE_ATTRIBUTE_NORMAL)
        os.remove('text.txt')
    except Exception as e:
        pass
    
    # Create the file and hide it
    with open('test.txt', 'w') as f:
        print('test', file=f)
    ctypes.windll.kernel32.SetFileAttributesW("test.txt", FILE_ATTRIBUTE_HIDDEN)
    
    with open('test.txt', 'r') as f:  # read it
        print('read:', f.read())
    with open('test.txt', 'r+') as f:  # update it
        f.truncate()
        print('written', file=f)
    with open('test.txt', 'r') as f:  # read it back
        print('write:', f.read())
    

    Output:

    read: test
    
    write: written
    

    The command prompt works the same way:

    C:>attrib -h test.txt
    
    C:>del test.txt
    
    C:>echo line1 >test.txt
    
    C:>attrib +h test.txt
    
    C:>type test.txt
    line1
    
    C:>echo line2 >test.txt                 # can't be directly written
    Access is denied.
    
    C:>echo line2 >>test.txt                # CAN be appended (updated).
    
    C:>type test.txt
    line1
    line2