1# ex:ts=8 sw=4: 2# $OpenBSD: PackageLocation.pm,v 1.61 2023/06/13 09:07:17 espie Exp $ 3# 4# Copyright (c) 2003-2007 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::PackageLocation; 21 22use OpenBSD::PackageInfo; 23use OpenBSD::Temp; 24use OpenBSD::Error; 25use OpenBSD::Paths; 26 27sub new($class, $repository, $name) 28{ 29 return bless { 30 repository => $repository, 31 name => $repository->canonicalize($name) 32 }, $class; 33 34} 35 36sub decorate($self, $plist) 37{ 38 $self->{repository}->decorate($plist, $self); 39} 40 41sub url($self) 42{ 43 return $self->{repository}->url($self->name); 44} 45 46sub name($self) 47{ 48 return $self->{name}; 49} 50 51OpenBSD::Auto::cache(pkgname, 52 sub($self) { 53 return OpenBSD::PackageName->from_string($self->name); 54 }); 55 56OpenBSD::Auto::cache(update_info, 57 sub($self) { 58 my $name = $self->name; 59 if ($name =~ /^quirks\-/) { 60 return $self->plist; 61 } 62 my $state = $self->{repository}{state}; 63 my $info = $self->{repository}->get_cached_info($name); 64 if (defined $info && 65 !defined $state->defines("CACHING_RECHECK")) { 66 return $info; 67 } 68 my $result = $self->plist(\&OpenBSD::PackingList::UpdateInfoOnly); 69 if (defined $info) { 70 my $s1 = OpenBSD::Signature->from_plist($info); 71 my $s2 = OpenBSD::Signature->from_plist($result); 72 my $r = $s1->compare($s2, $state); 73 if (defined $r && $r == 0) { 74 $state->say("Cache comparison for #1 is okay", $name) 75 if $state->defines("TEST_CACHING_VERBOSE"); 76 return $result; 77 } else { 78 $state->fatal("Signatures differ cache=#1, regular=#2", 79 $s1->string, $s2->string); 80 } 81 } 82 return $result; 83 }); 84 85# make sure self is opened and move to the right location if need be. 86sub _opened($self) 87{ 88 if (defined $self->{fh}) { 89 return $self; 90 } 91 my $fh = $self->{repository}->open($self); 92 if (!defined $fh) { 93 $self->{repository}->parse_problems($self->{errors}, undef, 94 $self) if defined $self->{errors}; 95 undef $self->{errors}; 96 return; 97 } 98 require OpenBSD::Ustar; 99 my $archive = OpenBSD::Ustar->new($fh, $self->{repository}{state}); 100 $archive->set_description($self->{repository}->url($self->{name})); 101 $self->{_archive} = $archive; 102 $self->_set_callback; 103 104 if (defined $self->{_current_name}) { 105 while (my $e = $self->{_archive}->next) { 106 if ($e->{name} eq $self->{_current_name}) { 107 $self->{_current} = $e; 108 return $self; 109 } 110 } 111 } 112 return $self; 113} 114 115sub _set_callback($self) 116{ 117 if (defined $self->{callback} && defined $self->{_archive}) { 118 $self->{_archive}->set_callback($self->{callback}); 119 } 120} 121 122sub find_contents($self) 123{ 124 while (my $e = $self->next) { 125 if ($e->isFile && is_info_name($e->{name})) { 126 if ($e->{name} eq CONTENTS ) { 127 my $v = $e->contents; 128 return $v; 129 } 130 } else { 131 $self->unput; 132 last; 133 } 134 } 135} 136 137sub contents($self) 138{ 139 if (!defined $self->{contents}) { 140 if (!$self->_opened) { 141 return; 142 } 143 $self->{contents} = $self->find_contents; 144 } 145 146 return $self->{contents}; 147} 148 149sub grab_info($self) 150{ 151 my $dir = $self->{dir} = OpenBSD::Temp->dir; 152 if (!defined $dir) { 153 $self->{repository}{state}->fatal(OpenBSD::Temp->last_error); 154 } 155 156 my $c = $self->contents; 157 if (!defined $c) { 158 return 0; 159 } 160 161 if (! -f $dir.CONTENTS) { 162 open my $fh, '>', $dir.CONTENTS or 163 die "write to ",$dir.CONTENTS, ": ", $!; 164 print $fh $self->contents; 165 close $fh; 166 } 167 168 while (my $e = $self->next) { 169 if ($e->isFile && is_info_name($e->{name})) { 170 $e->{name} = $dir.$e->{name}; 171 undef $e->{mtime}; 172 undef $e->{atime}; 173 eval { $e->create; }; 174 if ($@) { 175 unlink($e->{name}); 176 $@ =~ s/\s+at.*//o; 177 $self->{repository}{state}->errprint('#1', $@); 178 return 0; 179 } 180 } else { 181 $self->unput; 182 last; 183 } 184 } 185 return 1; 186} 187 188sub grabPlist($self, $code = \&OpenBSD::PackingList::defaultCode) 189{ 190 my $plist = $self->plist($code); 191 if (defined $plist) { 192 $self->wipe_info; 193 $self->close_now; 194 return $plist; 195 } else { 196 return; 197 } 198} 199 200sub forget($self) 201{ 202 $self->wipe_info; 203 $self->close_now; 204} 205 206sub wipe_info($self) 207{ 208 $self->{repository}->wipe_info($self); 209 $self->{repository}->close_now($self); 210 delete $self->{contents}; 211 $self->deref; 212 delete $self->{_current_name}; 213 delete $self->{update_info}; 214 delete $self->{_unput}; 215} 216 217sub info($self) 218{ 219 if (!defined $self->{dir}) { 220 $self->grab_info; 221 } 222 return $self->{dir}; 223} 224 225sub plist($self, $code = \&OpenBSD::PackingList::defaultCode) 226{ 227 require OpenBSD::PackingList; 228 229 if (defined $self->{dir} && -f $self->{dir}.CONTENTS) { 230 my $plist = 231 OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS, 232 $code); 233 $plist->set_infodir($self->{dir}); 234 return $plist; 235 } 236 if (my $value = $self->contents) { 237 return OpenBSD::PackingList->fromfile(\$value, $code); 238 } 239 # hopeless 240 $self->close_with_client_error; 241 242 return; 243} 244 245sub close($self, $hint = 0) 246{ 247 $self->{repository}->close($self, $hint); 248} 249 250sub finish_and_close($self) 251{ 252 $self->{repository}->finish_and_close($self); 253} 254 255sub close_now($self) 256{ 257 $self->{repository}->close_now($self); 258} 259 260sub close_after_error($self) 261{ 262 $self->{repository}->close_after_error($self); 263} 264 265sub close_with_client_error($self) 266{ 267 $self->{repository}->close_with_client_error($self); 268} 269 270sub deref($self) 271{ 272 delete $self->{fh}; 273 delete $self->{pid2}; 274 delete $self->{_archive}; 275 delete $self->{_current}; 276} 277 278# proxy for archive operations 279sub next($self) 280{ 281 if (!$self->_opened) { 282 return; 283 } 284 if (!$self->{_unput}) { 285 $self->{_current} = $self->getNext; 286 if (defined $self->{_current}) { 287 $self->{_current_name} = $self->{_current}{name}; 288 } else { 289 delete $self->{_current_name}; 290 } 291 } else { 292 $self->{_unput} = 0; 293 } 294 return $self->{_current}; 295} 296 297sub unput($self) 298{ 299 $self->{_unput} = 1; 300} 301 302sub getNext($self) 303{ 304 return $self->{_archive}->next; 305} 306 307sub skip($self) 308{ 309 return $self->{_archive}->skip; 310} 311 312sub set_callback($self, $code) 313{ 314 $self->{callback} = $code; 315 $self->_set_callback; 316} 317 318package OpenBSD::PackageLocation::Installed; 319our @ISA = qw(OpenBSD::PackageLocation); 320 321 322sub info($self) 323{ 324 require OpenBSD::PackageInfo; 325 $self->{dir} = OpenBSD::PackageInfo::installed_info($self->name); 326} 327 328sub plist($self, $code = \&OpenBSD::PackingList::defaultCode) 329{ 330 require OpenBSD::PackingList; 331 return OpenBSD::PackingList->from_installation($self->name, $code); 332} 333 3341; 335