LCOV - code coverage report
Current view: top level - libpkg - pkg_delete.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 126 163 77.3 %
Date: 2015-08-15 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*-
       2             :  * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
       3             :  * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
       4             :  * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
       5             :  * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
       6             :  * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
       7             :  * All rights reserved.
       8             :  * 
       9             :  * Redistribution and use in source and binary forms, with or without
      10             :  * modification, are permitted provided that the following conditions
      11             :  * are met:
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer
      14             :  *    in this position and unchanged.
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  * 
      19             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
      20             :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      21             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      22             :  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
      23             :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      24             :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      28             :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29             :  */
      30             : 
      31             : #ifdef HAVE_CONFIG_H
      32             : #include "pkg_config.h"
      33             : #endif
      34             : 
      35             : #include <assert.h>
      36             : #include <errno.h>
      37             : #include <string.h>
      38             : #include <unistd.h>
      39             : #include <stdlib.h>
      40             : #include <fcntl.h>
      41             : 
      42             : #include <bsd_compat.h>
      43             : 
      44             : #include "pkg.h"
      45             : #include "private/event.h"
      46             : #include "private/pkg.h"
      47             : #include "private/pkgdb.h"
      48             : #include "private/utils.h"
      49             : 
      50             : #if defined(UF_NOUNLINK)
      51             : #define NOCHANGESFLAGS  (UF_IMMUTABLE | UF_APPEND | UF_NOUNLINK | SF_IMMUTABLE | SF_APPEND | SF_NOUNLINK)
      52             : #else
      53             : #define NOCHANGESFLAGS  (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
      54             : #endif
      55             : 
      56             : int
      57           6 : pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
      58             : {
      59             :         int              ret;
      60           6 :         bool             handle_rc = false;
      61           6 :         const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
      62             :                                         PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS;
      63             : 
      64           6 :         assert(pkg != NULL);
      65           6 :         assert(db != NULL);
      66             : 
      67           6 :         if (pkgdb_ensure_loaded(db, pkg, load_flags) != EPKG_OK)
      68           0 :                 return (EPKG_FATAL);
      69             : 
      70           6 :         if ((flags & PKG_DELETE_UPGRADE) == 0)
      71           6 :                 pkg_emit_deinstall_begin(pkg);
      72             : 
      73             :         /* If the package is locked */
      74           6 :         if (pkg->locked) {
      75           0 :                 pkg_emit_locked(pkg);
      76           0 :                 return (EPKG_LOCKED);
      77             :         }
      78             : 
      79             :         /*
      80             :          * stop the different related services if the users do want that
      81             :          * and that the service is running
      82             :          */
      83           6 :         handle_rc = pkg_object_bool(pkg_config_get("HANDLE_RC_SCRIPTS"));
      84           6 :         if (handle_rc)
      85           0 :                 pkg_start_stop_rc_scripts(pkg, PKG_RC_STOP);
      86             : 
      87           6 :         if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
      88           6 :                 if (flags & PKG_DELETE_UPGRADE) {
      89           0 :                         ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_UPGRADE);
      90           0 :                         if (ret != EPKG_OK)
      91           0 :                                 return (ret);
      92             :                 } else {
      93           6 :                         ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_DEINSTALL);
      94           6 :                         if (ret != EPKG_OK)
      95           0 :                                 return (ret);
      96             :                 }
      97             :         }
      98             : 
      99           6 :         if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
     100             :             != EPKG_OK)
     101           0 :                 return (ret);
     102             : 
     103           6 :         if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
     104           6 :                 ret = pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL);
     105           6 :                 if (ret != EPKG_OK)
     106           0 :                         return (ret);
     107             :         }
     108             : 
     109           6 :         ret = pkg_delete_dirs(db, pkg, NULL);
     110           6 :         if (ret != EPKG_OK)
     111           0 :                 return (ret);
     112             : 
     113           6 :         if ((flags & PKG_DELETE_UPGRADE) == 0)
     114           6 :                 pkg_emit_deinstall_finished(pkg);
     115             : 
     116           6 :         return (pkgdb_unregister_pkg(db, pkg->id));
     117             : }
     118             : 
     119             : void
     120          10 : pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir)
     121             : {
     122             :         char path[MAXPATHLEN];
     123             :         char *tmp;
     124             :         size_t i, len, len2;
     125             : 
     126          10 :         strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
     127             : 
     128          10 :         if (file != NULL) {
     129           7 :                 tmp = strrchr(path, '/');
     130           7 :                 tmp[1] = '\0';
     131             :         }
     132             : 
     133          10 :         len = strlen(path);
     134             : 
     135             :         /* make sure to finish by a / */
     136          10 :         if (path[len - 1] != '/') {
     137           3 :                 path[len] = '/';
     138           3 :                 len++;
     139           3 :                 path[len] = '\0';
     140             :         }
     141             : 
     142          11 :         for (i = 0; i < pkg->dir_to_del_len ; i++) {
     143           5 :                 len2 = strlen(pkg->dir_to_del[i]);
     144           5 :                 if (len2 >= len && strncmp(path, pkg->dir_to_del[i], len) == 0)
     145           8 :                         return;
     146             : 
     147           1 :                 if (strncmp(path, pkg->dir_to_del[i], len2) == 0) {
     148           0 :                         pkg_debug(1, "Replacing in deletion %s with %s",
     149           0 :                             pkg->dir_to_del[i], path);
     150           0 :                         free(pkg->dir_to_del[i]);
     151           0 :                         pkg->dir_to_del[i] = strdup(path);
     152           0 :                         return;
     153             :                 }
     154             :         }
     155             : 
     156           6 :         pkg_debug(1, "Adding to deletion %s", path);
     157             : 
     158           6 :         if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
     159           5 :                 pkg->dir_to_del_cap += 64;
     160           5 :                 pkg->dir_to_del = realloc(pkg->dir_to_del,
     161           5 :                     pkg->dir_to_del_cap * sizeof(char *));
     162             :         }
     163             : 
     164           6 :         pkg->dir_to_del[pkg->dir_to_del_len++] = strdup(path);
     165             : }
     166             : 
     167             : static void
     168           7 : rmdir_p(struct pkgdb *db, struct pkg *pkg, char *dir, const char *prefix_r)
     169             : {
     170             :         char *tmp;
     171             :         int64_t cnt;
     172             :         char fullpath[MAXPATHLEN];
     173             :         size_t len;
     174             :         struct stat st;
     175             : #if defined(HAVE_CHFLAGS) && !defined(HAVE_CHFLAGSAT)
     176             :         int fd;
     177             : #endif
     178             : 
     179           7 :         len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
     180          21 :         while (fullpath[len -1] == '/') {
     181           7 :                 fullpath[len - 1] = '\0';
     182           7 :                 len--;
     183             :         }
     184           7 :         if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
     185           4 :                 return;
     186             : 
     187           7 :         pkg_debug(1, "Number of packages owning the directory '%s': %d",
     188             :             fullpath, cnt);
     189             :         /*
     190             :          * At this moment the package we are removing have already been removed
     191             :          * from the local database so if anything else is owning the directory
     192             :          * that is another package meaning only remove the diretory is cnt == 0
     193             :          */
     194           7 :         if (cnt > 0)
     195           1 :                 return;
     196             : 
     197           6 :         if (strcmp(prefix_r, fullpath + 1) == 0)
     198           1 :                 return;
     199             : 
     200           5 :         pkg_debug(1, "removing directory %s", fullpath);
     201             : #ifdef HAVE_CHFLAGS
     202           5 :         if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
     203           5 :                 if (st.st_flags & NOCHANGESFLAGS) {
     204             : #ifdef HAVE_CHFLAGSAT
     205             :                         /* Disable all flags*/
     206           0 :                         chflagsat(pkg->rootfd, dir, 0, AT_SYMLINK_NOFOLLOW);
     207             : #else
     208             :                         fd = openat(pkg->rootfd, dir, O_NOFOLLOW);
     209             :                         if (fd > 0) {
     210             :                                 fchflags(fd, 0);
     211             :                                 close(fd);
     212             :                         }
     213             : #endif
     214             :                 }
     215             :         }
     216             : #endif
     217             : 
     218           5 :         if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
     219           2 :                 if (errno != ENOTEMPTY && errno != EBUSY)
     220           0 :                         pkg_emit_errno("unlinkat", dir);
     221             :                 /* If the directory was already removed by a bogus script, continue removing parents */
     222           2 :                 if (errno != ENOENT)
     223           2 :                         return;
     224             :         }
     225             : 
     226             :         /* No recursivity for packages out of the prefix */
     227           3 :         if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
     228           0 :                 return;
     229             : 
     230             :         /* remove the trailing '/' */
     231           3 :         tmp = strrchr(dir, '/');
     232           3 :         if (tmp == dir)
     233           0 :                 return;
     234             : 
     235           3 :         tmp[0] = '\0';
     236           3 :         tmp = strrchr(dir, '/');
     237           3 :         if (tmp == NULL)
     238           0 :                 return;
     239             : 
     240           3 :         tmp[1] = '\0';
     241             : 
     242           3 :         rmdir_p(db, pkg, dir, prefix_r);
     243             : }
     244             : 
     245             : static void
     246           6 : pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
     247             : {
     248             :         char prefix_r[MAXPATHLEN];
     249             :         size_t i;
     250             : 
     251           6 :         snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix + 1);
     252          10 :         for (i = 0; i < pkg->dir_to_del_len; i++)
     253           4 :                 rmdir_p(db, pkg, pkg->dir_to_del[i], prefix_r);
     254           6 : }
     255             : 
     256             : void
     257           9 : pkg_delete_file(struct pkg *pkg, struct pkg_file *file, unsigned force)
     258             : {
     259             :         const char *path;
     260             :         const char *prefix_rel;
     261             :         struct stat st;
     262             :         size_t len;
     263             : #if defined(HAVE_CHFLAGS) && !defined(HAVE_CHFLAGSAT)
     264             :         int fd;
     265             : #endif
     266             : 
     267           9 :         pkg_open_root_fd(pkg);
     268             : 
     269           9 :         path = file->path;
     270           9 :         path++;
     271             : 
     272           9 :         prefix_rel = pkg->prefix;
     273           9 :         prefix_rel++;
     274           9 :         len = strlen(prefix_rel);
     275          23 :         while (prefix_rel[len - 1] == '/')
     276           5 :                 len--;
     277             : 
     278             :         /* Regular files and links */
     279             :         /* check checksum */
     280           9 :         if (!force && file->sum != NULL) {
     281           0 :                 if (!pkg_checksum_validate_fileat(pkg->rootfd, path, file->sum)) {
     282           0 :                         pkg_emit_error("%s%s%s different from original "
     283           0 :                             "checksum, not removing", pkg->rootpath,
     284           0 :                             pkg->rootpath[strlen(pkg->rootpath) - 1] == '/' ? "" : "/",
     285             :                             path);
     286           0 :                         return;
     287             :                 }
     288             :         }
     289             : 
     290             : #ifdef HAVE_CHFLAGS
     291           9 :         if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
     292           9 :                 if (st.st_flags & NOCHANGESFLAGS) {
     293             : #ifdef HAVE_CHFLAGSAT
     294           0 :                         chflagsat(pkg->rootfd, path,
     295           0 :                             st.st_flags & ~NOCHANGESFLAGS,
     296             :                             AT_SYMLINK_NOFOLLOW);
     297             : #else
     298             :                         fd = openat(pkg->rootfd, path, O_NOFOLLOW);
     299             :                         if (fd > 0) {
     300             :                                 fchflags(fd, st.st_flags & ~NOCHANGESFLAGS);
     301             :                                 close(fd);
     302             :                         }
     303             : #endif
     304             :                 }
     305             :         }
     306             : #endif
     307           9 :         pkg_debug(1, "Deleting file: '%s'", path);
     308           9 :         if (unlinkat(pkg->rootfd, path, 0) == -1) {
     309           0 :                 if (force < 2)
     310           0 :                         pkg_emit_errno("unlinkat", path);
     311           0 :                 return;
     312             :         }
     313             : 
     314             :         /* do not bother about directories not in prefix */
     315           9 :         if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
     316           6 :                 pkg_add_dir_to_del(pkg, path, NULL);
     317             : }
     318             : 
     319             : int
     320           6 : pkg_delete_files(struct pkg *pkg, unsigned force)
     321             :         /* force: 0 ... be careful and vocal about it. 
     322             :          *        1 ... remove files without bothering about checksums.
     323             :          *        2 ... like 1, but remain silent if removal fails.
     324             :          */
     325             : {
     326           6 :         struct pkg_file *file = NULL;
     327             : 
     328           6 :         int             nfiles, cur_file = 0;
     329             : 
     330           6 :         nfiles = kh_count(pkg->files);
     331             : 
     332           6 :         if (nfiles == 0)
     333           1 :                 return (EPKG_OK);
     334             : 
     335           5 :         pkg_emit_delete_files_begin(pkg);
     336           5 :         pkg_emit_progress_start(NULL);
     337             : 
     338          19 :         while (pkg_files(pkg, &file) == EPKG_OK) {
     339           9 :                 pkg_emit_progress_tick(cur_file++, nfiles);
     340           9 :                 pkg_delete_file(pkg, file, force);
     341             :         }
     342             : 
     343           5 :         pkg_emit_progress_tick(nfiles, nfiles);
     344           5 :         pkg_emit_delete_files_finished(pkg);
     345             : 
     346           5 :         return (EPKG_OK);
     347             : }
     348             : 
     349             : void
     350           1 : pkg_delete_dir(struct pkg *pkg, struct pkg_dir *dir)
     351             : {
     352             :         const char *path;
     353             :         const char *prefix_rel;
     354             :         size_t len;
     355             : 
     356           1 :         pkg_open_root_fd(pkg);
     357             : 
     358           1 :         path = dir->path;
     359             :         /* remove the first / */
     360           1 :         path++;
     361             : 
     362           1 :         prefix_rel = pkg->prefix;
     363           1 :         prefix_rel++;
     364           1 :         len = strlen(prefix_rel);
     365           3 :         while (prefix_rel[len - 1] == '/')
     366           1 :                 len--;
     367             : 
     368           1 :         if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
     369           1 :                 pkg_add_dir_to_del(pkg, NULL, path);
     370             :         } else {
     371           0 :                 if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
     372           0 :                         pkg->dir_to_del_cap += 64;
     373           0 :                         pkg->dir_to_del = realloc(pkg->dir_to_del,
     374           0 :                             pkg->dir_to_del_cap * sizeof(char *));
     375             :                 }
     376           0 :                 pkg->dir_to_del[pkg->dir_to_del_len++] = strdup(path);
     377             :         }
     378           1 : }
     379             : 
     380             : int
     381           6 : pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
     382             : {
     383           6 :         struct pkg_dir  *dir = NULL;
     384             : 
     385          13 :         while (pkg_dirs(pkg, &dir) == EPKG_OK) {
     386           1 :                 if (new != NULL && !pkg_has_dir(new, dir->path))
     387           0 :                         continue;
     388           1 :                 pkg_delete_dir(pkg, dir);
     389             :         }
     390             : 
     391           6 :         pkg_effective_rmdir(db, pkg);
     392             : 
     393           6 :         return (EPKG_OK);
     394             : }

Generated by: LCOV version 1.10