* This also adds undue complexity, by supporting those as admin aliases.
The admin generated redirecting symlinks are already annoying, I'd rather
not add further to that pile. I don't really want to support admins doing
this (dpkg-divert does not even support diverting a directory).
[ As an aside, I think ideally eventually nothing distro provided should
be allowed to be installed within an aliased dir, and dpkg should
eventually just error out in those cases, which eventually would get
rid of the aliasing problems and any such complexity (I'm not sure how
or when that would be feasible though, but obviously in Debian at
least not until nothing ships files there). ]
So this still looks like a terrible interface, like it did at the time
it was discarded; founded on a hack, an interface that seems wants to
be kind of a file-type override but it cannot be, and cannot even
properly act as record tracker, etc…
I thought it would be clear that if there is stuff that depends on
any of this kind of changes to dpkg, relying on those changes in
Debian would not be possible until after trixie+1. Of course there is
always the route to further pile up over the Jenga tower of hacks,
by for example adding huge amounts of Pre-Depends…
So given the above, I don't see why the apparent rush here. And as I've mentioned many times now, I'm planning to continue working on the fsys metadata stuff for 1.22.x, probably at the cost of database duplication
if necessary, if current blockers have not adapted by then. But as I've mentioned before, that might not guarantee this support is sufficient to support fixing this mess. But all other proposed changes I've seen
flying around for changes to dpkg are just conceptually wrong in one way
or another.
I'd also be interested on how you plan to move important files in essential packages. This is an aspect raised by Simon Richter and where
I do not see an obvious answer yet.
Do you have a pointer? Not sure I follow what "important" files means
here, doesn't ring a bell.
In <669234b3-555b-4e2a-ccc7-dd5510b6e9c1@debian.org>, Simon Richter
said:
Dpkg already has defined behaviour for directory vs symlink: the directory wins. In principle a future version of dpkg could change that, but /lib/ld-linux.so.2 is just too special, we'd never want to have a package that
actually moves it.
This and /bin/sh is the kind of files I'd consider important. And then
upon thinking further it became more and more difficult for me to make
sense of the objection. On a merged system, we can just move that file
to its canonical location without having any trouble even with an
unmodified dpkg. So from my pov, the question about important files can
be disregarded. I hope Simon Richter agrees.
Let us circle back to your "broken" approach. It sure is simple (just
move all the files and be done) and if we could just skip over the
upgrade issues and have all the files moved without having to modify
dpkg, that would actually be a better result than DEP 17. Just how do we avoid the issue of file loss arising from the aliasing in your scenario?
There kinda is an obvious solution here. We just need to tell dpkg that
it needs to remove the package containing the file that is being moved
before unpacking the replacing package. This semantic actually exists
and we call it "Conflicts". So instead of using Breaks+Replaces, the
solution is to use Conflicts! Problem solved, right?
Yeah, I think this solves a number of cases, but there are two areas
where it does not:
* We generally prefer Breaks over Conflicts for a reason. It gives the
dependency resolver more freedom and in taking this freedom away, it
may fail to find solutions to complex upgrades. (Which is why Breaks
got introduced in the first place.)
* We cannot use Conflicts inside the transitively essential set.
So if we move all those files, in 90% (number made up) of the cases it
will go well (since we don't move between packages) and in 90% (number
made up) of the remaining 10%, we can use Conflicts, but what do we do
about that remaining 1%?
If we look deeper into the dpkg toolbox, we pause at diversions. What if
the new package were to add a (--no-rename) diversion for files that are
at risk of being accidentally deleted in newpkg.preinst and then remove
that diversion in newpkg.postinst? Any such diversion will cause package removal of the oldpkg to skip removal of the diverted file (and instead deleted the non-existent path that we diverted to). Thus we retain the
files we were interested in.
This way, we can just move the files as you suggested.
* For 90% of the packages, this will just work.
* For 9% of the packages, we'll need to turn Breaks+Replaces into
Conflicts.
* And for 1% of the packages, we'll need to add complex diversions to
maintainer scripts.
And while this sounds super ugly in the 1% of cases, it's a complexity
that maybe isn't necessary at all and we can remove it after trixie
unlike the complexity being added in DEP 17.
In order to better understand the mechanics at hand (and due to Simon Richter's call for test cases), I sat down and wrote some. So in this
mail you find 4 files attached:
* runtest.sh is a wrapper script to run each case inside a fresh
chroot created by mmdebstrap (as you don't want to mess your system).
* case1.sh demonstrates the file loss problem with Breaks+Replaces and
thus fails.
* case2.sh demonstrates how Conflicts fix the problem.
* case3.sh demonstrates how diversions fix the problem.
In sincerely hope that this fixed-up plan doesn't have any serious
issues. If you find any please tell.
And before closing this mail, I would like to express my gratitude and
thanks to Emilio Pozuelo Monfort for enduring me in numerous discussions
on this matter and providing so many of the key insights captured in
this mail.
move all the files and be done) and if we could just skip over the
upgrade issues and have all the files moved without having to modify
dpkg, that would actually be a better result than DEP 17. Just how do we avoid the issue of file loss arising from the aliasing in your scenario?
There kinda is an obvious solution here. We just need to tell dpkg that
it needs to remove the package containing the file that is being moved
before unpacking the replacing package. This semantic actually exists
and we call it "Conflicts". So instead of using Breaks+Replaces, the
solution is to use Conflicts! Problem solved, right?
Yeah, I think this solves a number of cases, but there are two areas
where it does not:
* We generally prefer Breaks over Conflicts for a reason. It gives the
dependency resolver more freedom and in taking this freedom away, it
may fail to find solutions to complex upgrades. (Which is why Breaks
got introduced in the first place.)
* We cannot use Conflicts inside the transitively essential set.
So if we move all those files, in 90% (number made up) of the cases it
will go well (since we don't move between packages) and in 90% (number
made up) of the remaining 10%, we can use Conflicts, but what do we do
about that remaining 1%?
If we look deeper into the dpkg toolbox, we pause at diversions. What if
the new package were to add a (--no-rename) diversion for files that are
at risk of being accidentally deleted in newpkg.preinst and then remove
that diversion in newpkg.postinst? Any such diversion will cause package removal of the oldpkg to skip removal of the diverted file (and instead deleted the non-existent path that we diverted to). Thus we retain the
files we were interested in.
This way, we can just move the files as you suggested.
* For 90% of the packages, this will just work.
* For 9% of the packages, we'll need to turn Breaks+Replaces into
Conflicts.
* And for 1% of the packages, we'll need to add complex diversions to
maintainer scripts.
diversion of /bin/zfgrep to /bin/zfgrep.gzip by zutils in stable, testing, unstable
diversion of /bin/zgrep to /bin/zgrep.gzip by zutils in stable, testing, unstable
All other diversion affect /etc or /usr and I think we're not going to
move any files from /usr to /. So this is a complete list as of today
and I have to say, I expected it to be longer. In effect, we're talking
about merely 8 packages.
For completeness sake, I also looked at the other packages mentioning dpkg-divert in their preinst to catch false negatives. I'll skip
diversions inside /usr as well as removals of diversions here:
* amazon-ec2-net-utils: diversion inside /etc
* angband: comment about diversions
* arpwatch: comment about diversions
* dash: complex use of conditional diversions via postinst
* dist: comment about diversions
* gpr: conditional diversion (inside /usr)
* iputils-arping: check for an existing diversion
* iputils-clockdiff: check for an existing diversion
* iputils-ping: check for an existing diversion
* ld10k1: comment about diversions
* mailagent: comment about diversions
* oping: checks for an existing diversion
* psgml: comment about diversions
* ucf: comment about diversions
* wireshark-common: checks for an existing diversion
So yeah, with the exception of dash, this looks fairly good. Let me also
dive into dash. Unlike the majority of diverters, it diverts in postinst rather than preinst to allow controlling /bin/sh via debconf. A similar technique is in effect by gpr. In any case, this is special, because
dash diverts its own files, so when moving dash's file, its diversions
can be migrated at the same time. It merely means, that we cannot have debhelper just move files (as that would horribly break dash) and
instead have to move files on a package-by-package way. We could also
opt for removing dash's diversion in the default case and there even is
a patch for doing so (#989632) since almost two years. Too bad we didn't apply it. In any case, as long as the file moving is not forced via debhelper, dash should be harmless.
With this number, another option is on the table. Rather than divert dpkg-divert, we could just fix these 8 packages to duplicate their
diversions for /usr and then when moving the underlying files add
versioned Conflicts to the old version of diverters (none of which are essential). So this is an order of 15 uploads (8 diverters, 6 diverted packages, dash).
Luca Boccassi kindly pointed me at config-package-dev though. This is a
tool for generating local packages and it also employs dpkg-divert.
There is a significant risk of breaking this use case. If we were to
divert dpkg-divert and automatically duplicate diversions, this use case
were automatically covered.
I am unsure how to proceed here and request assistance from the
debathena project to evaluate the situation. If possible, I'd like to
avoid the complexity of wrapping dpkg-divert.
5. Write a policy on how to perform changes and how to move files. Simon
Richter suggested a policy for dpkg-divert already. This needs to be
extended to cover other matters and refined.
6. Convert packages one by one by enabling the addon or bumping the
compat level. Such a conversion may require:
* Adding Pre-Depends: ${misc:Pre-Depends}
* Adding Conflicts
* Adding invocations of the usr-merge-helper
* Modifying maintainer scripts to canonicalize diversions
* ...
7. Continuously monitor the reports from the service and turn that
feedback into patches.
This plan definitely is incomplete and misses critical steps. I suspect
that Simon Richter could immediately tell at least two flaws if not
more.
A rather obvious flaw (that Simon Richter already mentioned) is how this breaks filesystem bootstrap. The policy requirement is that all
essential packages must work in unpacked state provided that they have
been configured at least once. (Please don't ask who added this
requirement to policy. I might be embarrassed.) In reality bootstrap implementations expect more. In order to perform that initial
configuration, we expect that they work sufficiently well to be able to
run maintainer scripts even in unpacked state prior to initial
configuration. This currently works due to the dynamic linker being
shipped in the non-canonical path it is referenced as. Moving it will
break bootstrap tooling unless we install /lib in some binary package
(as Simon Richter pointed out). However, due to how dpkg handles
directory vs symlink conflicts, it is not clear whether we can
reasonably add it as a symlink to any data.tar without causing havoc.
This definitely needs more research and is one of the areas where
teaching dpkg about aliasing brings significant benefits.
Let me restate that I am still not convinced that we can pull this stunt without painting us in a very dark corner. Too many moving parts of this approach do not have well-understood solutions. And even then, my
analysis has mainly focused on packages shipped by Debian. It neglected
other relevant aspects such as:
* custom Debian packages (e.g. equivs, config-package-dev, private
packages)
* vendor packages
* local diversions
* local statoverrides
* probably more
This would be in trixie, so even when unpacked on bookworm for the
upgrade case, the loader is guaranteed to be accessible from the
canonical path. Heck, we could even consider canonicalizing shebangs
in scripts shipped in essential packages? I'd imagine /bin/sh would
have the same issue as the loader?
I don't think we should lose sleep over third party/proprietary
packages. Given most of those are autogenerated from dubious scripts
(I have seen things...) I doubt they are using diverts and such, most packages I see from companies are built using cmake or java built-in
scripts that shove stuff in /opt and manually generate the .ar and
pack them, so...
I mean, we were not concerned at all when Canonical switched to zstd
for deb compression, creating a massive ticking time bomb given the
vast majority of third party .debs are built on some flavour of Ubuntu
and ship a single .deb for all consumers, and thus would stop being installable on Debian (because for years the dpkg patch was rejected, thankfully now it's all sorted and we are good for bookworm), I don't
think we should spend time trying to pre-empt fixing those. Noting in
the release notes that in case they do X and Y and Z they will need adjustments and pointing to appropriate documentation seems more than
enough to me, no?
The most popular ones (by virtue of the very fair and impartial
property of being currently installed on my laptop) like chrome,
vscode, edge, steam and signal do not ship anything in the legacy
directories anyway.
Same for local modifications, documenting in the release notes what
corner cases people need to be aware of and how to deal with them
seems standard practice to me when doing new releases. For example
there's a bunch of things you were supposed to do on your machine when upgrading to Bullseye as documented at: https://www.debian.org/releases/stable/amd64/release-notes/ch-information.en.html#upgrade-specific-issues
and without checking, I'm pretty sure this is normal for all releases.
Rather than change gcc, I think the interpreter could be changed after-the-fact using patchelf. We do have prior art for this since GUIX
uses this technique to redirect the loader below /gnu/store to implement
its versioning scheme and allow concurrent installations of glibc. The existence of GUIX should also limit the possible breakage. By not doing
it in gcc, we likely save apparmor's and heaptrack's test suites from breaking. Still radare2 an systemtap seem fragile in this regard. (You
can use codesearch.d.n with lib64/ld-linux-x86 and one of those packages
to discover the code I see breaking. My search was non-exhaustive.)
How about the long-term vision of this? Elsewhere you indicated that
you'd like the aliasing symlinks to not be shipped by any data.tar. Does
that imply that we'd keep patching the interpreter and using /usr/bin/sh forever in the essential set? If adding the links to base-files, it
would be of temporary nature only.
If adding the symlinks to base-files, how about /lib64? Would we ship it
for all architectures or just for those that need it (e.g. amd64,
loong64, mips64el, ppc64, ppc64el)? https://wiki.debian.org/ArchitectureSpecificsMemo has a list of dynamic loaders we also need /libx32 for x32 at least. If making this architecture-dependent, would base-files become Multi-Arch: same?
handling and no diversion, then it should be simple to handle: the debhelper in stable will not perform the conversion by definition as
the logic won't be present, and any dh upload to backports will have
such logic disabled, so that other packages that get uploaded to
backports and built with either the stable or the backports debhelper
won't have any change performed on them.
As much a I'd like to trust you on things actually being simple, we've
seen over and over again that the simple approaches have non-trivial
flaws. If you were to highlight resulting problems (and propose
solutions), that would be more convincing to me than continuously
labeling it simple.
Or to put it in another way: I think our defaults should prioritize
the Debian native use case. Given we ship our loader in /usr/lib/ld*
now, it makes sense to me that the default in GCC is to point to /usr/lib/ld*. Callers can override that as needed for third-party/external/foreign use cases.
I guess you'll be having a hard time convincing the toolchain
maintainers of this change, but my other point was that this is
unnecessary when we can use patchelf after the fact.
How about the long-term vision of this? Elsewhere you indicated that you'd like the aliasing symlinks to not be shipped by any data.tar. Does that imply that we'd keep patching the interpreter and using /usr/bin/sh forever in the essential set? If adding the links to base-files, it
would be of temporary nature only.
If adding the symlinks to base-files, how about /lib64? Would we ship it for all architectures or just for those that need it (e.g. amd64, loong64, mips64el, ppc64, ppc64el)? https://wiki.debian.org/ArchitectureSpecificsMemo has a list of dynamic loaders we also need /libx32 for x32 at least. If making this architecture-dependent, would base-files become Multi-Arch: same?
...
I think we should leave the long term vision for another day, and
focus on your requirements for the essential set unpacking right now.
Knowing the target state of a transition seems fairly fundamental to implementing it and base-files is part of the essential set. To me, it
is a significant difference whether we temporarily or permanently modify
the ELF interpreter in the essential set. For these reasons, I do think
the answers to these questions do matter at this time. As long as we do
not have answers here, we must not move ld.so nor /bin/sh regardless of whether we patch dpkg or not.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (0 / 16) |
Uptime: | 158:59:13 |
Calls: | 10,384 |
Calls today: | 1 |
Files: | 14,056 |
Messages: | 6,416,490 |