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-2014 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/arch.h>
44 #include <dpkg/file.h>
45 #include <dpkg/glob.h>
46 #include <dpkg/buffer.h>
47 #include <dpkg/options.h>
52 static const char printforhelp
[] = N_(
53 "Use --help for help about diverting files.");
55 static const char *admindir
;
57 static bool opt_pkgname_match_any
= true;
58 static const char *opt_pkgname
= NULL
;
59 static const char *opt_divertto
= NULL
;
61 static int opt_verbose
= 1;
62 static int opt_test
= 0;
63 static int opt_rename
= 0;
67 printversion(const struct cmdinfo
*cip
, const char *value
)
69 printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
73 "This is free software; see the GNU General Public License version 2 or\n"
74 "later for copying conditions. There is NO warranty.\n"));
76 m_output(stdout
, _("<standard output>"));
82 usage(const struct cmdinfo
*cip
, const char *value
)
85 "Usage: %s [<option> ...] <command>\n"
86 "\n"), dpkg_get_progname());
90 " [--add] <file> add a diversion.\n"
91 " --remove <file> remove the diversion.\n"
92 " --list [<glob-pattern>] show file diversions.\n"
93 " --listpackage <file> show what package diverts the file.\n"
94 " --truename <file> return the diverted file.\n"
99 " --package <package> name of the package whose copy of <file> will not\n"
101 " --local all packages' versions are diverted.\n"
102 " --divert <divert-to> the name used by other packages' versions.\n"
103 " --rename actually move the file aside (or back).\n"
104 " --admindir <directory> set the directory with the diversions file.\n"
105 " --test don't do anything, just demonstrate.\n"
106 " --quiet quiet operation, minimal output.\n"
107 " --help show this help message.\n"
108 " --version show the version.\n"
112 "When adding, default is --local and --divert <original>.distrib.\n"
113 "When removing, --package or --local and --divert must match if specified.\n"
114 "Package preinst/postrm scripts should always specify --package and --divert.\n"));
116 m_output(stdout
, _("<standard output>"));
132 file_init(struct file
*f
, const char *filename
)
135 f
->stat_state
= FILE_STAT_INVALID
;
139 file_stat(struct file
*f
)
143 if (f
->stat_state
!= FILE_STAT_INVALID
)
146 ret
= lstat(f
->name
, &f
->stat
);
147 if (ret
&& errno
!= ENOENT
)
148 ohshite(_("cannot stat file '%s'"), f
->name
);
151 f
->stat_state
= FILE_STAT_VALID
;
153 f
->stat_state
= FILE_STAT_NOFILE
;
157 check_writable_dir(struct file
*f
)
162 tmpname
= str_fmt("%s%s", f
->name
, ".dpkg-divert.tmp");
164 tmpfd
= creat(tmpname
, 0600);
166 ohshite(_("error checking '%s'"), f
->name
);
168 (void)unlink(tmpname
);
174 check_rename(struct file
*src
, struct file
*dst
)
178 /* If the source file is not present and we are not going to do
179 * the rename anyway there's no point in checking any further. */
180 if (src
->stat_state
== FILE_STAT_NOFILE
)
186 * Unfortunately we have to check for write access in both places,
187 * just having +w is not enough, since people do mount things RO,
188 * and we need to fail before we start mucking around with things.
189 * So we open a file with the same name as the diversions but with
190 * an extension that (hopefully) won't overwrite anything. If it
191 * succeeds, we assume a writable filesystem.
194 check_writable_dir(src
);
195 check_writable_dir(dst
);
197 if (src
->stat_state
== FILE_STAT_VALID
&&
198 dst
->stat_state
== FILE_STAT_VALID
&&
199 !(src
->stat
.st_dev
== dst
->stat
.st_dev
&&
200 src
->stat
.st_ino
== dst
->stat
.st_ino
))
201 ohshit(_("rename involves overwriting '%s' with\n"
202 " different file '%s', not allowed"),
203 dst
->name
, src
->name
);
209 file_copy(const char *src
, const char *dst
)
211 struct dpkg_error err
;
215 srcfd
= open(src
, O_RDONLY
);
217 ohshite(_("unable to open file '%s'"), src
);
219 tmp
= str_fmt("%s%s", dst
, ".dpkg-divert.tmp");
220 dstfd
= creat(tmp
, 0600);
222 ohshite(_("unable to create file '%s'"), tmp
);
224 push_cleanup(cu_filename
, ~ehflag_normaltidy
, NULL
, 0, 1, tmp
);
226 if (fd_fd_copy(srcfd
, dstfd
, -1, &err
) < 0)
227 ohshit(_("cannot copy '%s' to '%s': %s"), src
, tmp
, err
.str
);
232 ohshite(_("unable to sync file '%s'"), tmp
);
234 ohshite(_("unable to close file '%s'"), tmp
);
236 file_copy_perms(src
, tmp
);
238 if (rename(tmp
, dst
) != 0)
239 ohshite(_("cannot rename '%s' to '%s'"), tmp
, dst
);
243 pop_cleanup(ehflag_normaltidy
);
247 file_rename(struct file
*src
, struct file
*dst
)
249 if (src
->stat_state
== FILE_STAT_NOFILE
)
252 if (dst
->stat_state
== FILE_STAT_VALID
) {
253 if (unlink(src
->name
))
254 ohshite(_("rename: remove duplicate old link '%s'"),
257 if (rename(src
->name
, dst
->name
) == 0)
260 /* If a rename didn't work try moving the file instead. */
261 file_copy(src
->name
, dst
->name
);
263 if (unlink(src
->name
))
264 ohshite(_("unable to remove copied source file '%s'"),
270 diversion_check_filename(const char *filename
)
272 if (filename
[0] != '/')
273 badusage(_("filename \"%s\" is not absolute"), filename
);
274 if (strchr(filename
, '\n') != NULL
)
275 badusage(_("file may not contain newlines"));
279 diversion_pkg_name(struct diversion
*d
)
281 if (d
->pkgset
== NULL
)
284 return d
->pkgset
->name
;
288 varbuf_diversion(struct varbuf
*str
, const char *pkgname
,
289 const char *filename
, const char *divertto
)
293 if (pkgname
== NULL
) {
294 if (divertto
== NULL
)
295 varbuf_printf(str
, _("local diversion of %s"), filename
);
297 varbuf_printf(str
, _("local diversion of %s to %s"),
300 if (divertto
== NULL
)
301 varbuf_printf(str
, _("diversion of %s by %s"),
304 varbuf_printf(str
, _("diversion of %s to %s by %s"),
305 filename
, divertto
, pkgname
);
312 diversion_current(const char *filename
)
314 static struct varbuf str
= VARBUF_INIT
;
316 if (opt_pkgname_match_any
) {
319 if (opt_divertto
== NULL
)
320 varbuf_printf(&str
, _("any diversion of %s"), filename
);
322 varbuf_printf(&str
, _("any diversion of %s to %s"),
323 filename
, opt_divertto
);
325 return varbuf_diversion(&str
, opt_pkgname
, filename
, opt_divertto
);
332 diversion_describe(struct diversion
*d
)
334 static struct varbuf str
= VARBUF_INIT
;
336 const char *name_from
, *name_to
;
339 name_from
= d
->camefrom
->name
;
340 name_to
= d
->camefrom
->divert
->useinstead
->name
;
342 name_from
= d
->useinstead
->divert
->camefrom
->name
;
343 name_to
= d
->useinstead
->name
;
346 if (d
->pkgset
== NULL
)
349 pkgname
= d
->pkgset
->name
;
351 return varbuf_diversion(&str
, pkgname
, name_from
, name_to
);
358 struct atomic_file
*file
;
359 struct fileiterator
*iter
;
360 struct filenamenode
*namenode
;
362 dbname
= dpkg_db_get_path(DIVERSIONSFILE
);
364 file
= atomic_file_new(dbname
, ATOMIC_FILE_BACKUP
);
365 atomic_file_open(file
);
367 iter
= files_db_iter_new();
368 while ((namenode
= files_db_iter_next(iter
))) {
369 struct diversion
*d
= namenode
->divert
;
371 if (d
== NULL
|| d
->useinstead
== NULL
)
374 fprintf(file
->fp
, "%s\n%s\n%s\n",
375 d
->useinstead
->divert
->camefrom
->name
,
377 diversion_pkg_name(d
));
379 files_db_iter_free(iter
);
381 atomic_file_sync(file
);
382 atomic_file_close(file
);
383 atomic_file_commit(file
);
384 atomic_file_free(file
);
390 diversion_is_owned_by_self(struct pkgset
*set
, struct filenamenode
*namenode
)
393 struct filepackages_iterator
*iter
;
399 for (pkg
= &set
->pkg
; pkg
; pkg
= pkg
->arch_next
)
400 ensure_packagefiles_available(pkg
);
402 iter
= filepackages_iter_new(namenode
);
403 while ((pkg
= filepackages_iter_next(iter
))) {
404 if (pkg
->set
== set
) {
409 filepackages_iter_free(iter
);
415 diversion_add(const char *const *argv
)
417 const char *filename
= argv
[0];
418 struct file file_from
, file_to
;
419 struct diversion
*contest
, *altname
;
420 struct filenamenode
*fnn_from
, *fnn_to
;
421 struct pkgset
*pkgset
;
423 opt_pkgname_match_any
= false;
425 /* Handle filename. */
426 if (!filename
|| argv
[1])
427 badusage(_("--%s needs a single argument"), cipaction
->olong
);
429 diversion_check_filename(filename
);
431 file_init(&file_from
, filename
);
432 file_stat(&file_from
);
434 if (file_from
.stat_state
== FILE_STAT_VALID
&&
435 S_ISDIR(file_from
.stat
.st_mode
))
436 badusage(_("cannot divert directories"));
438 fnn_from
= findnamenode(filename
, 0);
440 /* Handle divertto. */
441 if (opt_divertto
== NULL
)
442 opt_divertto
= str_fmt("%s.distrib", filename
);
444 if (strcmp(filename
, opt_divertto
) == 0)
445 badusage(_("cannot divert file '%s' to itself"), filename
);
447 file_init(&file_to
, opt_divertto
);
449 fnn_to
= findnamenode(opt_divertto
, 0);
451 /* Handle package name. */
452 if (opt_pkgname
== NULL
)
455 pkgset
= pkg_db_find_set(opt_pkgname
);
457 /* Check we are not stomping over an existing diversion. */
458 if (fnn_from
->divert
|| fnn_to
->divert
) {
459 if (fnn_to
->divert
&& fnn_to
->divert
->camefrom
&&
460 strcmp(fnn_to
->divert
->camefrom
->name
, filename
) == 0 &&
461 fnn_from
->divert
&& fnn_from
->divert
->useinstead
&&
462 strcmp(fnn_from
->divert
->useinstead
->name
, opt_divertto
) == 0 &&
463 fnn_from
->divert
->pkgset
== pkgset
) {
465 printf(_("Leaving '%s'\n"),
466 diversion_describe(fnn_from
->divert
));
470 ohshit(_("'%s' clashes with '%s'"),
471 diversion_current(filename
),
473 diversion_describe(fnn_from
->divert
) :
474 diversion_describe(fnn_to
->divert
));
477 /* Create new diversion. */
478 contest
= nfmalloc(sizeof(*contest
));
479 altname
= nfmalloc(sizeof(*altname
));
481 altname
->camefrom
= fnn_from
;
482 altname
->camefrom
->divert
= contest
;
483 altname
->useinstead
= NULL
;
484 altname
->pkgset
= pkgset
;
486 contest
->useinstead
= fnn_to
;
487 contest
->useinstead
->divert
= altname
;
488 contest
->camefrom
= NULL
;
489 contest
->pkgset
= pkgset
;
491 /* Update database and file system if needed. */
493 printf(_("Adding '%s'\n"), diversion_describe(contest
));
495 opt_rename
= check_rename(&file_from
, &file_to
);
496 /* Check we are not renaming a file owned by the diverting pkgset. */
497 if (opt_rename
&& diversion_is_owned_by_self(pkgset
, fnn_from
)) {
499 printf(_("Ignoring request to rename file '%s' "
500 "owned by diverting package '%s'\n"),
501 filename
, pkgset
->name
);
507 file_rename(&file_from
, &file_to
);
514 diversion_is_shared(struct pkgset
*set
, struct filenamenode
*namenode
)
516 const char *archname
;
518 struct dpkg_arch
*arch
;
519 struct filepackages_iterator
*iter
;
525 archname
= getenv("DPKG_MAINTSCRIPT_ARCH");
526 arch
= dpkg_arch_find(archname
);
527 if (arch
->type
== DPKG_ARCH_NONE
|| arch
->type
== DPKG_ARCH_EMPTY
)
530 for (pkg
= &set
->pkg
; pkg
; pkg
= pkg
->arch_next
)
531 ensure_packagefiles_available(pkg
);
533 iter
= filepackages_iter_new(namenode
);
534 while ((pkg
= filepackages_iter_next(iter
))) {
535 if (pkg
->set
== set
&& pkg
->installed
.arch
!= arch
) {
540 filepackages_iter_free(iter
);
546 diversion_remove(const char *const *argv
)
548 const char *filename
= argv
[0];
549 struct filenamenode
*namenode
;
550 struct diversion
*contest
, *altname
;
551 struct file file_from
, file_to
;
552 struct pkgset
*pkgset
;
554 if (!filename
|| argv
[1])
555 badusage(_("--%s needs a single argument"), cipaction
->olong
);
557 diversion_check_filename(filename
);
559 namenode
= findnamenode(filename
, fnn_nonew
);
561 if (namenode
== NULL
|| namenode
->divert
== NULL
||
562 namenode
->divert
->useinstead
== NULL
) {
564 printf(_("No diversion '%s', none removed.\n"),
565 diversion_current(filename
));
569 if (opt_pkgname
== NULL
)
572 pkgset
= pkg_db_find_set(opt_pkgname
);
574 contest
= namenode
->divert
;
575 altname
= contest
->useinstead
->divert
;
577 if (opt_divertto
!= NULL
&&
578 strcmp(opt_divertto
, contest
->useinstead
->name
) != 0)
579 ohshit(_("mismatch on divert-to\n"
580 " when removing '%s'\n"
582 diversion_current(filename
),
583 diversion_describe(contest
));
585 if (!opt_pkgname_match_any
&& pkgset
!= contest
->pkgset
)
586 ohshit(_("mismatch on package\n"
587 " when removing '%s'\n"
589 diversion_current(filename
),
590 diversion_describe(contest
));
592 /* Ignore removal request if the diverted file is still owned
593 * by another package in the same set. */
594 if (diversion_is_shared(pkgset
, namenode
)) {
596 printf(_("Ignoring request to remove shared diversion '%s'.\n"),
597 diversion_describe(contest
));
602 printf(_("Removing '%s'\n"), diversion_describe(contest
));
604 file_init(&file_from
, altname
->camefrom
->name
);
605 file_init(&file_to
, contest
->useinstead
->name
);
607 /* Remove entries from database. */
608 contest
->useinstead
->divert
= NULL
;
609 altname
->camefrom
->divert
= NULL
;
612 opt_rename
= check_rename(&file_to
, &file_from
);
613 if (opt_rename
&& !opt_test
)
614 file_rename(&file_to
, &file_from
);
623 diversion_list(const char *const *argv
)
625 struct fileiterator
*iter
;
626 struct filenamenode
*namenode
;
627 struct glob_node
*glob_list
= NULL
;
630 while ((pattern
= *argv
++))
631 glob_list_prepend(&glob_list
, m_strdup(pattern
));
633 if (glob_list
== NULL
)
634 glob_list_prepend(&glob_list
, m_strdup("*"));
636 iter
= files_db_iter_new();
637 while ((namenode
= files_db_iter_next(iter
))) {
639 struct diversion
*contest
= namenode
->divert
;
640 struct diversion
*altname
;
643 if (contest
->useinstead
== NULL
)
646 altname
= contest
->useinstead
->divert
;
648 pkgname
= diversion_pkg_name(contest
);
650 for (g
= glob_list
; g
; g
= g
->next
) {
651 if (fnmatch(g
->pattern
, pkgname
, 0) == 0 ||
652 fnmatch(g
->pattern
, contest
->useinstead
->name
, 0) == 0 ||
653 fnmatch(g
->pattern
, altname
->camefrom
->name
, 0) == 0) {
654 printf("%s\n", diversion_describe(contest
));
659 files_db_iter_free(iter
);
661 glob_list_free(glob_list
);
667 diversion_truename(const char *const *argv
)
669 const char *filename
= argv
[0];
670 struct filenamenode
*namenode
;
672 if (!filename
|| argv
[1])
673 badusage(_("--%s needs a single argument"), cipaction
->olong
);
675 diversion_check_filename(filename
);
677 namenode
= findnamenode(filename
, fnn_nonew
);
679 /* Print the given name if file is not diverted. */
680 if (namenode
&& namenode
->divert
->useinstead
)
681 printf("%s\n", namenode
->divert
->useinstead
->name
);
683 printf("%s\n", filename
);
689 diversion_listpackage(const char *const *argv
)
691 const char *filename
= argv
[0];
692 struct filenamenode
*namenode
;
694 if (!filename
|| argv
[1])
695 badusage(_("--%s needs a single argument"), cipaction
->olong
);
697 diversion_check_filename(filename
);
699 namenode
= findnamenode(filename
, fnn_nonew
);
701 /* Print nothing if file is not diverted. */
702 if (namenode
== NULL
)
705 if (namenode
->divert
->pkgset
== NULL
)
706 /* Indicate package is local using something not in package
710 printf("%s\n", namenode
->divert
->pkgset
->name
);
716 set_package(const struct cmdinfo
*cip
, const char *value
)
718 opt_pkgname_match_any
= false;
720 /* If value is NULL we are being called from --local. */
723 if (opt_pkgname
&& strchr(opt_pkgname
, '\n') != NULL
)
724 badusage(_("package may not contain newlines"));
728 set_divertto(const struct cmdinfo
*cip
, const char *value
)
730 opt_divertto
= value
;
732 if (opt_divertto
[0] != '/')
733 badusage(_("filename \"%s\" is not absolute"), opt_divertto
);
734 if (strchr(opt_divertto
, '\n') != NULL
)
735 badusage(_("divert-to may not contain newlines"));
738 static const struct cmdinfo cmdinfo_add
=
739 ACTION("add", 0, 0, diversion_add
);
741 static const struct cmdinfo cmdinfos
[] = {
742 ACTION("add", 0, 0, diversion_add
),
743 ACTION("remove", 0, 0, diversion_remove
),
744 ACTION("list", 0, 0, diversion_list
),
745 ACTION("listpackage", 0, 0, diversion_listpackage
),
746 ACTION("truename", 0, 0, diversion_truename
),
748 { "admindir", 0, 1, NULL
, &admindir
, NULL
},
749 { "divert", 0, 1, NULL
, NULL
, set_divertto
},
750 { "package", 0, 1, NULL
, NULL
, set_package
},
751 { "local", 0, 0, NULL
, NULL
, set_package
},
752 { "quiet", 0, 0, &opt_verbose
, NULL
, NULL
, 0 },
753 { "rename", 0, 0, &opt_rename
, NULL
, NULL
, 1 },
754 { "test", 0, 0, &opt_test
, NULL
, NULL
, 1 },
755 { "help", '?', 0, NULL
, NULL
, usage
},
756 { "version", 0, 0, NULL
, NULL
, printversion
},
761 main(int argc
, const char * const *argv
)
763 const char *env_pkgname
;
766 dpkg_locales_init(PACKAGE
);
767 dpkg_program_init("dpkg-divert");
768 dpkg_options_parse(&argv
, cmdinfos
, printforhelp
);
770 admindir
= dpkg_db_set_dir(admindir
);
772 env_pkgname
= getenv("DPKG_MAINTSCRIPT_PACKAGE");
773 if (opt_pkgname_match_any
&& env_pkgname
)
774 set_package(NULL
, env_pkgname
);
777 setaction(&cmdinfo_add
, NULL
);
779 modstatdb_open(msdbrw_readonly
);
783 ret
= cipaction
->action(argv
);
785 modstatdb_shutdown();