view _test/read_repo.pl @ 1:30a88ad2b2fd

added validation mode and automatic errors fixing
author cin
date Tue, 22 Oct 2013 05:36:00 +0400
parents 93b83e3c38d6
children f2a86753b494
line wrap: on
line source

#!/usr/bin/perl
use strict;
use warnings;

use Digest;
use File::Path qw(make_path);
use IMPL::require {
	XMLReader => 'XML::LibXML::Reader',
	UserAgent => 'LWP::UserAgent',
	Request   => 'HTTP::Request',
	MDParser  => 'Yours::Parsers::MDParser',
	PMDParser => 'Yours::Parsers::PMDParser',
	Dumper    => 'Data::Dumper',
	Uncompress => 'IO::Uncompress::AnyUncompress'
};

BEGIN {
	XMLReader->import;

}

my %digestTypes = (
	sha512 => 'SHA-512',
	sha384 => 'SHA-384',
	sha256 => 'SHA-256',
	sha1 => 'SHA-1',
	md5 => 'MD5'
);

my $repoUrl =
  'http://download.opensuse.org/repositories/Mono/openSUSE_12.3/';
my $repoDir = 'repo';
my $validate = 0;

my $agent = UserAgent->new();
$agent->env_proxy();

# список файлов, которые должны быть в репозитарии, заполняется по мере загрузки/проверки целостности
my %files;
# список каталогов, которые должны быть в репозитарии, заполняется по мере загрузки/проверки целостности
my %dirs;

print "loading metadata\n";

make_path(File::Spec->catdir($repoDir,'repodata')) unless -d File::Spec->catdir($repoDir,'repodata');

# загружаем основные метаданные

my $mdLocation = "repodata/repomd.xml";
my $mdFile = MakeLocalName($mdLocation);

my @initial = (
	$mdLocation,
	"repodata/repomd.xml.asc",
	"repodata/repomd.xml.key"
);

$files{MakeLocalName($_)} = { location => "${repoUrl}$_" } foreach @initial;

unless ($validate) {
	foreach my $initLocation (@initial) {
		my $file = MakeLocalName($initLocation);
		my $resp = $agent->get(
			"${repoUrl}$initLocation",
			':content_file' => $file
		);
		
		die "failed to load metadata $initLocation: ", $resp->code, " ", $resp->message
			unless $resp->is_success;
	}
}

my $parser = MDParser->new();
$parser->Parse( { location => $mdFile, no_blanks => 1 } );

# загружаем метаданные о пакетах 
my %indexMd;
foreach my $md (@{$parser->data->{data} || []}) {
	$indexMd{$md->{type}} = $md;
	
	print "\t$md->{type}: $md->{location}\n";	
	
	my $file = MakeLocalName($md->{location});
	$md->{file} = $file;
	$files{$file} = $md;
	
	unless (-f $file) {
		my $resp = $agent->get(
			"${repoUrl}$md->{location}",
			':content_file' => $file
		);
		die "failed to load $md->{location}: ", $resp->code, " ", $resp->message
			unless $resp->is_success;
	}
}

my $primaryMd = $indexMd{primary}{file};

my $hdata = Uncompress->new($primaryMd)
	or die "failed to uncompress $primaryMd";
	
print "processing contents\n";

PMDParser->new(sub {
	my ($parser,$package) = @_;
	my $location = $package->{location};
	
	my $file = MakeLocalName($location);
	$files{$file} = $package;
	
	
	
	unless (-f $file) {
		my $size = sprintf("%0.2fM",$package->{size}/(1024*1024));
		print "\tfetch $location [${size}]\n";
		
		$agent->get("${repoUrl}$location", ":content_file" => $file);
	}
})->Parse({ IO => $hdata, no_blanks => 1 });

print "cleanup\n";

foreach my $dir (keys %dirs) {
	print "\t$dir\n";
	if (opendir(my $hdir, $dir)) {
		while(my $file = readdir $hdir) {
			next if $file eq '.' || $file eq '..';
			my $fullPath = File::Spec->catfile($dir,$file);
			next unless -f $fullPath;
			
			unless( $files{$fullPath} ){
				print "\t\t- $file\n";
				unlink $fullPath;
			}
		}
	}
}

print "validating\n";

my @bad;

while(my ($file,$md) = each %files) {
	if (my $checksum = $md->{checksum}) {
		if( my $type = $digestTypes{lc($checksum->{type})} ) {
			if(open my $hfile, "<$file") {
				binmode $hfile;
				my $digest = Digest->new($type)->addfile($hfile)->hexdigest;
				next if $digest eq $checksum->{value};
				
				print "\t$file: $digest ne $checksum->{value}\n";
			} else {
				print "\t$file: ", -f $file ? "unable to open" : "missing","\n"; 
			}
			push @bad,$md;
		} else {
			print "\t$file: unknown hash algorithm: $checksum->{type}\n";
		}
	}
}

print "fixing\n";

foreach my $md (@bad) {
	my $location = $md->{location};
	
	my $file = MakeLocalName($location);

	my $size = sprintf("%0.2fM",$md->{size}/(1024*1024));
	print "\tfetch $location [${size}]\n";
		
	$agent->get("${repoUrl}$location", ":content_file" => $file);
}

print "total files: ", scalar keys %files, "\n";

sub MakeLocalName {
	my ($url) = @_;
	
	my @parts = split /\//, $url;
	my $file = pop @parts;
	
	my $dir = File::Spec->catdir($repoDir,@parts);
	
	make_path($dir)
		unless $dirs{$dir};
	$dirs{$dir} = 1;
	
	return File::Spec->catfile($dir,$file);
}