File metadata and permissions

os · Advanced · 12 min

Build tools, backups and security checks all decide what to do based on file metadata.

os.stat(path) returns a stat_result with the file's metadata:

  • st_size size in bytes
  • st_mtime last modification, st_atime last access, as float seconds since the epoch (st_mtime_ns gives exact integer nanoseconds)
  • st_mode file type and permission bits; st_uid, st_gid owner on Unix
  • st_ino and st_dev identify the file; two paths with equal pairs are the same file

os.lstat (or follow_symlinks=False) describes a symlink itself rather than its target.

The stat module decodes st_mode: stat.S_ISDIR(mode), stat.S_ISREG(mode), and stat.filemode(mode) which renders it like ls -l, e.g. -rw-r--r--. The permission bits are mode & 0o777.

os.chmod(path, 0o600) sets permissions; write them in octal: owner, group, others, each a sum of read 4, write 2, execute 1. Add a bit with mode | stat.S_IXUSR. On Windows, chmod can only toggle the read-only flag.

New files get 0o666 (or 0o777 for directories) minus the process umask, typically 0o022, which is why files usually come out rw-r--r--.

os.access(path, os.W_OK) asks whether the real user could write, but like any check-then-act it can race; prefer attempting the operation and handling PermissionError.

Example

import os
import stat
import time

with open("secret.txt", "w") as f:
    f.write("token=abc")

st = os.stat("secret.txt")
print(st.st_size)                     # 9
print(stat.S_ISREG(st.st_mode))       # True
print(abs(st.st_mtime - time.time()) < 5)  # True

os.chmod("secret.txt", 0o600)          # owner read/write only
mode = os.stat("secret.txt").st_mode
print(stat.filemode(mode))            # -rw-------
print(oct(mode & 0o777))              # 0o600

os.chmod("secret.txt", mode | stat.S_IXUSR)
print(stat.filemode(os.stat("secret.txt").st_mode))  # -rwx------

Remember

  • os.stat gives size, timestamps, mode, owner and identity of a file
  • Decode modes with the stat module; permissions are st_mode & 0o777
  • Write chmod modes in octal; new files are 0o666 minus the umask

Watch out for

  • Writing os.chmod(p, 600) in decimal; that is 0o1130, a nonsense mode. Use 0o600.

Check yourself

  1. Which mode string corresponds to os.chmod(path, 0o640)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/os.html#os.stat