Fetch.pm 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. # ex:ts=8 sw=4:
  2. # $OpenBSD: Fetch.pm,v 1.76 2017/05/07 16:50:22 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 DPB::Clock;
  20. use DPB::Distfile;
  21. use OpenBSD::md5;
  22. use DPB::User;
  23. # handles fetch information, if required
  24. package DPB::Fetch;
  25. our @ISA = (qw(DPB::UserProxy));
  26. sub new
  27. {
  28. my ($class, $distdir, $logger, $state) = @_;
  29. my $o = bless {distdir => $distdir, sha => {}, reverse => {},
  30. logger => $logger,
  31. known_sha => {}, known_files => {},
  32. known_short => {},
  33. user => $state->{fetch_user},
  34. state => $state,
  35. cache => {},
  36. build_user => $state->{build_user},
  37. fetch_only => $state->{fetch_only}}, $class;
  38. if (defined $state->{subst}->value('FTP_ONLY')) {
  39. $o->{ftp_only} = 1;
  40. }
  41. if (defined $state->{subst}->value('CDROM_ONLY')) {
  42. $o->{cdrom_only} = 1;
  43. }
  44. my $fh = $o->open('<', "$distdir/distinfo");
  45. if (defined $fh) {
  46. print "Reading distinfo...";
  47. while (<$fh>) {
  48. if (m/^SHA256\s*\((.*)\) \= (.*)/) {
  49. next unless -f "$distdir/$1";
  50. $o->{sha}{$1} = OpenBSD::sha->fromstring($2);
  51. $o->{reverse}{$2} = $1;
  52. }
  53. }
  54. close $fh;
  55. }
  56. print "zap duplicates...";
  57. # rewrite "more or less" the same info, so we flush duplicates,
  58. # e.g., keep only most recent checksum seen
  59. $o->make_path($distdir);
  60. $fh = $o->open('>', "$distdir/distinfo.new");
  61. if (defined $fh) {
  62. for my $k (sort keys %{$o->{sha}}) {
  63. print $fh "SHA256 ($k) = ", $o->{sha}{$k}->stringize,
  64. "\n";
  65. }
  66. close ($fh);
  67. }
  68. print "Done\n";
  69. $o->rename("$distdir/distinfo.new", "$distdir/distinfo");
  70. $o->{log} = $o->open(">>", "$distdir/distinfo");
  71. DPB::Util->make_hot($o->{log});
  72. return $o;
  73. }
  74. sub mark_sha
  75. {
  76. my ($self, $sha, $file) = @_;
  77. $self->{known_sha}{$sha}{$file} = 1;
  78. # next cases are only needed to weed out by_cipher of extra links
  79. if ($file =~ m/^.*\/([^\/]+)$/) {
  80. $self->{known_short}{$sha}{$1} = 1;
  81. }
  82. # in particular, double / in $sha will vanish thanks to the fs
  83. my $do = 0;
  84. if ($sha =~ s/\/\//\//g) {
  85. $do++;
  86. }
  87. if ($sha =~ s/^\///) {
  88. $do++;
  89. }
  90. if ($do) {
  91. if ($file =~ m/^.*\/([^\/]+)$/) {
  92. $self->{known_short}{$sha}{$1} = 1;
  93. } else {
  94. $self->{known_short}{$sha}{$file} = 1;
  95. }
  96. }
  97. }
  98. sub known_file
  99. {
  100. my ($self, $sha, $file) = @_;
  101. $self->mark_sha($sha->stringize, $file);
  102. $self->{known_file}{$file} = 1;
  103. }
  104. sub run_expire_old
  105. {
  106. my ($self, $core, $opt_e) = @_;
  107. $core->unsquiggle;
  108. $core->start_job(DPB::Job::Normal->new(
  109. sub {
  110. $self->expire_old;
  111. },
  112. sub {
  113. # and we will never need this again
  114. delete $self->{known_file};
  115. delete $self->{known_sha};
  116. delete $self->{known_short};
  117. if (!$opt_e) {
  118. $core->mark_ready;
  119. }
  120. return 0;
  121. },
  122. "UPDATING DISTFILES HISTORY"));
  123. return 1;
  124. }
  125. sub parse_old
  126. {
  127. my ($self, $fh, $fh2) = @_;
  128. while (<$fh>) {
  129. if (my ($ts, $file, $sha) =
  130. m/^(\d+)\s+SHA256\s*\((.*)\) \= (.*\=)$/) {
  131. $file = DPB::Distfile->normalize($file);
  132. if (!$self->{known_sha}{$sha}{$file}) {
  133. $self->mark_sha($sha, $file);
  134. $self->{known_file}{$file} = 1;
  135. print $fh2 "$ts SHA256 ($file) = $sha\n";
  136. }
  137. }
  138. }
  139. }
  140. sub expire_old
  141. {
  142. my $self = shift;
  143. my $ts = time();
  144. my $distdir = $self->distdir;
  145. chdir($distdir) or die "can't change to distdir: $!";
  146. my $fh2 = $self->open(">", "history.new");
  147. return if !$fh2;
  148. if (my $fh = $self->open('<', "history")) {
  149. $self->parse_old($fh, $fh2);
  150. close $fh;
  151. }
  152. while (my ($sha, $file) = each %{$self->{reverse}}) {
  153. next if $self->{known_sha}{$sha}{$file};
  154. print $fh2 "$ts SHA256 ($file) = $sha\n";
  155. $self->{known_file}{$file} = 1;
  156. }
  157. for my $special (qw(Makefile distinfo history)) {
  158. $self->{known_file}{$special} = 1;
  159. }
  160. # let's also scan the directory proper
  161. require File::Find;
  162. File::Find::find(sub {
  163. if (-d $_ &&
  164. ($File::Find::name eq "./by_cipher" ||
  165. $File::Find::name eq "./list" ||
  166. $File::Find::name eq "./build-stats")) {
  167. $File::Find::prune = 1;
  168. return;
  169. }
  170. return unless -f _;
  171. return if m/\.part$/;
  172. my $actual = $File::Find::name;
  173. $actual =~ s/^.\///;
  174. return if $self->{known_file}{$actual};
  175. my $sha = OpenBSD::sha->new($_)->stringize;
  176. print $fh2 "$ts SHA256 ($actual) = $sha\n";
  177. $self->mark_sha($sha, $actual);
  178. }, ".");
  179. my $c = "by_cipher/sha256";
  180. if (-d $c) {
  181. # and scan the ciphers as well !
  182. File::Find::find(sub {
  183. return unless -f $_;
  184. if ($File::Find::dir =~
  185. m/^\.\/by_cipher\/sha256\/..?\/(.*)$/) {
  186. my $sha = $1;
  187. return if $self->{known_sha}{$sha}{$_};
  188. return if $self->{known_short}{$sha}{$_};
  189. print $fh2 "$ts SHA256 ($_) = ", $sha, "\n";
  190. }
  191. }, $c);
  192. }
  193. close $fh2;
  194. $self->rename("history.new", "history");
  195. }
  196. sub forget_cache
  197. {
  198. my $self = shift;
  199. $self->{cache} = {};
  200. }
  201. sub distdir
  202. {
  203. my $self = shift;
  204. return $self->{distdir};
  205. }
  206. sub read_checksums
  207. {
  208. my ($self, $filename) = @_;
  209. # XXX the fetch user might not have read access there ?
  210. my $fh = $self->{build_user}->open('<', $filename);
  211. return if !defined $fh;
  212. my $r = { size => {}, sha => {}};
  213. while (<$fh>) {
  214. if (my ($file, $sz) = m/^SIZE \((.*)\) \= (\d+)$/) {
  215. $r->{size}{DPB::Distfile->normalize($file)} = $sz;
  216. } elsif (my ($file2, $sha) = m/^SHA256 \((.*)\) \= (.*)$/) {
  217. $r->{sha}{DPB::Distfile->normalize($file2)} =
  218. OpenBSD::sha->fromstring($sha);
  219. }
  220. # next!
  221. }
  222. return $r;
  223. }
  224. sub build_distinfo
  225. {
  226. my ($self, $h, $mirror) = @_;
  227. for my $v (values %$h) {
  228. my $info = $v->{info};
  229. next unless defined $info->{DISTFILES} ||
  230. defined $info->{PATCHFILES} ||
  231. defined $info->{SUPDISTFILES};
  232. my $dir = $info->{DIST_SUBDIR};
  233. my $checksum_file = $info->{CHECKSUM_FILE};
  234. if (!defined $checksum_file) {
  235. $v->break("No checksum file");
  236. next;
  237. }
  238. $checksum_file = $checksum_file->string;
  239. # collapse identical checksum files together
  240. $checksum_file =~ s,/[^/]+/\.\./,/,g;
  241. $self->{cache}{$checksum_file} //=
  242. $self->read_checksums(
  243. $self->{state}->anchor($checksum_file));
  244. my $checksums = $self->{cache}{$checksum_file};
  245. my $files = {};
  246. my $build = sub {
  247. my $arg = shift;
  248. my $site = 'MASTER_SITES';
  249. my $url;
  250. if ($arg =~ m/^(.*)\:(\d)$/) {
  251. $arg = $1;
  252. $site.= $2;
  253. }
  254. if ($arg =~ m/^(.*)\{(.*)\}(.*)$/) {
  255. $arg = $1 . $3;
  256. $url = $2 . $3;
  257. }
  258. if (!defined $info->{$site}) {
  259. $v->break("Can't find $site for $arg");
  260. return;
  261. }
  262. return DPB::Distfile->new($arg, $url, $dir,
  263. $info->{$site}, $checksums, $v, $self);
  264. };
  265. for my $d ((keys %{$info->{DISTFILES}}), (keys %{$info->{PATCHFILES}})) {
  266. my $file = &$build($d);
  267. $files->{$file} = $file if defined $file;
  268. }
  269. if ($mirror) {
  270. for my $d (keys %{$info->{SUPDISTFILES}}) {
  271. my $file = &$build($d);
  272. $files->{$file} = $file if defined $file;
  273. }
  274. }
  275. for my $k (qw(DIST_SUBDIR CHECKSUM_FILE DISTFILES
  276. PATCHFILES SUPDISTFILES MASTER_SITES MASTER_SITES0
  277. MASTER_SITES1 MASTER_SITES2 MASTER_SITES3
  278. MASTER_SITES4 MASTER_SITES5 MASTER_SITES6
  279. MASTER_SITES7 MASTER_SITES8 MASTER_SITES9)) {
  280. delete $info->{$k};
  281. }
  282. bless $files, "AddDepends";
  283. $info->{DIST} = $files;
  284. if ($self->{cdrom_only} &&
  285. defined $info->{PERMIT_PACKAGE_CDROM}) {
  286. $info->{DISTIGNORE} = 1;
  287. $info->{IGNORE} //= AddIgnore->new(
  288. "Distfile not allowed for cdrom");
  289. } elsif ($self->{ftp_only} &&
  290. defined $info->{PERMIT_PACKAGE_FTP}) {
  291. $info->{DISTIGNORE} = 1;
  292. $info->{IGNORE} //= AddIgnore->new(
  293. "Distfile not allowed for ftp");
  294. }
  295. }
  296. }
  297. sub fetch
  298. {
  299. my ($self, $file, $core, $endcode) = @_;
  300. require DPB::Job::Fetch;
  301. my $job = DPB::Job::Fetch->new($file, $endcode, $self,
  302. $self->{logger});
  303. $core->start_job($job, $file);
  304. }
  305. 1;