#986644 dpkg-divert: document how to use with --no-rename

Package:
dpkg
Source:
dpkg
Description:
Debian package management system
Submitter:
Lorenzo Puliti
Date:
2025-01-04 13:06:02 UTC
Severity:
wishlist
#986644#5
Date:
2021-04-08 17:15:30 UTC
From:
To:
Dear maintainer,

I want one of my packages to divert files from an essential package: I used code
from examples (with --rename option) online [1] that works fine, but lintian complains.
Moving essential files can be dangerous, use the --no-rename.
Ok. So I assumend that the same code would work fine, just replacing '--rename' with
'no-rename', silly me.. instead it broke my system.
It is true that the manpage states under [ --add/remove]
"The file is currently not renamed, see --rename"
However reading the --rename/--no-rename description I was under the impression that the
--no-rename was a safer option - overwrite during unpack instead of moving aside
before the unpack, and that it's safer also when the diversion is removed.
This is not the case, or maybe there is an easy way that i'm not considering
but it's not documented anywere and also serching the code base for examples [2]
provides no clue.

Somehow i found a way but it requires some additional code in preinst and giving that
lintian screams in all the four cardinal direction I suspect it's not the right way
[ example appended at the end ]

I'm having the following problems with --no-rename:

* At preinst, it doesn't create a copy of the original file "file.real"

* at postrm, the file disappear and it's not replaced by anything;
  also, if there is a copy "file.real" is left there (i guess piuparts would complain)

Even if one use the --rename option in postrm, the "file.real" is not granted
to exists untill the package that ship the original file is upgraded or reinstalled.
Finally, while during the unpack the file is overwritten, during the removal there is
still a moment where the file disappear (am I wrong?) so overall it does look more
complex to handle but not that safer than a standard --rename.

Regards,
Lorenzo

[1] https://www.debian.org/doc/debian-policy/ap-pkg-diversions.html
[2] https://codesearch.debian.net/search?q=dpkg-divert+--no-rename&literal=1


Example

$ cat runit-init.preinst
#!/bin/sh

set -e
# make sure that the *.real diverted file exists when postrm is called
# as with --no-rename the diverted file is not created: if this package
# is removed before init-system-helpers is upgraded the system will be
# left without helpers!! see postrm
if [ upgrade != "$1" ] || dpkg --compare-versions "$2" lt 2.1.2-41; then
    for file in /usr/sbin/invoke-rc.d /usr/sbin/service /usr/sbin/update-rc.d ; do
        if [ ! -e "$file".real ] ; then
            cp "$file" "$file".real
        fi
    done
fi

# install helpers with runit support
if [ upgrade != "$1" ] || dpkg --compare-versions "$2" lt 2.1.2-41; then
    dpkg-divert --package runit-init --add --no-rename \
        --divert /usr/sbin/invoke-rc.d.real /usr/sbin/invoke-rc.d
    dpkg-divert --package runit-init --add --no-rename \
        --divert /usr/sbin/service.real /usr/sbin/service
    dpkg-divert --package runit-init --add --no-rename \
        --divert /usr/sbin/update-rc.d.real /usr/sbin/update-rc.d
fi

#DEBHELPER#

exit 0
-------------------

$ cat runit-init.postrm
#!/bin/sh

set -e

# need to use --rename here, with --no-rename the *.real diverted file will
# not be back and the system is left without helpers!
# *.real is granted to be there because of preinst
if [ "$1" = "remove" ] || [ "$1" = "abort-install" ] || [ "$1" = "disappear" ] ; then
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/invoke-rc.d.real /usr/sbin/invoke-rc.d
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/service.real /usr/sbin/service
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/update-rc.d.real /usr/sbin/update-rc.d
fi

#the following can be removed as runit 2.1.2-41 is out of oldoldstable
if [ "abort-upgrade" = "$1" ] && dpkg --compare-versions "$2" lt 2.1.2-41; then
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/invoke-rc.d.real /usr/sbin/invoke-rc.d
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/service.real /usr/sbin/service
        dpkg-divert --package runit-init --remove --rename \
          --divert /usr/sbin/update-rc.d.real /usr/sbin/update-rc.d
fi

#DEBHELPER#

exit 0
-------------------- $ cat runit-init.install debian/contrib/helpers/invoke-rc.d /usr/sbin debian/contrib/helpers/service /usr/sbin debian/contrib/helpers/update-rc.d /usr/sbin
--------------------- $ lintian -Ev --pedantic runit_2.1.2-41_amd64.changes N: Using profile debian/main. N: Starting on group runit/2.1.2-41 N: Finished processing group runit/2.1.2-41 E: runit-init: diversion-for-unknown-file --no-rename/usr/sbin/invoke-rc.d preinst:19 E: runit-init: diversion-for-unknown-file --no-rename/usr/sbin/service preinst:21 E: runit-init: diversion-for-unknown-file --no-rename/usr/sbin/update-rc.d preinst:23 E: runit-init: orphaned-diversion --no-rename/usr/sbin/invoke-rc.d preinst E: runit-init: orphaned-diversion --no-rename/usr/sbin/service preinst E: runit-init: orphaned-diversion --no-rename/usr/sbin/update-rc.d preinst E: runit-init: remove-of-unknown-diversion usr/sbin/invoke-rc.d postrm:10 E: runit-init: remove-of-unknown-diversion usr/sbin/invoke-rc.d postrm:20 E: runit-init: remove-of-unknown-diversion usr/sbin/service postrm:12 E: runit-init: remove-of-unknown-diversion usr/sbin/service postrm:22 E: runit-init: remove-of-unknown-diversion usr/sbin/update-rc.d postrm:14 E: runit-init: remove-of-unknown-diversion usr/sbin/update-rc.d postrm:24 W: runit-init: no-manual-page usr/sbin/invoke-rc.d W: runit-init: no-manual-page usr/sbin/service W: runit-init: no-manual-page usr/sbin/update-rc.d X: runit source: debian-watch-does-not-check-gpg-signature X: runit-run: systemd-service-file-missing-hardening-features lib/systemd/system/runit.service N: 13 hints overridden (1 warning, 12 info) [1] https://www.debian.org/doc/debian-policy/ap-pkg-diversions.html [2] https://codesearch.debian.net/search?q=dpkg-divert+--no-rename&literal=1
#986644#12
Date:
2022-03-28 23:22:04 UTC
From:
To:
Hi Guillem,

Could you please give a hint if the behavior described in my previous
message is expected or am I missing something obvious?

Best Regards,
Lorenzo

#986644#17
Date:
2025-01-04 13:02:47 UTC
From:
To:
Hi Lorenzo and maintainer,

I too am befuddled by the documentation of 'dpkg-divert --no-rename', as
I was looking possibly to use this feature myself.

The explanation seems to be accurate as to what it does but it seems
pointless with the current behaviour so I wondered whether it was
incorrectly documented; apparently it is not. What use is the diversion
in the database without any actually diversion activity?

I would have thought in the '--no-rename' case we would want to use a
copy rather than a rename. And maybe it would be safe to use '--rename'
at removal time.

That would in fact still leave a race, though, as the file to be
installed may not be written in one go. That could be solved by
unpacking the new file to a temporary location and then renaming it.
That would require cooperation with the install phase though. (Isn't
this already a problem for installing essential files anyway? Maybe this
already happens - I don't know how the installer works.)

Personally I'm inclined to think that in many cases '--rename' would not
actually be that unsafe because the failure mode of an operation failing
with an error is not the worst possible failure mode.

The man page is in any case out of date as it talks about future changes
in dpkg-1.20.x but we are already at 1.22.13.