2 * dpkg-divert - override a package's version of a file
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>
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.
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.
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/>.
27 #include <sys/types.h>
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>
52 static const char printforhelp
[] = N_(
53 "Use --help for help about diverting files.");
55 static bool opt_pkgname_match_any
= true;
56 static const char *opt_pkgname
= NULL
;
57 static const char *opt_divertto
= NULL
;
59 static int opt_verbose
= 1;
60 static int opt_test
= 0;
61 static int opt_rename
= -1;
65 printversion(const char *const *argv
)
67 printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
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"));
74 m_output(stdout
, _("<standard output>"));
80 usage(const char *const *argv
)
83 "Usage: %s [<option>...] <command>\n"
84 "\n"), dpkg_get_progname());
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"
97 " --package <package> name of the package whose copy of <file> will not\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"
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"));
117 m_output(stdout
, _("<standard output>"));
123 opt_rename_setup(void)
129 warning(_("please specify --no-rename explicitly, the default "
130 "will change to --rename in 1.20.x"));
144 file_init(struct file
*f
, const char *filename
)
146 struct varbuf usefilename
= VARBUF_INIT
;
148 varbuf_add_str(&usefilename
, dpkg_fsys_get_dir());
149 varbuf_add_str(&usefilename
, filename
);
150 varbuf_end_str(&usefilename
);
152 f
->name
= varbuf_detach(&usefilename
);
153 f
->stat_state
= FILE_STAT_INVALID
;
157 file_destroy(struct file
*f
)
163 file_stat(struct file
*f
)
167 if (f
->stat_state
!= FILE_STAT_INVALID
)
170 ret
= lstat(f
->name
, &f
->stat
);
171 if (ret
&& errno
!= ENOENT
)
172 ohshite(_("cannot stat file '%s'"), f
->name
);
175 f
->stat_state
= FILE_STAT_VALID
;
177 f
->stat_state
= FILE_STAT_NOFILE
;
181 check_writable_dir(struct file
*f
)
186 tmpname
= str_fmt("%s%s", f
->name
, ".dpkg-divert.tmp");
188 tmpfd
= creat(tmpname
, 0600);
190 ohshite(_("error checking '%s'"), f
->name
);
192 (void)unlink(tmpname
);
198 check_rename(struct file
*src
, struct file
*dst
)
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 if (src
->stat_state
== FILE_STAT_NOFILE
)
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.
218 check_writable_dir(src
);
219 check_writable_dir(dst
);
221 if (src
->stat_state
== FILE_STAT_VALID
&&
222 dst
->stat_state
== FILE_STAT_VALID
&&
223 !(src
->stat
.st_dev
== dst
->stat
.st_dev
&&
224 src
->stat
.st_ino
== dst
->stat
.st_ino
))
225 ohshit(_("rename involves overwriting '%s' with\n"
226 " different file '%s', not allowed"),
227 dst
->name
, src
->name
);
233 file_copy(const char *src
, const char *dst
)
235 struct dpkg_error err
;
239 srcfd
= open(src
, O_RDONLY
);
241 ohshite(_("unable to open file '%s'"), src
);
243 tmp
= str_fmt("%s%s", dst
, ".dpkg-divert.tmp");
244 dstfd
= creat(tmp
, 0600);
246 ohshite(_("unable to create file '%s'"), tmp
);
248 push_cleanup(cu_filename
, ~ehflag_normaltidy
, 1, tmp
);
250 if (fd_fd_copy(srcfd
, dstfd
, -1, &err
) < 0)
251 ohshit(_("cannot copy '%s' to '%s': %s"), src
, tmp
, err
.str
);
256 ohshite(_("unable to sync file '%s'"), tmp
);
258 ohshite(_("unable to close file '%s'"), tmp
);
260 file_copy_perms(src
, tmp
);
262 if (rename(tmp
, dst
) != 0)
263 ohshite(_("cannot rename '%s' to '%s'"), tmp
, dst
);
267 pop_cleanup(ehflag_normaltidy
);
271 file_rename(struct file
*src
, struct file
*dst
)
273 if (src
->stat_state
== FILE_STAT_NOFILE
)
276 if (dst
->stat_state
== FILE_STAT_VALID
) {
277 if (unlink(src
->name
))
278 ohshite(_("rename: remove duplicate old link '%s'"),
281 if (rename(src
->name
, dst
->name
) == 0)
284 /* If a rename didn't work try moving the file instead. */
285 file_copy(src
->name
, dst
->name
);
287 if (unlink(src
->name
))
288 ohshite(_("unable to remove copied source file '%s'"),
294 diversion_check_filename(const char *filename
)
296 if (filename
[0] != '/')
297 badusage(_("filename \"%s\" is not absolute"), filename
);
298 if (strchr(filename
, '\n') != NULL
)
299 badusage(_("file may not contain newlines"));
303 diversion_pkg_name(struct fsys_diversion
*d
)
305 if (d
->pkgset
== NULL
)
308 return d
->pkgset
->name
;
312 varbuf_diversion(struct varbuf
*str
, const char *pkgname
,
313 const char *filename
, const char *divertto
)
317 if (pkgname
== NULL
) {
318 if (divertto
== NULL
)
319 varbuf_printf(str
, _("local diversion of %s"), filename
);
321 varbuf_printf(str
, _("local diversion of %s to %s"),
324 if (divertto
== NULL
)
325 varbuf_printf(str
, _("diversion of %s by %s"),
328 varbuf_printf(str
, _("diversion of %s to %s by %s"),
329 filename
, divertto
, pkgname
);
336 diversion_current(const char *filename
)
338 static struct varbuf str
= VARBUF_INIT
;
340 if (opt_pkgname_match_any
) {
343 if (opt_divertto
== NULL
)
344 varbuf_printf(&str
, _("any diversion of %s"), filename
);
346 varbuf_printf(&str
, _("any diversion of %s to %s"),
347 filename
, opt_divertto
);
349 return varbuf_diversion(&str
, opt_pkgname
, filename
, opt_divertto
);
356 diversion_describe(struct fsys_diversion
*d
)
358 static struct varbuf str
= VARBUF_INIT
;
360 const char *name_from
, *name_to
;
363 name_from
= d
->camefrom
->name
;
364 name_to
= d
->camefrom
->divert
->useinstead
->name
;
366 name_from
= d
->useinstead
->divert
->camefrom
->name
;
367 name_to
= d
->useinstead
->name
;
370 if (d
->pkgset
== NULL
)
373 pkgname
= d
->pkgset
->name
;
375 return varbuf_diversion(&str
, pkgname
, name_from
, name_to
);
382 struct atomic_file
*file
;
383 struct fsys_hash_iter
*iter
;
384 struct fsys_namenode
*namenode
;
386 dbname
= dpkg_db_get_path(DIVERSIONSFILE
);
388 file
= atomic_file_new(dbname
, ATOMIC_FILE_BACKUP
);
389 atomic_file_open(file
);
391 iter
= fsys_hash_iter_new();
392 while ((namenode
= fsys_hash_iter_next(iter
))) {
393 struct fsys_diversion
*d
= namenode
->divert
;
395 if (d
== NULL
|| d
->useinstead
== NULL
)
398 fprintf(file
->fp
, "%s\n%s\n%s\n",
399 d
->useinstead
->divert
->camefrom
->name
,
401 diversion_pkg_name(d
));
403 fsys_hash_iter_free(iter
);
405 atomic_file_sync(file
);
406 atomic_file_close(file
);
407 atomic_file_commit(file
);
408 atomic_file_free(file
);
414 diversion_is_essential(struct fsys_namenode
*namenode
)
417 struct pkg_hash_iter
*pkg_iter
;
418 struct fsys_node_pkgs_iter
*iter
;
419 bool essential
= false;
421 pkg_iter
= pkg_hash_iter_new();
422 while ((pkg
= pkg_hash_iter_next_pkg(pkg_iter
))) {
423 if (pkg
->installed
.essential
)
424 ensure_packagefiles_available(pkg
);
426 pkg_hash_iter_free(pkg_iter
);
428 iter
= fsys_node_pkgs_iter_new(namenode
);
429 while ((pkg
= fsys_node_pkgs_iter_next(iter
))) {
430 if (pkg
->installed
.essential
) {
435 fsys_node_pkgs_iter_free(iter
);
441 diversion_is_owned_by_self(struct pkgset
*set
, struct fsys_namenode
*namenode
)
444 struct fsys_node_pkgs_iter
*iter
;
450 for (pkg
= &set
->pkg
; pkg
; pkg
= pkg
->arch_next
)
451 ensure_packagefiles_available(pkg
);
453 iter
= fsys_node_pkgs_iter_new(namenode
);
454 while ((pkg
= fsys_node_pkgs_iter_next(iter
))) {
455 if (pkg
->set
== set
) {
460 fsys_node_pkgs_iter_free(iter
);
466 diversion_add(const char *const *argv
)
468 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
;
474 opt_pkgname_match_any
= false;
477 /* Handle filename. */
478 if (!filename
|| argv
[1])
479 badusage(_("--%s needs a single argument"), cipaction
->olong
);
481 diversion_check_filename(filename
);
483 modstatdb_open(msdbrw_readonly
);
486 file_init(&file_from
, filename
);
487 file_stat(&file_from
);
489 if (file_from
.stat_state
== FILE_STAT_VALID
&&
490 S_ISDIR(file_from
.stat
.st_mode
))
491 badusage(_("cannot divert directories"));
493 fnn_from
= fsys_hash_find_node(filename
, 0);
495 /* Handle divertto. */
496 if (opt_divertto
== NULL
)
497 opt_divertto
= str_fmt("%s.distrib", filename
);
499 if (strcmp(filename
, opt_divertto
) == 0)
500 badusage(_("cannot divert file '%s' to itself"), filename
);
502 file_init(&file_to
, opt_divertto
);
504 fnn_to
= fsys_hash_find_node(opt_divertto
, 0);
506 /* Handle package name. */
507 if (opt_pkgname
== NULL
)
510 pkgset
= pkg_hash_find_set(opt_pkgname
);
512 /* Check we are not stomping over an existing diversion. */
513 if (fnn_from
->divert
|| fnn_to
->divert
) {
514 if (fnn_to
->divert
&& fnn_to
->divert
->camefrom
&&
515 strcmp(fnn_to
->divert
->camefrom
->name
, filename
) == 0 &&
516 fnn_from
->divert
&& fnn_from
->divert
->useinstead
&&
517 strcmp(fnn_from
->divert
->useinstead
->name
, opt_divertto
) == 0 &&
518 fnn_from
->divert
->pkgset
== pkgset
) {
520 printf(_("Leaving '%s'\n"),
521 diversion_describe(fnn_from
->divert
));
523 file_destroy(&file_from
);
524 file_destroy(&file_to
);
526 modstatdb_shutdown();
531 ohshit(_("'%s' clashes with '%s'"),
532 diversion_current(filename
),
534 diversion_describe(fnn_from
->divert
) :
535 diversion_describe(fnn_to
->divert
));
538 /* Create new diversion. */
539 contest
= nfmalloc(sizeof(*contest
));
540 altname
= nfmalloc(sizeof(*altname
));
542 altname
->camefrom
= fnn_from
;
543 altname
->camefrom
->divert
= contest
;
544 altname
->useinstead
= NULL
;
545 altname
->pkgset
= pkgset
;
547 contest
->useinstead
= fnn_to
;
548 contest
->useinstead
->divert
= altname
;
549 contest
->camefrom
= NULL
;
550 contest
->pkgset
= pkgset
;
552 /* Update database and file system if needed. */
554 printf(_("Adding '%s'\n"), diversion_describe(contest
));
556 opt_rename
= check_rename(&file_from
, &file_to
);
557 /* Check we are not renaming a file owned by the diverting pkgset. */
558 if (opt_rename
&& diversion_is_owned_by_self(pkgset
, fnn_from
)) {
560 printf(_("Ignoring request to rename file '%s' "
561 "owned by diverting package '%s'\n"),
562 filename
, pkgset
->name
);
565 if (opt_rename
&& diversion_is_essential(fnn_from
))
566 warning(_("diverting file '%s' from an Essential package with "
567 "rename is dangerous, use --no-rename"), filename
);
571 file_rename(&file_from
, &file_to
);
574 file_destroy(&file_from
);
575 file_destroy(&file_to
);
577 modstatdb_shutdown();
583 diversion_is_shared(struct pkgset
*set
, struct fsys_namenode
*namenode
)
585 const char *archname
;
587 struct dpkg_arch
*arch
;
588 struct fsys_node_pkgs_iter
*iter
;
594 archname
= getenv("DPKG_MAINTSCRIPT_ARCH");
595 arch
= dpkg_arch_find(archname
);
596 if (arch
->type
== DPKG_ARCH_NONE
|| arch
->type
== DPKG_ARCH_EMPTY
)
599 for (pkg
= &set
->pkg
; pkg
; pkg
= pkg
->arch_next
)
600 ensure_packagefiles_available(pkg
);
602 iter
= fsys_node_pkgs_iter_new(namenode
);
603 while ((pkg
= fsys_node_pkgs_iter_next(iter
))) {
604 if (pkg
->set
== set
&& pkg
->installed
.arch
!= arch
) {
609 fsys_node_pkgs_iter_free(iter
);
615 diversion_remove(const char *const *argv
)
617 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
;
625 if (!filename
|| argv
[1])
626 badusage(_("--%s needs a single argument"), cipaction
->olong
);
628 diversion_check_filename(filename
);
630 modstatdb_open(msdbrw_readonly
);
633 namenode
= fsys_hash_find_node(filename
, FHFF_NONEW
);
635 if (namenode
== NULL
|| namenode
->divert
== NULL
||
636 namenode
->divert
->useinstead
== NULL
) {
638 printf(_("No diversion '%s', none removed.\n"),
639 diversion_current(filename
));
640 modstatdb_shutdown();
644 if (opt_pkgname
== NULL
)
647 pkgset
= pkg_hash_find_set(opt_pkgname
);
649 contest
= namenode
->divert
;
650 altname
= contest
->useinstead
->divert
;
652 if (opt_divertto
!= NULL
&&
653 strcmp(opt_divertto
, contest
->useinstead
->name
) != 0)
654 ohshit(_("mismatch on divert-to\n"
655 " when removing '%s'\n"
657 diversion_current(filename
),
658 diversion_describe(contest
));
660 if (!opt_pkgname_match_any
&& pkgset
!= contest
->pkgset
)
661 ohshit(_("mismatch on package\n"
662 " when removing '%s'\n"
664 diversion_current(filename
),
665 diversion_describe(contest
));
667 /* Ignore removal request if the diverted file is still owned
668 * by another package in the same set. */
669 if (diversion_is_shared(pkgset
, namenode
)) {
671 printf(_("Ignoring request to remove shared diversion '%s'.\n"),
672 diversion_describe(contest
));
673 modstatdb_shutdown();
678 printf(_("Removing '%s'\n"), diversion_describe(contest
));
680 file_init(&file_from
, altname
->camefrom
->name
);
681 file_init(&file_to
, contest
->useinstead
->name
);
683 /* Remove entries from database. */
684 contest
->useinstead
->divert
= NULL
;
685 altname
->camefrom
->divert
= NULL
;
688 opt_rename
= check_rename(&file_to
, &file_from
);
689 if (opt_rename
&& !opt_test
)
690 file_rename(&file_to
, &file_from
);
695 file_destroy(&file_from
);
696 file_destroy(&file_to
);
698 modstatdb_shutdown();
704 diversion_list(const char *const *argv
)
706 struct fsys_hash_iter
*iter
;
707 struct fsys_namenode
*namenode
;
708 struct glob_node
*glob_list
= NULL
;
711 modstatdb_open(msdbrw_readonly
);
714 while ((pattern
= *argv
++))
715 glob_list_prepend(&glob_list
, m_strdup(pattern
));
717 if (glob_list
== NULL
)
718 glob_list_prepend(&glob_list
, m_strdup("*"));
720 iter
= fsys_hash_iter_new();
721 while ((namenode
= fsys_hash_iter_next(iter
))) {
723 struct fsys_diversion
*contest
= namenode
->divert
;
724 struct fsys_diversion
*altname
;
727 if (contest
== NULL
|| contest
->useinstead
== NULL
)
730 altname
= contest
->useinstead
->divert
;
732 pkgname
= diversion_pkg_name(contest
);
734 for (g
= glob_list
; g
; g
= g
->next
) {
735 if (fnmatch(g
->pattern
, pkgname
, 0) == 0 ||
736 fnmatch(g
->pattern
, contest
->useinstead
->name
, 0) == 0 ||
737 fnmatch(g
->pattern
, altname
->camefrom
->name
, 0) == 0) {
738 printf("%s\n", diversion_describe(contest
));
743 fsys_hash_iter_free(iter
);
745 glob_list_free(glob_list
);
747 modstatdb_shutdown();
753 diversion_truename(const char *const *argv
)
755 const char *filename
= argv
[0];
756 struct fsys_namenode
*namenode
;
758 if (!filename
|| argv
[1])
759 badusage(_("--%s needs a single argument"), cipaction
->olong
);
761 diversion_check_filename(filename
);
763 modstatdb_open(msdbrw_readonly
);
766 namenode
= fsys_hash_find_node(filename
, FHFF_NONEW
);
768 /* Print the given name if file is not diverted. */
769 if (namenode
&& namenode
->divert
&& namenode
->divert
->useinstead
)
770 printf("%s\n", namenode
->divert
->useinstead
->name
);
772 printf("%s\n", filename
);
774 modstatdb_shutdown();
780 diversion_listpackage(const char *const *argv
)
782 const char *filename
= argv
[0];
783 struct fsys_namenode
*namenode
;
785 if (!filename
|| argv
[1])
786 badusage(_("--%s needs a single argument"), cipaction
->olong
);
788 diversion_check_filename(filename
);
790 modstatdb_open(msdbrw_readonly
);
793 namenode
= fsys_hash_find_node(filename
, FHFF_NONEW
);
795 /* Print nothing if file is not diverted. */
796 if (namenode
== NULL
|| namenode
->divert
== NULL
)
799 if (namenode
->divert
->pkgset
== NULL
)
800 /* Indicate package is local using something not in package
804 printf("%s\n", namenode
->divert
->pkgset
->name
);
806 modstatdb_shutdown();
812 set_package(const struct cmdinfo
*cip
, const char *value
)
814 opt_pkgname_match_any
= false;
816 /* If value is NULL we are being called from --local. */
819 if (opt_pkgname
&& strchr(opt_pkgname
, '\n') != NULL
)
820 badusage(_("package may not contain newlines"));
824 set_divertto(const struct cmdinfo
*cip
, const char *value
)
826 opt_divertto
= value
;
828 if (opt_divertto
[0] != '/')
829 badusage(_("filename \"%s\" is not absolute"), opt_divertto
);
830 if (strchr(opt_divertto
, '\n') != NULL
)
831 badusage(_("divert-to may not contain newlines"));
834 static const struct cmdinfo cmdinfo_add
=
835 ACTION("add", 0, 0, diversion_add
);
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
),
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 },
860 main(int argc
, const char * const *argv
)
862 const char *env_pkgname
;
865 dpkg_locales_init(PACKAGE
);
866 dpkg_program_init("dpkg-divert");
867 dpkg_options_parse(&argv
, cmdinfos
, printforhelp
);
869 debug(dbg_general
, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
871 env_pkgname
= getenv("DPKG_MAINTSCRIPT_PACKAGE");
872 if (opt_pkgname_match_any
&& env_pkgname
)
873 set_package(NULL
, env_pkgname
);
876 setaction(&cmdinfo_add
, NULL
);
878 ret
= cipaction
->action(argv
);