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