classification
Title: Text representation of Windows' file attributes similar to stat.filemode()
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: eryksun, scrool
Priority: normal Keywords:

Created on 2020-05-16 11:44 by scrool, last changed 2020-05-17 09:34 by scrool.

Messages (4)
msg369041 - (view) Author: Pavol Babinčák (scrool) * Date: 2020-05-16 11:44
I'm using Windows and lets' say I have this directory structure listed with cmd:

> dir /A
...

16.05.20  11:15    <DIR>          directory
16.05.20  10:47                 0 hidden
16.05.20  11:25    <SYMLINK>      link [regular]
16.05.20  10:47                 0 readonly
16.05.20  11:15                 0 regular
16.05.20  10:48                 0 system

...

or PowerShell:

PS > dir -Force

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----          16.5.20     11:15                directory
-a-h--          16.5.20     10:47              0 hidden
-a---l          16.5.20     11:25              0 link
-ar---          16.5.20     10:47              0 readonly
-a----          16.5.20     11:15              0 regular
-a--s-          16.5.20     10:48              0 system

or attrib:

> attrib
A   H                hidden
A                    link
A    R               readonly
A                    regular
A  S                 system




I'd like to print file attributes in a text form. If I use stat.filemode():

>>> import os, stat
>>> print("\n".join(["{} {}".format(stat.filemode(os.stat(n).st_mode), n) for n in os.listdir('.')])) 
drwxrwxrwx directory
-rw-rw-rw- hidden
-rw-rw-rw- link
-r--r--r-- readonly
-rw-rw-rw- regular
-rw-rw-rw- system
>>>

not surprisingly I miss all windows attributes. On the top of that I get only values of stat.S_IWRITE and stat.S_IREAD as documented in os.chmod().


I'd like to have a new function, let's say fileattributes() which would behave like this:

>>> print("\n".join(["{} {}".format(stat.fileattributes(os.stat(n).st_file_attributes), n) for n in os.listdir('.')]))
d----- directory
-a-h-- hidden
-a---l link
-ar--- readonly
-a---- regular
-a--s- system
>>>

In this example I have used same format of attributes as in PowerShell because it is most similar to filemode().

I guess link cannot be currently identified with contants in stat module.
msg369071 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2020-05-16 22:09
There's no reason this can't be generalized to file attributes/flags on other platforms such as st_flags on BSD/macOS and st_attributes on Linux (depending on bpo-39533 -- but statx only returns a small subset of attributes that are available via chattr and lsattr).

Here's a table of approximately corresponding file attributes in BSD, Linux, and Windows:


BSD [1]         Linux                       Windows
------------------------------------------------------------------------
UF_NODUMP       STATX_ATTR_NODUMP -d
UF_APPEND       STATX_ATTR_APPEND -a
                STATX_ATTR_ENCRYPTED -E     FILE_ATTRIBUTE_ENCRYPTED -E
UF_COMPRESSED   STATX_ATTR_COMPRESSED -c    FILE_ATTRIBUTE_COMPRESSED -C
UF_IMMUTABLE    STATX_ATTR_IMMUTABLE -i     FILE_ATTRIBUTE_READONLY -R [2]
UF_NOUNLINK                                 FILE_ATTRIBUTE_READONLY -R [2]
UF_READONLY                                 FILE_ATTRIBUTE_READONLY -R
UF_HIDDEN                                   FILE_ATTRIBUTE_HIDDEN -H
UF_SYSTEM                                   FILE_ATTRIBUTE_SYSTEM -S
UF_ARCHIVE                                  FILE_ATTRIBUTE_ARCHIVE -A
UF_SPARSE                                   FILE_ATTRIBUTE_SPARSE_FILE -P
UF_REPARSE                                  FILE_ATTRIBUTE_REPARSE_POINT -L
UF_OFFLINE                                  FILE_ATTRIBUTE_OFFLINE -O
                                            
[1] Not supported on all BSD platforms, including macOS.
[2] Readonly applies to regular file data, not metadata or directory
    contents (index data). Also, it disallows unlink but allows rename.

> not surprisingly I miss all windows attributes. On the top of that I 
> get only values of stat.S_IWRITE and stat.S_IREAD as documented in
> os.chmod().

Windows doesn't implement a direct equivalent of the Unix file mode. But Windows file attributes partially overlap the Unix file mode for the S_IFMT filetype bits. In particular, WinAPI GetFileType classifies an open file based on the device type as one of FILE_TYPE_CHAR (S_IFCHR), FILE_TYPE_PIPE (S_IFIFO, S_IFSOCK), or FILE_TYPE_DISK (S_IFBLK, S_IFREG, S_IFDIR, S_IFLNK). For the latter, FILE_ATTRIBUTE_DIRECTORY and FILE_ATTRIBUTE_REPARSE_POINT distinguish S_IFDIR and reparse points, including S_IFLNK, depending on the reparse tag. The lack of either attribute indicates S_IFREG, and no support for file attributes indicates S_IFBLK. For example:

    >>> stat.filemode(os.stat('//./nul').st_mode) # S_IFCHR
    'c---------'
    >>> stat.filemode(os.stat('//./pipe').st_mode) # S_IFIFO
    'p---------'
    >>> stat.filemode(os.stat('//./C:').st_mode) # S_IFBLK
    'b---------'

(Apparently, we aren't fabricating any bogus permissions for the above cases.)

Free of charge, you also get a hack that sets the execute bit on directories, and also on files that have a file extension in the set {".COM", ".EXE", ".BAT", ".CMD"}. Caveat emptor: this has nothing to do with whether the file or directory actually grants the caller execute access.

    >>> stat.filemode(os.stat('C:/').st_mode) # S_IFDIR
    'drwxrwxrwx'
    >>> os.stat('C:/Temp/spam.bat').st_file_attributes & stat.FILE_ATTRIBUTE_READONLY
    1
    >>> os.readlink('C:/Temp/symlink.bat')
    'spam.bat'
    >>> stat.filemode(os.lstat('C:/Temp/symlink.bat').st_mode) # S_IFLNK
    'lrwxrwxrwx'
    >>> stat.filemode(os.stat('C:/Temp/symlink.bat').st_mode) # S_IFREG
    '-r-xr-xr-x'

Regarding the permission mode bits, many environments (including Python) misuse FILE_ATTRIBUTE_READONLY as a write permission, i.e. readonly removes the S_IWUSR | S_IWGRP | S_IWOTH bits. Certainly readonly should be a factor in os.access(), but it is not a permission; no one can be granted permission to write to a readonly file. Using it as such is inconsistent with UF_IMMUTABLE in BSD and STATX_ATTR_IMMUTABLE in Linux. It's also inconsistent with how write permission works in Unix, since readonly disallows deleting the file, which has nothing to do with write permission on a file in Unix.
msg369108 - (view) Author: Pavol Babinčák (scrool) * Date: 2020-05-17 09:33
Thanks for exhaustive response!

The way how I understand this is it might be beneficial to extend request to get text representation of file attributes on other architectures.

I didn't know about statx() syscall. From what I can tell there is no binary, similar to 'stat', that would print attributes of a file in a text form.

I guess there is no common way to print those attributes from statx()?

---

I see that filemode() works consistently across platforms even on Windows. I guess I could use this everywhere:

stat.filemode(stat.S_IFMT(os.lstat(n).st_mode)

---

And finally modes on Windows won't be ever complete in filemode() because of lack of file modes. I still have some approximation with users' mode (rwx):

format(stat.filemode(stat.S_IMODE(os.lstat(n).st_mode))[1:4]

To get:

r - as readable file or link
w - as not readonly
x - executable by file extension

---

Is that accurate summary? I'm wondering if it would make sense to rename this issue to leave out Windows?
msg369109 - (view) Author: Pavol Babinčák (scrool) * Date: 2020-05-17 09:34
In a second example I meant:

stat.filemode(stat.S_IMODE(os.lstat(n).st_mode))[1:4]
History
Date User Action Args
2020-05-17 09:34:38scroolsetmessages: + msg369109
2020-05-17 09:33:17scroolsetmessages: + msg369108
2020-05-16 22:09:58eryksunsetnosy: + eryksun
messages: + msg369071
2020-05-16 11:44:47scroolcreate