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 : : }
|