summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2025-06-28 09:11:42 +0200
committerHelmut Grohne <helmut@subdivi.de>2025-06-28 09:18:36 +0200
commit823b6754d68b01a6d30431411506cc9084560e68 (patch)
tree921b01e364b660d438231db2db8a65c748ac2023
parent7d6895d7ca0fe472c64964793b12f8ea536decde (diff)
downloadpython-linuxnamespaces-823b6754d68b01a6d30431411506cc9084560e68.tar.gz
change/improve populate_dev API
populate_dev may be used before unsharing a pid namespace with the intention of unsharing it. Then, /dev/pts should not be mounted and instead that mount needs to happen inside the newly created pid namespace. To allow for this usage, rename the pidns argument to pts and turn it into a literal. It may also be desired to have a /dev without pts, so add that option as well. It's a breaking change, but it does add clarity.
-rwxr-xr-xexamples/chrootfuse.py2
-rwxr-xr-xexamples/chroottar.py2
-rwxr-xr-xexamples/userchroot.py2
-rw-r--r--linuxnamespaces/__init__.py16
-rw-r--r--tests/test_simple.py2
5 files changed, 15 insertions, 9 deletions
diff --git a/examples/chrootfuse.py b/examples/chrootfuse.py
index 7e79a2e..d8ca965 100755
--- a/examples/chrootfuse.py
+++ b/examples/chrootfuse.py
@@ -85,7 +85,7 @@ def main() -> None:
os.chdir("/mnt")
linuxnamespaces.bind_mount("/proc", "proc", recursive=True)
linuxnamespaces.bind_mount("/sys", "sys", recursive=True)
- linuxnamespaces.populate_dev("/", ".", pidns=False, tun=False)
+ linuxnamespaces.populate_dev("/", ".", pts="host", tun=False)
if readonly:
linuxnamespaces.mount(
"tmpfs", "tmp", "tmpfs", linuxnamespaces.MountFlags.NODEV
diff --git a/examples/chroottar.py b/examples/chroottar.py
index 42c09ef..cf0f87e 100755
--- a/examples/chroottar.py
+++ b/examples/chroottar.py
@@ -83,7 +83,7 @@ def main() -> None:
os.chdir("/mnt")
linuxnamespaces.bind_mount("/proc", "proc", recursive=True)
linuxnamespaces.bind_mount("/sys", "sys", recursive=True)
- linuxnamespaces.populate_dev("/", ".", pidns=False, tun=False)
+ linuxnamespaces.populate_dev("/", ".", pts="host", tun=False)
linuxnamespaces.pivot_root(".", ".")
linuxnamespaces.umount(".", linuxnamespaces.UmountFlags.DETACH)
if args.command:
diff --git a/examples/userchroot.py b/examples/userchroot.py
index 2caea33..4006dc6 100755
--- a/examples/userchroot.py
+++ b/examples/userchroot.py
@@ -39,7 +39,7 @@ def main() -> None:
linuxnamespaces.bind_mount(chrootdir, "/mnt", recursive=True)
linuxnamespaces.bind_mount("/proc", "/mnt/proc", recursive=True)
linuxnamespaces.bind_mount("/sys", "/mnt/sys", recursive=True)
- linuxnamespaces.populate_dev("/", "/mnt", pidns=False)
+ linuxnamespaces.populate_dev("/", "/mnt", pts="host")
os.chdir("/mnt")
linuxnamespaces.pivot_root(".", ".")
linuxnamespaces.umount(".", linuxnamespaces.UmountFlags.DETACH)
diff --git a/linuxnamespaces/__init__.py b/linuxnamespaces/__init__.py
index a1f790e..83358b6 100644
--- a/linuxnamespaces/__init__.py
+++ b/linuxnamespaces/__init__.py
@@ -245,7 +245,7 @@ def populate_dev(
newroot: PathConvertible,
*,
fuse: bool = True,
- pidns: bool = True,
+ pts: typing.Literal["defer", "host", "new", "absent"] = "new",
tun: bool = True,
) -> None:
"""Mount a tmpfs to the dev directory beneath newroot and populate it with
@@ -255,6 +255,12 @@ def populate_dev(
Even though a CAP_SYS_ADMIN-enabled process can umount components of the
/dev hierarchy, they they cannot gain privileges in doing so as no
hierarchies are restricted via tmpfs mounts or read-only bind mounts.
+
+ The /dev/fuse and /dev/net/tun devices are optional and can be enabled or
+ disabled as desired. /dev/pts (and /dev/ptmx) can be shared with the host
+ or mounted as a new instance. Since a PID namespace is usually required for
+ mounting a new instance, it can also be deferred to a later manual mount.
+ If not desired, it can be left absent.
"""
origdev = AtLocation(origroot) / "dev"
newdev = AtLocation(newroot) / "dev"
@@ -278,9 +284,7 @@ def populate_dev(
bind_mounts["fuse"] = exitstack.enter_context(
open_tree(origdev / "fuse", OpenTreeFlags.CLONE)
)
- if pidns:
- symlinks["ptmx"] = "pts/ptmx"
- else:
+ if pts == "host":
bind_mounts["pts"] = exitstack.enter_context(
open_tree(
origdev / "pts",
@@ -291,6 +295,8 @@ def populate_dev(
bind_mounts["ptmx"] = exitstack.enter_context(
open_tree(origdev / "ptmx", OpenTreeFlags.CLONE)
)
+ elif pts != "absent":
+ symlinks["ptmx"] = "pts/ptmx"
if tun:
directories.add("net")
files.add("net/tun")
@@ -320,7 +326,7 @@ def populate_dev(
(newdev / fn).mknod(stat.S_IFREG)
for fn, target in symlinks.items():
(newdev / fn).symlink_to(target)
- if pidns:
+ if pts == "new":
mount(
"devpts",
newdev / "pts",
diff --git a/tests/test_simple.py b/tests/test_simple.py
index 38a253d..114b922 100644
--- a/tests/test_simple.py
+++ b/tests/test_simple.py
@@ -239,7 +239,7 @@ class UnshareTest(unittest.TestCase):
)
linuxnamespaces.mount("tmpfs", "/mnt", "tmpfs", data="mode=0755")
os.mkdir("/mnt/dev")
- linuxnamespaces.populate_dev("/", "/mnt", pidns=False)
+ linuxnamespaces.populate_dev("/", "/mnt", pts="host")
self.assertTrue(os.access("/mnt/dev/null", os.W_OK))
pathlib.Path("/mnt/dev/null").write_text("")