1# ex:ts=8 sw=4: 2# $OpenBSD: Delete.pm,v 1.160 2019/07/24 18:05:26 espie Exp $ 3# 4# Copyright (c) 2003-2014 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::Delete; 22use OpenBSD::Error; 23use OpenBSD::PackageInfo; 24use OpenBSD::RequiredBy; 25use OpenBSD::Paths; 26use File::Basename; 27 28sub keep_old_files 29{ 30 my ($state, $plist) = @_; 31 my $p = OpenBSD::PackingList->new; 32 my $borked = borked_package($plist->pkgname); 33 $p->set_infodir(installed_info($borked)); 34 mkdir($p->infodir); 35 36 $plist->copy_old_stuff($p, $state); 37 $p->set_pkgname($borked); 38 $p->to_installation; 39 return $borked; 40} 41 42sub manpages_unindex 43{ 44 my ($state) = @_; 45 return unless defined $state->{rmman}; 46 my $destdir = $state->{destdir}; 47 48 while (my ($k, $v) = each %{$state->{rmman}}) { 49 my @l = map { "$destdir$k/$_" } @$v; 50 if ($state->{not}) { 51 $state->say("Removing manpages in #1: #2", 52 $destdir.$k, join(' ', @l)) if $state->verbose; 53 } else { 54 $state->run_makewhatis(['-u', $destdir.$k], \@l); 55 } 56 } 57 delete $state->{rmman}; 58} 59 60sub validate_plist 61{ 62 my ($plist, $state) = @_; 63 64 $plist->prepare_for_deletion($state, $plist->pkgname); 65} 66 67sub remove_packing_info 68{ 69 my ($plist, $state) = @_; 70 71 my $dir = $plist->infodir; 72 73 for my $fname (info_names()) { 74 unlink($dir.$fname); 75 } 76 OpenBSD::RequiredBy->forget($dir); 77 OpenBSD::Requiring->forget($dir); 78 rmdir($dir) or 79 $state->fatal("can't finish removing directory #1: #2", $dir, $!); 80} 81 82sub delete_handle 83{ 84 my ($handle, $state) = @_; 85 my $pkgname = $handle->pkgname; 86 $state->progress->message($state->f("reading list for #1", $pkgname)); 87 my $plist = $handle->plist; 88 if ($plist->has('firmware') && !$state->defines('FW_UPDATE')) { 89 if ($state->is_interactive) { 90 if (!$state->confirm_defaults_to_no( 91 "\nDelete firmware #1", $pkgname)) { 92 $state->errsay("NOT deleting #1", $pkgname); 93 return; 94 } 95 } else { 96 $state->errsay("NOT deleting #1: use fw_update -d", 97 $pkgname); 98 return; 99 } 100 } 101 102 $state->{problems} = 0; 103 validate_plist($plist, $state); 104 $state->fatal("can't recover from deinstalling #1", $pkgname) 105 if $state->{problems}; 106 $state->vstat->synchronize; 107 108 delete_plist($plist, $state); 109} 110 111sub unregister_dependencies 112{ 113 my ($plist, $state) = @_; 114 115 my $pkgname = $plist->pkgname; 116 my $l = OpenBSD::Requiring->new($pkgname); 117 118 for my $name ($l->list) { 119 $state->say("remove dependency on #1", $name) 120 if $state->verbose >= 3; 121 local $@; 122 try { 123 OpenBSD::RequiredBy->new($name)->delete($pkgname); 124 } catch { 125 $state->errsay($_); 126 }; 127 } 128 $l->erase; 129} 130 131sub delete_plist 132{ 133 my ($plist, $state) = @_; 134 135 my $pkgname = $plist->pkgname; 136 $state->{pkgname} = $pkgname; 137 if (!$state->defines('stub')) { 138 if (!$state->{size_only}) { 139 $plist->register_manpage($state, 'rmman'); 140 manpages_unindex($state); 141 $state->progress->visit_with_size($plist, 'delete'); 142 } 143 } 144 145 unregister_dependencies($plist, $state); 146 return if $state->{not}; 147 if ($state->{baddelete}) { 148 my $borked = keep_old_files($state, $plist); 149 $state->log("Files kept as #1 package", $borked); 150 delete $state->{baddelete}; 151 } 152 153 154 remove_packing_info($plist, $state); 155 delete_installed($pkgname); 156} 157 158package OpenBSD::PackingElement; 159 160sub rename_file_to_temp 161{ 162 my ($self, $state) = @_; 163 require OpenBSD::Temp; 164 165 my $n = $self->realname($state); 166 167 my (undef, $j) = OpenBSD::Temp::permanent_file(undef, $n); 168 if (!defined $j) { 169 $state->errsay(OpenBSD::Temp->last_error); 170 return; 171 } 172 if (rename($n, $j)) { 173 $state->say("Renaming old file #1 to #2", $n, $j); 174 if ($self->name !~ m/^\//o && $self->cwd ne '.') { 175 my $c = $self->cwd; 176 $j =~ s|^\Q$c\E/||; 177 } 178 $self->set_name($j); 179 } else { 180 $state->errsay("Bad rename #1 to #2: #3", $n, $j, $!); 181 } 182} 183 184sub prepare_for_deletion 185{ 186} 187 188sub delete 189{ 190} 191 192sub record_shared 193{ 194} 195 196sub copy_old_stuff 197{ 198} 199 200package OpenBSD::PackingElement::Cwd; 201 202sub copy_old_stuff 203{ 204 my ($self, $plist, $state) = @_; 205 $self->add_object($plist); 206} 207 208package OpenBSD::PackingElement::FileObject; 209use File::Basename; 210 211sub mark_directory 212{ 213 my ($self, $state, $dir) = @_; 214 215 $state->{dirs_okay}{$dir} = 1; 216 my $d2 = dirname($dir); 217 if ($d2 ne $dir) { 218 $self->mark_directory($state, $d2); 219 } 220} 221 222sub mark_dir 223{ 224 my ($self, $state) = @_; 225 226 $self->mark_directory($state, dirname($self->fullname)); 227} 228 229sub do_not_delete 230{ 231 my ($self, $state) = @_; 232 233 my $realname = $self->realname($state); 234 $state->{baddelete} = 1; 235 $self->{stillaround} = 1; 236 237 delete $self->{symlink}; 238 delete $self->{link}; 239 my $algo = $self->{d}; 240 delete $self->{d}; 241 242 if (-l $realname) { 243 $self->{symlink} = readlink $realname; 244 } elsif (-f _) { 245 $self->{d} = $self->compute_digest($realname, $algo); 246 } elsif (-d _) { 247 # what should we do ? 248 } 249} 250 251 252package OpenBSD::PackingElement::DirlikeObject; 253sub mark_dir 254{ 255 my ($self, $state) = @_; 256 $self->mark_directory($state, $self->fullname); 257} 258 259package OpenBSD::PackingElement::RcScript; 260# XXX we should check stuff more thoroughly 261 262sub delete 263{ 264 my ($self, $state) = @_; 265 $state->{delete_rcscripts}{$self->fullname} = 1; 266 $self->SUPER::delete($state); 267} 268 269package OpenBSD::PackingElement::NewUser; 270sub delete 271{ 272 my ($self, $state) = @_; 273 274 if ($state->verbose >= 2) { 275 $state->say("rmuser: #1", $self->name); 276 } 277 278 $self->record_shared($state->{recorder}, $state->{pkgname}); 279} 280 281sub record_shared 282{ 283 my ($self, $recorder, $pkgname) = @_; 284 $recorder->{users}{$self->name} = $pkgname; 285} 286 287package OpenBSD::PackingElement::NewGroup; 288sub delete 289{ 290 my ($self, $state) = @_; 291 292 if ($state->verbose >= 2) { 293 $state->say("rmgroup: #1", $self->name); 294 } 295 296 $self->record_shared($state->{recorder}, $state->{pkgname}); 297} 298 299sub record_shared 300{ 301 my ($self, $recorder, $pkgname) = @_; 302 $recorder->{groups}{$self->name} = $pkgname; 303} 304 305package OpenBSD::PackingElement::DirBase; 306sub prepare_for_deletion 307{ 308 my ($self, $state, $pkgname) = @_; 309 $state->vstat->remove_directory( 310 $self->retrieve_fullname($state, $pkgname), $self); 311} 312 313sub delete 314{ 315 my ($self, $state) = @_; 316 317 if ($state->verbose >= 5) { 318 $state->say("rmdir: #1", $self->fullname); 319 } 320 321 $self->record_shared($state->{recorder}, $state->{pkgname}); 322} 323 324sub record_shared 325{ 326 my ($self, $recorder, $pkgname) = @_; 327 # enough for the entry to exist, we only record interesting 328 # entries more thoroughly 329 $recorder->{dirs}{$self->fullname} //= []; 330} 331 332package OpenBSD::PackingElement::Mandir; 333sub record_shared 334{ 335 my ($self, $recorder, $pkgname) = @_; 336 $self->{pkgname} = $pkgname; 337 push(@{$recorder->{dirs}{$self->fullname}} , $self); 338} 339 340package OpenBSD::PackingElement::Fontdir; 341sub record_shared 342{ 343 my ($self, $recorder, $pkgname) = @_; 344 $self->{pkgname} = $pkgname; 345 push(@{$recorder->{dirs}{$self->fullname}} , $self); 346 $recorder->{fonts_todo}{$self->fullname} = 1; 347} 348 349package OpenBSD::PackingElement::Infodir; 350sub record_shared 351{ 352 &OpenBSD::PackingElement::Mandir::record_shared; 353} 354 355package OpenBSD::PackingElement::Unexec; 356sub delete 357{ 358 my ($self, $state) = @_; 359 if ($self->should_run($state)) { 360 $self->run($state); 361 } 362} 363 364sub should_run() { 1 } 365 366package OpenBSD::PackingElement::UnexecDelete; 367sub should_run 368{ 369 my ($self, $state) = @_; 370 return !$state->replacing; 371} 372 373package OpenBSD::PackingElement::UnexecUpdate; 374sub should_run 375{ 376 my ($self, $state) = @_; 377 return $state->replacing; 378} 379 380package OpenBSD::PackingElement::DefineTag::Atend; 381sub delete 382{ 383 my ($self, $state) = @_; 384 if (!$state->replacing) { 385 $state->{tags}{deleted}{$self->name} = 1; 386 } 387} 388 389 390package OpenBSD::PackingElement::Tag; 391sub delete 392{ 393 my ($self, $state) = @_; 394 395 for my $d (@{$self->{definition_list}}) { 396 $d->add_tag($self, "delete", $state); 397 } 398} 399 400package OpenBSD::PackingElement::FileBase; 401use OpenBSD::Error; 402 403sub prepare_for_deletion 404{ 405 my ($self, $state, $pkgname) = @_; 406 407 my $fname = $self->retrieve_fullname($state, $pkgname); 408 my $s; 409 my $size = $self->{tied} ? 0 : $self->retrieve_size; 410 if ($state->{delete_first}) { 411 $s = $state->vstat->remove_first($fname, $size); 412 } else { 413 $s = $state->vstat->remove($fname, $size); 414 } 415 return unless defined $s; 416 if ($s->ro) { 417 $s->report_ro($state, $fname); 418 } 419} 420 421sub is_intact 422{ 423 my ($self, $state, $realname) = @_; 424 return 1 if defined($self->{link}) or $self->{nochecksum}; 425 if (!defined $self->{d}) { 426 if ($self->fullname eq $realname) { 427 $state->say("NOT deleting #1 (no checksum)", $realname); 428 } else { 429 $state->say("Not deleting #1 (no checksum for #2", 430 $realname, $self->fullname); 431 } 432 $state->log("Couldn't delete #1 (no checksum)", $realname); 433 return 0; 434 } 435 return 1 unless $state->defines('checksum'); 436 my $d = $self->compute_digest($realname, $self->{d}); 437 return 1 if $d->equals($self->{d}); 438 if ($self->fullname eq $realname) { 439 $state->say("NOT deleting #1 (bad checksum)", $realname); 440 } else { 441 $state->say("Not deleting #1 (bad checksum for #2)", 442 $realname, $self->fullname); 443 } 444 $state->log("Couldn't delete #1 (bad checksum)", $realname); 445 return 0; 446} 447 448sub delete 449{ 450 my ($self, $state) = @_; 451 my $realname = $self->realname($state); 452 453 if (defined $self->{symlink}) { 454 if (-l $realname) { 455 my $contents = readlink $realname; 456 if ($contents ne $self->{symlink}) { 457 $state->say("Symlink does not match: #1 (#2 vs. #3)", 458 $realname, $contents, $self->{symlink}); 459 $self->do_not_delete($state); 460 return; 461 } 462 } else { 463 $state->say("Bogus symlink: #1", $realname); 464 $self->do_not_delete($state); 465 return; 466 } 467 } else { 468 if (-l $realname) { 469 $state->say("Unexpected symlink: #1", $realname); 470 $self->do_not_delete($state); 471 } else { 472 if (!-f $realname) { 473 $state->say("File #1 does not exist", $realname); 474 return; 475 } 476 if (!$self->is_intact($state, $realname)) { 477 $self->do_not_delete($state); 478 return; 479 } 480 } 481 } 482 if ($state->verbose >= 5) { 483 $state->say("deleting: #1", $realname); 484 } 485 return if $state->{not}; 486 if ($state->{delete_first} && $self->{tied}) { 487 push(@{$state->{delayed}}, $realname); 488 } else { 489 if (!unlink $realname) { 490 $state->errsay("Problem deleting #1: #2", $realname, 491 $!); 492 $state->log("deleting #1 failed: #2", $realname, $!); 493 } 494 } 495} 496 497sub copy_old_stuff 498{ 499 my ($self, $plist, $state) = @_; 500 501 if (defined $self->{stillaround}) { 502 delete $self->{stillaround}; 503 if ($state->replacing) { 504 $self->rename_file_to_temp($state); 505 } 506 $self->add_object($plist); 507 } 508} 509 510package OpenBSD::PackingElement::SpecialFile; 511use OpenBSD::PackageInfo; 512 513sub copy_old_stuff 514{ 515} 516 517package OpenBSD::PackingElement::Meta; 518sub copy_old_stuff 519{ 520 my ($self, $plist, $state) = @_; 521 $self->add_object($plist); 522} 523 524package OpenBSD::PackingElement::DigitalSignature; 525sub copy_old_stuff 526{ 527} 528 529package OpenBSD::PackingElement::FDESC; 530sub copy_old_stuff 531{ 532 my ($self, $plist, $state) = @_; 533 require File::Copy; 534 535 File::Copy::copy($self->fullname, $plist->infodir); 536 $self->add_object($plist); 537} 538 539package OpenBSD::PackingElement::Sample; 540use OpenBSD::Error; 541use File::Basename; 542 543sub delete 544{ 545 my ($self, $state) = @_; 546 my $realname = $self->realname($state); 547 548 my $orig = $self->{copyfrom}; 549 if (!defined $orig) { 550 $state->fatal("\@sample element does not reference a valid file"); 551 } 552 my $action = $state->replacing ? "check" : "remove"; 553 my $origname = $orig->realname($state); 554 if (! -e $realname) { 555 $state->log("File #1 does not exist", $realname); 556 return; 557 } 558 if (! -f $realname) { 559 $state->log("File #1 is not a file", $realname); 560 return; 561 } 562 563 if (!defined $orig->{d}) { 564 $state->log("Couldn't delete #1 (no checksum)", $realname); 565 return; 566 } 567 568 if ($state->{quick} && $state->{quick} >= 2) { 569 unless ($state->{extra}) { 570 $self->mark_dir($state); 571 $state->log("You should also #1 #2", $action, $realname ); 572 return; 573 } 574 } else { 575 my $d = $self->compute_digest($realname, $orig->{d}); 576 if ($d->equals($orig->{d})) { 577 $state->say("File #1 identical to sample", $realname) if $state->verbose >= 2; 578 } else { 579 unless ($state->{extra}) { 580 $self->mark_dir($state); 581 $state->log("You should also #1 #2 (which was modified)", $action, $realname); 582 return; 583 } 584 } 585 } 586 $state->say("deleting #1", $realname) if $state->verbose >= 2; 587 return if $state->{not}; 588 if (!unlink $realname) { 589 $state->errsay("Problem deleting #1: #2", $realname, $!); 590 $state->log("deleting #1 failed: #2", $realname, $!); 591 } 592} 593 594 595package OpenBSD::PackingElement::InfoFile; 596use File::Basename; 597use OpenBSD::Error; 598sub delete 599{ 600 my ($self, $state) = @_; 601 unless ($state->{not}) { 602 my $fullname = $state->{destdir}.$self->fullname; 603 $state->vsystem(OpenBSD::Paths->install_info, 604 "--delete", "--info-dir=".dirname($fullname), '--', $fullname); 605 } 606 $self->SUPER::delete($state); 607} 608 609package OpenBSD::PackingElement::Shell; 610sub delete 611{ 612 my ($self, $state) = @_; 613 unless ($state->{not}) { 614 my $destdir = $state->{destdir}; 615 my $fullname = $self->fullname; 616 my @l=(); 617 if (open(my $shells, '<', $destdir.OpenBSD::Paths->shells)) { 618 while(<$shells>) { 619 push(@l, $_); 620 s/^\#.*//o; 621 if ($_ =~ m/^\Q$fullname\E\s*$/) { 622 pop(@l); 623 } 624 } 625 close($shells); 626 open(my $shells2, '>', $destdir.OpenBSD::Paths->shells); 627 print $shells2 @l; 628 close $shells2; 629 $state->say("Shell #1 removed from #2", 630 $fullname, $destdir.OpenBSD::Paths->shells) 631 if $state->verbose; 632 } 633 } 634 $self->SUPER::delete($state); 635} 636 637package OpenBSD::PackingElement::Extra; 638use File::Basename; 639 640sub delete 641{ 642 my ($self, $state) = @_; 643 my $realname = $self->realname($state); 644 if ($state->verbose >= 2 && $state->{extra}) { 645 $state->say("deleting extra file: #1", $realname); 646 } 647 return if $state->{not}; 648 return unless -e $realname or -l $realname; 649 if ($state->replacing) { 650 $state->log("Remember to update #1", $realname); 651 $self->mark_dir($state); 652 } elsif ($state->{extra}) { 653 unlink($realname) or 654 $state->say("problem deleting extra file #1: #2", $realname, $!); 655 } else { 656 $state->log("You should also remove #1", $realname); 657 $self->mark_dir($state); 658 } 659} 660 661 662package OpenBSD::PackingElement::Extradir; 663sub delete 664{ 665 my ($self, $state) = @_; 666 return unless $state->{extra}; 667 my $realname = $self->realname($state); 668 return if $state->replacing; 669 if ($state->{extra}) { 670 $self->SUPER::delete($state); 671 } else { 672 $state->log("You should also remove the directory #1", $realname); 673 $self->mark_dir($state); 674 } 675} 676 677package OpenBSD::PackingElement::ExtraUnexec; 678 679sub delete 680{ 681 my ($self, $state) = @_; 682 if ($state->{extra}) { 683 $self->run($state); 684 } else { 685 $state->log("You should also run #1", $self->{expanded}); 686 } 687} 688 689package OpenBSD::PackingElement::Lib; 690sub delete 691{ 692 my ($self, $state) = @_; 693 $self->SUPER::delete($state); 694 $self->mark_ldconfig_directory($state); 695} 696 697package OpenBSD::PackingElement::Depend; 698sub copy_old_stuff 699{ 700 my ($self, $plist, $state) = @_; 701 702 OpenBSD::PackingElement::Comment->add($plist, "\@".$self->keyword." ".$self->stringize); 703} 704 705package OpenBSD::PackingElement::FDISPLAY; 706sub delete 707{ 708 my ($self, $state) = @_; 709 $state->{current_set}{known_displays}{$self->{d}->key} = 1; 710 $self->SUPER::delete($state); 711} 712 713package OpenBSD::PackingElement::FUNDISPLAY; 714sub delete 715{ 716 my ($self, $state) = @_; 717 my $d = $self->{d}; 718 if (!$state->{current_set}{known_displays}{$self->{d}->key}) { 719 $self->prepare($state); 720 } 721 $self->SUPER::delete($state); 722} 723 724package OpenBSD::PackingElement::Mandir; 725sub delete 726{ 727 my ($self, $state) = @_; 728 $state->{current_set}{known_mandirs}{$self->fullname} = 1; 729 $self->SUPER::delete($state); 730} 731 7321; 733