1# ex:ts=8 sw=4:
2# $OpenBSD: CollisionReport.pm,v 1.48 2019/09/04 12:27:38 espie Exp $
3#
4# Copyright (c) 2003-2006 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use strict;
19use warnings;
20
21package OpenBSD::PackingElement;
22sub handle_collisions
23{
24}
25
26package OpenBSD::PackingElement::FileBase;
27sub handle_collisions
28{
29	my ($self, $todo, $pkg, $bypkg) = @_;
30	my $name = $self->fullname;
31	if (defined $todo->{$name}) {
32		push(@{$bypkg->{$pkg}}, $name);
33		delete $todo->{$name};
34	}
35}
36
37package OpenBSD::CollisionReport;
38use OpenBSD::PackingList;
39use OpenBSD::PackageInfo;
40
41sub find_collisions
42{
43	my ($todo, $state) = @_;
44	my $verbose = $state->verbose >= 3;
45	my $bypkg = {};
46	for my $name (keys %$todo) {
47		my $pkg = $state->vstat->value($state->{destdir}.$name);
48		if (defined $pkg) {
49			push(@{$bypkg->{$pkg}}, $name);
50			delete $todo->{$name};
51		}
52	}
53
54
55	if (!%$todo) {
56		return $bypkg;
57	}
58	for my $pkg (installed_packages()) {
59		$state->say("Looking for collisions in #1", $pkg) if $verbose;
60		# XXX in -n mode, some stuff is not really there
61		# avoid warnings
62		next unless -d installed_info($pkg);
63		my $plist = OpenBSD::PackingList->from_installation($pkg,
64		    \&OpenBSD::PackingList::FilesOnly);
65		next if !defined $plist;
66		$plist->handle_collisions($todo, $pkg, $bypkg);
67	}
68	return $bypkg;
69}
70
71sub collision_report
72{
73	my ($list, $state, $set) = @_;
74
75	my $destdir = $state->{destdir};
76
77	if ($state->defines('removecollisions')) {
78		require OpenBSD::Error;
79		for my $f (@$list) {
80			$state->unlink(1, $destdir.$f->fullname);
81		}
82		return;
83	}
84	my %todo = map {($_->fullname, $_->{d})} @$list;
85	my %extra = map {($_->fullname, $_->{newly_found})} @$list;
86	my $clueless_bat;
87	my $clueless_bat2;
88	my $found = 0;
89
90	$state->errsay("Collision in #1: the following files already exist",
91	    $set->print);
92	if (!$state->defines('dontfindcollisions')) {
93		my $bypkg = find_collisions(\%todo, $state);
94		for my $pkg (sort keys %$bypkg) {
95		    for my $item (sort @{$bypkg->{$pkg}}) {
96		    	$found++;
97			$state->errsay("\t#1 (#2 and #3)", $item, $pkg,
98			    $extra{$item});
99		    }
100		    if ($pkg =~ m/^(?:partial\-|borked\.\d+$)/o) {
101			$clueless_bat = $pkg;
102		    }
103		    if ($pkg =~ m/^\.libs\d*-*$/o) {
104			$clueless_bat2 = $pkg;
105		    }
106		}
107	}
108	if (%todo) {
109
110		for my $item (sort keys %todo) {
111			my $old = $todo{$item};
112		    $state->errprint("\t#1 from #2", $item, $extra{$item});
113		    if (defined $old && -f $destdir.$item) {
114			    my $d = $old->new($destdir.$item);
115
116			    if ($d->equals($old)) {
117				    $state->errsay(" (same checksum)");
118			    } else {
119				    $state->errsay(" (different checksum)");
120			    }
121		    } else {
122			    $state->errsay;
123		    }
124	    	}
125	}
126	if (defined $clueless_bat) {
127		$state->errprint("The package name #1 suggests that a former installation\n".
128		    "of a similar package got interrupted.  It is likely that\n".
129		    "\tpkg_delete #1\n".
130		    "will solve the problem\n", $clueless_bat);
131	}
132	if (defined $clueless_bat2) {
133		$state->errprint("The package name #1 suggests remaining libraries\n".
134		    "from a former package update.  It is likely that\n".
135		    "\tpkg_delete #1\n".
136		    "will solve the problem\n", $clueless_bat2);
137	}
138	my $dorepair = 0;
139	if ($found == 0) {
140		$dorepair = $state->defines('repair') ||
141		    $state->confirm_defaults_to_no(
142		    "It seems to be a missing package registration\nRepair");
143	}
144	if ($dorepair == 1) {
145		for my $f (@$list) {
146
147			if ($state->unlink($state->verbose >= 2,
148			    $destdir.$f->fullname)) {
149				$state->{problems}--;
150			} else {
151				return;
152			}
153		}
154		$state->{repairdependencies} = 1;
155	}
156}
157
1581;
159