libopkg: fix double-free crash on recursive package removal
[project/opkg-lede.git] / libopkg / opkg_remove.c
1 /* opkg_remove.c - the opkg package management system
2
3 Carl D. Worth
4
5 Copyright (C) 2001 University of Southern California
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16 */
17
18 #include <stdio.h>
19 #include <glob.h>
20 #include <unistd.h>
21
22 #include "opkg_message.h"
23 #include "opkg_remove.h"
24 #include "opkg_cmd.h"
25 #include "file_util.h"
26 #include "sprintf_alloc.h"
27 #include "libbb/libbb.h"
28
29 /*
30 * Returns number of the number of packages depending on the packages provided by this package.
31 * Every package implicitly provides itself.
32 */
33 int
34 pkg_has_installed_dependents(pkg_t *pkg, abstract_pkg_t *** pdependents)
35 {
36 int nprovides = pkg->provides_count;
37 abstract_pkg_t **provides = pkg->provides;
38 unsigned int n_installed_dependents = 0;
39 int i;
40 for (i = 0; i < nprovides; i++) {
41 abstract_pkg_t *providee = provides[i];
42 abstract_pkg_t **dependers = providee->depended_upon_by;
43 abstract_pkg_t *dep_ab_pkg;
44 if (dependers == NULL)
45 continue;
46 while ((dep_ab_pkg = *dependers++) != NULL) {
47 if (dep_ab_pkg->state_status == SS_INSTALLED){
48 n_installed_dependents++;
49 }
50 }
51
52 }
53 /* if caller requested the set of installed dependents */
54 if (pdependents) {
55 int p = 0;
56 abstract_pkg_t **dependents = xcalloc((n_installed_dependents+1), sizeof(abstract_pkg_t *));
57
58 *pdependents = dependents;
59 for (i = 0; i < nprovides; i++) {
60 abstract_pkg_t *providee = provides[i];
61 abstract_pkg_t **dependers = providee->depended_upon_by;
62 abstract_pkg_t *dep_ab_pkg;
63 if (dependers == NULL)
64 continue;
65 while ((dep_ab_pkg = *dependers++) != NULL) {
66 if (dep_ab_pkg->state_status == SS_INSTALLED && !(dep_ab_pkg->state_flag & SF_MARKED)) {
67 dependents[p++] = dep_ab_pkg;
68 dep_ab_pkg->state_flag |= SF_MARKED;
69 }
70 }
71 }
72 dependents[p] = NULL;
73 /* now clear the marks */
74 for (i = 0; i < p; i++) {
75 abstract_pkg_t *dep_ab_pkg = dependents[i];
76 dep_ab_pkg->state_flag &= ~SF_MARKED;
77 }
78 }
79 return n_installed_dependents;
80 }
81
82 static int
83 opkg_remove_dependent_pkgs(pkg_t *pkg, abstract_pkg_t **dependents)
84 {
85 int i;
86 int a;
87 int count;
88 pkg_vec_t *dependent_pkgs;
89 abstract_pkg_t * ab_pkg;
90
91 if((ab_pkg = pkg->parent) == NULL){
92 opkg_msg(ERROR, "Internal error: pkg %s isn't in hash table\n",
93 pkg->name);
94 return 0;
95 }
96
97 if (dependents == NULL)
98 return 0;
99
100 // here i am using the dependencies_checked
101 if (ab_pkg->dependencies_checked == 2) // variable to make out whether this package
102 return 0; // has already been encountered in the process
103 // of marking packages for removal - Karthik
104 ab_pkg->dependencies_checked = 2;
105
106 i = 0;
107 count = 1;
108 dependent_pkgs = pkg_vec_alloc();
109
110 while (dependents [i] != NULL) {
111 abstract_pkg_t *dep_ab_pkg = dependents[i];
112
113 if (dep_ab_pkg->dependencies_checked == 2){
114 i++;
115 continue;
116 }
117 if (dep_ab_pkg->state_status == SS_INSTALLED) {
118 for (a = 0; a < dep_ab_pkg->pkgs->len; a++) {
119 pkg_t *dep_pkg = dep_ab_pkg->pkgs->pkgs[a];
120 if (dep_pkg->state_status == SS_INSTALLED) {
121 pkg_vec_insert(dependent_pkgs, dep_pkg);
122 count++;
123 }
124 }
125 }
126 i++;
127 /* 1 - to keep track of visited ab_pkgs when checking for possiblility of a broken removal of pkgs.
128 * 2 - to keep track of pkgs whose deps have been checked alrdy - Karthik */
129 }
130
131 if (count == 1) {
132 pkg_vec_free(dependent_pkgs);
133 return 0;
134 }
135
136
137 int err=0;
138 for (i = 0; i < dependent_pkgs->len; i++) {
139 err = opkg_remove_pkg(dependent_pkgs->pkgs[i],0);
140 if (err) {
141 break;
142 }
143 }
144 pkg_vec_free(dependent_pkgs);
145 return err;
146 }
147
148 static void
149 print_dependents_warning(pkg_t *pkg, abstract_pkg_t **dependents)
150 {
151 abstract_pkg_t *dep_ab_pkg;
152 opkg_msg(ERROR, "Package %s is depended upon by packages:\n", pkg->name);
153 while ((dep_ab_pkg = *dependents++) != NULL) {
154 if (dep_ab_pkg->state_status == SS_INSTALLED)
155 opkg_msg(ERROR, "\t%s\n", dep_ab_pkg->name);
156 }
157 opkg_msg(ERROR, "These might cease to work if package %s is removed.\n\n",
158 pkg->name);
159 opkg_msg(ERROR, "Force removal of this package with --force-depends.\n");
160 opkg_msg(ERROR, "Force removal of this package and its dependents\n");
161 opkg_msg(ERROR, "with --force-removal-of-dependent-packages.\n");
162 }
163
164 /*
165 * Find and remove packages that were autoinstalled and are orphaned
166 * by the removal of pkg.
167 */
168 static int
169 remove_autoinstalled(pkg_t *pkg)
170 {
171 int i, j;
172 int err = 0;
173 int n_deps;
174 pkg_t *p;
175 struct compound_depend *cdep;
176 abstract_pkg_t **dependents;
177
178 int count = pkg->pre_depends_count +
179 pkg->depends_count +
180 pkg->recommends_count +
181 pkg->suggests_count;
182
183 for (i=0; i<count; i++) {
184 cdep = &pkg->depends[i];
185 if (cdep->type != PREDEPEND
186 && cdep->type != DEPEND
187 && cdep->type != RECOMMEND)
188 continue;
189 for (j=0; j<cdep->possibility_count; j++) {
190 p = pkg_hash_fetch_installed_by_name(
191 cdep->possibilities[j]->pkg->name);
192
193 /* If the package is not installed, this could have
194 * been a circular dependency and the package has
195 * already been removed.
196 */
197 if (!p)
198 return -1;
199
200 if (!p->auto_installed)
201 continue;
202
203 n_deps = pkg_has_installed_dependents(p, &dependents);
204 if (n_deps == 0) {
205 opkg_msg(NOTICE, "%s was autoinstalled and is "
206 "now orphaned, removing.\n",
207 p->name);
208 if (opkg_remove_pkg(p, 0) != 0) {
209 err = -1;
210 }
211 } else
212 opkg_msg(INFO, "%s was autoinstalled and is "
213 "still required by %d "
214 "installed packages.\n",
215 p->name, n_deps);
216
217 if (dependents)
218 free(dependents);
219 }
220 }
221
222 return err;
223 }
224
225 int
226 opkg_remove_pkg(pkg_t *pkg, int from_upgrade)
227 {
228 int err;
229 abstract_pkg_t *parent_pkg = NULL;
230
231 /*
232 * If called from an upgrade and not from a normal remove,
233 * ignore the essential flag.
234 */
235 if (pkg->essential && !from_upgrade) {
236 if (conf->force_removal_of_essential_packages) {
237 opkg_msg(NOTICE,
238 "Removing essential package %s under your coercion.\n"
239 "\tIf your system breaks, you get to keep both pieces\n",
240 pkg->name);
241 } else {
242 opkg_msg(NOTICE, "Refusing to remove essential package %s.\n"
243 "\tRemoving an essential package may lead to an unusable system, but if\n"
244 "\tyou enjoy that kind of pain, you can force opkg to proceed against\n"
245 "\tits will with the option: --force-removal-of-essential-packages\n",
246 pkg->name);
247 return -1;
248 }
249 }
250
251 if ((parent_pkg = pkg->parent) == NULL)
252 return 0;
253
254 /* only attempt to remove dependent installed packages if
255 * force_depends is not specified or the package is being
256 * replaced.
257 */
258 if (!conf->force_depends
259 && !(pkg->state_flag & SF_REPLACE)) {
260 abstract_pkg_t **dependents;
261 int has_installed_dependents =
262 pkg_has_installed_dependents(pkg, &dependents);
263
264 if (has_installed_dependents) {
265 /*
266 * if this package is depended upon by others, then either we should
267 * not remove it or we should remove it and all of its dependents
268 */
269
270 if (!conf->force_removal_of_dependent_packages) {
271 print_dependents_warning(pkg, dependents);
272 free(dependents);
273 return -1;
274 }
275
276 /* remove packages depending on this package - Karthik */
277 err = opkg_remove_dependent_pkgs(pkg, dependents);
278 if (err) {
279 free(dependents);
280 return err;
281 }
282 }
283 if (dependents)
284 free(dependents);
285 }
286
287 if (from_upgrade == 0) {
288 opkg_msg(NOTICE, "Removing package %s from %s...\n",
289 pkg->name, pkg->dest->name);
290 }
291 pkg->state_flag |= SF_FILELIST_CHANGED;
292
293 pkg->state_want = SW_DEINSTALL;
294 opkg_state_changed++;
295
296 if (pkg_run_script(pkg, "prerm", "remove") != 0) {
297 if (!conf->force_remove) {
298 opkg_msg(ERROR, "not removing package \"%s\", "
299 "prerm script failed\n", pkg->name);
300 opkg_msg(NOTICE, "You can force removal of packages with failed "
301 "prerm scripts with the option: \n"
302 "\t--force-remove\n");
303 return -1;
304 }
305 }
306
307 /* DPKG_INCOMPATIBILITY: dpkg is slightly different here. It
308 maintains an empty filelist rather than deleting it. That seems
309 like a big pain, and I don't see that that should make a big
310 difference, but for anyone who wants tighter compatibility,
311 feel free to fix this. */
312 remove_data_files_and_list(pkg);
313
314 err = pkg_run_script(pkg, "postrm", "remove");
315
316 remove_maintainer_scripts(pkg);
317 pkg->state_status = SS_NOT_INSTALLED;
318
319 if (parent_pkg)
320 parent_pkg->state_status = SS_NOT_INSTALLED;
321
322 /* remove autoinstalled packages that are orphaned by the removal of this one */
323 if (conf->autoremove) {
324 if (remove_autoinstalled(pkg) != 0) {
325 err = -1;
326 }
327 }
328 return err;
329 }
330
331 void
332 remove_data_files_and_list(pkg_t *pkg)
333 {
334 str_list_t installed_dirs;
335 str_list_t *installed_files;
336 str_list_elt_t *iter;
337 char *file_name;
338 conffile_t *conffile;
339 int removed_a_dir;
340 pkg_t *owner;
341 int rootdirlen = 0;
342
343 installed_files = pkg_get_installed_files(pkg);
344 if (installed_files == NULL) {
345 opkg_msg(ERROR, "Failed to determine installed "
346 "files for %s. None removed.\n", pkg->name);
347 return;
348 }
349
350 str_list_init(&installed_dirs);
351
352 /* don't include trailing slash */
353 if (conf->offline_root)
354 rootdirlen = strlen(conf->offline_root);
355
356 for (iter = str_list_first(installed_files); iter; iter = str_list_next(installed_files, iter)) {
357 file_name = (char *)iter->data;
358
359 owner = file_hash_get_file_owner(file_name);
360 if (owner != pkg)
361 /* File may have been claimed by another package. */
362 continue;
363
364 if (file_is_dir(file_name)) {
365 str_list_append(&installed_dirs, file_name);
366 continue;
367 }
368
369 conffile = pkg_get_conffile(pkg, file_name+rootdirlen);
370 if (conffile) {
371 if (conffile_has_been_modified(conffile)) {
372 opkg_msg(NOTICE, "Not deleting modified conffile %s.\n",
373 file_name);
374 continue;
375 }
376 }
377
378 if (!conf->noaction) {
379 opkg_msg(INFO, "Deleting %s.\n", file_name);
380 unlink(file_name);
381 } else
382 opkg_msg(INFO, "Not deleting %s. (noaction)\n",
383 file_name);
384
385 file_hash_remove(file_name);
386 }
387
388 /* Remove empty directories */
389 if (!conf->noaction) {
390 do {
391 removed_a_dir = 0;
392 for (iter = str_list_first(&installed_dirs); iter; iter = str_list_next(&installed_dirs, iter)) {
393 file_name = (char *)iter->data;
394
395 if (rmdir(file_name) == 0) {
396 opkg_msg(INFO, "Deleting %s.\n", file_name);
397 removed_a_dir = 1;
398 str_list_remove(&installed_dirs, &iter);
399 }
400 }
401 } while (removed_a_dir);
402 }
403
404 pkg_free_installed_files(pkg);
405 pkg_remove_installed_files_list(pkg);
406
407 /* Don't print warning for dirs that are provided by other packages */
408 for (iter = str_list_first(&installed_dirs); iter; iter = str_list_next(&installed_dirs, iter)) {
409 file_name = (char *)iter->data;
410
411 owner = file_hash_get_file_owner(file_name);
412 if (owner) {
413 free(iter->data);
414 iter->data = NULL;
415 str_list_remove(&installed_dirs, &iter);
416 }
417 }
418
419 /* cleanup */
420 while (!void_list_empty(&installed_dirs)) {
421 iter = str_list_pop(&installed_dirs);
422 free(iter->data);
423 free(iter);
424 }
425 str_list_deinit(&installed_dirs);
426 }
427
428 void
429 remove_maintainer_scripts(pkg_t *pkg)
430 {
431 int i, err;
432 char *globpattern;
433 glob_t globbuf;
434
435 if (conf->noaction)
436 return;
437
438 sprintf_alloc(&globpattern, "%s/%s.*",
439 pkg->dest->info_dir, pkg->name);
440
441 err = glob(globpattern, 0, NULL, &globbuf);
442 free(globpattern);
443 if (err)
444 return;
445
446 for (i = 0; i < globbuf.gl_pathc; i++) {
447 opkg_msg(INFO, "Deleting %s.\n", globbuf.gl_pathv[i]);
448 unlink(globbuf.gl_pathv[i]);
449 }
450 globfree(&globbuf);
451 }