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();