LCOV - code coverage report
Current view: top level - libpkg - pkg_delete.c (source / functions) Hit Total Coverage
Test: rapport Lines: 165 203 81.3 %
Date: 2021-12-10 16:22:55 Functions: 8 8 100.0 %
Branches: 78 126 61.9 %

           Branch data     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                 :        172 : pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
      58                 :            : {
      59                 :            :         struct pkg_message      *msg;
      60                 :        172 :         xstring         *message = NULL;
      61                 :            :         int              ret;
      62                 :        172 :         bool             handle_rc = false;
      63                 :        172 :         const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
      64                 :            :                                         PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS|PKG_LOAD_LUA_SCRIPTS;
      65                 :            : 
      66         [ +  - ]:        172 :         assert(pkg != NULL);
      67         [ -  + ]:        172 :         assert(db != NULL);
      68                 :            : 
      69         [ -  + ]:        172 :         if (pkgdb_ensure_loaded(db, pkg, load_flags) != EPKG_OK)
      70                 :          0 :                 return (EPKG_FATAL);
      71                 :            : 
      72         [ -  + ]:        172 :         if ((flags & PKG_DELETE_UPGRADE) == 0) {
      73                 :        172 :                 pkg_emit_new_action();
      74                 :        172 :                 pkg_emit_deinstall_begin(pkg);
      75                 :        172 :         }
      76                 :            : 
      77                 :            :         /* If the package is locked */
      78         [ -  + ]:        172 :         if (pkg->locked) {
      79                 :          0 :                 pkg_emit_locked(pkg);
      80                 :          0 :                 return (EPKG_LOCKED);
      81                 :            :         }
      82                 :            : 
      83                 :            :         /*
      84                 :            :          * stop the different related services if the users do want that
      85                 :            :          * and that the service is running
      86                 :            :          */
      87                 :        172 :         handle_rc = pkg_object_bool(pkg_config_get("HANDLE_RC_SCRIPTS"));
      88         [ +  - ]:        172 :         if (handle_rc)
      89                 :          0 :                 pkg_start_stop_rc_scripts(pkg, PKG_RC_STOP);
      90                 :            : 
      91         [ -  + ]:        172 :         if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
      92                 :        172 :                 pkg_open_root_fd(pkg);
      93         [ -  + ]:        172 :                 if (!(flags & PKG_DELETE_UPGRADE)) {
      94                 :        172 :                         ret = pkg_lua_script_run(pkg, PKG_LUA_PRE_DEINSTALL, false);
      95   [ -  +  #  # ]:        172 :                         if (ret != EPKG_OK && ctx.developer_mode)
      96                 :          0 :                                 return (ret);
      97                 :        172 :                         ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_DEINSTALL, false);
      98   [ -  +  #  # ]:        172 :                         if (ret != EPKG_OK && ctx.developer_mode)
      99                 :          0 :                                 return (ret);
     100                 :        172 :                 }
     101                 :        172 :         }
     102                 :            : 
     103   [ -  +  -  + ]:        344 :         if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
     104                 :        172 :             != EPKG_OK)
     105                 :          0 :                 return (ret);
     106                 :            : 
     107         [ -  + ]:        172 :         if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
     108                 :        172 :                 pkg_lua_script_run(pkg, PKG_LUA_POST_DEINSTALL, false);
     109                 :        172 :                 pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL, false);
     110                 :        172 :         }
     111                 :            : 
     112                 :        172 :         ret = pkg_delete_dirs(db, pkg, NULL);
     113         [ -  + ]:        172 :         if (ret != EPKG_OK)
     114                 :          0 :                 return (ret);
     115                 :            : 
     116         [ -  + ]:        172 :         if ((flags & PKG_DELETE_UPGRADE) == 0) {
     117                 :        172 :                 pkg_emit_deinstall_finished(pkg);
     118         [ +  + ]:        256 :                 LL_FOREACH(pkg->message, msg) {
     119         [ +  + ]:         84 :                         if (msg->type == PKG_MESSAGE_REMOVE) {
     120         [ -  + ]:         12 :                                 if (message == NULL) {
     121                 :         12 :                                         message = xstring_new();
     122                 :         24 :                                         pkg_fprintf(message->fp, "Message from "
     123                 :         12 :                                             "%n-%v:\n", pkg, pkg);
     124                 :         12 :                                 }
     125                 :         12 :                                 fprintf(message->fp, "%s\n", msg->str);
     126                 :         12 :                         }
     127                 :         84 :                 }
     128   [ +  +  -  + ]:        172 :                 if (pkg->message != NULL && message != NULL) {
     129                 :         12 :                         fflush(message->fp);
     130                 :         12 :                         pkg_emit_message(message->buf);
     131                 :         12 :                         xstring_free(message);
     132                 :         12 :                 }
     133                 :            : 
     134                 :        172 :         }
     135                 :            : 
     136                 :        172 :         return (pkgdb_unregister_pkg(db, pkg->id));
     137                 :        172 : }
     138                 :            : 
     139                 :            : void
     140                 :         75 : pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir)
     141                 :            : {
     142                 :            :         char path[MAXPATHLEN];
     143                 :            :         char *tmp;
     144                 :            :         size_t i, len, len2;
     145                 :            : 
     146         [ +  + ]:         75 :         strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
     147                 :            : 
     148         [ +  + ]:         75 :         if (file != NULL) {
     149                 :         45 :                 tmp = strrchr(path, '/');
     150                 :         45 :                 tmp[1] = '\0';
     151                 :         45 :         }
     152                 :            : 
     153                 :         75 :         len = strlen(path);
     154                 :            : 
     155                 :            :         /* make sure to finish by a / */
     156         [ +  + ]:         75 :         if (path[len - 1] != '/') {
     157                 :         30 :                 path[len] = '/';
     158                 :         30 :                 len++;
     159                 :         30 :                 path[len] = '\0';
     160                 :         30 :         }
     161                 :            : 
     162         [ +  + ]:         88 :         for (i = 0; i < pkg->dir_to_del_len ; i++) {
     163                 :         38 :                 len2 = strlen(pkg->dir_to_del[i]);
     164   [ +  -  +  + ]:         38 :                 if (len2 >= len && strncmp(path, pkg->dir_to_del[i], len) == 0)
     165                 :         25 :                         return;
     166                 :            : 
     167         [ +  - ]:         13 :                 if (strncmp(path, pkg->dir_to_del[i], len2) == 0) {
     168                 :          0 :                         pkg_debug(1, "Replacing in deletion %s with %s",
     169                 :          0 :                             pkg->dir_to_del[i], path);
     170                 :          0 :                         free(pkg->dir_to_del[i]);
     171                 :          0 :                         pkg->dir_to_del[i] = xstrdup(path);
     172                 :          0 :                         return;
     173                 :            :                 }
     174                 :         13 :         }
     175                 :            : 
     176                 :         50 :         pkg_debug(1, "Adding to deletion %s", path);
     177                 :            : 
     178         [ +  + ]:         50 :         if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
     179                 :         37 :                 pkg->dir_to_del_cap += 64;
     180                 :         74 :                 pkg->dir_to_del = xrealloc(pkg->dir_to_del,
     181                 :         37 :                     pkg->dir_to_del_cap * sizeof(char *));
     182                 :         37 :         }
     183                 :            : 
     184                 :         50 :         pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
     185                 :         75 : }
     186                 :            : 
     187                 :            : static void
     188                 :         36 : rmdir_p(struct pkgdb *db, struct pkg *pkg, char *dir, const char *prefix_r)
     189                 :            : {
     190                 :            :         char *tmp;
     191                 :            :         int64_t cnt;
     192                 :            :         char fullpath[MAXPATHLEN];
     193                 :            :         size_t len;
     194                 :            : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
     195                 :            :         struct stat st;
     196                 :            : #if !defined(HAVE_CHFLAGSAT)
     197                 :            :         int fd;
     198                 :            : #endif
     199                 :            : #endif
     200                 :            : 
     201                 :         36 :         len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
     202         [ +  + ]:         72 :         while (fullpath[len -1] == '/') {
     203                 :         36 :                 fullpath[len - 1] = '\0';
     204                 :         36 :                 len--;
     205                 :            :         }
     206         [ -  + ]:         36 :         if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
     207                 :          0 :                 return;
     208                 :            : 
     209                 :         36 :         pkg_debug(1, "Number of packages owning the directory '%s': %d",
     210                 :         36 :             fullpath, (int)cnt);
     211                 :            :         /*
     212                 :            :          * At this moment the package we are removing have already been removed
     213                 :            :          * from the local database so if anything else is owning the directory
     214                 :            :          * that is another package meaning only remove the diretory is cnt == 0
     215                 :            :          */
     216         [ +  + ]:         36 :         if (cnt > 0)
     217                 :          4 :                 return;
     218                 :            : 
     219         [ +  + ]:         32 :         if (strcmp(prefix_r, fullpath + 1) == 0)
     220                 :         12 :                 return;
     221                 :            : 
     222                 :         20 :         pkg_debug(1, "removing directory %s", fullpath);
     223                 :            : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
     224         [ +  - ]:         20 :         if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
     225         [ +  - ]:         20 :                 if (st.st_flags & NOCHANGESFLAGS) {
     226                 :            : #ifdef HAVE_CHFLAGSAT
     227                 :            :                         /* Disable all flags*/
     228                 :          0 :                         chflagsat(pkg->rootfd, dir, 0, AT_SYMLINK_NOFOLLOW);
     229                 :            : #else
     230                 :            :                         fd = openat(pkg->rootfd, dir, O_NOFOLLOW);
     231                 :            :                         if (fd > 0) {
     232                 :            :                                 fchflags(fd, 0);
     233                 :            :                                 close(fd);
     234                 :            :                         }
     235                 :            : #endif
     236                 :          0 :                 }
     237                 :         20 :         }
     238                 :            : #endif
     239                 :            : 
     240         [ +  + ]:         20 :         if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
     241   [ -  +  #  # ]:          8 :                 if (errno != ENOTEMPTY && errno != EBUSY)
     242                 :          0 :                         pkg_emit_errno("unlinkat", dir);
     243                 :            :                 /* If the directory was already removed by a bogus script, continue removing parents */
     244         [ +  - ]:          8 :                 if (errno != ENOENT)
     245                 :          8 :                         return;
     246                 :          0 :         }
     247                 :            : 
     248                 :            :         /* No recursivity for packages out of the prefix */
     249         [ -  + ]:         12 :         if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
     250                 :          0 :                 return;
     251                 :            : 
     252                 :            :         /* remove the trailing '/' */
     253                 :         12 :         tmp = strrchr(dir, '/');
     254         [ +  - ]:         12 :         if (tmp == NULL)
     255                 :          0 :                 return;
     256         [ +  - ]:         12 :         if (tmp == dir)
     257                 :          0 :                 return;
     258                 :            : 
     259                 :         12 :         tmp[0] = '\0';
     260                 :         12 :         tmp = strrchr(dir, '/');
     261         [ -  + ]:         12 :         if (tmp == NULL)
     262                 :          0 :                 return;
     263                 :            : 
     264                 :         12 :         tmp[1] = '\0';
     265                 :            : 
     266                 :         12 :         rmdir_p(db, pkg, dir, prefix_r);
     267                 :         36 : }
     268                 :            : 
     269                 :            : static void
     270                 :        304 : pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
     271                 :            : {
     272                 :            :         char prefix_r[MAXPATHLEN];
     273                 :            :         size_t i;
     274                 :            : 
     275                 :        304 :         snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix + 1);
     276         [ +  + ]:        328 :         for (i = 0; i < pkg->dir_to_del_len; i++)
     277                 :         24 :                 rmdir_p(db, pkg, pkg->dir_to_del[i], prefix_r);
     278                 :        304 : }
     279                 :            : 
     280                 :            : void
     281                 :        158 : pkg_delete_file(struct pkg *pkg, struct pkg_file *file, unsigned force)
     282                 :            : {
     283                 :            :         const char *path;
     284                 :            :         const char *prefix_rel;
     285                 :            :         size_t len;
     286                 :            : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
     287                 :            :         struct stat st;
     288                 :            : #if !defined(HAVE_CHFLAGSAT)
     289                 :            :         int fd;
     290                 :            : #endif
     291                 :            : #endif
     292                 :        158 :         pkg_open_root_fd(pkg);
     293                 :            : 
     294                 :        158 :         path = file->path;
     295                 :        158 :         path++;
     296                 :            : 
     297                 :        158 :         prefix_rel = pkg->prefix;
     298                 :        158 :         prefix_rel++;
     299                 :        158 :         len = strlen(prefix_rel);
     300   [ +  +  +  + ]:        174 :         while (len > 0 && prefix_rel[len - 1] == '/')
     301                 :         16 :                 len--;
     302                 :            : 
     303                 :            : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
     304         [ -  + ]:        158 :         if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
     305         [ +  - ]:        158 :                 if (st.st_flags & NOCHANGESFLAGS) {
     306                 :            : #ifdef HAVE_CHFLAGSAT
     307                 :          0 :                         chflagsat(pkg->rootfd, path,
     308                 :          0 :                             st.st_flags & ~NOCHANGESFLAGS,
     309                 :            :                             AT_SYMLINK_NOFOLLOW);
     310                 :            : #else
     311                 :            :                         fd = openat(pkg->rootfd, path, O_NOFOLLOW);
     312                 :            :                         if (fd > 0) {
     313                 :            :                                 fchflags(fd, st.st_flags & ~NOCHANGESFLAGS);
     314                 :            :                                 close(fd);
     315                 :            :                         }
     316                 :            : #endif
     317                 :          0 :                 }
     318                 :        158 :         }
     319                 :            : #endif
     320                 :        158 :         pkg_debug(1, "Deleting file: '%s'", path);
     321         [ +  - ]:        158 :         if (unlinkat(pkg->rootfd, path, 0) == -1) {
     322         [ #  # ]:          0 :                 if (force < 2) {
     323         [ #  # ]:          0 :                         if (errno == ENOENT)
     324                 :          0 :                                 pkg_emit_file_missing(pkg, file);
     325                 :            :                         else
     326                 :          0 :                                 pkg_emit_errno("unlinkat", path);
     327                 :          0 :                 }
     328                 :          0 :                 return;
     329                 :            :         }
     330                 :            : 
     331                 :            :         /* do not bother about directories not in prefix */
     332   [ +  +  +  + ]:        158 :         if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
     333                 :         32 :                 pkg_add_dir_to_del(pkg, path, NULL);
     334                 :        158 : }
     335                 :            : 
     336                 :            : int
     337                 :        172 : pkg_delete_files(struct pkg *pkg, unsigned force)
     338                 :            :         /* force: 0 ... be careful and vocal about it.
     339                 :            :          *        1 ... remove files without bothering about checksums.
     340                 :            :          *        2 ... like 1, but remain silent if removal fails.
     341                 :            :          */
     342                 :            : {
     343                 :        172 :         struct pkg_file *file = NULL;
     344                 :            : 
     345                 :        172 :         int             nfiles, cur_file = 0;
     346                 :            : 
     347                 :        172 :         nfiles = pkghash_count(pkg->filehash);
     348                 :            : 
     349         [ +  + ]:        172 :         if (nfiles == 0)
     350                 :         79 :                 return (EPKG_OK);
     351                 :            : 
     352                 :         93 :         pkg_emit_delete_files_begin(pkg);
     353                 :         93 :         pkg_emit_progress_start(NULL);
     354                 :            : 
     355         [ +  + ]:        243 :         while (pkg_files(pkg, &file) == EPKG_OK) {
     356                 :        150 :                 append_touched_file(file->path);
     357                 :        150 :                 pkg_emit_progress_tick(cur_file++, nfiles);
     358                 :        150 :                 pkg_delete_file(pkg, file, force);
     359                 :            :         }
     360                 :            : 
     361                 :         93 :         pkg_emit_progress_tick(nfiles, nfiles);
     362                 :         93 :         pkg_emit_delete_files_finished(pkg);
     363                 :            : 
     364                 :         93 :         return (EPKG_OK);
     365                 :        172 : }
     366                 :            : 
     367                 :            : void
     368                 :          4 : pkg_delete_dir(struct pkg *pkg, struct pkg_dir *dir)
     369                 :            : {
     370                 :            :         const char *path;
     371                 :            :         const char *prefix_rel;
     372                 :            :         size_t len;
     373                 :            : 
     374                 :          4 :         pkg_open_root_fd(pkg);
     375                 :            : 
     376                 :          4 :         path = dir->path;
     377                 :            :         /* remove the first / */
     378                 :          4 :         path++;
     379                 :            : 
     380                 :          4 :         prefix_rel = pkg->prefix;
     381                 :          4 :         prefix_rel++;
     382                 :          4 :         len = strlen(prefix_rel);
     383         [ +  + ]:          8 :         while (prefix_rel[len - 1] == '/')
     384                 :          4 :                 len--;
     385                 :            : 
     386   [ +  -  +  - ]:          4 :         if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
     387                 :          4 :                 pkg_add_dir_to_del(pkg, NULL, path);
     388                 :          4 :         } else {
     389         [ #  # ]:          0 :                 if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
     390                 :          0 :                         pkg->dir_to_del_cap += 64;
     391                 :          0 :                         pkg->dir_to_del = xrealloc(pkg->dir_to_del,
     392                 :          0 :                             pkg->dir_to_del_cap * sizeof(char *));
     393                 :          0 :                 }
     394                 :          0 :                 pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
     395                 :            :         }
     396                 :          4 : }
     397                 :            : 
     398                 :            : int
     399                 :        304 : pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
     400                 :            : {
     401                 :        304 :         struct pkg_dir  *dir = NULL;
     402                 :            : 
     403         [ +  + ]:        308 :         while (pkg_dirs(pkg, &dir) == EPKG_OK) {
     404   [ -  +  #  # ]:          4 :                 if (new != NULL && !pkg_has_dir(new, dir->path))
     405                 :          0 :                         continue;
     406                 :          4 :                 pkg_delete_dir(pkg, dir);
     407                 :            :         }
     408                 :            : 
     409                 :        304 :         pkg_effective_rmdir(db, pkg);
     410                 :            : 
     411                 :        304 :         return (EPKG_OK);
     412                 :            : }

Generated by: LCOV version 1.15