[Piuparts-devel] [Git][debian/piuparts][develop] 10 commits: improve compatibility of mount_proc with unprivileged namespaces

Nicolas Dandrimont (@olasd) gitlab at salsa.debian.org
Wed Nov 8 21:16:33 GMT 2023



Nicolas Dandrimont pushed to branch develop at Debian / piuparts


Commits:
4d436fc3 by Helmut Grohne at 2023-11-08T08:55:34+01:00
improve compatibility of mount_proc with unprivileged namespaces

When run in an unprivileged namespace, both the mknod and the mount
operation may be denied and piuparts may fail here. Looking deeper, this
branch happens when /dev/ptmx is not already a symlink and what is bind
mounted can be expressed as a symlink, so opt for implementing this
device as a symlink when it is missing entirely. If nothing else, one
less mount invocation is a minor speedup.

- - - - -
b7f4db99 by Helmut Grohne at 2023-11-08T09:00:43+01:00
make --bindmount recursive

When performing a bind mount, Linux will not do so recursively by
default. So if a subdirectory of the provided bind mount is a mount
point itself, this mount point will not be propagated and the underlying
hierarchy will be exposed inside piuparts. This may be intentional, but
likely is not.

Since a bind mount may be used to access a hierarchy that was hidden by
another mount, bind mounts are denied in unprivileged namespaces. On the
other hand, recursive bind mounts just work there.

- - - - -
13d93cee by Helmut Grohne at 2023-11-08T09:07:14+01:00
allow using --bindmount with things that are not directories

While typically think of directories when it comes to mounting, one can
also mount regular files or devices. Doing so only works when the mount
target is not a directory though and piuparts kindly creates such
directories. So when the mount options reveal that we're doing a bind
mount and the source is not a directory, we create an empty file
instead.

- - - - -
eb94e796 by Helmut Grohne at 2023-11-08T12:03:18+01:00
allow using tarballs compressed with non-gzip

The extension of the filename given to --basetgz with or without --save
will be checked for known compression formats by tar. Therefore gzip
tarballs must now have a suffix of .gz or .tgz.

- - - - -
d78f6457 by Helmut Grohne at 2023-11-08T14:01:24+01:00
refactor the opts parameter to mount into a list

Reported-by: Nicolas Dandrimont <nicolas at dandrimont.eu>

- - - - -
cdd25f5e by Nicolas Dandrimont at 2023-11-08T13:35:20+00:00
Explain why we bind mount ptmx when it exists
- - - - -
bd46522f by Nicolas Dandrimont at 2023-11-08T22:11:54+01:00
Merge branch 'helmutg/feature-tar-compression' into develop

- - - - -
4c861838 by Nicolas Dandrimont at 2023-11-08T22:12:09+01:00
Merge branch 'helmutg/improve-mount_proc' into develop

- - - - -
9e1fc883 by Nicolas Dandrimont at 2023-11-08T22:12:22+01:00
Merge branch 'helmutg/feature-recursive-bindmounts' into develop

- - - - -
929b4a4a by Nicolas Dandrimont at 2023-11-08T22:14:19+01:00
Merge branch 'helmutg/feature-bindmount-file' into develop

- - - - -


2 changed files:

- docs/piuparts/piuparts.1.txt
- piuparts.py


Changes:

=====================================
docs/piuparts/piuparts.1.txt
=====================================
@@ -61,7 +61,7 @@ Options must come before the other command line arguments.
 
   Use :file:`tarball` as the contents of the initial chroot, instead of building a new one with :program:`debootstrap`.
 
-  The tarball can be created with the :option:`-s` option, or you can use one that :program:`pbuilder` has created (see :option:`-p`). If you create one manually, make sure the root of the chroot is the root of the tarball.
+  The tarball can be created with the :option:`-s` option, or you can use one that :program:`pbuilder` has created (see :option:`-p`). If you create one manually, make sure the root of the chroot is the root of the tarball. Despite the option name implying gzip compression, the compression scheme is deduced by :program:`tar` from the filename suffix.
 
 .. option:: --bindmount dir
 


=====================================
piuparts.py
=====================================
@@ -893,7 +893,7 @@ class Chroot:
         cleanup_tmpfile = lambda: os.remove(tmpfile)
         panic_handler_id = do_on_panic(cleanup_tmpfile)
 
-        run(['tar', '-czf', tmpfile, '--one-file-system', '--exclude', 'tmp/scripts', '-C', self.name, './'])
+        run(['tar', '--auto-compress', '-cf', tmpfile, '--one-file-system', '--exclude', 'tmp/scripts', '-C', self.name, './'])
 
         os.chmod(tmpfile, 0o644)
         os.rename(tmpfile, result)
@@ -905,7 +905,7 @@ class Chroot:
         prefix = []
         if settings.eatmydata and os.path.isfile('/usr/bin/eatmydata'):
             prefix.append('eatmydata')
-        run(prefix + ["tar", "-C", self.name, "-zxf", tarball])
+        run(prefix + ["tar", "-C", self.name, "--auto-compress", "-xf", tarball])
 
     def setup_from_schroot(self, schroot):
         self.schroot_session = schroot.split(":", 1)[-1] + "-" + str(uuid.uuid1()) + "-piuparts"
@@ -1174,7 +1174,7 @@ class Chroot:
         self.create_policy_rc_d()
         self.create_resolv_conf()
         for bindmount in settings.bindmounts:
-            self.mount(bindmount, bindmount, opts="bind")
+            self.mount(bindmount, bindmount, opts=["rbind"])
         if not os.path.exists(self.name + '/dev/null'):
             run(['mknod', '-m' ,'666', self.name + '/dev/null', 'c', '1', '3'])
 
@@ -1764,15 +1764,21 @@ class Chroot:
 
     def mount(self, source, path, fstype=None, opts=None, no_mkdir=False):
         """Mount something into the chroot and remember it for unmount_all()."""
+        if opts is None:
+            opts = []
         path = canonicalize_path(self.name, path)
-        if not no_mkdir:
-            self.mkdir_p(path)
         fullpath = self.relative(path)
+        if not no_mkdir:
+            if not ("bind" in opts or "rbind" in opts) or os.path.isdir(source):
+                self.mkdir_p(path)
+            elif not os.path.exists(fullpath):
+                self.mkdir_p(os.path.dirname(path))
+                os.mknod(fullpath, stat.S_IFREG)
         command = ["mount"]
         if fstype is not None:
             command.extend(["-t", fstype])
-        if opts is not None:
-            command.extend(["-o", opts])
+        if opts:
+            command.extend(["-o", ",".join(opts)])
         command.extend([source, fullpath])
         run(command)
         self.mounts.append(fullpath)
@@ -1796,12 +1802,16 @@ class Chroot:
         etcmtab = self.relative("etc/mtab")
         if not os.path.lexists(etcmtab):
             os.symlink("../proc/mounts", etcmtab)
-        self.mount("devpts", "/dev/pts", fstype="devpts", opts="newinstance,noexec,nosuid,gid=5,mode=0620,ptmxmode=0666")
+        self.mount("devpts", "/dev/pts", fstype="devpts", opts=["newinstance", "noexec", "nosuid", "gid=5", "mode=0620", "ptmxmode=0666"])
         dev_ptmx_rel_path = self.relative("dev/ptmx")
         if not os.path.islink(dev_ptmx_rel_path):
             if not os.path.exists(dev_ptmx_rel_path):
-                os.mknod(dev_ptmx_rel_path, 0o0666 | stat.S_IFCHR, os.makedev(5, 2))
-            self.mount(self.relative("dev/pts/ptmx"), "/dev/ptmx", opts="bind", no_mkdir=True)
+                # /dev/pts/ptmx has been created by the previous self.mount("devpts", ...), we can symlink /dev/ptmx to it.
+                os.symlink("pts/ptmx", dev_ptmx_rel_path)
+            else:
+                # /dev/ptmx is an unknown entity. Override it with a safe bind mount of /dev/pts/ptmx.
+                # Unlinking the existing /dev/ptmx could fail, this is safer.
+                self.mount(self.relative("dev/pts/ptmx"), "/dev/ptmx", opts=["bind"], no_mkdir=True)
         p = subprocess.Popen(["tty"], stdout=subprocess.PIPE,
                              universal_newlines=True)
         stdout, _ = p.communicate()
@@ -1810,10 +1820,10 @@ class Chroot:
             dev_console = self.relative("/dev/console")
             if not os.path.exists(dev_console):
                 os.mknod(dev_console, 0o0600, os.makedev(5, 1))
-            self.mount(current_tty, "/dev/console", opts="bind", no_mkdir=True)
-        self.mount("tmpfs", "/dev/shm", fstype="tmpfs", opts="size=65536k")
+            self.mount(current_tty, "/dev/console", opts=["bind"], no_mkdir=True)
+        self.mount("tmpfs", "/dev/shm", fstype="tmpfs", opts=["size=65536k"])
         if selinux_enabled():
-            self.mount("/sys/fs/selinux", self.selinuxfs_path(), opts="bind,ro")
+            self.mount("/sys/fs/selinux", self.selinuxfs_path(), opts=["bind", "ro"])
 
     def is_ignored(self, pathname, info="PATH"):
         """Is a file (or dir or whatever) to be ignored?"""



View it on GitLab: https://salsa.debian.org/debian/piuparts/-/compare/6d601ebe7298eaf9829a19f84d820f5bfae5d63e...929b4a4a54d9407dcb9bc757a12580718a7c4ed1

-- 
View it on GitLab: https://salsa.debian.org/debian/piuparts/-/compare/6d601ebe7298eaf9829a19f84d820f5bfae5d63e...929b4a4a54d9407dcb9bc757a12580718a7c4ed1
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/piuparts-devel/attachments/20231108/62eb2c40/attachment-0001.htm>


More information about the Piuparts-devel mailing list