summaryrefslogtreecommitdiff
path: root/linuxnamespaces/syscalls.py
diff options
context:
space:
mode:
Diffstat (limited to 'linuxnamespaces/syscalls.py')
-rw-r--r--linuxnamespaces/syscalls.py117
1 files changed, 117 insertions, 0 deletions
diff --git a/linuxnamespaces/syscalls.py b/linuxnamespaces/syscalls.py
index 0e33a44..338e602 100644
--- a/linuxnamespaces/syscalls.py
+++ b/linuxnamespaces/syscalls.py
@@ -112,6 +112,123 @@ class MountFlags(enum.IntFlag):
PROPAGATION_FLAGS = UNBINDABLE | PRIVATE | SLAVE | SHARED
+ # Map each flag to:
+ # * The flag value
+ # * Whether the flag value is negated
+ # * Whether the flag must be negated
+ # * Whether the flag can be negated
+ __flagstrmap = {
+ "acl": (POSIXACL, False, False, False),
+ "async": (SYNCHRONOUS, True, False, False),
+ "atime": (NOATIME, True, False, True),
+ "bind": (BIND, False, False, False),
+ "dev": (NODEV, True, False, True),
+ "diratime": (NODIRATIME, True, False, True),
+ "dirsync": (DIRSYNC, False, False, False),
+ "exec": (NOEXEC, True, False, True),
+ "iversion": (I_VERSION, False, False, True),
+ "lazytime": (LAZYTIME, False, False, True),
+ "loud": (SILENT, True, False, False),
+ "mand": (MANDLOCK, False, False, True),
+ "private": (PRIVATE, False, False, False),
+ "rbind": (BIND | REC, False, False, False),
+ "relatime": (RELATIME, False, False, True),
+ "remount": (REMOUNT, False, False, True),
+ "ro": (RDONLY, False, False, False),
+ "rprivate": (PRIVATE | REC, False, False, False),
+ "rshared": (SHARED | REC, False, False, False),
+ "rslave": (SLAVE | REC, False, False, False),
+ "runbindable": (UNBINDABLE | REC, False, False, False),
+ "rw": (RDONLY, True, False, False),
+ "shared": (SHARED, False, False, False),
+ "silent": (SILENT, False, False, False),
+ "slave": (SLAVE, False, False, False),
+ "strictatime": (STRICTATIME, False, False, True),
+ "suid": (NOSUID, True, False, True),
+ "symfollow": (NOSYMFOLLOW, True, False, True),
+ "sync": (SYNCHRONOUS, False, False, False),
+ "unbindable": (UNBINDABLE, False, False, False),
+ }
+
+ def change(self, flagsstr: str) -> "MountFlags":
+ """Return modified mount flags after applying comma-separated mount
+ flags represented as a str. Raise a ValueError if any given flag
+ does not correspond to a textual mount flag.
+ """
+ ret = self
+ for flagstr in flagsstr.split(","):
+ if not flagstr:
+ continue
+ flag, negated, mustnegate, cannegate = self.__flagstrmap.get(
+ flagstr.removeprefix("no"),
+ (MountFlags.NONE, False, True, False),
+ )
+ if mustnegate <= flagstr.startswith("no") <= cannegate:
+ if negated ^ flagstr.startswith("no"):
+ ret &= ~flag
+ else:
+ if flag & MountFlags.PROPAGATION_FLAGS:
+ ret &= ~MountFlags.PROPAGATION_FLAGS
+ ret |= flag
+ else:
+ raise ValueError(f"not a valid mount flag: {flagstr!r}")
+ return ret
+
+ @staticmethod
+ def fromstr(flagsstr: str) -> "MountFlags":
+ """Construct mount flags by changing flags according to the passed
+ flagsstr using the change method on an initial value with all flags
+ cleared.
+ """
+ return MountFlags.NONE.change(flagsstr)
+
+ __flagvals: list[tuple[int, str]] = sorted(
+ [
+ (RDONLY, "ro"),
+ (NOSUID, "nosuid"),
+ (NODEV, "nodev"),
+ (NOEXEC, "noexec"),
+ (SYNCHRONOUS, "sync"),
+ (REMOUNT, "remount"),
+ (MANDLOCK, "mand"),
+ (DIRSYNC, "dirsync"),
+ (NOSYMFOLLOW, "nosymfollow"),
+ (NOATIME, "noatime"),
+ (NODIRATIME, "nodiratime"),
+ (BIND, "bind"),
+ (BIND | REC, "rbind"),
+ (SILENT, "silent"),
+ (POSIXACL, "acl"),
+ (UNBINDABLE, "unbindable"),
+ (UNBINDABLE | REC, "runbindable"),
+ (PRIVATE, "private"),
+ (PRIVATE | REC, "rprivate"),
+ (SLAVE, "slave"),
+ (SLAVE | REC, "rslave"),
+ (SHARED, "shared"),
+ (SHARED | REC, "rshared"),
+ (RELATIME, "relatime"),
+ (I_VERSION, "iversion"),
+ (STRICTATIME, "strictatime"),
+ (LAZYTIME, "lazytime"),
+ ],
+ reverse=True
+ )
+
+ def tostr(self) -> str:
+ """Attempt to represent the flags in a comma-separated, textual way."""
+ if (self & MountFlags.PROPAGATION_FLAGS).bit_count() > 1:
+ raise ValueError("cannot represent conflicting propagtion flags")
+ parts: list[str] = []
+ remain = self
+ for val, text in MountFlags.__flagvals:
+ if remain & val == val:
+ parts.insert(0, text)
+ remain &= ~val
+ if remain:
+ raise ValueError("cannot represent flags {remain}")
+ return ",".join(parts)
+
class MountSetattrFlags(enum.IntFlag):
"""This value may be supplied as flags to mount_setattr(2)."""