libopkg: fix double-free crash on recursive package removal
[oweals/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 }