[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#184635: dpkg and Replaces:



tag 184635 patch
thanks

> Suppose I have two packages A and B. B Replaces: A but does not provide
> or conflict with A, it merely replaces a few files from A. If I install
> A first and then B, it works. If I install B first and then A, dpkg
> complains that files from A already exist in B and aborts installation.

archive.c needs to be changed to support this behaviour. The following
patch does so. It works by checking replaces in both directions, rather
than just one, and if the package that owns the conflicting file does
replace us, it removes the current file from the filelist (the oldnifd
stuff), and just copies the file we're working on to /dev/null.

Hrm, actually that's probably a bug: there probably needs to be
a check for ti->type == NormalFile0 or NormalFile1 around the
fd_null_copy/safe_read in the if (alreadyreplaced) block. Test by
having a symlink in common between the replaced packages rather than a
regular file.

(It's weird. I'm sure I did this patch before already. I must've never
gotten it to work though.)

I've given it some minimal testing, and I think it's basically sound. The
oldnifd stuff is a memory leak, unfortunately, but trying to avoid that
seems a lot riskier than just leaving it.

diff -urb dpkg-1.10.18/main/archives.c dpkg-1.10.18-aj/main/archives.c
--- dpkg-1.10.18/main/archives.c	2003-10-26 06:03:20.000000000 +1000
+++ dpkg-1.10.18-aj/main/archives.c	2003-11-03 04:40:27.000000000 +1000
@@ -314,11 +314,11 @@
   const char *usename;
     
   struct tarcontext *tc= (struct tarcontext*)ti->UserData;
-  int statr, fd, i, existingdirectory;
+  int statr, fd, i, existingdirectory, alreadyreplaced;
   size_t r;
   struct stat stab, stabd;
   char databuf[TARBLKSZ];
-  struct fileinlist *nifd;
+  struct fileinlist *nifd, **oldnifd;
   struct pkginfo *divpkg, *otherpkg;
   struct filepackages *packageslump;
   mode_t am;
@@ -331,6 +331,7 @@
    */
   nifd= obstack_alloc(&tar_obs, sizeof(struct fileinlist));
   nifd->namenode= findnamenode(ti->Name, 0);
+  oldnifd= tc->newfilesp;  /* in case we don't want this file after all :( */
   nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next;
   nifd->namenode->flags |= fnnf_new_inarchive;
 
@@ -423,6 +424,7 @@
     ohshit(_("archive contained object `%.255s' of unknown type 0x%x"),ti->Name,ti->Type);
   }
 
+  alreadyreplaced= 0;
   if (!existingdirectory) {
     for (packageslump= nifd->namenode->packages;
          packageslump;
@@ -441,19 +443,40 @@
                 divpkg ? divpkg->name : "<none>");
           if (otherpkg == divpkg || tc->pkg == divpkg) continue;
         }
+
         /* Nope ?  Hmm, file conflict, perhaps.  Check Replaces. */
-        if (otherpkg->clientdata->replacingfilesandsaid) continue;
+
+        /* Already noticed this package is replacing us or being replaced? */
+        if (otherpkg->clientdata->replacingfilesandsaid == 1) continue;
+        if (otherpkg->clientdata->replacingfilesandsaid == 2) {
+	  alreadyreplaced= 1;
+	  continue;
+	}
+
         /* Is the package with the conflicting file in the `config files
          * only' state ?  If so it must be a config file and we can
          * silenty take it over.
          */
         if (otherpkg->status == stat_configfiles) continue;
+
         /* Perhaps we're removing a conflicting package ? */
         if (otherpkg->clientdata->istobe == itb_remove) continue;
+
         if (does_replace(tc->pkg,&tc->pkg->available,otherpkg)) {
           printf(_("Replacing files in old package %s ...\n"),otherpkg->name);
           otherpkg->clientdata->replacingfilesandsaid= 1;
-        } else {
+          continue;
+        }
+
+	/* Maybe we're being replaced? */
+	if (does_replace(otherpkg,&otherpkg->installed,tc->pkg)) {
+	  printf(_("Replacing files using old package %s ...\n"),otherpkg->name);
+	  otherpkg->clientdata->replacingfilesandsaid= 2;
+	  alreadyreplaced= 1;
+          continue;
+	}
+
+        {
           if (!statr && S_ISDIR(stab.st_mode)) {
             forcibleerr(fc_overwritedir, _("trying to overwrite directory `%.250s' "
                         "in package %.250s with nondirectory"),
@@ -481,6 +504,18 @@
 
   if (existingdirectory) return 0;
 
+  if (alreadyreplaced) {
+    { char fnamebuf[256];
+    fd_null_copy(tc->backendpipe,ti->Size,_("zap already replaced file `%.255s'"),quote_filename(fnamebuf,256,ti->Name));
+    }
+    r= ti->Size % TARBLKSZ;
+    if (r > 0) r= safe_read(tc->backendpipe,databuf,TARBLKSZ - r);
+
+    tc->newfilesp= oldnifd;
+    *oldnifd= 0;
+    return 0;
+  }
+
   /* Now we start to do things that we need to be able to undo
    * if something goes wrong.
    */


Anyway, FWIW, HTH, etc.

Cheers,
aj

-- 
Anthony Towns <aj@humbug.org.au> <http://azure.humbug.org.au/~aj/>
I don't speak for anyone save myself. GPG signed mail preferred.

Australian DMCA (the Digital Agenda Amendments) Under Review!
	-- http://azure.humbug.org.au/~aj/blog/copyright/digitalagenda




Reply to: