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
+

Reply via email to