Distfile.pm 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. # ex:ts=8 sw=4:
  2. # $OpenBSD: Distfile.pm,v 1.11 2017/05/13 09:56:03 espie Exp $
  3. #
  4. # Copyright (c) 2010-2013 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. use strict;
  18. use warnings;
  19. use OpenBSD::md5;
  20. use DPB::User;
  21. package DPB::Distfile;
  22. our @ISA = (qw(DPB::UserProxy));
  23. # same distfile may exist in several ports.
  24. # so we keep a hash based on full storage path.
  25. sub normalize
  26. {
  27. my ($class, $file) = @_;
  28. # XXX collapse name/../ aka "semarie rule"
  29. while ($file =~ s/[^\/]+\/\.\.\///) {
  30. }
  31. # remove duplicate slashes as well
  32. while ($file =~ s/\/\/+/\//g) {
  33. }
  34. return $file;
  35. }
  36. my $cache = {};
  37. sub create
  38. {
  39. my ($class, $file, $short, $site, $distinfo, $v, $repo) = @_;
  40. bless {
  41. name => $file,
  42. short => $short,
  43. path => $v,
  44. repo => $repo
  45. }, $class;
  46. }
  47. # complete object with sha/size info, error out if not same info
  48. sub complete
  49. {
  50. my ($self, $file, $short, $site, $distinfo, $v, $repo) = @_;
  51. my $sz = $distinfo->{size}{$file};
  52. my $sha = $distinfo->{sha}{$file};
  53. my $error = 0;
  54. if (!defined $sz || !defined $sha) {
  55. $v->break("Incomplete info for $file");
  56. return;
  57. }
  58. if (defined $self->{sz}) {
  59. if ($self->{sz} != $sz) {
  60. $v->break("Inconsistent info for $file: $self->{sz} vs $sz(".$v->fullpkgpath." vs ".$self->{path}->fullpkgpath.")");
  61. $error = 1;
  62. }
  63. if (!$self->{sha}->equals($sha)) {
  64. $v->break("Inconsistent info for $file ".
  65. $self->{sha}->stringize. " vs ". $sha->stringize.
  66. "(".$v->fullpkgpath." vs ".
  67. $self->{path}->fullpkgpath.")");
  68. $error = 1;
  69. }
  70. }
  71. if ($error) {
  72. return;
  73. } else {
  74. $repo->known_file($sha, $file);
  75. $self->{sz} = $sz;
  76. $self->{sha} = $sha;
  77. $self->{site} = $site;
  78. return $self;
  79. }
  80. }
  81. sub new
  82. {
  83. my ($class, $file, $url, $dir, @r) = @_;
  84. my $full = (defined $dir) ? join('/', $dir->string, $file) : $file;
  85. $full = DPB::Distfile->normalize($full);
  86. if (!defined $url) {
  87. $url = $file;
  88. }
  89. my $c = $cache->{$full} //= $class->create($full, $url, @r);
  90. $c->complete($full, $url, @r);
  91. return $c;
  92. }
  93. sub user
  94. {
  95. my $self = shift;
  96. return $self->{repo}->user;
  97. }
  98. sub distdir
  99. {
  100. my ($self, @rest) = @_;
  101. return join('/', $self->{repo}->distdir, @rest);
  102. }
  103. sub path
  104. {
  105. return shift->{path};
  106. }
  107. sub logger
  108. {
  109. my $self = shift;
  110. return $self->{repo}{logger};
  111. }
  112. sub debug_dump
  113. {
  114. my $self = shift;
  115. my $msg = $self->logname;
  116. if ($self->{okay}) {
  117. $msg .= "(okay)";
  118. }
  119. }
  120. sub cached
  121. {
  122. my $self = shift;
  123. return $self->{repo}{sha};
  124. }
  125. sub logname
  126. {
  127. my $self = shift;
  128. return $self->{path}->fullpkgpath.":".$self->{name};
  129. }
  130. sub lockname
  131. {
  132. return shift->{name}.".dist";
  133. }
  134. sub simple_lockname
  135. {
  136. &lockname;
  137. }
  138. # should be used for rebuild_info and logging only
  139. sub fullpkgpath
  140. {
  141. return shift->{path}->fullpkgpath;
  142. }
  143. sub print_parent
  144. {
  145. my ($self, $fh) = @_;
  146. $self->{path}->print_parent($fh);
  147. }
  148. sub pkgpath_and_flavors
  149. {
  150. return shift->{path}->pkgpath_and_flavors;
  151. }
  152. sub tempfilename
  153. {
  154. my $self = shift;
  155. return $self->filename.".part";
  156. }
  157. sub filename
  158. {
  159. my $self = shift;
  160. return $self->distdir($self->{name});
  161. }
  162. # this is the entry point from the Engine, this is run as soon as the path
  163. # has been scanned. For performance reasons, we cannot run a sha at that point.
  164. sub check
  165. {
  166. my $self = shift;
  167. # XXX in fetch_only mode, we won't build anything, so this is
  168. # the only place we can check the file is okay
  169. if ($self->{repo}{fetch_only}) {
  170. return $self->checksum_and_cache($self->filename);
  171. } else {
  172. return $self->checkcache_or_size($self->filename);
  173. }
  174. }
  175. sub make_link
  176. {
  177. my $self = shift;
  178. my $sha = $self->{sha}->stringize;
  179. if ($sha =~ m/^(..)/) {
  180. my $result = $self->distdir('by_cipher', 'sha256', $1, $sha);
  181. $self->make_path($result);
  182. my $dest = $self->{name};
  183. $dest =~ s/^.*\///;
  184. $self->link($self->filename, "$result/$dest");
  185. }
  186. }
  187. sub find_copy
  188. {
  189. my ($self, $name) = @_;
  190. # sha256 must match AND size as well
  191. my $alternate = $self->{repo}{reverse}{$self->{sha}->stringize};
  192. if (defined $alternate) {
  193. my $full = $self->distdir($alternate);
  194. if (($self->stat($full))[7] == $self->{sz}) {
  195. $self->unlink($name);
  196. if ($self->link($full, $name)) {
  197. $self->do_cache;
  198. $self->{okay} = 1;
  199. return 1;
  200. }
  201. }
  202. }
  203. return 0;
  204. }
  205. sub checkcache_or_size
  206. {
  207. my ($self, $name) = @_;
  208. # XXX if we matched once, then we match "forever"
  209. return 1 if $self->{okay};
  210. if (defined $self->cached->{$self->{name}}) {
  211. return $self->checkcached($name);
  212. }
  213. return $self->checksize($name);
  214. }
  215. sub checksize
  216. {
  217. my ($self, $name) = @_;
  218. # XXX if we matched once, then we match "forever"
  219. return 1 if $self->{okay};
  220. if (!defined $self->{sz}) {
  221. my $fh = $self->logger->append('dist/'.$self->{name});
  222. print $fh "incomplete distinfo: no size\n";
  223. }
  224. if (!$self->stat($name)) {
  225. return $self->find_copy($name);
  226. }
  227. if (($self->stat($name))[7] != $self->{sz}) {
  228. my $fh = $self->logger->append('dist/'.$self->{name});
  229. print $fh "size does not match\n";
  230. return 0;
  231. }
  232. return 1;
  233. }
  234. sub checkcached
  235. {
  236. my ($self, $name) = @_;
  237. if (!defined $self->{sha}) {
  238. my $fh = $self->logger->append('dist/'.$self->{name});
  239. print $fh "incomplete distinfo: no sha\n";
  240. return 0;
  241. }
  242. if (!defined $self->{sz}) {
  243. my $fh = $self->logger->append('dist/'.$self->{name});
  244. print $fh "incomplete distinfo: no size\n";
  245. return 0;
  246. }
  247. if (!$self->stat($name) || ($self->stat($name))[7] != $self->{sz}) {
  248. delete $self->cached->{$self->{name}};
  249. delete $self->{repo}{reverse}{$self->{sha}->stringize};
  250. $self->run_as(
  251. sub {
  252. unlink($name);
  253. });
  254. my $fh = $self->logger->append('dist/'.$self->{name});
  255. print $fh "size does not match, actual file deleted\n";
  256. return 0;
  257. }
  258. if ($self->cached->{$self->{name}}->equals($self->{sha})) {
  259. $self->{okay} = 1;
  260. return 1;
  261. } else {
  262. delete $self->cached->{$self->{name}};
  263. my $fh = $self->logger->append('dist/'.$self->{name});
  264. print $fh "sha cache info does not match,";
  265. if ($self->caches_okay($name)) {
  266. print $fh "but actual file had the right sha\n";
  267. return 1;
  268. } else {
  269. print $fh "and actual file was wrong, deleted\n";
  270. return 0;
  271. }
  272. }
  273. }
  274. sub do_cache
  275. {
  276. my $self = shift;
  277. eval {
  278. $self->make_link;
  279. print {$self->{repo}->{log}} "SHA256 ($self->{name}) = ",
  280. $self->{sha}->stringize, "\n";
  281. };
  282. # also enter ourselves into the internal repository
  283. $self->cached->{$self->{name}} = $self->{sha};
  284. }
  285. # this is where we actually enter new files in the cache, when they do match.
  286. sub caches_okay
  287. {
  288. my ($self, $name) = @_;
  289. $self->run_as(
  290. sub {
  291. if (-f -r $name) {
  292. if (OpenBSD::sha->new($name)->equals($self->{sha})) {
  293. $self->{okay} = 1;
  294. $self->do_cache;
  295. return 1;
  296. } else {
  297. unlink($name);
  298. }
  299. }
  300. return 0;
  301. });
  302. }
  303. sub checksum_and_cache
  304. {
  305. my ($self, $name) = @_;
  306. # XXX if we matched once, then we match "forever"
  307. return 1 if $self->{okay};
  308. if (!defined $self->{sha}) {
  309. return 0;
  310. }
  311. if (defined $self->cached->{$self->{name}}) {
  312. return $self->checkcached($name);
  313. }
  314. if ($self->caches_okay($name)) {
  315. return 1;
  316. }
  317. return $self->find_copy($name);
  318. }
  319. sub cache
  320. {
  321. my $self = shift;
  322. # XXX if we matched once, then we match "forever"
  323. return 1 if $self->{okay};
  324. $self->{okay} = 1;
  325. # already done
  326. if (defined $self->cached->{$self->{name}}) {
  327. if ($self->cached->{$self->{name}}->equals($self->{sha})) {
  328. return;
  329. }
  330. }
  331. $self->do_cache;
  332. }
  333. sub checksum
  334. {
  335. my ($self, $name) = @_;
  336. # XXX if we matched once, then we match "forever"
  337. return 1 if $self->{okay};
  338. print "checksum for $name: ";
  339. if (!defined $self->{sha}) {
  340. print "NONE\n";
  341. return 0;
  342. }
  343. if (defined $self->cached->{$self->{name}}) {
  344. if ($self->cached->{$self->{name}}->equals($self->{sha})) {
  345. print "OK (cached)\n";
  346. $self->{okay} = 1;
  347. return 1;
  348. }
  349. }
  350. if ($self->caches_okay($name)) {
  351. print "OK\n";
  352. return 1;
  353. }
  354. print "BAD\n";
  355. return 0;
  356. }
  357. sub cached_checksum
  358. {
  359. my ($self, $fh, $name) = @_;
  360. # XXX if we matched once, then we match "forever"
  361. return 1 if $self->{okay};
  362. print $fh "checksum for $name: ";
  363. if (!defined $self->{sha}) {
  364. print $fh "NONE\n";
  365. return 0;
  366. }
  367. if (defined $self->cached->{$self->{name}}) {
  368. if ($self->cached->{$self->{name}}->equals($self->{sha})) {
  369. print $fh "OK (cached)\n";
  370. $self->{okay} = 1;
  371. return 1;
  372. }
  373. }
  374. print $fh "UNKNOWN (uncached)\n";
  375. return 0;
  376. }
  377. sub unlock_conditions
  378. {
  379. my ($self, $engine) = @_;
  380. return $self->check;
  381. }
  382. sub requeue
  383. {
  384. my ($v, $engine) = @_;
  385. $engine->requeue_dist($v);
  386. }
  387. sub forget
  388. {
  389. my $self = shift;
  390. delete $self->{sz};
  391. delete $self->{sha};
  392. delete $self->{okay};
  393. }
  394. 1;