Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg - main program for package management | |
3 | * remove.c - functionality for removing packages | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
23 | #include <compat.h> | |
24 | ||
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | ||
28 | #include <errno.h> | |
29 | #include <string.h> | |
30 | #include <fcntl.h> | |
31 | #include <dirent.h> | |
32 | #include <unistd.h> | |
33 | #include <stdlib.h> | |
34 | #include <stdio.h> | |
35 | ||
36 | #include <dpkg/i18n.h> | |
37 | #include <dpkg/c-ctype.h> | |
38 | #include <dpkg/dpkg.h> | |
39 | #include <dpkg/dpkg-db.h> | |
40 | #include <dpkg/pkg.h> | |
41 | #include <dpkg/path.h> | |
42 | #include <dpkg/dir.h> | |
43 | #include <dpkg/options.h> | |
44 | #include <dpkg/triglib.h> | |
45 | ||
46 | #include "infodb.h" | |
47 | #include "filesdb.h" | |
48 | #include "main.h" | |
49 | ||
50 | /* | |
51 | * pkgdepcheck may be a virtual pkg. | |
52 | */ | |
53 | static void checkforremoval(struct pkginfo *pkgtoremove, | |
54 | struct pkgset *pkgdepcheck, | |
55 | enum dep_check *rokp, struct varbuf *raemsgs) | |
56 | { | |
57 | struct deppossi *possi; | |
58 | struct pkginfo *depender; | |
59 | enum dep_check ok; | |
60 | struct varbuf_state raemsgs_state; | |
61 | ||
62 | for (possi = pkgdepcheck->depended.installed; possi; possi = possi->rev_next) { | |
63 | if (possi->up->type != dep_depends && possi->up->type != dep_predepends) continue; | |
64 | depender= possi->up->up; | |
65 | debug(dbg_depcon, "checking depending package '%s'", | |
66 | pkg_name(depender, pnaw_always)); | |
67 | if (depender->status < PKG_STAT_UNPACKED) | |
68 | continue; | |
69 | if (ignore_depends(depender)) { | |
70 | debug(dbg_depcon, "ignoring depending package '%s'", | |
71 | pkg_name(depender, pnaw_always)); | |
72 | continue; | |
73 | } | |
74 | if (dependtry > 1) { if (findbreakcycle(pkgtoremove)) sincenothing= 0; } | |
75 | varbuf_snapshot(raemsgs, &raemsgs_state); | |
76 | ok= dependencies_ok(depender,pkgtoremove,raemsgs); | |
77 | if (ok == DEP_CHECK_HALT && | |
78 | depender->clientdata->istobe == PKG_ISTOBE_REMOVE) | |
79 | ok = DEP_CHECK_DEFER; | |
80 | if (ok == DEP_CHECK_DEFER) | |
81 | /* Don't burble about reasons for deferral. */ | |
82 | varbuf_rollback(raemsgs, &raemsgs_state); | |
83 | if (ok < *rokp) *rokp= ok; | |
84 | } | |
85 | } | |
86 | ||
87 | void deferred_remove(struct pkginfo *pkg) { | |
88 | struct varbuf raemsgs = VARBUF_INIT; | |
89 | struct dependency *dep; | |
90 | enum dep_check rok; | |
91 | ||
92 | debug(dbg_general, "deferred_remove package %s", | |
93 | pkg_name(pkg, pnaw_always)); | |
94 | ||
95 | if (!f_pending && pkg->want != PKG_WANT_UNKNOWN) { | |
96 | if (cipaction->arg_int == act_purge) | |
97 | pkg_set_want(pkg, PKG_WANT_PURGE); | |
98 | else | |
99 | pkg_set_want(pkg, PKG_WANT_DEINSTALL); | |
100 | ||
101 | if (!f_noact) | |
102 | modstatdb_note(pkg); | |
103 | } | |
104 | ||
105 | if (pkg->status == PKG_STAT_NOTINSTALLED) { | |
106 | sincenothing = 0; | |
107 | warning(_("ignoring request to remove %.250s which isn't installed"), | |
108 | pkg_name(pkg, pnaw_nonambig)); | |
109 | pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; | |
110 | return; | |
111 | } else if (!f_pending && | |
112 | pkg->status == PKG_STAT_CONFIGFILES && | |
113 | cipaction->arg_int != act_purge) { | |
114 | sincenothing = 0; | |
115 | warning(_("ignoring request to remove %.250s, only the config\n" | |
116 | " files of which are on the system; use --purge to remove them too"), | |
117 | pkg_name(pkg, pnaw_nonambig)); | |
118 | pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; | |
119 | return; | |
120 | } | |
121 | ||
122 | if (pkg->installed.essential && pkg->status != PKG_STAT_CONFIGFILES) | |
123 | forcibleerr(fc_removeessential, | |
124 | _("this is an essential package; it should not be removed")); | |
125 | ||
126 | debug(dbg_general, "checking dependencies for remove '%s'", | |
127 | pkg_name(pkg, pnaw_always)); | |
128 | rok = DEP_CHECK_OK; | |
129 | checkforremoval(pkg, pkg->set, &rok, &raemsgs); | |
130 | for (dep= pkg->installed.depends; dep; dep= dep->next) { | |
131 | if (dep->type != dep_provides) continue; | |
132 | debug(dbg_depcon, "checking virtual package '%s'", dep->list->ed->name); | |
133 | checkforremoval(pkg, dep->list->ed, &rok, &raemsgs); | |
134 | } | |
135 | ||
136 | if (rok == DEP_CHECK_DEFER) { | |
137 | varbuf_destroy(&raemsgs); | |
138 | pkg->clientdata->istobe = PKG_ISTOBE_REMOVE; | |
139 | enqueue_package(pkg); | |
140 | return; | |
141 | } else if (rok == DEP_CHECK_HALT) { | |
142 | sincenothing= 0; | |
143 | varbuf_end_str(&raemsgs); | |
144 | notice(_("dependency problems prevent removal of %s:\n%s"), | |
145 | pkg_name(pkg, pnaw_nonambig), raemsgs.buf); | |
146 | ohshit(_("dependency problems - not removing")); | |
147 | } else if (raemsgs.used) { | |
148 | varbuf_end_str(&raemsgs); | |
149 | notice(_("%s: dependency problems, but removing anyway as you requested:\n%s"), | |
150 | pkg_name(pkg, pnaw_nonambig), raemsgs.buf); | |
151 | } | |
152 | varbuf_destroy(&raemsgs); | |
153 | sincenothing= 0; | |
154 | ||
155 | if (pkg->eflag & PKG_EFLAG_REINSTREQ) | |
156 | forcibleerr(fc_removereinstreq, | |
157 | _("package is in a very bad inconsistent state; you should\n" | |
158 | " reinstall it before attempting a removal")); | |
159 | ||
160 | ensure_allinstfiles_available(); | |
161 | filesdbinit(); | |
162 | ||
163 | if (f_noact) { | |
164 | printf(_("Would remove or purge %s (%s) ...\n"), | |
165 | pkg_name(pkg, pnaw_nonambig), | |
166 | versiondescribe(&pkg->installed.version, vdew_nonambig)); | |
167 | pkg_set_status(pkg, PKG_STAT_NOTINSTALLED); | |
168 | pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; | |
169 | return; | |
170 | } | |
171 | ||
172 | pkg_conffiles_mark_old(pkg); | |
173 | ||
174 | /* Only print and log removal action once. This avoids duplication when | |
175 | * using --remove and --purge in sequence. */ | |
176 | if (pkg->status > PKG_STAT_CONFIGFILES) { | |
177 | printf(_("Removing %s (%s) ...\n"), pkg_name(pkg, pnaw_nonambig), | |
178 | versiondescribe(&pkg->installed.version, vdew_nonambig)); | |
179 | log_action("remove", pkg, &pkg->installed); | |
180 | } | |
181 | ||
182 | trig_activate_packageprocessing(pkg); | |
183 | if (pkg->status >= PKG_STAT_HALFCONFIGURED) { | |
184 | static enum pkgstatus oldpkgstatus; | |
185 | ||
186 | oldpkgstatus= pkg->status; | |
187 | pkg_set_status(pkg, PKG_STAT_HALFCONFIGURED); | |
188 | modstatdb_note(pkg); | |
189 | push_cleanup(cu_prermremove, ~ehflag_normaltidy, NULL, 0, 2, | |
190 | (void *)pkg, (void *)&oldpkgstatus); | |
191 | maintscript_installed(pkg, PRERMFILE, "pre-removal", "remove", NULL); | |
192 | ||
193 | /* Will turn into ‘half-installed’ soon ... */ | |
194 | pkg_set_status(pkg, PKG_STAT_UNPACKED); | |
195 | } | |
196 | ||
197 | removal_bulk(pkg); | |
198 | } | |
199 | ||
200 | static void push_leftover(struct fileinlist **leftoverp, | |
201 | struct filenamenode *namenode) { | |
202 | struct fileinlist *newentry; | |
203 | newentry= nfmalloc(sizeof(struct fileinlist)); | |
204 | newentry->next= *leftoverp; | |
205 | newentry->namenode= namenode; | |
206 | *leftoverp= newentry; | |
207 | } | |
208 | ||
209 | static void | |
210 | removal_bulk_remove_file(const char *filename, const char *filetype) | |
211 | { | |
212 | /* We need the postrm and list files for --purge. */ | |
213 | if (strcmp(filetype, LISTFILE) == 0 || | |
214 | strcmp(filetype, POSTRMFILE) == 0) | |
215 | return; | |
216 | ||
217 | debug(dbg_stupidlyverbose, "removal_bulk info not postrm or list"); | |
218 | ||
219 | if (unlink(filename)) | |
220 | ohshite(_("unable to delete control info file '%.250s'"), filename); | |
221 | ||
222 | debug(dbg_scripts, "removal_bulk info unlinked %s", filename); | |
223 | } | |
224 | ||
225 | static bool | |
226 | removal_bulk_file_is_shared(struct pkginfo *pkg, struct filenamenode *namenode) | |
227 | { | |
228 | struct filepackages_iterator *iter; | |
229 | struct pkginfo *otherpkg; | |
230 | bool shared = false; | |
231 | ||
232 | if (pkgset_installed_instances(pkg->set) <= 1) | |
233 | return false; | |
234 | ||
235 | iter = filepackages_iter_new(namenode); | |
236 | while ((otherpkg = filepackages_iter_next(iter))) { | |
237 | if (otherpkg == pkg) | |
238 | continue; | |
239 | if (otherpkg->set != pkg->set) | |
240 | continue; | |
241 | ||
242 | debug(dbg_eachfiledetail, "removal_bulk file shared with %s, skipping", | |
243 | pkg_name(otherpkg, pnaw_always)); | |
244 | shared = true; | |
245 | break; | |
246 | } | |
247 | filepackages_iter_free(iter); | |
248 | ||
249 | return shared; | |
250 | } | |
251 | ||
252 | static void | |
253 | removal_bulk_remove_files(struct pkginfo *pkg) | |
254 | { | |
255 | struct reversefilelistiter rev_iter; | |
256 | struct fileinlist *leftover; | |
257 | struct filenamenode *namenode; | |
258 | static struct varbuf fnvb; | |
259 | struct varbuf_state fnvb_state; | |
260 | struct stat stab; | |
261 | ||
262 | pkg_set_status(pkg, PKG_STAT_HALFINSTALLED); | |
263 | modstatdb_note(pkg); | |
264 | push_checkpoint(~ehflag_bombout, ehflag_normaltidy); | |
265 | ||
266 | reversefilelist_init(&rev_iter, pkg->clientdata->files); | |
267 | leftover = NULL; | |
268 | while ((namenode = reversefilelist_next(&rev_iter))) { | |
269 | struct filenamenode *usenode; | |
270 | bool is_dir; | |
271 | ||
272 | debug(dbg_eachfile, "removal_bulk '%s' flags=%o", | |
273 | namenode->name, namenode->flags); | |
274 | ||
275 | usenode = namenodetouse(namenode, pkg, &pkg->installed); | |
276 | ||
277 | varbuf_reset(&fnvb); | |
278 | varbuf_add_str(&fnvb, instdir); | |
279 | varbuf_add_str(&fnvb, usenode->name); | |
280 | varbuf_end_str(&fnvb); | |
281 | varbuf_snapshot(&fnvb, &fnvb_state); | |
282 | ||
283 | is_dir = stat(fnvb.buf, &stab) == 0 && S_ISDIR(stab.st_mode); | |
284 | ||
285 | /* A pkgset can share files between its instances that we | |
286 | * don't want to remove, we just want to forget them. This | |
287 | * applies to shared conffiles too. */ | |
288 | if (!is_dir && removal_bulk_file_is_shared(pkg, namenode)) | |
289 | continue; | |
290 | ||
291 | /* Non-shared conffiles are kept. */ | |
292 | if (namenode->flags & fnnf_old_conff) { | |
293 | push_leftover(&leftover, namenode); | |
294 | continue; | |
295 | } | |
296 | ||
297 | if (is_dir) { | |
298 | debug(dbg_eachfiledetail, "removal_bulk is a directory"); | |
299 | /* Only delete a directory or a link to one if we're the only | |
300 | * package which uses it. Other files should only be listed | |
301 | * in this package (but we don't check). */ | |
302 | if (dir_has_conffiles(namenode, pkg)) { | |
303 | push_leftover(&leftover,namenode); | |
304 | continue; | |
305 | } | |
306 | if (dir_is_used_by_pkg(namenode, pkg, leftover)) { | |
307 | push_leftover(&leftover, namenode); | |
308 | continue; | |
309 | } | |
310 | if (dir_is_used_by_others(namenode, pkg)) | |
311 | continue; | |
312 | ||
313 | if (strcmp(usenode->name, "/.") == 0) { | |
314 | debug(dbg_eachfiledetail, | |
315 | "removal_bulk '%s' root directory, cannot remove", fnvb.buf); | |
316 | push_leftover(&leftover, namenode); | |
317 | continue; | |
318 | } | |
319 | } | |
320 | ||
321 | trig_path_activate(usenode, pkg); | |
322 | ||
323 | varbuf_rollback(&fnvb, &fnvb_state); | |
324 | varbuf_add_str(&fnvb, DPKGTEMPEXT); | |
325 | varbuf_end_str(&fnvb); | |
326 | debug(dbg_eachfiledetail, "removal_bulk cleaning temp '%s'", fnvb.buf); | |
327 | path_remove_tree(fnvb.buf); | |
328 | ||
329 | varbuf_rollback(&fnvb, &fnvb_state); | |
330 | varbuf_add_str(&fnvb, DPKGNEWEXT); | |
331 | varbuf_end_str(&fnvb); | |
332 | debug(dbg_eachfiledetail, "removal_bulk cleaning new '%s'", fnvb.buf); | |
333 | path_remove_tree(fnvb.buf); | |
334 | ||
335 | varbuf_rollback(&fnvb, &fnvb_state); | |
336 | varbuf_end_str(&fnvb); | |
337 | ||
338 | debug(dbg_eachfiledetail, "removal_bulk removing '%s'", fnvb.buf); | |
339 | if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue; | |
340 | if (errno == ENOTEMPTY || errno == EEXIST) { | |
341 | debug(dbg_eachfiledetail, | |
342 | "removal_bulk '%s' was not empty, will try again later", | |
343 | fnvb.buf); | |
344 | push_leftover(&leftover,namenode); | |
345 | continue; | |
346 | } else if (errno == EBUSY || errno == EPERM) { | |
347 | warning(_("while removing %.250s, unable to remove directory '%.250s': " | |
348 | "%s - directory may be a mount point?"), | |
349 | pkg_name(pkg, pnaw_nonambig), namenode->name, strerror(errno)); | |
350 | push_leftover(&leftover,namenode); | |
351 | continue; | |
352 | } | |
353 | if (errno != ENOTDIR) | |
354 | ohshite(_("cannot remove '%.250s'"), fnvb.buf); | |
355 | debug(dbg_eachfiledetail, "removal_bulk unlinking '%s'", fnvb.buf); | |
356 | if (secure_unlink(fnvb.buf)) | |
357 | ohshite(_("unable to securely remove '%.250s'"), fnvb.buf); | |
358 | } | |
359 | write_filelist_except(pkg, &pkg->installed, leftover, 0); | |
360 | maintscript_installed(pkg, POSTRMFILE, "post-removal", "remove", NULL); | |
361 | ||
362 | trig_parse_ci(pkg_infodb_get_file(pkg, &pkg->installed, TRIGGERSCIFILE), | |
363 | trig_cicb_interest_delete, NULL, pkg, &pkg->installed); | |
364 | trig_file_interests_save(); | |
365 | ||
366 | debug(dbg_general, "removal_bulk cleaning info directory"); | |
367 | pkg_infodb_foreach(pkg, &pkg->installed, removal_bulk_remove_file); | |
368 | dir_sync_path(pkg_infodb_get_dir()); | |
369 | ||
370 | pkg_set_status(pkg, PKG_STAT_CONFIGFILES); | |
371 | pkg->installed.essential = false; | |
372 | modstatdb_note(pkg); | |
373 | push_checkpoint(~ehflag_bombout, ehflag_normaltidy); | |
374 | } | |
375 | ||
376 | static void removal_bulk_remove_leftover_dirs(struct pkginfo *pkg) { | |
377 | struct reversefilelistiter rev_iter; | |
378 | struct fileinlist *leftover; | |
379 | struct filenamenode *namenode; | |
380 | static struct varbuf fnvb; | |
381 | struct stat stab; | |
382 | ||
383 | /* We may have modified this previously. */ | |
384 | ensure_packagefiles_available(pkg); | |
385 | ||
386 | modstatdb_note(pkg); | |
387 | push_checkpoint(~ehflag_bombout, ehflag_normaltidy); | |
388 | ||
389 | reversefilelist_init(&rev_iter, pkg->clientdata->files); | |
390 | leftover = NULL; | |
391 | while ((namenode = reversefilelist_next(&rev_iter))) { | |
392 | struct filenamenode *usenode; | |
393 | ||
394 | debug(dbg_eachfile, "removal_bulk '%s' flags=%o", | |
395 | namenode->name, namenode->flags); | |
396 | if (namenode->flags & fnnf_old_conff) { | |
397 | /* This can only happen if removal_bulk_remove_configfiles() got | |
398 | * interrupted half way. */ | |
399 | debug(dbg_eachfiledetail, "removal_bulk expecting only left over dirs, " | |
400 | "ignoring conffile '%s'", namenode->name); | |
401 | continue; | |
402 | } | |
403 | ||
404 | usenode = namenodetouse(namenode, pkg, &pkg->installed); | |
405 | ||
406 | varbuf_reset(&fnvb); | |
407 | varbuf_add_str(&fnvb, instdir); | |
408 | varbuf_add_str(&fnvb, usenode->name); | |
409 | varbuf_end_str(&fnvb); | |
410 | ||
411 | if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) { | |
412 | debug(dbg_eachfiledetail, "removal_bulk is a directory"); | |
413 | /* Only delete a directory or a link to one if we're the only | |
414 | * package which uses it. Other files should only be listed | |
415 | * in this package (but we don't check). */ | |
416 | if (dir_is_used_by_pkg(namenode, pkg, leftover)) { | |
417 | push_leftover(&leftover, namenode); | |
418 | continue; | |
419 | } | |
420 | if (dir_is_used_by_others(namenode, pkg)) | |
421 | continue; | |
422 | ||
423 | if (strcmp(usenode->name, "/.") == 0) { | |
424 | debug(dbg_eachfiledetail, | |
425 | "removal_bulk '%s' root directory, cannot remove", fnvb.buf); | |
426 | push_leftover(&leftover, namenode); | |
427 | continue; | |
428 | } | |
429 | } | |
430 | ||
431 | trig_path_activate(usenode, pkg); | |
432 | ||
433 | debug(dbg_eachfiledetail, "removal_bulk removing '%s'", fnvb.buf); | |
434 | if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue; | |
435 | if (errno == ENOTEMPTY || errno == EEXIST) { | |
436 | warning(_("while removing %.250s, directory '%.250s' not empty so not removed"), | |
437 | pkg_name(pkg, pnaw_nonambig), namenode->name); | |
438 | push_leftover(&leftover,namenode); | |
439 | continue; | |
440 | } else if (errno == EBUSY || errno == EPERM) { | |
441 | warning(_("while removing %.250s, unable to remove directory '%.250s': " | |
442 | "%s - directory may be a mount point?"), | |
443 | pkg_name(pkg, pnaw_nonambig), namenode->name, strerror(errno)); | |
444 | push_leftover(&leftover,namenode); | |
445 | continue; | |
446 | } | |
447 | if (errno != ENOTDIR) | |
448 | ohshite(_("cannot remove '%.250s'"), fnvb.buf); | |
449 | ||
450 | if (lstat(fnvb.buf, &stab) == 0 && S_ISLNK(stab.st_mode)) { | |
451 | debug(dbg_eachfiledetail, "removal_bulk is a symlink to a directory"); | |
452 | ||
453 | if (unlink(fnvb.buf)) | |
454 | ohshite(_("cannot remove '%.250s'"), fnvb.buf); | |
455 | ||
456 | continue; | |
457 | } | |
458 | ||
459 | push_leftover(&leftover,namenode); | |
460 | continue; | |
461 | } | |
462 | write_filelist_except(pkg, &pkg->installed, leftover, 0); | |
463 | ||
464 | modstatdb_note(pkg); | |
465 | push_checkpoint(~ehflag_bombout, ehflag_normaltidy); | |
466 | } | |
467 | ||
468 | static void removal_bulk_remove_configfiles(struct pkginfo *pkg) { | |
469 | static const char *const removeconffexts[] = { REMOVECONFFEXTS, NULL }; | |
470 | int rc; | |
471 | int conffnameused, conffbasenamelen; | |
472 | char *conffbasename; | |
473 | struct conffile *conff, **lconffp; | |
474 | struct fileinlist *searchfile; | |
475 | DIR *dsd; | |
476 | struct dirent *de; | |
477 | char *p; | |
478 | const char *const *ext; | |
479 | ||
480 | printf(_("Purging configuration files for %s (%s) ...\n"), | |
481 | pkg_name(pkg, pnaw_nonambig), | |
482 | versiondescribe(&pkg->installed.version, vdew_nonambig)); | |
483 | log_action("purge", pkg, &pkg->installed); | |
484 | trig_activate_packageprocessing(pkg); | |
485 | ||
486 | /* We may have modified this above. */ | |
487 | ensure_packagefiles_available(pkg); | |
488 | ||
489 | /* We're about to remove the configuration, so remove the note | |
490 | * about which version it was ... */ | |
491 | dpkg_version_blank(&pkg->configversion); | |
492 | modstatdb_note(pkg); | |
493 | ||
494 | /* Remove from our list any conffiles that aren't ours any more or | |
495 | * are involved in diversions, except if we are the package doing the | |
496 | * diverting. */ | |
497 | for (lconffp = &pkg->installed.conffiles; (conff = *lconffp) != NULL; ) { | |
498 | for (searchfile= pkg->clientdata->files; | |
499 | searchfile && strcmp(searchfile->namenode->name,conff->name); | |
500 | searchfile= searchfile->next); | |
501 | if (!searchfile) { | |
502 | debug(dbg_conff, "removal_bulk conffile not ours any more '%s'", | |
503 | conff->name); | |
504 | *lconffp= conff->next; | |
505 | } else if (searchfile->namenode->divert && | |
506 | (searchfile->namenode->divert->camefrom || | |
507 | (searchfile->namenode->divert->useinstead && | |
508 | searchfile->namenode->divert->pkgset != pkg->set))) { | |
509 | debug(dbg_conff, "removal_bulk conffile '%s' ignored due to diversion", | |
510 | conff->name); | |
511 | *lconffp= conff->next; | |
512 | } else { | |
513 | debug(dbg_conffdetail, "removal_bulk set to new conffile '%s'", | |
514 | conff->name); | |
515 | conff->hash = NEWCONFFILEFLAG; | |
516 | lconffp= &conff->next; | |
517 | } | |
518 | } | |
519 | modstatdb_note(pkg); | |
520 | ||
521 | for (conff= pkg->installed.conffiles; conff; conff= conff->next) { | |
522 | struct filenamenode *namenode, *usenode; | |
523 | static struct varbuf fnvb, removevb; | |
524 | struct varbuf_state removevb_state; | |
525 | ||
526 | if (conff->obsolete) { | |
527 | debug(dbg_conffdetail, "removal_bulk conffile obsolete %s", | |
528 | conff->name); | |
529 | } | |
530 | varbuf_reset(&fnvb); | |
531 | rc = conffderef(pkg, &fnvb, conff->name); | |
532 | debug(dbg_conffdetail, "removal_bulk conffile '%s' (= '%s')", | |
533 | conff->name, rc == -1 ? "<rc == -1>" : fnvb.buf); | |
534 | if (rc == -1) | |
535 | continue; | |
536 | ||
537 | namenode = findnamenode(conff->name, 0); | |
538 | usenode = namenodetouse(namenode, pkg, &pkg->installed); | |
539 | ||
540 | trig_path_activate(usenode, pkg); | |
541 | ||
542 | conffnameused = fnvb.used; | |
543 | if (unlink(fnvb.buf) && errno != ENOENT && errno != ENOTDIR) | |
544 | ohshite(_("cannot remove old config file '%.250s' (= '%.250s')"), | |
545 | conff->name, fnvb.buf); | |
546 | p= strrchr(fnvb.buf,'/'); if (!p) continue; | |
547 | *p = '\0'; | |
548 | varbuf_reset(&removevb); | |
549 | varbuf_add_str(&removevb, fnvb.buf); | |
550 | varbuf_add_char(&removevb, '/'); | |
551 | varbuf_end_str(&removevb); | |
552 | varbuf_snapshot(&removevb, &removevb_state); | |
553 | ||
554 | dsd= opendir(removevb.buf); | |
555 | if (!dsd) { | |
556 | int e=errno; | |
557 | debug(dbg_conffdetail, "removal_bulk conffile no dsd %s %s", | |
558 | fnvb.buf, strerror(e)); errno= e; | |
559 | if (errno == ENOENT || errno == ENOTDIR) continue; | |
560 | ohshite(_("cannot read config file directory '%.250s' (from '%.250s')"), | |
561 | fnvb.buf, conff->name); | |
562 | } | |
563 | debug(dbg_conffdetail, "removal_bulk conffile cleaning dsd %s", fnvb.buf); | |
564 | push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd); | |
565 | *p= '/'; | |
566 | conffbasenamelen= strlen(++p); | |
567 | conffbasename= fnvb.buf+conffnameused-conffbasenamelen; | |
568 | while ((de = readdir(dsd)) != NULL) { | |
569 | debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry='%s'" | |
570 | " conffbasename='%s' conffnameused=%d conffbasenamelen=%d", | |
571 | de->d_name, conffbasename, conffnameused, conffbasenamelen); | |
572 | if (strncmp(de->d_name, conffbasename, conffbasenamelen) == 0) { | |
573 | debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts right"); | |
574 | for (ext= removeconffexts; *ext; ext++) | |
575 | if (strcmp(*ext, de->d_name + conffbasenamelen) == 0) | |
576 | goto yes_remove; | |
577 | p= de->d_name+conffbasenamelen; | |
578 | if (*p++ == '~') { | |
579 | while (*p && c_isdigit(*p)) | |
580 | p++; | |
581 | if (*p == '~' && !*++p) goto yes_remove; | |
582 | } | |
583 | } | |
584 | debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts wrong"); | |
585 | if (de->d_name[0] == '#' && | |
586 | strncmp(de->d_name + 1, conffbasename, conffbasenamelen) == 0 && | |
587 | strcmp(de->d_name + 1 + conffbasenamelen, "#") == 0) | |
588 | goto yes_remove; | |
589 | debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry not it"); | |
590 | continue; | |
591 | yes_remove: | |
592 | varbuf_rollback(&removevb, &removevb_state); | |
593 | varbuf_add_str(&removevb, de->d_name); | |
594 | varbuf_end_str(&removevb); | |
595 | debug(dbg_conffdetail, "removal_bulk conffile dsd entry removing '%s'", | |
596 | removevb.buf); | |
597 | if (unlink(removevb.buf) && errno != ENOENT && errno != ENOTDIR) | |
598 | ohshite(_("cannot remove old backup config file '%.250s' (of '%.250s')"), | |
599 | removevb.buf, conff->name); | |
600 | } | |
601 | pop_cleanup(ehflag_normaltidy); /* closedir */ | |
602 | } | |
603 | ||
604 | /* Remove the conffiles from the file list file. */ | |
605 | write_filelist_except(pkg, &pkg->installed, pkg->clientdata->files, | |
606 | fnnf_old_conff); | |
607 | ||
608 | pkg->installed.conffiles = NULL; | |
609 | modstatdb_note(pkg); | |
610 | ||
611 | maintscript_installed(pkg, POSTRMFILE, "post-removal", "purge", NULL); | |
612 | } | |
613 | ||
614 | /* | |
615 | * This is used both by deferred_remove() in this file, and at the end of | |
616 | * process_archive() in archives.c if it needs to finish removing a | |
617 | * conflicting package. | |
618 | */ | |
619 | void removal_bulk(struct pkginfo *pkg) { | |
620 | bool foundpostrm; | |
621 | ||
622 | debug(dbg_general, "removal_bulk package %s", pkg_name(pkg, pnaw_always)); | |
623 | ||
624 | if (pkg->status == PKG_STAT_HALFINSTALLED || | |
625 | pkg->status == PKG_STAT_UNPACKED) { | |
626 | removal_bulk_remove_files(pkg); | |
627 | } | |
628 | ||
629 | foundpostrm = pkg_infodb_has_file(pkg, &pkg->installed, POSTRMFILE); | |
630 | ||
631 | debug(dbg_general, "removal_bulk purging? foundpostrm=%d",foundpostrm); | |
632 | ||
633 | if (!foundpostrm && !pkg->installed.conffiles) { | |
634 | /* If there are no config files and no postrm script then we | |
635 | * go straight into ‘purge’. */ | |
636 | debug(dbg_general, "removal_bulk no postrm, no conffiles, purging"); | |
637 | ||
638 | pkg_set_want(pkg, PKG_WANT_PURGE); | |
639 | dpkg_version_blank(&pkg->configversion); | |
640 | } else if (pkg->want == PKG_WANT_PURGE) { | |
641 | ||
642 | removal_bulk_remove_configfiles(pkg); | |
643 | ||
644 | } | |
645 | ||
646 | /* I.e., either of the two branches above. */ | |
647 | if (pkg->want == PKG_WANT_PURGE) { | |
648 | const char *filename; | |
649 | ||
650 | /* Retry empty directories, and warn on any leftovers that aren't. */ | |
651 | removal_bulk_remove_leftover_dirs(pkg); | |
652 | ||
653 | filename = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); | |
654 | debug(dbg_general, "removal_bulk purge done, removing list '%s'", | |
655 | filename); | |
656 | if (unlink(filename) && errno != ENOENT) | |
657 | ohshite(_("cannot remove old files list")); | |
658 | ||
659 | filename = pkg_infodb_get_file(pkg, &pkg->installed, POSTRMFILE); | |
660 | debug(dbg_general, "removal_bulk purge done, removing postrm '%s'", | |
661 | filename); | |
662 | if (unlink(filename) && errno != ENOENT) | |
663 | ohshite(_("can't remove old postrm script")); | |
664 | ||
665 | pkg_set_status(pkg, PKG_STAT_NOTINSTALLED); | |
666 | pkg_set_want(pkg, PKG_WANT_UNKNOWN); | |
667 | ||
668 | /* This will mess up reverse links, but if we follow them | |
669 | * we won't go back because pkg->status is PKG_STAT_NOTINSTALLED. */ | |
670 | pkgbin_blank(&pkg->installed); | |
671 | } | |
672 | ||
673 | pkg_reset_eflags(pkg); | |
674 | modstatdb_note(pkg); | |
675 | ||
676 | debug(dbg_general, "removal done"); | |
677 | } |