summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFrank Lichtenheld <djpig@debian.org>2006-02-10 16:22:50 +0000
committerFrank Lichtenheld <djpig@debian.org>2006-02-10 16:22:50 +0000
commit854ad168370000ca46f4ba091c2d5bb05274b6ef (patch)
tree486038229fb28c385cb573792b2adf0638bf3dfa /src
parentf9fab0b1a4afd66af3d3d9a3d26119732050dcf4 (diff)
downloaddpkg-854ad168370000ca46f4ba091c2d5bb05274b6ef.tar.gz
Improve processing of disappearing conffiles (Ian Jackson).
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.
Diffstat (limited to 'src')
-rw-r--r--src/archives.c56
-rw-r--r--src/archives.h3
-rw-r--r--src/filesdb.h1
-rw-r--r--src/help.c36
-rw-r--r--src/main.h13
-rw-r--r--src/processarc.c153
-rw-r--r--src/remove.c5
7 files changed, 193 insertions, 74 deletions
diff --git a/src/archives.c b/src/archives.c
index 78daddc4f..7b1e96957 100644
--- a/src/archives.c
+++ b/src/archives.c
@@ -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
*/
diff --git a/src/archives.h b/src/archives.h
index 6141b6933..60b3aae38 100644
--- a/src/archives.h
+++ b/src/archives.h
@@ -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 */
diff --git a/src/filesdb.h b/src/filesdb.h
index 3abaa205d..912dea12d 100644
--- a/src/filesdb.h
+++ b/src/filesdb.h
@@ -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 */
diff --git a/src/help.c b/src/help.c
index 2514ea92a..a8b8c2008 100644
--- a/src/help.c
+++ b/src/help.c
@@ -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);
diff --git a/src/main.h b/src/main.h
index 44a4fd631..8e9731b51 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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*);
diff --git a/src/processarc.c b/src/processarc.c
index 56536e2d2..55b4683b5 100644
--- a/src/processarc.c
+++ b/src/processarc.c
@@ -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;
diff --git a/src/remove.c b/src/remove.c
index be7c71c8b..86b993929 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -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')",