Package: dpkg Version: 1.20.7.1 Severity: wishlist Dear Maintainer,
This is a wishlist item relate to: Re: Bug#924401: #924401 base-files fails postinst when base-passwd is unpacked With the patch included below the problem packages, Provides: awk and base-passwd can be trivially modified so that they provide their essential functionality via the fsys-tarfile only and do not need to rely on the configure scripts to work before ever being configured. This means that you need nothing more than apt to bootstrap a release as apt can correctly work out the right order to configure the packages. I also include example patches to base-passwd and mawk (gawk and original-awk need almost identical changes to mawk) The core feature is to add support for a flag "write-once" for conffiles. In most circumstances this operates identical to a regular conffile, however, it disables all questions about future changes and always leaves the installed version untouched (except on purge). The two base-passwd files are tagged like this and no longer need to be created in the maintainer scripts for base-passwd essential functionality to be avilable. gawk/mawk/original-awk ship with a symlink /usr/bin/awk -> /usr/bin/{g,m,original-}awk that is tagged similarly. This does, currently cause a warning from dpkg-deb as the package is built. Under normal use cases /usr/bin/awk will exist and be maintained via the alternatives mechanism, so dpkg will not use this packaged symlink as it will see an existing file and "keep" it. But during bootstrapping when the deb is initially unpacked using tar the symlink will be created and so the awk functionality will be available before the correct symlink is created via the maintainter scripts. N.B. I think that it is probably necessary that gawk, mawk and essential-awk are patched at the same time after this dpkg feature is added. Otherwise I think that installing a package that doesn't use this feature for the /usr/bin/awk symlink and then purging one that does will result in the symlink being removed! There may will be other corner cases like this that I'm unaware of. ===== dpkg patch diff -urN dpkg-1.20.7.1.orig/dpkg-deb/build.c dpkg-1.20.7.1/dpkg-deb/build.c --- dpkg-1.20.7.1.orig/dpkg-deb/build.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/dpkg-deb/build.c 2021-02-25 16:44:23.661053441 +0000 @@ -316,6 +316,9 @@ if (strcmp(flag, "remove-on-upgrade") == 0) remove_on_upgrade = true; + else if (strcmp(flag, "write-once") == 0) { + /* do nothing */ + } else ohshit(_("unknown flag '%s' for conffile '%s'"), flag, conffilename); } diff -urN dpkg-1.20.7.1.orig/lib/dpkg/dpkg-db.h dpkg-1.20.7.1/lib/dpkg/dpkg-db.h --- dpkg-1.20.7.1.orig/lib/dpkg/dpkg-db.h 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/lib/dpkg/dpkg-db.h 2021-02-25 16:42:48.269440879 +0000 @@ -83,6 +83,7 @@ const char *hash; bool obsolete; bool remove_on_upgrade; + bool write_once; }; struct archivedetails { diff -urN dpkg-1.20.7.1.orig/lib/dpkg/dump.c dpkg-1.20.7.1/lib/dpkg/dump.c --- dpkg-1.20.7.1.orig/lib/dpkg/dump.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/lib/dpkg/dump.c 2021-02-25 16:39:12.917286986 +0000 @@ -396,6 +396,8 @@ varbuf_add_str(vb, " obsolete"); if (i->remove_on_upgrade) varbuf_add_str(vb, " remove-on-upgrade"); + if (i->write_once) + varbuf_add_str(vb, " write-once"); } if (flags&fw_printheader) varbuf_add_char(vb, '\n'); diff -urN dpkg-1.20.7.1.orig/lib/dpkg/fields.c dpkg-1.20.7.1/lib/dpkg/fields.c --- dpkg-1.20.7.1.orig/lib/dpkg/fields.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/lib/dpkg/fields.c 2021-02-25 16:42:39.697116256 +0000 @@ -347,10 +347,11 @@ { static const char obsolete_str[]= "obsolete"; static const char remove_on_upgrade_str[] = "remove-on-upgrade"; + static const char write_once_str[] = "write-once"; struct conffile **lastp, *newlink; const char *endent, *endfn, *hashstart; int c, namelen, hashlen; - bool obsolete, remove_on_upgrade; + bool obsolete, remove_on_upgrade, write_once; char *newptr; lastp = &pkgbin->conffiles; @@ -371,6 +372,12 @@ conffvalue_lastword(value, endfn, endent, &hashstart, &hashlen, &endfn, ps); + write_once = (hashlen == sizeof(write_once_str) - 1 && + memcmp(hashstart, write_once_str, hashlen) == 0); + if (write_once) + conffvalue_lastword(value, endfn, endent, &hashstart, &hashlen, &endfn, + ps); + obsolete= (hashlen == sizeof(obsolete_str)-1 && memcmp(hashstart, obsolete_str, hashlen) == 0); if (obsolete) @@ -395,6 +402,7 @@ newlink->hash= newptr; newlink->obsolete= obsolete; newlink->remove_on_upgrade = remove_on_upgrade; + newlink->write_once = write_once; newlink->next =NULL; *lastp= newlink; lastp= &newlink->next; diff -urN dpkg-1.20.7.1.orig/lib/dpkg/fsys.h dpkg-1.20.7.1/lib/dpkg/fsys.h --- dpkg-1.20.7.1.orig/lib/dpkg/fsys.h 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/lib/dpkg/fsys.h 2021-02-28 11:51:40.978902743 +0000 @@ -79,6 +79,8 @@ FNNF_FILTERED = DPKG_BIT(9), /** Conffile removal requested by upgrade. */ FNNF_RM_CONFF_ON_UPGRADE = DPKG_BIT(10), + /** Write once conffile (see bug# 924401) */ + FNNF_WRITE_ONCE = DPKG_BIT(11), }; /** diff -urN dpkg-1.20.7.1.orig/src/archives.c dpkg-1.20.7.1/src/archives.c --- dpkg-1.20.7.1.orig/src/archives.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/src/archives.c 2021-02-28 13:20:24.610233744 +0000 @@ -782,8 +782,13 @@ * do anything. This has to be done now so that we don't die due to * a file overwriting conflict. */ existingdir = false; + keepexisting = false; switch (ti->type) { case TAR_FILETYPE_SYMLINK: + if (!statr && (nifd->namenode->flags & FNNF_WRITE_ONCE) ) { + keepexisting = true; + break; + } /* If it's already an existing directory, do nothing. */ if (!statr && S_ISDIR(stab.st_mode)) { debug(dbg_eachfiledetail, "tarobject symlink exists as directory"); @@ -805,13 +810,15 @@ case TAR_FILETYPE_BLOCKDEV: case TAR_FILETYPE_FIFO: case TAR_FILETYPE_HARDLINK: + //Hmmm, should this just be for TAR_FILETYPE_FILE? Does it matter? + if (!statr && (nifd->namenode->flags & FNNF_WRITE_ONCE) ) + keepexisting = true; break; default: ohshit(_("archive contained object '%.255s' of unknown type 0x%x"), ti->name, ti->type); } - keepexisting = false; refcounting = false; if (!existingdir) { struct fsys_node_pkgs_iter *iter; diff -urN dpkg-1.20.7.1.orig/src/configure.c dpkg-1.20.7.1/src/configure.c --- dpkg-1.20.7.1.orig/src/configure.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/src/configure.c 2021-02-25 22:46:52.954391324 +0000 @@ -475,6 +475,14 @@ what = CFO_NEW_CONFF; useredited = -1; distedited = -1; + } else if (conff->write_once) { + useredited = -1; + distedited = -1; + if (strcmp(currenthash, NONEXISTENTFLAG) == 0) + what = CFO_NEW_CONFF; + else + /* We never overwrite a write once conffile */ + what = CFO_KEEP; } else if (strcmp(conff->hash, NEWCONFFILEFLAG) == 0) { if (strcmp(currenthash, NONEXISTENTFLAG) == 0) { what = CFO_NEW_CONFF; diff -urN dpkg-1.20.7.1.orig/src/unpack.c dpkg-1.20.7.1/src/unpack.c --- dpkg-1.20.7.1.orig/src/unpack.c 2021-01-09 06:04:59.000000000 +0000 +++ dpkg-1.20.7.1/src/unpack.c 2021-02-28 11:53:46.995532012 +0000 @@ -377,6 +377,8 @@ if (strcmp(flag, "remove-on-upgrade") == 0) { confflags |= FNNF_RM_CONFF_ON_UPGRADE; confflags &= ~FNNF_NEW_CONFF; + } else if (strcmp(flag, "write-once") == 0) { + confflags |= FNNF_WRITE_ONCE; } else { if (c_isspace(flag[0])) warning(_("line with conffile filename '%s' has leading white spaces"), @@ -599,7 +601,8 @@ if ((namenode->flags & FNNF_NEW_CONFF) || (namenode->flags & FNNF_RM_CONFF_ON_UPGRADE) || - (namenode->flags & FNNF_NEW_INARCHIVE)) + (namenode->flags & FNNF_NEW_INARCHIVE) || + (namenode->flags & FNNF_WRITE_ONCE)) continue; usenode = namenodetouse(namenode, pkg, &pkg->installed); @@ -821,6 +824,8 @@ newiconff->obsolete = !!(cfile->namenode->flags & FNNF_OBS_CONFF); newiconff->remove_on_upgrade = !!( cfile->namenode->flags & FNNF_RM_CONFF_ON_UPGRADE); + newiconff->write_once = !!( + cfile->namenode->flags & FNNF_WRITE_ONCE); *iconffileslastp = newiconff; iconffileslastp = &newiconff->next; } ===== patch to base-passwd diff -urN base-passwd-3.5.49.orig/debian/postinst base-passwd-3.5.49/debian/postinst --- base-passwd-3.5.49.orig/debian/postinst 2021-02-06 17:48:56.000000000 +0000 +++ base-passwd-3.5.49/debian/postinst 2021-02-27 22:12:59.989336348 +0000 @@ -55,14 +55,6 @@ exit 0 fi -if [ ! -e /etc/passwd ] ; then - cp /usr/share/base-passwd/passwd.master /etc/passwd -fi - -if [ ! -e /etc/group ] ; then - cp /usr/share/base-passwd/group.master /etc/group -fi - if [ "$2" = "3.2.2" ] && [ -f /etc/passwd.org ] ; then cat <<EOF ====== patch to mawk (gawk and original-awk need similar) diff -urN mawk-1.3.3.orig/debian/rules mawk-1.3.3/debian/rules --- mawk-1.3.3.orig/debian/rules 2021-02-27 18:09:00.000000000 +0000 +++ mawk-1.3.3/debian/rules 2021-02-27 22:17:20.707233778 +0000 @@ -14,3 +14,11 @@ ifeq (,$(filter $(DEB_BUILD_OPTIONS),nocheck)) make mawk_test fpe_test endif + +override_dh_installdeb: + dh_installdeb + echo "write-once /usr/bin/awk" >> debian/mawk/DEBIAN/conffiles + ln -s mawk debian/mawk/usr/bin/awk + chmod 0644 -- debian/mawk/DEBIAN/conffiles + chown 0:0 -- debian/mawk/DEBIAN/conffiles +