]> err.no Git - dpkg/commitdiff
Improve processing of disappearing conffiles (Ian Jackson).
authorFrank Lichtenheld <djpig@debian.org>
Fri, 10 Feb 2006 16:22:50 +0000 (16:22 +0000)
committerFrank Lichtenheld <djpig@debian.org>
Fri, 10 Feb 2006 16:22:50 +0000 (16:22 +0000)
This is part of the fix for #108587.

* lib/dpkg-db.h (conffile): Add `obsolete' field.
* lib/dump.c (w_conffiles): Write "obsolete" at the
end of conffile entry if obsolete is set.
* lib/fields.c (f_conffiles): Parse entries for
obsolete conffiles correctly.
* src/filesdb.h (filenamenode.flags): Add new
flag for obsolete conffiles.
* src/remove.c (removal_bulk_remove_configfiles):
Handle obsolete conffiles.
* src/archives.c (newconff_append): New function
to append a filenamenode to a fileinlist.
(addfiletolist): New function to add a filenamenode
to a tarcontext.
(tarobject): Use new addfiletolist function.
Handle case where a new package takes over
an obsolete conffile from another package.
* src/archives.h: Add declaration of the
addfiletolist function.
* src/processarc.c (process_archive): Use new
newconff_append function from archives.c.
Detect obsoleted conffiles and mark them as such.
* src/help.c (chmodsafe_unlink): Make it possible
to differentiate between failed chmod and failed
unlink by adding a new `failed' argument which
will be set to the name of the failed command.
(chmodsafe_unlink_statted): New function that
can be called if we already have a stat result for
the file/directory to be removed.
(ensure_pathname_nonexisting): Give better error
messages by utilizing the changes to
chmodsafe_unlink.
* src/main.h: Reflect changes in archives.c
and help.c (add declarations for newconff_append
and chmodsafe_unlink_statted and change the
one of chmodsafe_unlink).
(conffopt): Add new isold flag.

12 files changed:
ChangeLog
debian/changelog
lib/dpkg-db.h
lib/dump.c
lib/fields.c
src/archives.c
src/archives.h
src/filesdb.h
src/help.c
src/main.h
src/processarc.c
src/remove.c

index c9a34d4df92523f8a390e3c7e0ff507487a0809f..efe517dd9369736fde8fb1831dcba21273dd2aac 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
        a warning that was produced when trying to
        backup a non-existant file.
 
+       * lib/dpkg-db.h (conffile): Add `obsolete' field.
+       * lib/dump.c (w_conffiles): Write "obsolete" at the
+       end of conffile entry if obsolete is set.
+       * lib/fields.c (f_conffiles): Parse entries for
+       obsolete conffiles correctly.
+       * src/filesdb.h (filenamenode.flags): Add new
+       flag for obsolete conffiles.
+       * src/remove.c (removal_bulk_remove_configfiles):
+       Handle obsolete conffiles.
+       * src/archives.c (newconff_append): New function
+       to append a filenamenode to a fileinlist.
+       (addfiletolist): New function to add a filenamenode
+       to a tarcontext.
+       (tarobject): Use new addfiletolist function.
+       Handle case where a new package takes over
+       an obsolete conffile from another package.
+       * src/archives.h: Add declaration of the
+       addfiletolist function.
+       * src/processarc.c (process_archive): Use new
+       newconff_append function from archives.c.
+       Detect obsoleted conffiles and mark them as such.
+       * src/help.c (chmodsafe_unlink): Make it possible
+       to differentiate between failed chmod and failed
+       unlink by adding a new `failed' argument which
+       will be set to the name of the failed command.
+       (chmodsafe_unlink_statted): New function that
+       can be called if we already have a stat result for
+       the file/directory to be removed.
+       (ensure_pathname_nonexisting): Give better error
+       messages by utilizing the changes to
+       chmodsafe_unlink.
+       * src/main.h: Reflect changes in archives.c
+       and help.c (add declarations for newconff_append
+       and chmodsafe_unlink_statted and change the
+       one of chmodsafe_unlink).
+       (conffopt): Add new isold flag.
+
 2006-02-10  James R. Van Zandt  <jrvz@comcast.net>
 
        * man/C/dpkg.1: Document the default log file. The behaviour in case
index 1069813a2896364286b6f46c8354588c32cf80fa..435b0453a55c41df29abfbeb5a88e6bf314d9472 100644 (file)
@@ -44,6 +44,8 @@ dpkg (1.13.14~) UNRELEASED; urgency=low
     variables.
   * On package configuration, differentiate between modified and
     deleted configuration files (Ian Jackson). Closes: #351361
+  * Improve processing of disappearing conffiles (Ian Jackson).
+    This is part of the fix for #108587.
   * Let dpkg-source -x touch all patched files to have the same
     timestamp to mitigate time-skew problems (Denis Barbier).
     Closes: #105750
index 6c512095ce72f3715e816af0eeae82e8482dac94..763c695696dc3928041a4498d9174623c7bd3079 100644 (file)
@@ -84,6 +84,7 @@ struct conffile {
   struct conffile *next;
   const char *name;
   const char *hash;
+  int obsolete;
 };
 
 struct filedetails {
index 2b12ffd71d93edd429ed69c9bde3a8e4c82874f9..9e8bb0de15e4a0593d804725fac20968487d11f1 100644 (file)
@@ -233,6 +233,7 @@ void w_conffiles(struct varbuf *vb,
     if (i!=pifp->conffiles) varbufaddc(vb,'\n');
     varbufaddc(vb,' '); varbufaddstr(vb,i->name); varbufaddc(vb,' ');
     varbufaddstr(vb,i->hash);
+    if (i->obsolete) varbufaddstr(vb," obsolete");
   }
   if (flags&fw_printheader)
     varbufaddc(vb,'\n');
index 9d79c14a4130c5697a4b41813c166975cf76da7c..907ea0b960f3eca0996dfff15ba93e9fb3590d1d 100644 (file)
@@ -224,13 +224,38 @@ void f_configversion(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                      "in Config-Version string `%.250s': %.250s"),value,emsg);
 }
 
+void conffvalue_lastword(const char *value, const char *from,
+                        const char *endent,
+                        const char **word_start_r, int *word_len_r,
+                        const char **new_from_r,
+                        const char *filename, int lno,
+                        FILE *warnto, int *warncount, struct pkginfo *pigp) {
+  /* the code in f_conffiles ensures that value[-1]==' ', which is helpful */
+  const char *lastspc;
+  
+  if (from <= value+1) goto malformed;
+  for (lastspc= from-1; *lastspc != ' '; lastspc--);
+  if (lastspc <= value+1 || lastspc >= endent-1) goto malformed;
+
+  *new_from_r= lastspc;
+  *word_start_r= lastspc + 1;
+  *word_len_r= (int)(from - *word_start_r);
+  return;
+
+malformed:
+  parseerr(NULL,filename,lno, warnto,warncount,pigp,0,
+          _("value for `conffiles' has malformatted line `%.*s'"),
+          (int)(endent-value > 250 ? 250 : endent-value), value);
+}
+
 void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                  enum parsedbflags flags,
                  const char *filename, int lno, FILE *warnto, int *warncount,
                  const char *value, const struct fieldinfo *fip) {
+  static const char obsolete_str[]= "obsolete";
   struct conffile **lastp, *newlink;
-  const char *endent, *endfn;
-  int c, namelen, hashlen;
+  const char *endent, *endfn, *hashstart;
+  int c, namelen, hashlen, obsolete;
   char *newptr;
   
   lastp= &pifp->conffiles;
@@ -240,11 +265,15 @@ void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp,
     if (c != ' ') parseerr(NULL,filename,lno, warnto,warncount,pigp,0, _("value for"
                            " `conffiles' has line starting with non-space `%c'"), c);
     for (endent= value; (c= *endent)!=0 && c != '\n'; endent++);
-    for (endfn= endent; *endfn != ' '; endfn--);
-    if (endfn <= value+1 || endfn >= endent-1)
-      parseerr(NULL,filename,lno, warnto,warncount,pigp,0,
-               _("value for `conffiles' has malformatted line `%.*s'"),
-               (int)(endent-value > 250 ? 250 : endent-value), value);
+    conffvalue_lastword(value, endent, endent,
+                       &hashstart, &hashlen, &endfn,
+                       filename,lno,warnto,warncount,pigp);
+    obsolete= (hashlen == sizeof(obsolete_str)-1 &&
+              !memcmp(hashstart, obsolete_str, hashlen));
+    if (obsolete)
+      conffvalue_lastword(value, endfn, endent,
+                         &hashstart, &hashlen, &endfn,
+                         filename,lno,warnto,warncount,pigp);
     newlink= nfmalloc(sizeof(struct conffile));
     value= skip_slash_dotslash(value);
     namelen= (int)(endfn-value);
@@ -255,9 +284,10 @@ void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp,
     memcpy(newptr+1,value,namelen);
     newptr[namelen+1]= 0;
     newlink->name= newptr;
-    hashlen= (int)(endent-endfn)-1; newptr= nfmalloc(hashlen+1);
-    memcpy(newptr,endfn+1,hashlen); newptr[hashlen]= 0;
+    newptr= nfmalloc(hashlen+1);
+    memcpy(newptr,hashstart,hashlen); newptr[hashlen]= 0;
     newlink->hash= newptr;
+    newlink->obsolete= obsolete;
     newlink->next =NULL;
     *lastp= newlink;
     lastp= &newlink->next;
index 78daddc4fade4cffaa8889ec04a551036e9fbbfe..7b1e9695734ec6343555bbdd107fed3017f485f0 100644 (file)
@@ -315,14 +315,25 @@ int unlinkorrmdir(const char *filename) {
   errno= e; return r;
 }
 
+struct fileinlist *addfiletolist(struct tarcontext *tc,
+                                struct filenamenode *namenode) {
+  struct fileinlist *nifd;
+  
+  nifd= obstack_alloc(&tar_obs, sizeof(struct fileinlist));
+  nifd->namenode= namenode;
+  nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next;
+  return nifd;
+}
+
 int tarobject(struct TarInfo *ti) {
   static struct varbuf conffderefn, hardlinkfn, symlinkfn;
   const char *usename;
-    
+
+  struct conffile *conff;
   struct tarcontext *tc= (struct tarcontext*)ti->UserData;
   int statr, fd, i, existingdirectory, keepexisting;
   size_t r;
-  struct stat stab, stabd;
+  struct stat stab, stabtmp;
   char databuf[TARBLKSZ];
   struct fileinlist *nifd, **oldnifd;
   struct pkginfo *divpkg, *otherpkg;
@@ -336,9 +347,7 @@ int tarobject(struct TarInfo *ti) {
    * been stripped by TarExtractor (lib/tarfn.c).
    */
   oldnifd= tc->newfilesp;
-  nifd= obstack_alloc(&tar_obs, sizeof(struct fileinlist));
-  nifd->namenode= findnamenode(ti->Name, 0);
-  nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next;
+  nifd= addfiletolist(tc, findnamenode(ti->Name, 0));
   nifd->namenode->flags |= fnnf_new_inarchive;
 
   debug(dbg_eachfile,
@@ -417,7 +426,7 @@ int tarobject(struct TarInfo *ti) {
     break;
   case Directory:
     /* If it's already an existing directory, do nothing. */
-    if (!stat(fnamevb.buf,&stabd) && S_ISDIR(stabd.st_mode)) {
+    if (!stat(fnamevb.buf,&stabtmp) && S_ISDIR(stabtmp.st_mode)) {
       debug(dbg_eachfiledetail,"tarobject Directory exists");
       existingdirectory= 1;
     }
@@ -463,6 +472,29 @@ int tarobject(struct TarInfo *ti) {
         if (otherpkg->status == stat_configfiles) continue;
         /* Perhaps we're removing a conflicting package ? */
         if (otherpkg->clientdata->istobe == itb_remove) continue;
+
+       /* Is the file an obsolete conffile in the other package
+        * and a conffile in the new package ? */
+       if ((nifd->namenode->flags & fnnf_new_conff) &&
+           !statr && S_ISREG(stab.st_mode)) {
+         for (conff= otherpkg->installed.conffiles;
+              conff;
+              conff= conff->next) {
+           if (!conff->obsolete)
+             continue;
+           if (stat(conff->name, &stabtmp))
+             if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP)
+               continue;
+           if (stabtmp.st_dev == stab.st_dev &&
+               stabtmp.st_ino == stab.st_ino)
+             break;
+         }
+         if (conff)
+           debug(dbg_eachfiledetail,"tarobject other's obsolete conffile");
+           /* processarc.c will have copied its hash already. */
+           continue;
+       }
+
         if (does_replace(tc->pkg,&tc->pkg->available,otherpkg)) {
           printf(_("Replacing files in old package %s ...\n"),otherpkg->name);
           otherpkg->clientdata->replacingfilesandsaid= 1;
@@ -1103,5 +1135,17 @@ int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int sa
   }
 }
 
+struct fileinlist *newconff_append(struct fileinlist ***newconffileslastp_io,
+                                  struct filenamenode *namenode) {
+  struct fileinlist *newconff;
+
+  newconff= m_malloc(sizeof(struct fileinlist));
+  newconff->next= 0;
+  newconff->namenode= namenode;
+  **newconffileslastp_io= newconff;
+  *newconffileslastp_io= &newconff->next;
+  return newconff;
+}
+
 /* vi: ts=8 sw=2
  */
index 6141b693377075b040b9f215201e7709299dbbd3..60b3aae38cb133371fd6b4af204b20e6bb0e74d8 100644 (file)
@@ -66,6 +66,9 @@ int filesavespackage(struct fileinlist*, struct pkginfo*,
 void check_conflict(struct dependency *dep, struct pkginfo *pkg,
                     const char *pfilename);
 
+struct fileinlist *addfiletolist(struct tarcontext *tc,
+                                struct filenamenode *namenode);
+
 extern int cleanup_pkg_failed, cleanup_conflictor_failed;
 
 #endif /* ARCHIVES_H */
index 3abaa205d6cfbae46a8517eccfa81a88b8069182..912dea12dd038398a88d2014cf3d353f2147216f 100644 (file)
@@ -65,6 +65,7 @@ struct filenamenode {
     fnnf_new_conff=           000001, /* in the newconffiles list */
     fnnf_new_inarchive=       000002, /* in the new filesystem archive */
     fnnf_old_conff=           000004, /* in the old package's conffiles list */
+    fnnf_obs_conff=           000100, /* obsolete conffile */
     fnnf_elide_other_lists=   000010, /* must remove from other packages' lists */
     fnnf_no_atomic_overwrite= 000020, /* >=1 instance is a dir, cannot rename over */
     fnnf_placed_on_disk=      000040, /* new file has been placed on the disk */
index 2514ea92a255539d5e30fe73590c89f1fc782caa..a8b8c2008089bfbfa3b88486dc2b3b932db94435 100644 (file)
@@ -430,23 +430,35 @@ void oldconffsetflags(const struct conffile *searchconff) {
   while (searchconff) {
     namenode= findnamenode(searchconff->name, 0); /* XXX */
     namenode->flags |= fnnf_old_conff;
+    if (!namenode->oldhash)
+      namenode->oldhash= searchconff->hash;
     debug(dbg_conffdetail, "oldconffsetflags `%s' namenode %p flags %o",
           searchconff->name, namenode, namenode->flags);
     searchconff= searchconff->next;
   }
 }
 
-int chmodsafe_unlink(const char *pathname) {
+int chmodsafe_unlink(const char *pathname, const char **failed) {
+  /* Sets *failed to `chmod' or `unlink' if those calls fail (which is
+   * always unexpected).  If stat fails it leaves *failed alone. */
   struct stat stab;
 
   if (lstat(pathname,&stab)) return -1;
-  if (S_ISREG(stab.st_mode) ? (stab.st_mode & 07000) :
-      !(S_ISLNK(stab.st_mode) || S_ISDIR(stab.st_mode) ||
-   S_ISFIFO(stab.st_mode) || S_ISSOCK(stab.st_mode))) {
+  *failed= N_("unlink");
+  return chmodsafe_unlink_statted(pathname, &stab, failed);
+}
+  
+int chmodsafe_unlink_statted(const char *pathname, const struct stat *stab,
+                            const char **failed) {
+  /* Sets *failed to `chmod'' if that call fails (which is always
+   * unexpected).  If unlink fails it leaves *failed alone. */
+  if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) :
+      !(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) ||
+       S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) {
     /* We chmod it if it is 1. a sticky or set-id file, or 2. an unrecognised
-     * object (ie, not a file, link, directory, fifo or socket
+     * object (ie, not a file, link, directory, fifo or socket)
      */
-    if (chmod(pathname,0600)) return -1;
+    if (chmod(pathname,0600)) { *failed= N_("chmod"); return -1; }
   }
   if (unlink(pathname)) return -1;
   return 0;
@@ -454,7 +466,7 @@ int chmodsafe_unlink(const char *pathname) {
 
 void ensure_pathname_nonexisting(const char *pathname) {
   int c1;
-  const char *u;
+  const char *u, *failed;
 
   u= skip_slash_dotslash(pathname);
   assert(*u);
@@ -462,15 +474,19 @@ void ensure_pathname_nonexisting(const char *pathname) {
   debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname);
   if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */
   if (errno == ENOENT || errno == ELOOP) return;
+  failed= N_("delete");
   if (errno == ENOTDIR) {
     /* Either it's a file, or one of the path components is.  If one
      * of the path components is this will fail again ...
      */
-    if (!chmodsafe_unlink(pathname)) return; /* OK, it was */
+    if (!chmodsafe_unlink(pathname, &failed)) return; /* OK, it was */
     if (errno == ENOTDIR) return;
   }
-  if (errno != ENOTEMPTY) /* Huh ? */
-    ohshite(_("failed to rmdir/unlink `%.255s'"),pathname);
+  if (errno != ENOTEMPTY) { /* Huh ? */
+    char mbuf[250];
+    snprintf(mbuf, sizeof(mbuf), N_("failed to %s `%%.255s'"), failed);
+    ohshite(_(mbuf),pathname);
+  }
   c1= m_fork();
   if (!c1) {
     execlp(RM,"rm","-rf","--",pathname,(char*)0);
index 44a4fd63100066d17d6551b55ba40c5d4c8cdbdc..8e9731b51292c6373b787df7ec83c7b15a6f1412 100644 (file)
@@ -61,9 +61,10 @@ enum conffopt {
   cfof_prompt        =     001,
   cfof_keep          =     002,
   cfof_install       =     004,
-  cfof_backup        =    0100,
-  cfof_newconff      =    0200,
-  cfof_isnew         =    0400,
+  cfof_backup        =   00100,
+  cfof_newconff      =   00200,
+  cfof_isnew         =   00400,
+  cfof_isold         =   01000,
   cfom_main          =     007,
   cfo_keep           =   cfof_keep,
   cfo_prompt_keep    =   cfof_keep | cfof_prompt,
@@ -103,6 +104,8 @@ void filesdbinit(void);
 void archivefiles(const char *const *argv);
 void process_archive(const char *filename);
 int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int saywhy);
+struct fileinlist *newconff_append(struct fileinlist ***newconffileslastp_io,
+                                  struct filenamenode *namenode);
 
 /* from update.c */
 
@@ -179,7 +182,9 @@ void ensure_package_clientdata(struct pkginfo *pkg);
 const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile);
 void oldconffsetflags(const struct conffile *searchconff);
 void ensure_pathname_nonexisting(const char *pathname);
-int chmodsafe_unlink(const char *pathname); /* chmod 600, then unlink */
+int chmodsafe_unlink(const char *pathname, const char **failed);
+int chmodsafe_unlink_statted(const char *pathname, const struct stat *stab,
+                            const char **failed);
 void checkpath(void);
 struct filenamenode *namenodetouse(struct filenamenode*, struct pkginfo*);
 
index 56536e2d20319b1ba8729d56af54d27549625025..55b4683b5cef5af74d921e57c89df53f1dba02a1 100644 (file)
@@ -68,7 +68,7 @@ void process_archive(const char *filename) {
   struct pkginfo *pkg, *otherpkg, *divpkg;
   char *cidir, *cidirrest, *p;
   char *pfilenamebuf, conffilenamebuf[MAXCONFFILENAME];
-  const char *pfilename, *newinfofilename;
+  const char *pfilename, *newinfofilename, *failed;
   struct fileinlist *newconff, **newconffileslastp;
   struct fileinlist *cfile;
   struct reversefilelistiter rlistit;
@@ -81,7 +81,7 @@ void process_archive(const char *filename) {
   DIR *dsd;
   struct filenamenode *namenode;
   struct dirent *de;
-  struct stat stab;
+  struct stat stab, oldfs;
   struct packageinlist *deconpil, *deconpiltemp;
   
   cleanup_pkg_failed= cleanup_conflictor_failed= 0;
@@ -313,12 +313,10 @@ void process_archive(const char *filename) {
       while (p > conffilenamebuf && isspace(p[-1])) --p;
       if (p == conffilenamebuf) continue;
       *p= 0;
-      newconff= m_malloc(sizeof(struct fileinlist));
-      newconff->next= 0;
-      newconff->namenode= findnamenode(conffilenamebuf, 0);
-      *newconffileslastp= newconff;
-      newconffileslastp= &newconff->next;
-      newconff->namenode->oldhash= NEWCONFFILEFLAG;
+      namenode= findnamenode(conffilenamebuf, 0);
+      namenode->oldhash= NEWCONFFILEFLAG;
+      newconff= newconff_append(&newconffileslastp, namenode);
+      
       /* Let's see if any packages have this file.  If they do we
        * check to see if they listed it as a conffile, and if they did
        * we copy the hash across.  Since (for plain file conffiles,
@@ -356,6 +354,7 @@ void process_archive(const char *filename) {
     xit_conff_hashcopy_srch:
       if (searchconff) {
         newconff->namenode->oldhash= searchconff->hash;
+       /* we don't copy `obsolete'; it's not obsolete in the new package */
       } else {
         debug(dbg_conff,"process_archive conffile `%s' no package, no hash",
               newconff->namenode->name);
@@ -523,8 +522,10 @@ void process_archive(const char *filename) {
    * package isn't one we're already processing, and the package's
    * list becomes empty as a result, we `vanish' the package.  This
    * means that we run its postrm with the `disappear' argument, and
-   * put the package in the `not-installed' state.  Its conffiles are
-   * ignored and forgotten about.
+   * put the package in the `not-installed' state.  If it had any
+   * conffiles, their hashes and ownership will have been transferred
+   * already, so we just ignore those and forget about them from the
+   * point of view of the disappearing package.
    *
    * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the
    * files get replaced `as we go'.
@@ -592,8 +593,7 @@ void process_archive(const char *filename) {
    */
   reversefilelist_init(&rlistit,pkg->clientdata->files);
   while ((namenode= reversefilelist_next(&rlistit))) {
-    if ((namenode->flags & fnnf_old_conff) ||
-        (namenode->flags & fnnf_new_conff) ||
+    if ((namenode->flags & fnnf_new_conff) ||
         (namenode->flags & fnnf_new_inarchive))
       continue;
     if (!stat(namenode->name,&stab) && S_ISDIR(stab.st_mode)) {
@@ -604,10 +604,30 @@ void process_archive(const char *filename) {
     fnamevb.used= fnameidlu;
     varbufaddstr(&fnamevb, namenodetouse(namenode,pkg)->name);
     varbufaddc(&fnamevb,0);
-    if (!rmdir(fnamevb.buf)) continue;
-    if (errno == ENOENT || errno == ELOOP) continue;
-    if (errno == ENOTDIR) {
+
+    if (lstat(fnamevb.buf, &oldfs)) {
+      if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
+       fprintf(stderr,
+               _("dpkg: warning - could not stat old file `%.250s'"
+                 " so not deleting it: %s"),
+               fnamevb.buf, strerror(errno));
+      continue;
+    }
+    if (S_ISDIR(oldfs.st_mode)) {
+      if (rmdir(fnamevb.buf)) {
+       fprintf(stderr,
+               _("dpkg: warning - unable to delete old directory"
+                 " `%.250s': %s\n"), namenode->name, strerror(errno));
+      } else if ((namenode->flags & fnnf_old_conff)) {
+       fprintf(stderr,
+               _("dpkg: warning - old conffile `%.250s' was an empty"
+                 " directory (and has now been deleted)\n"),
+               namenode->name);
+      }
+    } else {
       /* Ok, it's an old file, but is it really not in the new package?
+       * It might be known by a different name because of symlinks.
+       *
        * We need to check to make sure, so we stat the file, then compare
        * it to the new list. If we find a dev/inode match, we assume they
        * are the same file, and leave it alone. NOTE: we don't check in
@@ -618,54 +638,76 @@ void process_archive(const char *filename) {
        * the process a little leaner. We are only worried about new ones
        * since ones that stayed the same don't really apply here.
        */
-      struct stat oldfs;
-      int donotrm = 0;
+      struct fileinlist *sameas= 0;
       /* If we can't stat the old or new file, or it's a directory,
        * we leave it up to the normal code
        */
       debug(dbg_eachfile, "process_archive: checking %s for same files on "
-         "upgrade/downgrade", fnamevb.buf);
-      if (!lstat(fnamevb.buf, &oldfs) && !S_ISDIR(oldfs.st_mode)) {
-       for (cfile = newfileslist; cfile; cfile = cfile->next) {
-         if (!cfile->namenode->filestat) {
-           cfile->namenode->filestat = (struct stat *) nfmalloc(sizeof(struct stat));
-           if (lstat(cfile->namenode->name, cfile->namenode->filestat)) {
-             cfile->namenode->filestat= 0;
-             continue;
-           }
-         }
-         if (S_ISDIR(cfile->namenode->filestat->st_mode))
+           "upgrade/downgrade", fnamevb.buf);
+
+      for (cfile= newfileslist; cfile; cfile= cfile->next) {
+       if (!cfile->namenode->filestat) {
+         cfile->namenode->filestat= nfmalloc(sizeof(struct stat));
+         if (lstat(cfile->namenode->name, cfile->namenode->filestat)) {
+           if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
+             ohshite(_("unable to stat other new file `%.250s'"),
+                     cfile->namenode->name);
+           memset(cfile->namenode->filestat, 0,
+                  sizeof(cfile->namenode->filestat));
            continue;
-          if (oldfs.st_dev == cfile->namenode->filestat->st_dev &&
-              oldfs.st_ino == cfile->namenode->filestat->st_ino) {
-           donotrm = 1;
-           debug(dbg_eachfile, "process_archive: not removing %s, since it matches %s",
-               fnamevb.buf, cfile->namenode->name);
          }
        }
-      } else
-       debug(dbg_eachfile, "process_archive: could not stat %s, skipping", fnamevb.buf);
-      if (donotrm) continue;
-      {
-       /*
-        * If file to remove is a device or s[gu]id, change its mode
-        * so that a malicious user cannot use it even if it's linked
-        * to another file.
-        */
-       struct stat stat_buf;
-       if (lstat(fnamevb.buf,&stat_buf)==0) {
-         if (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode))
-           chmod(fnamevb.buf, 0);
-         if (stat_buf.st_mode & (S_ISUID|S_ISGID))
-           chmod(fnamevb.buf, stat_buf.st_mode & ~(S_ISUID|S_ISGID));
+       if (!cfile->namenode->filestat->st_mode) continue;
+       if (oldfs.st_dev == cfile->namenode->filestat->st_dev &&
+           oldfs.st_ino == cfile->namenode->filestat->st_ino) {
+         if (sameas)
+           fprintf(stderr, _("dpkg: warning - old file `%.250s' is the same"
+                     " as several new files!  (both `%.250s' and `%.250s'"),
+                   fnamevb.buf,
+                   sameas->namenode->name, cfile->namenode->name);
+         sameas= cfile;
+         debug(dbg_eachfile, "process_archive: not removing %s,"
+               " since it matches %s", fnamevb.buf, cfile->namenode->name);
        }
       }
-      if (!unlink(fnamevb.buf)) continue;
-      if (errno == ENOTDIR) continue;
-    }
-    fprintf(stderr,
-            _("dpkg: warning - unable to delete old file `%.250s': %s\n"),
-            namenode->name, strerror(errno));
+
+      if ((namenode->flags & fnnf_old_conff)) {
+       if (sameas) {
+         if (sameas->namenode->flags & fnnf_new_conff) {
+           if (!strcmp(sameas->namenode->oldhash, NEWCONFFILEFLAG)) {
+             sameas->namenode->oldhash= namenode->oldhash;
+             debug(dbg_eachfile, "process_archive: old conff %s"
+                   " is same as new conff %s, copying hash",
+                   namenode->name, sameas->namenode->name);
+           } else {
+             debug(dbg_eachfile, "process_archive: old conff %s"
+                   " is same as new conff %s but latter already has hash",
+                   namenode->name, sameas->namenode->name);
+           }
+         }
+       } else {
+         debug(dbg_eachfile, "process_archive: old conff %s"
+               " is disappearing", namenode->name);
+         namenode->flags |= fnnf_obs_conff;
+         newconff_append(&newconffileslastp, namenode);
+         addfiletolist(&tc, namenode);
+       }
+       continue;
+      }
+      
+      if (sameas)
+       continue;
+
+      failed= N_("delete");
+      if (chmodsafe_unlink_statted(fnamevb.buf, &oldfs, &failed)) {
+       char mbuf[250];
+       snprintf(mbuf, sizeof(mbuf),
+                N_("dpkg: warning - unable to %s old file `%%.250s': %%s\n"),
+                failed);
+       fprintf(stderr, _(mbuf), namenode->name, strerror(errno));
+      }
+
+    } /* !S_ISDIR */
   }
 
   /* OK, now we can write the updated files-in-this package list,
@@ -827,6 +869,7 @@ void process_archive(const char *filename) {
     newiconff->next= 0;
     newiconff->name= nfstrsave(cfile->namenode->name);
     newiconff->hash= nfstrsave(cfile->namenode->oldhash);
+    newiconff->obsolete= !!(cfile->namenode->flags & fnnf_obs_conff);
     *iconffileslastp= newiconff;
     iconffileslastp= &newiconff->next;
   }
@@ -1033,6 +1076,8 @@ void process_archive(const char *filename) {
    *
    * Note that we don't ever delete things that were in the old
    * package as a conffile and don't appear at all in the new.
+   * They stay recorded as obsolete conffiles and will eventually
+   * (if not taken over by another package) be forgotten.
    */
   for (cfile= newfileslist; cfile; cfile= cfile->next) {
     if (cfile->namenode->flags & fnnf_new_conff) continue;
index be7c71c8b36ba3dbabcaaf762376054d4955645e..86b9939298418d11be2314b28a6be2c2d6a51836 100644 (file)
@@ -429,6 +429,11 @@ static void removal_bulk_remove_configfiles(struct pkginfo *pkg) {
     
     for (conff= pkg->installed.conffiles; conff; conff= conff->next) {
     static struct varbuf fnvb, removevb;
+      if (conff->obsolete) {
+       debug(dbg_conffdetail, "removal_bulk conffile obsolete %s",
+             conff->name);
+       continue;
+      }
       varbufreset(&fnvb);
       r= conffderef(pkg, &fnvb, conff->name);
       debug(dbg_conffdetail, "removal_bulk conffile `%s' (= `%s')",