LCOV - code coverage report
Current view: top level - src/divert - main.c (source / functions) Coverage Total Hit
Test: dpkg 1.22.7-3-g89f48 C code coverage Lines: 87.0 % 386 336
Test Date: 2024-07-17 02:53:43 Functions: 96.3 % 27 26
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 75.6 % 242 183

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

Generated by: LCOV version 2.0-1