LCOV - code coverage report
Current view: top level - src/divert - main.c (source / functions) Hit Total Coverage
Test: dpkg 1.21.11 C code coverage Lines: 337 387 87.1 %
Date: 2022-12-03 00:40:01 Functions: 26 27 96.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 183 242 75.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * dpkg-divert - override a package's version of a file
       3                 :            :  *
       4                 :            :  * Copyright © 1995 Ian Jackson
       5                 :            :  * Copyright © 2000, 2001 Wichert Akkerman
       6                 :            :  * Copyright © 2006-2015, 2017-2018 Guillem Jover <guillem@debian.org>
       7                 :            :  * Copyright © 2011 Linaro Limited
       8                 :            :  * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
       9                 :            :  *
      10                 :            :  * This program is free software; you can redistribute it and/or modify
      11                 :            :  * it under the terms of the GNU General Public License as published by
      12                 :            :  * the Free Software Foundation; either version 2 of the License, or
      13                 :            :  * (at your option) any later version.
      14                 :            :  *
      15                 :            :  * This program is distributed in the hope that it will be useful,
      16                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18                 :            :  * GNU General Public License for more details.
      19                 :            :  *
      20                 :            :  * You should have received a copy of the GNU General Public License
      21                 :            :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include <config.h>
      25                 :            : #include <compat.h>
      26                 :            : 
      27                 :            : #include <sys/types.h>
      28                 :            : #include <sys/stat.h>
      29                 :            : 
      30                 :            : #include <errno.h>
      31                 :            : #if HAVE_LOCALE_H
      32                 :            : #include <locale.h>
      33                 :            : #endif
      34                 :            : #include <fcntl.h>
      35                 :            : #include <fnmatch.h>
      36                 :            : #include <string.h>
      37                 :            : #include <stdlib.h>
      38                 :            : #include <unistd.h>
      39                 :            : 
      40                 :            : #include <dpkg/i18n.h>
      41                 :            : #include <dpkg/dpkg.h>
      42                 :            : #include <dpkg/dpkg-db.h>
      43                 :            : #include <dpkg/debug.h>
      44                 :            : #include <dpkg/arch.h>
      45                 :            : #include <dpkg/file.h>
      46                 :            : #include <dpkg/glob.h>
      47                 :            : #include <dpkg/buffer.h>
      48                 :            : #include <dpkg/options.h>
      49                 :            : #include <dpkg/db-fsys.h>
      50                 :            : 
      51                 :            : 
      52                 :            : static const char printforhelp[] = N_(
      53                 :            : "Use --help for help about diverting files.");
      54                 :            : 
      55                 :            : static bool opt_pkgname_match_any = true;
      56                 :            : static const char *opt_pkgname = NULL;
      57                 :            : static const char *opt_divertto = NULL;
      58                 :            : 
      59                 :            : static int opt_verbose = 1;
      60                 :            : static int opt_test = 0;
      61                 :            : static int opt_rename = -1;
      62                 :            : 
      63                 :            : 
      64                 :            : static int
      65                 :         49 : printversion(const char *const *argv)
      66                 :            : {
      67                 :         49 :         printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
      68                 :            :                PACKAGE_RELEASE);
      69                 :            : 
      70                 :         49 :         printf(_(
      71                 :            : "This is free software; see the GNU General Public License version 2 or\n"
      72                 :            : "later for copying conditions. There is NO warranty.\n"));
      73                 :            : 
      74                 :         49 :         m_output(stdout, _("<standard output>"));
      75                 :            : 
      76                 :         49 :         return 0;
      77                 :            : }
      78                 :            : 
      79                 :            : static int
      80                 :          1 : usage(const char *const *argv)
      81                 :            : {
      82                 :          1 :         printf(_(
      83                 :            : "Usage: %s [<option>...] <command>\n"
      84                 :            : "\n"), dpkg_get_progname());
      85                 :            : 
      86                 :          1 :         printf(_(
      87                 :            : "Commands:\n"
      88                 :            : "  [--add] <file>           add a diversion.\n"
      89                 :            : "  --remove <file>          remove the diversion.\n"
      90                 :            : "  --list [<glob-pattern>]  show file diversions.\n"
      91                 :            : "  --listpackage <file>     show what package diverts the file.\n"
      92                 :            : "  --truename <file>        return the diverted file.\n"
      93                 :            : "\n"));
      94                 :            : 
      95                 :          1 :         printf(_(
      96                 :            : "Options:\n"
      97                 :            : "  --package <package>      name of the package whose copy of <file> will not\n"
      98                 :            : "                             be diverted.\n"
      99                 :            : "  --local                  all packages' versions are diverted.\n"
     100                 :            : "  --divert <divert-to>     the name used by other packages' versions.\n"
     101                 :            : "  --rename                 actually move the file aside (or back).\n"
     102                 :            : "  --no-rename              do not move the file aside (or back) (default).\n"
     103                 :            : "  --admindir <directory>   set the directory with the diversions file.\n"
     104                 :            : "  --instdir <directory>    set the root directory, but not the admin dir.\n"
     105                 :            : "  --root <directory>       set the directory of the root filesystem.\n"
     106                 :            : "  --test                   don't do anything, just demonstrate.\n"
     107                 :            : "  --quiet                  quiet operation, minimal output.\n"
     108                 :            : "  --help                   show this help message.\n"
     109                 :            : "  --version                show the version.\n"
     110                 :            : "\n"));
     111                 :            : 
     112                 :          1 :         printf(_(
     113                 :            : "When adding, default is --local and --divert <original>.distrib.\n"
     114                 :            : "When removing, --package or --local and --divert must match if specified.\n"
     115                 :            : "Package preinst/postrm scripts should always specify --package and --divert.\n"));
     116                 :            : 
     117                 :          1 :         m_output(stdout, _("<standard output>"));
     118                 :            : 
     119                 :          1 :         return 0;
     120                 :            : }
     121                 :            : 
     122                 :            : static void
     123                 :         57 : opt_rename_setup(void)
     124                 :            : {
     125         [ +  + ]:         57 :         if (opt_rename >= 0)
     126                 :         42 :                 return;
     127                 :            : 
     128                 :         15 :         opt_rename = 0;
     129                 :         15 :         warning(_("please specify --no-rename explicitly, the default "
     130                 :            :                   "will change to --rename in 1.20.x"));
     131                 :            : }
     132                 :            : 
     133                 :            : struct file {
     134                 :            :         char *name;
     135                 :            :         enum {
     136                 :            :                 FILE_STAT_INVALID,
     137                 :            :                 FILE_STAT_VALID,
     138                 :            :                 FILE_STAT_NOFILE,
     139                 :            :         } stat_state;
     140                 :            :         struct stat stat;
     141                 :            : };
     142                 :            : 
     143                 :            : static void
     144                 :         90 : file_init(struct file *f, const char *filename)
     145                 :            : {
     146                 :         90 :         struct varbuf usefilename = VARBUF_INIT;
     147                 :            : 
     148                 :         90 :         varbuf_add_str(&usefilename, dpkg_fsys_get_dir());
     149                 :         90 :         varbuf_add_str(&usefilename, filename);
     150                 :         90 :         varbuf_end_str(&usefilename);
     151                 :            : 
     152                 :         90 :         f->name = varbuf_detach(&usefilename);
     153                 :         90 :         f->stat_state = FILE_STAT_INVALID;
     154                 :         90 : }
     155                 :            : 
     156                 :            : static void
     157                 :         62 : file_destroy(struct file *f)
     158                 :            : {
     159                 :         62 :         free(f->name);
     160                 :         62 : }
     161                 :            : 
     162                 :            : static void
     163                 :         77 : file_stat(struct file *f)
     164                 :            : {
     165                 :            :         int ret;
     166                 :            : 
     167         [ +  + ]:         77 :         if (f->stat_state != FILE_STAT_INVALID)
     168                 :         20 :                 return;
     169                 :            : 
     170                 :         57 :         ret = lstat(f->name, &f->stat);
     171   [ +  +  +  + ]:         57 :         if (ret && errno != ENOENT)
     172                 :          2 :                 ohshite(_("cannot stat file '%s'"), f->name);
     173                 :            : 
     174         [ +  + ]:         55 :         if (ret == 0)
     175                 :         17 :                 f->stat_state = FILE_STAT_VALID;
     176                 :            :         else
     177                 :         38 :                 f->stat_state = FILE_STAT_NOFILE;
     178                 :            : }
     179                 :            : 
     180                 :            : static void
     181                 :         23 : check_writable_dir(struct file *f)
     182                 :            : {
     183                 :            :         char *tmpname;
     184                 :            :         int tmpfd;
     185                 :            : 
     186                 :         23 :         tmpname = str_fmt("%s%s", f->name, ".dpkg-divert.tmp");
     187                 :            : 
     188                 :         23 :         tmpfd = creat(tmpname, 0600);
     189         [ +  + ]:         23 :         if (tmpfd < 0)
     190                 :          2 :                 ohshite(_("error checking '%s'"), f->name);
     191                 :         21 :         close(tmpfd);
     192                 :         21 :         (void)unlink(tmpname);
     193                 :            : 
     194                 :         21 :         free(tmpname);
     195                 :         21 : }
     196                 :            : 
     197                 :            : static bool
     198                 :         23 : check_rename(struct file *src, struct file *dst)
     199                 :            : {
     200                 :         23 :         file_stat(src);
     201                 :            : 
     202                 :            :         /* If the source file is not present and we are not going to do
     203                 :            :          * the rename anyway there's no point in checking any further. */
     204         [ +  + ]:         23 :         if (src->stat_state == FILE_STAT_NOFILE)
     205                 :         10 :                 return false;
     206                 :            : 
     207                 :         13 :         file_stat(dst);
     208                 :            : 
     209                 :            :         /*
     210                 :            :          * Unfortunately we have to check for write access in both places,
     211                 :            :          * just having +w is not enough, since people do mount things RO,
     212                 :            :          * and we need to fail before we start mucking around with things.
     213                 :            :          * So we open a file with the same name as the diversions but with
     214                 :            :          * an extension that (hopefully) won't overwrite anything. If it
     215                 :            :          * succeeds, we assume a writable filesystem.
     216                 :            :          */
     217                 :            : 
     218                 :         12 :         check_writable_dir(src);
     219                 :         11 :         check_writable_dir(dst);
     220                 :            : 
     221         [ +  - ]:         10 :         if (src->stat_state == FILE_STAT_VALID &&
     222         [ +  + ]:         10 :             dst->stat_state == FILE_STAT_VALID &&
     223         [ +  - ]:          2 :             !(src->stat.st_dev == dst->stat.st_dev &&
     224         [ +  + ]:          2 :               src->stat.st_ino == dst->stat.st_ino))
     225                 :          1 :                 ohshit(_("rename involves overwriting '%s' with\n"
     226                 :            :                          "  different file '%s', not allowed"),
     227                 :            :                         dst->name, src->name);
     228                 :            : 
     229                 :          9 :         return true;
     230                 :            : }
     231                 :            : 
     232                 :            : static void
     233                 :          0 : file_copy(const char *src, const char *dst)
     234                 :            : {
     235                 :            :         struct dpkg_error err;
     236                 :            :         char *tmp;
     237                 :            :         int srcfd, dstfd;
     238                 :            : 
     239                 :          0 :         srcfd = open(src, O_RDONLY);
     240         [ #  # ]:          0 :         if (srcfd < 0)
     241                 :          0 :                 ohshite(_("unable to open file '%s'"), src);
     242                 :            : 
     243                 :          0 :         tmp = str_fmt("%s%s", dst, ".dpkg-divert.tmp");
     244                 :          0 :         dstfd = creat(tmp, 0600);
     245         [ #  # ]:          0 :         if (dstfd < 0)
     246                 :          0 :                 ohshite(_("unable to create file '%s'"), tmp);
     247                 :            : 
     248                 :          0 :         push_cleanup(cu_filename, ~ehflag_normaltidy, 1, tmp);
     249                 :            : 
     250         [ #  # ]:          0 :         if (fd_fd_copy(srcfd, dstfd, -1, &err) < 0)
     251                 :          0 :                 ohshit(_("cannot copy '%s' to '%s': %s"), src, tmp, err.str);
     252                 :            : 
     253                 :          0 :         close(srcfd);
     254                 :            : 
     255         [ #  # ]:          0 :         if (fsync(dstfd))
     256                 :          0 :                 ohshite(_("unable to sync file '%s'"), tmp);
     257         [ #  # ]:          0 :         if (close(dstfd))
     258                 :          0 :                 ohshite(_("unable to close file '%s'"), tmp);
     259                 :            : 
     260                 :          0 :         file_copy_perms(src, tmp);
     261                 :            : 
     262         [ #  # ]:          0 :         if (rename(tmp, dst) != 0)
     263                 :          0 :                 ohshite(_("cannot rename '%s' to '%s'"), tmp, dst);
     264                 :            : 
     265                 :          0 :         free(tmp);
     266                 :            : 
     267                 :          0 :         pop_cleanup(ehflag_normaltidy);
     268                 :          0 : }
     269                 :            : 
     270                 :            : static void
     271                 :          6 : file_rename(struct file *src, struct file *dst)
     272                 :            : {
     273         [ -  + ]:          6 :         if (src->stat_state == FILE_STAT_NOFILE)
     274                 :          0 :                 return;
     275                 :            : 
     276         [ +  + ]:          6 :         if (dst->stat_state == FILE_STAT_VALID) {
     277         [ -  + ]:          1 :                 if (unlink(src->name))
     278                 :          0 :                         ohshite(_("rename: remove duplicate old link '%s'"),
     279                 :            :                                 src->name);
     280                 :            :         } else {
     281         [ +  - ]:          5 :                 if (rename(src->name, dst->name) == 0)
     282                 :          5 :                         return;
     283                 :            : 
     284                 :            :                 /* If a rename didn't work try moving the file instead. */
     285                 :          0 :                 file_copy(src->name, dst->name);
     286                 :            : 
     287         [ #  # ]:          0 :                 if (unlink(src->name))
     288                 :          0 :                         ohshite(_("unable to remove copied source file '%s'"),
     289                 :            :                                 src->name);
     290                 :            :         }
     291                 :            : }
     292                 :            : 
     293                 :            : static void
     294                 :         65 : diversion_check_filename(const char *filename)
     295                 :            : {
     296         [ +  + ]:         65 :         if (filename[0] != '/')
     297                 :          4 :                 badusage(_("filename \"%s\" is not absolute"), filename);
     298         [ +  + ]:         61 :         if (strchr(filename, '\n') != NULL)
     299                 :          4 :                 badusage(_("file may not contain newlines"));
     300                 :         57 : }
     301                 :            : 
     302                 :            : static const char *
     303                 :         73 : diversion_pkg_name(struct fsys_diversion *d)
     304                 :            : {
     305         [ +  + ]:         73 :         if (d->pkgset == NULL)
     306                 :         37 :                 return ":";
     307                 :            :         else
     308                 :         36 :                 return d->pkgset->name;
     309                 :            : }
     310                 :            : 
     311                 :            : static const char *
     312                 :         46 : varbuf_diversion(struct varbuf *str, const char *pkgname,
     313                 :            :                  const char *filename, const char *divertto)
     314                 :            : {
     315                 :         46 :         varbuf_reset(str);
     316                 :            : 
     317         [ +  + ]:         46 :         if (pkgname == NULL) {
     318         [ -  + ]:         27 :                 if (divertto == NULL)
     319                 :          0 :                         varbuf_printf(str, _("local diversion of %s"), filename);
     320                 :            :                 else
     321                 :         27 :                         varbuf_printf(str, _("local diversion of %s to %s"),
     322                 :            :                                       filename, divertto);
     323                 :            :         } else {
     324         [ +  + ]:         19 :                 if (divertto == NULL)
     325                 :          1 :                         varbuf_printf(str, _("diversion of %s by %s"),
     326                 :            :                                       filename, pkgname);
     327                 :            :                 else
     328                 :         18 :                         varbuf_printf(str, _("diversion of %s to %s by %s"),
     329                 :            :                                       filename, divertto, pkgname);
     330                 :            :         }
     331                 :            : 
     332                 :         46 :         return str->buf;
     333                 :            : }
     334                 :            : 
     335                 :            : static const char *
     336                 :         10 : diversion_current(const char *filename)
     337                 :            : {
     338                 :            :         static struct varbuf str = VARBUF_INIT;
     339                 :            : 
     340         [ +  + ]:         10 :         if (opt_pkgname_match_any) {
     341                 :          2 :                 varbuf_reset(&str);
     342                 :            : 
     343         [ +  + ]:          2 :                 if (opt_divertto == NULL)
     344                 :          1 :                         varbuf_printf(&str, _("any diversion of %s"), filename);
     345                 :            :                 else
     346                 :          1 :                         varbuf_printf(&str, _("any diversion of %s to %s"),
     347                 :            :                                       filename, opt_divertto);
     348                 :            :         } else {
     349                 :          8 :                 return varbuf_diversion(&str, opt_pkgname, filename, opt_divertto);
     350                 :            :         }
     351                 :            : 
     352                 :          2 :         return str.buf;
     353                 :            : }
     354                 :            : 
     355                 :            : static const char *
     356                 :         38 : diversion_describe(struct fsys_diversion *d)
     357                 :            : {
     358                 :            :         static struct varbuf str = VARBUF_INIT;
     359                 :            :         const char *pkgname;
     360                 :            :         const char *name_from, *name_to;
     361                 :            : 
     362         [ +  + ]:         38 :         if (d->camefrom) {
     363                 :          2 :                 name_from = d->camefrom->name;
     364                 :          2 :                 name_to = d->camefrom->divert->useinstead->name;
     365                 :            :         } else {
     366                 :         36 :                 name_from = d->useinstead->divert->camefrom->name;
     367                 :         36 :                 name_to = d->useinstead->name;
     368                 :            :         }
     369                 :            : 
     370         [ +  + ]:         38 :         if (d->pkgset == NULL)
     371                 :         23 :                 pkgname = NULL;
     372                 :            :         else
     373                 :         15 :                 pkgname = d->pkgset->name;
     374                 :            : 
     375                 :         38 :         return varbuf_diversion(&str, pkgname, name_from, name_to);
     376                 :            : }
     377                 :            : 
     378                 :            : static void
     379                 :         29 : divertdb_write(void)
     380                 :            : {
     381                 :            :         char *dbname;
     382                 :            :         struct atomic_file *file;
     383                 :            :         struct fsys_hash_iter *iter;
     384                 :            :         struct fsys_namenode *namenode;
     385                 :            : 
     386                 :         29 :         dbname = dpkg_db_get_path(DIVERSIONSFILE);
     387                 :            : 
     388                 :         29 :         file = atomic_file_new(dbname, ATOMIC_FILE_BACKUP);
     389                 :         29 :         atomic_file_open(file);
     390                 :            : 
     391                 :         28 :         iter = fsys_hash_iter_new();
     392         [ +  + ]:        116 :         while ((namenode = fsys_hash_iter_next(iter))) {
     393                 :         88 :                 struct fsys_diversion *d = namenode->divert;
     394                 :            : 
     395   [ +  +  +  + ]:         88 :                 if (d == NULL || d->useinstead == NULL)
     396                 :         48 :                         continue;
     397                 :            : 
     398                 :         40 :                 fprintf(file->fp, "%s\n%s\n%s\n",
     399                 :         40 :                         d->useinstead->divert->camefrom->name,
     400                 :         40 :                         d->useinstead->name,
     401                 :            :                         diversion_pkg_name(d));
     402                 :            :         }
     403                 :         28 :         fsys_hash_iter_free(iter);
     404                 :            : 
     405                 :         28 :         atomic_file_sync(file);
     406                 :         27 :         atomic_file_close(file);
     407                 :         27 :         atomic_file_commit(file);
     408                 :         26 :         atomic_file_free(file);
     409                 :            : 
     410                 :         26 :         free(dbname);
     411                 :         26 : }
     412                 :            : 
     413                 :            : static bool
     414                 :          6 : diversion_is_essential(struct fsys_namenode *namenode)
     415                 :            : {
     416                 :            :         struct pkginfo *pkg;
     417                 :            :         struct pkg_hash_iter *pkg_iter;
     418                 :            :         struct fsys_node_pkgs_iter *iter;
     419                 :          6 :         bool essential = false;
     420                 :            : 
     421                 :          6 :         pkg_iter = pkg_hash_iter_new();
     422         [ -  + ]:         12 :         while ((pkg = pkg_hash_iter_next_pkg(pkg_iter))) {
     423         [ #  # ]:          0 :                 if (pkg->installed.essential)
     424                 :          0 :                         ensure_packagefiles_available(pkg);
     425                 :            :         }
     426                 :          6 :         pkg_hash_iter_free(pkg_iter);
     427                 :            : 
     428                 :          6 :         iter = fsys_node_pkgs_iter_new(namenode);
     429         [ -  + ]:          6 :         while ((pkg = fsys_node_pkgs_iter_next(iter))) {
     430         [ #  # ]:          0 :                 if (pkg->installed.essential) {
     431                 :          0 :                         essential = true;
     432                 :          0 :                         break;
     433                 :            :                 }
     434                 :            :         }
     435                 :          6 :         fsys_node_pkgs_iter_free(iter);
     436                 :            : 
     437                 :          6 :         return essential;
     438                 :            : }
     439                 :            : 
     440                 :            : static bool
     441                 :          7 : diversion_is_owned_by_self(struct pkgset *set, struct fsys_namenode *namenode)
     442                 :            : {
     443                 :            :         struct pkginfo *pkg;
     444                 :            :         struct fsys_node_pkgs_iter *iter;
     445                 :          7 :         bool owned = false;
     446                 :            : 
     447         [ +  + ]:          7 :         if (set == NULL)
     448                 :          6 :                 return false;
     449                 :            : 
     450         [ +  + ]:          2 :         for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
     451                 :          1 :                 ensure_packagefiles_available(pkg);
     452                 :            : 
     453                 :          1 :         iter = fsys_node_pkgs_iter_new(namenode);
     454         [ +  - ]:          1 :         while ((pkg = fsys_node_pkgs_iter_next(iter))) {
     455         [ +  - ]:          1 :                 if (pkg->set == set) {
     456                 :          1 :                         owned = true;
     457                 :          1 :                         break;
     458                 :            :                 }
     459                 :            :         }
     460                 :          1 :         fsys_node_pkgs_iter_free(iter);
     461                 :            : 
     462                 :          1 :         return owned;
     463                 :            : }
     464                 :            : 
     465                 :            : static int
     466                 :         44 : diversion_add(const char *const *argv)
     467                 :            : {
     468                 :         44 :         const char *filename = argv[0];
     469                 :            :         struct file file_from, file_to;
     470                 :            :         struct fsys_diversion *contest, *altname;
     471                 :            :         struct fsys_namenode *fnn_from, *fnn_to;
     472                 :            :         struct pkgset *pkgset;
     473                 :            : 
     474                 :         44 :         opt_pkgname_match_any = false;
     475                 :         44 :         opt_rename_setup();
     476                 :            : 
     477                 :            :         /* Handle filename. */
     478   [ +  +  -  + ]:         44 :         if (!filename || argv[1])
     479                 :          1 :                 badusage(_("--%s needs a single argument"), cipaction->olong);
     480                 :            : 
     481                 :         43 :         diversion_check_filename(filename);
     482                 :            : 
     483                 :         41 :         modstatdb_open(msdbrw_readonly);
     484                 :         41 :         ensure_diversions();
     485                 :            : 
     486                 :         41 :         file_init(&file_from, filename);
     487                 :         41 :         file_stat(&file_from);
     488                 :            : 
     489         [ +  + ]:         40 :         if (file_from.stat_state == FILE_STAT_VALID &&
     490         [ +  + ]:         13 :             S_ISDIR(file_from.stat.st_mode))
     491                 :          1 :                 badusage(_("cannot divert directories"));
     492                 :            : 
     493                 :         39 :         fnn_from = fsys_hash_find_node(filename, 0);
     494                 :            : 
     495                 :            :         /* Handle divertto. */
     496         [ +  + ]:         39 :         if (opt_divertto == NULL)
     497                 :         34 :                 opt_divertto = str_fmt("%s.distrib", filename);
     498                 :            : 
     499         [ -  + ]:         39 :         if (strcmp(filename, opt_divertto) == 0)
     500                 :          0 :                 badusage(_("cannot divert file '%s' to itself"), filename);
     501                 :            : 
     502                 :         39 :         file_init(&file_to, opt_divertto);
     503                 :            : 
     504                 :         39 :         fnn_to = fsys_hash_find_node(opt_divertto, 0);
     505                 :            : 
     506                 :            :         /* Handle package name. */
     507         [ +  + ]:         39 :         if (opt_pkgname == NULL)
     508                 :         34 :                 pkgset = NULL;
     509                 :            :         else
     510                 :          5 :                 pkgset = pkg_hash_find_set(opt_pkgname);
     511                 :            : 
     512                 :            :         /* Check we are not stomping over an existing diversion. */
     513   [ +  +  +  + ]:         39 :         if (fnn_from->divert || fnn_to->divert) {
     514   [ +  +  +  + ]:          9 :                 if (fnn_to->divert && fnn_to->divert->camefrom &&
     515         [ +  + ]:          6 :                     strcmp(fnn_to->divert->camefrom->name, filename) == 0 &&
     516   [ +  -  +  - ]:          5 :                     fnn_from->divert && fnn_from->divert->useinstead &&
     517         [ +  - ]:          5 :                     strcmp(fnn_from->divert->useinstead->name, opt_divertto) == 0 &&
     518         [ +  + ]:          5 :                     fnn_from->divert->pkgset == pkgset) {
     519         [ +  - ]:          3 :                         if (opt_verbose > 0)
     520                 :          3 :                                 printf(_("Leaving '%s'\n"),
     521                 :            :                                        diversion_describe(fnn_from->divert));
     522                 :            : 
     523                 :          3 :                         file_destroy(&file_from);
     524                 :          3 :                         file_destroy(&file_to);
     525                 :            : 
     526                 :          3 :                         modstatdb_shutdown();
     527                 :            : 
     528                 :          3 :                         return 0;
     529                 :            :                 }
     530                 :            : 
     531                 :          6 :                 ohshit(_("'%s' clashes with '%s'"),
     532                 :            :                        diversion_current(filename),
     533         [ +  + ]:          6 :                        fnn_from->divert ?
     534                 :          4 :                        diversion_describe(fnn_from->divert) :
     535                 :          2 :                        diversion_describe(fnn_to->divert));
     536                 :            :         }
     537                 :            : 
     538                 :            :         /* Create new diversion. */
     539                 :         30 :         contest = nfmalloc(sizeof(*contest));
     540                 :         30 :         altname = nfmalloc(sizeof(*altname));
     541                 :            : 
     542                 :         30 :         altname->camefrom = fnn_from;
     543                 :         30 :         altname->camefrom->divert = contest;
     544                 :         30 :         altname->useinstead = NULL;
     545                 :         30 :         altname->pkgset = pkgset;
     546                 :            : 
     547                 :         30 :         contest->useinstead = fnn_to;
     548                 :         30 :         contest->useinstead->divert = altname;
     549                 :         30 :         contest->camefrom = NULL;
     550                 :         30 :         contest->pkgset = pkgset;
     551                 :            : 
     552                 :            :         /* Update database and file system if needed. */
     553         [ +  + ]:         30 :         if (opt_verbose > 0)
     554                 :          8 :                 printf(_("Adding '%s'\n"), diversion_describe(contest));
     555         [ +  + ]:         30 :         if (opt_rename)
     556                 :         20 :                 opt_rename = check_rename(&file_from, &file_to);
     557                 :            :         /* Check we are not renaming a file owned by the diverting pkgset. */
     558   [ +  +  +  + ]:         26 :         if (opt_rename && diversion_is_owned_by_self(pkgset, fnn_from)) {
     559         [ -  + ]:          1 :                 if (opt_verbose > 0)
     560                 :          0 :                         printf(_("Ignoring request to rename file '%s' "
     561                 :            :                                  "owned by diverting package '%s'\n"),
     562                 :            :                                filename, pkgset->name);
     563                 :          1 :                 opt_rename = false;
     564                 :            :         }
     565   [ +  +  -  + ]:         26 :         if (opt_rename && diversion_is_essential(fnn_from))
     566                 :          0 :                 warning(_("diverting file '%s' from an Essential package with "
     567                 :            :                           "rename is dangerous, use --no-rename"), filename);
     568         [ +  + ]:         26 :         if (!opt_test) {
     569                 :         25 :                 divertdb_write();
     570         [ +  + ]:         22 :                 if (opt_rename)
     571                 :          5 :                         file_rename(&file_from, &file_to);
     572                 :            :         }
     573                 :            : 
     574                 :         23 :         file_destroy(&file_from);
     575                 :         23 :         file_destroy(&file_to);
     576                 :            : 
     577                 :         23 :         modstatdb_shutdown();
     578                 :            : 
     579                 :         23 :         return 0;
     580                 :            : }
     581                 :            : 
     582                 :            : static bool
     583                 :          5 : diversion_is_shared(struct pkgset *set, struct fsys_namenode *namenode)
     584                 :            : {
     585                 :            :         const char *archname;
     586                 :            :         struct pkginfo *pkg;
     587                 :            :         struct dpkg_arch *arch;
     588                 :            :         struct fsys_node_pkgs_iter *iter;
     589                 :          5 :         bool shared = false;
     590                 :            : 
     591         [ +  + ]:          5 :         if (set == NULL)
     592                 :          4 :                 return false;
     593                 :            : 
     594                 :          1 :         archname = getenv("DPKG_MAINTSCRIPT_ARCH");
     595                 :          1 :         arch = dpkg_arch_find(archname);
     596   [ -  +  -  - ]:          1 :         if (arch->type == DPKG_ARCH_NONE || arch->type == DPKG_ARCH_EMPTY)
     597                 :          1 :                 return false;
     598                 :            : 
     599         [ #  # ]:          0 :         for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
     600                 :          0 :                 ensure_packagefiles_available(pkg);
     601                 :            : 
     602                 :          0 :         iter = fsys_node_pkgs_iter_new(namenode);
     603         [ #  # ]:          0 :         while ((pkg = fsys_node_pkgs_iter_next(iter))) {
     604   [ #  #  #  # ]:          0 :                 if (pkg->set == set && pkg->installed.arch != arch) {
     605                 :          0 :                         shared = true;
     606                 :          0 :                         break;
     607                 :            :                 }
     608                 :            :         }
     609                 :          0 :         fsys_node_pkgs_iter_free(iter);
     610                 :            : 
     611                 :          0 :         return shared;
     612                 :            : }
     613                 :            : 
     614                 :            : static int
     615                 :         13 : diversion_remove(const char *const *argv)
     616                 :            : {
     617                 :         13 :         const char *filename = argv[0];
     618                 :            :         struct fsys_namenode *namenode;
     619                 :            :         struct fsys_diversion *contest, *altname;
     620                 :            :         struct file file_from, file_to;
     621                 :            :         struct pkgset *pkgset;
     622                 :            : 
     623                 :         13 :         opt_rename_setup();
     624                 :            : 
     625   [ +  +  -  + ]:         13 :         if (!filename || argv[1])
     626                 :          1 :                 badusage(_("--%s needs a single argument"), cipaction->olong);
     627                 :            : 
     628                 :         12 :         diversion_check_filename(filename);
     629                 :            : 
     630                 :         10 :         modstatdb_open(msdbrw_readonly);
     631                 :         10 :         ensure_diversions();
     632                 :            : 
     633                 :         10 :         namenode = fsys_hash_find_node(filename, FHFF_NONE);
     634                 :            : 
     635   [ +  +  +  - ]:         10 :         if (namenode == NULL || namenode->divert == NULL ||
     636         [ -  + ]:          8 :             namenode->divert->useinstead == NULL) {
     637         [ +  + ]:          2 :                 if (opt_verbose > 0)
     638                 :          1 :                         printf(_("No diversion '%s', none removed.\n"),
     639                 :            :                                diversion_current(filename));
     640                 :          2 :                 modstatdb_shutdown();
     641                 :          2 :                 return 0;
     642                 :            :         }
     643                 :            : 
     644         [ +  + ]:          8 :         if (opt_pkgname == NULL)
     645                 :          5 :                 pkgset = NULL;
     646                 :            :         else
     647                 :          3 :                 pkgset = pkg_hash_find_set(opt_pkgname);
     648                 :            : 
     649                 :          8 :         contest = namenode->divert;
     650                 :          8 :         altname = contest->useinstead->divert;
     651                 :            : 
     652         [ +  + ]:          8 :         if (opt_divertto != NULL &&
     653         [ +  + ]:          3 :             strcmp(opt_divertto, contest->useinstead->name) != 0)
     654                 :          2 :                 ohshit(_("mismatch on divert-to\n"
     655                 :            :                          "  when removing '%s'\n"
     656                 :            :                          "  found '%s'"),
     657                 :            :                        diversion_current(filename),
     658                 :            :                        diversion_describe(contest));
     659                 :            : 
     660   [ +  +  +  + ]:          6 :         if (!opt_pkgname_match_any && pkgset != contest->pkgset)
     661                 :          1 :                 ohshit(_("mismatch on package\n"
     662                 :            :                          "  when removing '%s'\n"
     663                 :            :                          "  found '%s'"),
     664                 :            :                        diversion_current(filename),
     665                 :            :                        diversion_describe(contest));
     666                 :            : 
     667                 :            :         /* Ignore removal request if the diverted file is still owned
     668                 :            :          * by another package in the same set. */
     669         [ -  + ]:          5 :         if (diversion_is_shared(pkgset, namenode)) {
     670         [ #  # ]:          0 :                 if (opt_verbose > 0)
     671                 :          0 :                         printf(_("Ignoring request to remove shared diversion '%s'.\n"),
     672                 :            :                                diversion_describe(contest));
     673                 :          0 :                 modstatdb_shutdown();
     674                 :          0 :                 return 0;
     675                 :            :         }
     676                 :            : 
     677         [ +  + ]:          5 :         if (opt_verbose > 0)
     678                 :          3 :                 printf(_("Removing '%s'\n"), diversion_describe(contest));
     679                 :            : 
     680                 :          5 :         file_init(&file_from, altname->camefrom->name);
     681                 :          5 :         file_init(&file_to, contest->useinstead->name);
     682                 :            : 
     683                 :            :         /* Remove entries from database. */
     684                 :          5 :         contest->useinstead->divert = NULL;
     685                 :          5 :         altname->camefrom->divert = NULL;
     686                 :            : 
     687         [ +  + ]:          5 :         if (opt_rename)
     688                 :          3 :                 opt_rename = check_rename(&file_to, &file_from);
     689   [ +  +  +  + ]:          5 :         if (opt_rename && !opt_test)
     690                 :          1 :                 file_rename(&file_to, &file_from);
     691                 :            : 
     692         [ +  + ]:          5 :         if (!opt_test)
     693                 :          4 :                 divertdb_write();
     694                 :            : 
     695                 :          5 :         file_destroy(&file_from);
     696                 :          5 :         file_destroy(&file_to);
     697                 :            : 
     698                 :          5 :         modstatdb_shutdown();
     699                 :            : 
     700                 :          5 :         return 0;
     701                 :            : }
     702                 :            : 
     703                 :            : static int
     704                 :         17 : diversion_list(const char *const *argv)
     705                 :            : {
     706                 :            :         struct fsys_hash_iter *iter;
     707                 :            :         struct fsys_namenode *namenode;
     708                 :         17 :         struct glob_node *glob_list = NULL;
     709                 :            :         const char *pattern;
     710                 :            : 
     711                 :         17 :         modstatdb_open(msdbrw_readonly);
     712                 :         17 :         ensure_diversions();
     713                 :            : 
     714         [ +  + ]:         27 :         while ((pattern = *argv++))
     715                 :         13 :                 glob_list_prepend(&glob_list, m_strdup(pattern));
     716                 :            : 
     717         [ +  + ]:         14 :         if (glob_list == NULL)
     718                 :          2 :                 glob_list_prepend(&glob_list, m_strdup("*"));
     719                 :            : 
     720                 :         14 :         iter = fsys_hash_iter_new();
     721         [ +  + ]:         80 :         while ((namenode = fsys_hash_iter_next(iter))) {
     722                 :            :                 struct glob_node *g;
     723                 :         66 :                 struct fsys_diversion *contest = namenode->divert;
     724                 :            :                 struct fsys_diversion *altname;
     725                 :            :                 const char *pkgname;
     726                 :            : 
     727   [ +  -  +  + ]:         66 :                 if (contest == NULL || contest->useinstead == NULL)
     728                 :         33 :                         continue;
     729                 :            : 
     730                 :         33 :                 altname = contest->useinstead->divert;
     731                 :            : 
     732                 :         33 :                 pkgname = diversion_pkg_name(contest);
     733                 :            : 
     734         [ +  + ]:         53 :                 for (g = glob_list; g; g = g->next) {
     735   [ +  +  +  + ]:         63 :                         if (fnmatch(g->pattern, pkgname, 0) == 0 ||
     736         [ +  + ]:         54 :                             fnmatch(g->pattern, contest->useinstead->name, 0) == 0 ||
     737                 :         26 :                             fnmatch(g->pattern, altname->camefrom->name, 0) == 0) {
     738                 :         15 :                                 printf("%s\n", diversion_describe(contest));
     739                 :         15 :                                 break;
     740                 :            :                         }
     741                 :            :                 }
     742                 :            :         }
     743                 :         14 :         fsys_hash_iter_free(iter);
     744                 :            : 
     745                 :         14 :         glob_list_free(glob_list);
     746                 :            : 
     747                 :         14 :         modstatdb_shutdown();
     748                 :            : 
     749                 :         14 :         return 0;
     750                 :            : }
     751                 :            : 
     752                 :            : static int
     753                 :          6 : diversion_truename(const char *const *argv)
     754                 :            : {
     755                 :          6 :         const char *filename = argv[0];
     756                 :            :         struct fsys_namenode *namenode;
     757                 :            : 
     758   [ +  +  -  + ]:          6 :         if (!filename || argv[1])
     759                 :          1 :                 badusage(_("--%s needs a single argument"), cipaction->olong);
     760                 :            : 
     761                 :          5 :         diversion_check_filename(filename);
     762                 :            : 
     763                 :          3 :         modstatdb_open(msdbrw_readonly);
     764                 :          3 :         ensure_diversions();
     765                 :            : 
     766                 :          3 :         namenode = fsys_hash_find_node(filename, FHFF_NONE);
     767                 :            : 
     768                 :            :         /* Print the given name if file is not diverted. */
     769   [ +  +  +  -  :          3 :         if (namenode && namenode->divert && namenode->divert->useinstead)
                   +  + ]
     770                 :          1 :                 printf("%s\n", namenode->divert->useinstead->name);
     771                 :            :         else
     772                 :          2 :                 printf("%s\n", filename);
     773                 :            : 
     774                 :          3 :         modstatdb_shutdown();
     775                 :            : 
     776                 :          3 :         return 0;
     777                 :            : }
     778                 :            : 
     779                 :            : static int
     780                 :          6 : diversion_listpackage(const char *const *argv)
     781                 :            : {
     782                 :          6 :         const char *filename = argv[0];
     783                 :            :         struct fsys_namenode *namenode;
     784                 :            : 
     785   [ +  +  -  + ]:          6 :         if (!filename || argv[1])
     786                 :          1 :                 badusage(_("--%s needs a single argument"), cipaction->olong);
     787                 :            : 
     788                 :          5 :         diversion_check_filename(filename);
     789                 :            : 
     790                 :          3 :         modstatdb_open(msdbrw_readonly);
     791                 :          3 :         ensure_diversions();
     792                 :            : 
     793                 :          3 :         namenode = fsys_hash_find_node(filename, FHFF_NONE);
     794                 :            : 
     795                 :            :         /* Print nothing if file is not diverted. */
     796   [ +  +  -  + ]:          3 :         if (namenode == NULL || namenode->divert == NULL)
     797                 :          1 :                 return 0;
     798                 :            : 
     799         [ +  + ]:          2 :         if (namenode->divert->pkgset == NULL)
     800                 :            :                 /* Indicate package is local using something not in package
     801                 :            :                  * namespace. */
     802                 :          1 :                 printf("LOCAL\n");
     803                 :            :         else
     804                 :          1 :                 printf("%s\n", namenode->divert->pkgset->name);
     805                 :            : 
     806                 :          2 :         modstatdb_shutdown();
     807                 :            : 
     808                 :          2 :         return 0;
     809                 :            : }
     810                 :            : 
     811                 :            : static void
     812                 :         10 : set_package(const struct cmdinfo *cip, const char *value)
     813                 :            : {
     814                 :         10 :         opt_pkgname_match_any = false;
     815                 :            : 
     816                 :            :         /* If value is NULL we are being called from --local. */
     817                 :         10 :         opt_pkgname = value;
     818                 :            : 
     819   [ +  +  +  + ]:         10 :         if (opt_pkgname && strchr(opt_pkgname, '\n') != NULL)
     820                 :          1 :                 badusage(_("package may not contain newlines"));
     821                 :          9 : }
     822                 :            : 
     823                 :            : static void
     824                 :         11 : set_divertto(const struct cmdinfo *cip, const char *value)
     825                 :            : {
     826                 :         11 :         opt_divertto = value;
     827                 :            : 
     828         [ +  + ]:         11 :         if (opt_divertto[0] != '/')
     829                 :          2 :                 badusage(_("filename \"%s\" is not absolute"), opt_divertto);
     830         [ +  + ]:          9 :         if (strchr(opt_divertto, '\n') != NULL)
     831                 :          1 :                 badusage(_("divert-to may not contain newlines"));
     832                 :          8 : }
     833                 :            : 
     834                 :            : static const struct cmdinfo cmdinfo_add =
     835                 :            :         ACTION("add",         0, 0, diversion_add);
     836                 :            : 
     837                 :            : static const struct cmdinfo cmdinfos[] = {
     838                 :            :         ACTION("add",         0, 0, diversion_add),
     839                 :            :         ACTION("remove",      0, 0, diversion_remove),
     840                 :            :         ACTION("list",        0, 0, diversion_list),
     841                 :            :         ACTION("listpackage", 0, 0, diversion_listpackage),
     842                 :            :         ACTION("truename",    0, 0, diversion_truename),
     843                 :            :         ACTION("help",        '?', 0, usage),
     844                 :            :         ACTION("version",     0,   0, printversion),
     845                 :            : 
     846                 :            :         { "admindir",   0,   1,  NULL,         NULL,      set_admindir, 0 },
     847                 :            :         { "instdir",    0,   1,  NULL,         NULL,      set_instdir,  0 },
     848                 :            :         { "root",       0,   1,  NULL,         NULL,      set_root,     0 },
     849                 :            :         { "divert",     0,   1,  NULL,         NULL,      set_divertto  },
     850                 :            :         { "package",    0,   1,  NULL,         NULL,      set_package   },
     851                 :            :         { "local",      0,   0,  NULL,         NULL,      set_package   },
     852                 :            :         { "quiet",      0,   0,  &opt_verbose, NULL,      NULL, 0       },
     853                 :            :         { "rename",     0,   0,  &opt_rename,  NULL,      NULL, 1       },
     854                 :            :         { "no-rename",  0,   0,  &opt_rename,  NULL,      NULL, 0       },
     855                 :            :         { "test",       0,   0,  &opt_test,    NULL,      NULL, 1       },
     856                 :            :         {  NULL,        0                                               }
     857                 :            : };
     858                 :            : 
     859                 :            : int
     860                 :        145 : main(int argc, const char * const *argv)
     861                 :            : {
     862                 :            :         const char *env_pkgname;
     863                 :            :         int ret;
     864                 :            : 
     865                 :        145 :         dpkg_locales_init(PACKAGE);
     866                 :        145 :         dpkg_program_init("dpkg-divert");
     867                 :        145 :         dpkg_options_parse(&argv, cmdinfos, printforhelp);
     868                 :            : 
     869                 :        136 :         debug(dbg_general, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
     870                 :            : 
     871                 :        136 :         env_pkgname = getenv("DPKG_MAINTSCRIPT_PACKAGE");
     872   [ +  +  -  + ]:        136 :         if (opt_pkgname_match_any && env_pkgname)
     873                 :          0 :                 set_package(NULL, env_pkgname);
     874                 :            : 
     875         [ +  + ]:        136 :         if (!cipaction)
     876                 :         15 :                 setaction(&cmdinfo_add, NULL);
     877                 :            : 
     878                 :        136 :         ret = cipaction->action(argv);
     879                 :            : 
     880                 :        103 :         dpkg_program_done();
     881                 :        103 :         dpkg_locales_done();
     882                 :            : 
     883                 :        103 :         return ret;
     884                 :            : }

Generated by: LCOV version 1.16