arq_restore/ArqVerifyCommand.m
2011-08-16 15:19:44 -04:00

194 lines
7 KiB
Objective-C

//
// ArqVerifyCommand.m
// arq_restore
//
// Created by Stefan Reitshamer on 6/17/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "ArqVerifyCommand.h"
#import "S3AuthorizationProvider.h"
#import "S3Service.h"
#import "HTTP.h"
#import "RegexKitLite.h"
#import "BucketVerifier.h"
#import "NSError_extra.h"
#import "NSErrorCodes.h"
#import "ArqSalt.h"
#import "ArqRepo.h"
@interface ArqVerifyCommand (internal)
- (BOOL)loadObjectSHA1sForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error;
@end
@implementation ArqVerifyCommand
- (id)initWithAccessKey:(NSString *)theAccessKey secretKey:(NSString *)theSecretKey encryptionPassword:(NSString *)theEncryptionPassword {
if (self = [super init]) {
accessKey = [theAccessKey retain];
secretKey = [theSecretKey retain];
encryptionPassword = [theEncryptionPassword retain];
S3AuthorizationProvider *sap = [[S3AuthorizationProvider alloc] initWithAccessKey:accessKey secretKey:secretKey];
s3 = [[S3Service alloc] initWithS3AuthorizationProvider:sap useSSL:NO retryOnTransientError:YES];
[sap release];
}
return self;
}
- (void)dealloc {
[accessKey release];
[secretKey release];
[encryptionPassword release];
[s3 release];
[super dealloc];
}
- (void)setVerbose:(BOOL)isVerbose {
verbose = isVerbose;
}
- (BOOL)verifyAll:(NSError **)error {
NSArray *s3BucketNames = [S3Service s3BucketNamesForAccessKeyID:accessKey];
for (NSString *s3BucketName in s3BucketNames) {
printf("s3bucket name: %s\n", [s3BucketName UTF8String]);
}
for (NSString *s3BucketName in s3BucketNames) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
BOOL ret = [self verifyS3BucketName:s3BucketName error:error];
if (error != NULL) {
[*error retain];
}
[pool drain];
if (error != NULL) {
[*error autorelease];
}
if (!ret) {
return NO;
}
}
return YES;
}
- (BOOL)verifyS3BucketName:(NSString *)s3BucketName error:(NSError **)error {
printf("verifying s3Bucket %s\n", [s3BucketName UTF8String]);
NSString *computerUUIDPrefix = [NSString stringWithFormat:@"/%@/", s3BucketName];
NSError *myError = nil;
NSArray *computerUUIDs = [s3 commonPrefixesForPathPrefix:computerUUIDPrefix delimiter:@"/" error:&myError];
if (computerUUIDs == nil) {
if ([myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) {
// Skip.
printf("no computer UUIDs found in bucket %s\n", [s3BucketName UTF8String]);
return YES;
} else {
if (error != NULL) {
*error = myError;
}
return NO;
}
}
for (NSString *computerUUID in computerUUIDs) {
printf("found computer UUID %s\n", [computerUUID UTF8String]);
}
for (NSString *computerUUID in computerUUIDs) {
if (![self verifyS3BucketName:s3BucketName computerUUID:computerUUID error:error]) {
return NO;
}
}
return YES;
}
- (BOOL)verifyS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error {
printf("\nverifying computerUUID %s s3Bucket %s\n", [computerUUID UTF8String], [s3BucketName UTF8String]);
NSString *computerBucketsPrefix = [NSString stringWithFormat:@"/%@/%@/buckets", s3BucketName, computerUUID];
NSArray *s3BucketUUIDPaths = [s3 pathsWithPrefix:computerBucketsPrefix error:error];
if (s3BucketUUIDPaths == nil) {
return NO;
}
NSMutableArray *bucketUUIDs = [NSMutableArray array];
for (NSString *s3BucketUUIDPath in s3BucketUUIDPaths) {
NSString *bucketUUID = [s3BucketUUIDPath lastPathComponent];
printf("found bucket UUID %s\n", [bucketUUID UTF8String]);
[bucketUUIDs addObject:bucketUUID];
}
[objectSHA1s release];
objectSHA1s = nil;
if (![self loadObjectSHA1sForS3BucketName:s3BucketName computerUUID:computerUUID error:error]) {
return NO;
}
BOOL ret = YES;
NSAutoreleasePool *pool = nil;
for (NSString *bucketUUID in bucketUUIDs) {
[pool release];
pool = [[NSAutoreleasePool alloc] init];
if (![self verifyS3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID error:error]) {
ret = NO;
break;
}
}
if (!ret && error != NULL) {
[*error retain];
}
[pool drain];
if (!ret && error != NULL) {
[*error autorelease];
}
return ret;
}
- (BOOL)verifyS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID bucketUUID:(NSString *)bucketUUID error:(NSError **)error {
if (objectSHA1s == nil) {
if (![self loadObjectSHA1sForS3BucketName:s3BucketName computerUUID:computerUUID error:error]) {
return NO;
}
}
NSError *saltError = nil;
ArqSalt *arqSalt = [[[ArqSalt alloc] initWithAccessKeyID:accessKey secretAccessKey:secretKey s3BucketName:s3BucketName computerUUID:computerUUID] autorelease];
NSData *salt = [arqSalt salt:&saltError];
if (salt == nil) {
if ([saltError code] != ERROR_NOT_FOUND) {
if (error != NULL) {
*error = saltError;
}
return NO;
}
}
ArqRepo *repo = [[[ArqRepo alloc] initWithS3Service:s3
s3BucketName:s3BucketName
computerUUID:computerUUID
bucketUUID:bucketUUID
encryptionPassword:encryptionPassword
salt:salt
error:error] autorelease];
if (repo == nil) {
return NO;
}
printf("\nverifying bucketUUID %s computerUUID %s s3Bucket %s\n", [bucketUUID UTF8String], [computerUUID UTF8String], [s3BucketName UTF8String]);
BucketVerifier *bucketVerifier = [[[BucketVerifier alloc] initWithS3Service:s3
s3BucketName:s3BucketName
computerUUID:computerUUID
bucketUUID:bucketUUID
s3ObjectSHA1s:objectSHA1s
verbose:verbose
repo:repo] autorelease];
if (![bucketVerifier verify:error]) {
return NO;
}
return YES;
}
@end
@implementation ArqVerifyCommand (internal)
- (BOOL)loadObjectSHA1sForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error {
NSMutableSet *theObjectSHA1s = [NSMutableSet set];
NSString *objectsPrefix = [NSString stringWithFormat:@"/%@/%@/objects", s3BucketName, computerUUID];
printf("loading S3 object SHA1s with prefix %s\n", [objectsPrefix UTF8String]);
NSArray *objectPaths = [s3 pathsWithPrefix:objectsPrefix error:error];
if (objectPaths == nil) {
return NO;
}
for (NSString *objectPath in objectPaths) {
[theObjectSHA1s addObject:[objectPath lastPathComponent]];
}
objectSHA1s = [theObjectSHA1s retain];
printf("loaded %u object SHA1s with prefix %s\n", [objectSHA1s count], [objectsPrefix UTF8String]);
return YES;
}
@end