소스 검색

dpkg: Switch conflictor tracking from a fixed-size array to a queue

This fixes several related issues. The list of conflictors has always
really been a queue, where packages were added and then acted on,
although no package was removed from the list after being processed,
which in turn made subsequent conflictors accumulate and reach the
arbitrary limit due to previous unrelated Conflicts/Replaces pairs,
giving possibly very confusing error messages due to that.

Another theoretical problem is that because the list was not cleaned up
after processing it, a package could have been enqueued to be removed,
then a newer version reinstalled which could get removed again on a
subsequent package processing, being it on the list.

Closes: #726112
Guillem Jover 12 년 전
부모
커밋
84dc4b1dbd
4개의 변경된 파일41개의 추가작업 그리고 30개의 파일을 삭제
  1. 7 0
      debian/changelog
  2. 1 1
      src/archives.c
  3. 1 1
      src/archives.h
  4. 32 28
      src/unpack.c

+ 7 - 0
debian/changelog

@@ -100,6 +100,13 @@ dpkg (1.17.2) UNRELEASED; urgency=low
   * Add GnuPG 2.x support. Add gnupg2 and gpgv2 as alternative Recommends to
   * Add GnuPG 2.x support. Add gnupg2 and gpgv2 as alternative Recommends to
     gnupg and gpgv (to not pull them by default), but prefer gpgv2 over gpgv,
     gnupg and gpgv (to not pull them by default), but prefer gpgv2 over gpgv,
     and gpg2 over gpg at run-time if they are available.
     and gpg2 over gpg at run-time if they are available.
+  * Switch dpkg conflictor tracking from a fixed-size array to a queue,
+    fixing several related issues, due to conflictors not being removed from
+    the array after processing them. dpkg could fill it due to additions in
+    previous package processing producing very confusing error messages; and
+    a theoretical problem where a package could get appended to be removed,
+    then reinstalled as a new version, to get removed again when revisiting
+    the array in a subsequent package processing. Closes: #726112
 
 
   [ Updated programs translations ]
   [ Updated programs translations ]
   * German (Sven Joachim).
   * German (Sven Joachim).

+ 1 - 1
src/archives.c

@@ -1499,7 +1499,7 @@ void check_conflict(struct dependency *dep, struct pkginfo *pkg,
       }
       }
       if (!pdep) {
       if (!pdep) {
         /* This conflict is OK - we'll remove the conflictor. */
         /* This conflict is OK - we'll remove the conflictor. */
-        enqueue_conflictor(pkg, fixbyrm);
+        enqueue_conflictor(fixbyrm);
         varbuf_destroy(&conflictwhy); varbuf_destroy(&removalwhy);
         varbuf_destroy(&conflictwhy); varbuf_destroy(&removalwhy);
         notice(_("yes, will remove %s in favour of %s"),
         notice(_("yes, will remove %s in favour of %s"),
                pkg_name(fixbyrm, pnaw_nonambig),
                pkg_name(fixbyrm, pnaw_nonambig),

+ 1 - 1
src/archives.h

@@ -47,7 +47,7 @@ extern struct pkg_deconf_list *deconfigure;
 
 
 void clear_deconfigure_queue(void);
 void clear_deconfigure_queue(void);
 void enqueue_deconfigure(struct pkginfo *pkg, struct pkginfo *pkg_removal);
 void enqueue_deconfigure(struct pkginfo *pkg, struct pkginfo *pkg_removal);
-void enqueue_conflictor(struct pkginfo *pkg, struct pkginfo *pkg_fixbyrm);
+void enqueue_conflictor(struct pkginfo *pkg);
 
 
 void cu_pathname(int argc, void **argv);
 void cu_pathname(int argc, void **argv);
 void cu_cidir(int argc, void **argv);
 void cu_cidir(int argc, void **argv);

+ 32 - 28
src/unpack.c

@@ -45,6 +45,7 @@
 #include <dpkg/dpkg.h>
 #include <dpkg/dpkg.h>
 #include <dpkg/dpkg-db.h>
 #include <dpkg/dpkg-db.h>
 #include <dpkg/pkg.h>
 #include <dpkg/pkg.h>
+#include <dpkg/pkg-queue.h>
 #include <dpkg/path.h>
 #include <dpkg/path.h>
 #include <dpkg/buffer.h>
 #include <dpkg/buffer.h>
 #include <dpkg/subproc.h>
 #include <dpkg/subproc.h>
@@ -196,19 +197,12 @@ get_control_dir(char *cidir)
   return cidir;
   return cidir;
 }
 }
 
 
-#define MAXCONFLICTORS 20
-
-static struct pkginfo *conflictor[MAXCONFLICTORS];
-static int cflict_index = 0;
+static struct pkg_queue conflictors = PKG_QUEUE_INIT;
 
 
 void
 void
-enqueue_conflictor(struct pkginfo *pkg, struct pkginfo *pkg_fixbyrm)
+enqueue_conflictor(struct pkginfo *pkg)
 {
 {
-  if (cflict_index >= MAXCONFLICTORS)
-    ohshit(_("package %s has too many Conflicts/Replaces pairs"),
-           pkgbin_name(pkg, &pkg->available, pnaw_nonambig));
-
-  conflictor[cflict_index++] = pkg_fixbyrm;
+  pkg_queue_push(&conflictors, pkg);
 }
 }
 
 
 static void
 static void
@@ -423,10 +417,11 @@ void process_archive(const char *filename) {
 
 
   struct dpkg_error err;
   struct dpkg_error err;
   enum parsedbflags parsedb_flags;
   enum parsedbflags parsedb_flags;
-  int r, i;
+  int r;
   pid_t pid;
   pid_t pid;
   struct pkgiterator *it;
   struct pkgiterator *it;
   struct pkginfo *pkg, *otherpkg;
   struct pkginfo *pkg, *otherpkg;
+  struct pkg_list *conflictor_iter;
   char *cidirrest, *p;
   char *cidirrest, *p;
   char conffilenamebuf[MAXCONFFILENAME];
   char conffilenamebuf[MAXCONFFILENAME];
   char *psize;
   char *psize;
@@ -698,9 +693,10 @@ void process_archive(const char *filename) {
   /* All the old conffiles are marked with a flag, so that we don't delete
   /* All the old conffiles are marked with a flag, so that we don't delete
    * them if they seem to disappear completely. */
    * them if they seem to disappear completely. */
   oldconffsetflags(pkg->installed.conffiles);
   oldconffsetflags(pkg->installed.conffiles);
-  for (i = 0 ; i < cflict_index ; i++) {
-    oldconffsetflags(conflictor[i]->installed.conffiles);
-  }
+  for (conflictor_iter = conflictors.head;
+       conflictor_iter;
+       conflictor_iter = conflictor_iter->next)
+    oldconffsetflags(conflictor_iter->pkg->installed.conffiles);
 
 
   oldversionstatus= pkg->status;
   oldversionstatus= pkg->status;
 
 
@@ -778,24 +774,30 @@ void process_archive(const char *filename) {
     }
     }
   }
   }
 
 
-  for (i = 0 ; i < cflict_index; i++) {
-    if (!(conflictor[i]->status == stat_halfconfigured ||
-          conflictor[i]->status == stat_triggersawaited ||
-          conflictor[i]->status == stat_triggerspending ||
-          conflictor[i]->status == stat_installed)) continue;
-    trig_activate_packageprocessing(conflictor[i]);
-    pkg_set_status(conflictor[i], stat_halfconfigured);
-    modstatdb_note(conflictor[i]);
+  for (conflictor_iter = conflictors.head;
+       conflictor_iter;
+       conflictor_iter = conflictor_iter->next) {
+    struct pkginfo *conflictor = conflictor_iter->pkg;
+
+    if (!(conflictor->status == stat_halfconfigured ||
+          conflictor->status == stat_triggersawaited ||
+          conflictor->status == stat_triggerspending ||
+          conflictor->status == stat_installed))
+      continue;
+
+    trig_activate_packageprocessing(conflictor);
+    pkg_set_status(conflictor, stat_halfconfigured);
+    modstatdb_note(conflictor);
     push_cleanup(cu_prerminfavour, ~ehflag_normaltidy, NULL, 0,
     push_cleanup(cu_prerminfavour, ~ehflag_normaltidy, NULL, 0,
-                 2,(void*)conflictor[i],(void*)pkg);
-    maintscript_installed(conflictor[i], PRERMFILE, "pre-removal",
+                 2, conflictor, pkg);
+    maintscript_installed(conflictor, PRERMFILE, "pre-removal",
                           "remove", "in-favour",
                           "remove", "in-favour",
                           pkgbin_name(pkg, &pkg->available, pnaw_nonambig),
                           pkgbin_name(pkg, &pkg->available, pnaw_nonambig),
                           versiondescribe(&pkg->available.version,
                           versiondescribe(&pkg->available.version,
                                           vdew_nonambig),
                                           vdew_nonambig),
                           NULL);
                           NULL);
-    pkg_set_status(conflictor[i], stat_halfinstalled);
-    modstatdb_note(conflictor[i]);
+    pkg_set_status(conflictor, stat_halfinstalled);
+    modstatdb_note(conflictor);
   }
   }
 
 
   pkg_set_eflags(pkg, eflag_reinstreq);
   pkg_set_eflags(pkg, eflag_reinstreq);
@@ -1424,11 +1426,13 @@ void process_archive(const char *filename) {
    * The files list for the conflictor is still a little inconsistent in-core,
    * The files list for the conflictor is still a little inconsistent in-core,
    * as we have not yet updated the filename->packages mappings; however,
    * as we have not yet updated the filename->packages mappings; however,
    * the package->filenames mapping is. */
    * the package->filenames mapping is. */
-  for (i = 0 ; i < cflict_index ; i++) {
+  while (!pkg_queue_is_empty(&conflictors)) {
+    struct pkginfo *conflictor = pkg_queue_pop(&conflictors);
+
     /* We need to have the most up-to-date info about which files are
     /* We need to have the most up-to-date info about which files are
      * what ... */
      * what ... */
     ensure_allinstfiles_available();
     ensure_allinstfiles_available();
-    removal_bulk(conflictor[i]);
+    removal_bulk(conflictor);
   }
   }
 
 
   if (cipaction->arg_int == act_install)
   if (cipaction->arg_int == act_install)