Differences between revisions 3 and 4
Revision 3 as of 2020-02-23 23:43:25
Size: 5473
Editor: GuillemJover
Comment: Update status of how to avoid the breakage or and new reversibility prototype
Revision 4 as of 2020-05-22 00:20:18
Size: 5549
Editor: GuillemJover
Comment: Add cruft as yet more collateral damage
Deletions are marked like this. Additions are marked like this.
Line 37: Line 37:
  * DebianPkg:cruft is currently broken by this approach. DebianBug:941998


The main goal of the merged-/usr proposal is to merge the contents of several top root directories (/bin, /sbin, /lib*) into their counterparts in /usr. On Debian systems it is expected that during boot /usr will be available, because it is either not a separate partition, or it has already been mounted by an initramfs. This can reduce the overhead of distinct directories and where to put what. It also makes it easier to have a read-only /usr filesystem.

  • FIXME: expand, rationale, etc.


This proposal is composed of three parts:

  1. Where to store the objects in the filesystem.
  2. How to move them.
  3. How to provide backwards compatibility.


merged-/usr-via-aliased-dirs (was merged-/usr-via-symlinks)

  1. The objects end up stored under /usr/*.
  2. The move is performed at install-time by an unaware dpkg, through the aliased directories.
  3. The backward compatibility is provided by the aliased directories.

This approach goes behind dpkg's back, and has caused and do causes now problems due to the aliased directories, as multiple pathnames canonicalize into different pathnames that point to the same dentry, which can mess up anything that handles pathnames in databases and similar. Note: dpkg has supported for a long time symlinked directories as a way to allow local admins to manage filesystem size constraints, by moving directories contents into other partitions, but has never supported aliased directories via symlinks pointing to other directories already tracked by dpkg. This approach makes it impossible to know what is the canonical (from dpkg's PoV) pathname for an object just by looking at the filesystem, so it is trivial for users use the wrong one. One of the reasons for this approach was to make a migration quick and to require less effort in converting pathnames one-by-one, but has resulted instead in making the individual pathname migration more difficult and less declarative, and as such more fragile, and has created a bleeding problem due to the aliased directories:

  • all packages that shipped in the .deb compatibility symlinks between / and /usr/ or the reverse, required these to be moved into maintainer script and conditionalized on whether the system had aliased directories.
  • apparmor required fixes to its policies.
  • selinux required fixes to its policies.
  • <insert other projects handling filesystem pathnames here...>.

  • dpkg-shlibdeps required fixes due to dpkg-query breaking, which then required more fixes due to the "fix" breaking cross-compilation.
  • dpkg-query -S is currently broken by this approach.
  • dpkg-trigger is currently broken by this approach.
  • dpkg-divert is currently broken by this approach.
  • update-alternatives is currently broken by this approach.
  • tar -x on the root directory will break systems using this approach.
  • dpkg-deb -x on the root directory will break systems using this approach.
  • moving a pathname from a .deb (say /bin/foo in pkg A) to another .deb while changing the pathname across an aliased directory (say /usr/bin/foo in pkg B), is broken and can make (depending on the unpack order) the new pathname disappear from the filesystem.
  • cruft is currently broken by this approach. 941998

This approach is considered broken by design and is unsupported by dpkg. dpkg-buildinfo marks packages built on these systems as tainted in the .buildinfo file.

Unfortunately debootstrap defaults to using this approach, and by extension d-i installs broken systems since Debian buster, but there's a bug report 923091 with a patch to at least make it selectable in expert mode. For now you can pass --no-merged-usr to debootstrap (or you can use instead mmdebstrap which is not affected by this, is way faster, and it intends to replace all bootstrapping tools by making them completely unnecessary anyway); or for d-i install in export mode, step until before the "Install the base system", spawn a shell, and edit the /var/lib/dpkg/info/bootstrap-base.postinst, add the --no-merged-usr option to the run-debootstrap invocation, then exit the shell and proceed with the installation. Other systems might have used this approach via the usrmerge hack.

There is now a (completely untested) PoC that should be able to revert the breakage from these systems. It still needs testing and possibly further work: https://www.hadrons.org/~guillem/tmp/unmessusr.pl

  1. The objects end up stored under /usr/*.
  2. The move is performed at build-time with full collaboration with dpkg.
  3. The backward compatibility (if necessary, for binaries or ABI providing pathnames) can be done with symlinks.

This approach could have been fully automated via debhelper, in a safe way for every debhelper-using package, with no maintainer scripts involved. This would imply currently a ton of maintainer script due to having to support the broken merged-/usr-via-aliased-dirs approach that has been deployed on new systems or via the usrmerge hack. This approach does not break dpkg at all. Compatibility symlinks are explicit, and can be dropped once nothing uses those pathnames, making it obvious what still remains, and not confusing users that look into the filesystem.