From 4884a60a9d38fb35da278ca8b7d1a7f473fd9ecb Mon Sep 17 00:00:00 2001 From: Stefan Reitshamer Date: Fri, 18 Jun 2010 08:14:01 -0400 Subject: [PATCH] added arq_verify utility which verifies that all SHA1s referenced by all backups are available in your S3 account --- ArqVerifyCommand.h | 23 ++ ArqVerifyCommand.m | 139 ++++++++++++ PackSet.h | 1 + PackSet.m | 7 + PackSetSet.h | 1 + PackSetSet.m | 9 + S3Fark.h | 1 + S3Fark.m | 3 + S3Repo.h | 1 + S3Repo.m | 3 + arq_restore.xcodeproj/project.pbxproj | 296 +++++++++++++++++++++++++- arq_verify.m | 100 +++++++++ s3/BucketVerifier.h | 25 +++ s3/BucketVerifier.m | 144 +++++++++++++ 14 files changed, 751 insertions(+), 2 deletions(-) create mode 100644 ArqVerifyCommand.h create mode 100644 ArqVerifyCommand.m create mode 100644 arq_verify.m create mode 100644 s3/BucketVerifier.h create mode 100644 s3/BucketVerifier.m diff --git a/ArqVerifyCommand.h b/ArqVerifyCommand.h new file mode 100644 index 0000000..5a10057 --- /dev/null +++ b/ArqVerifyCommand.h @@ -0,0 +1,23 @@ +// +// ArqVerifyCommand.h +// arq_restore +// +// Created by Stefan Reitshamer on 6/17/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class S3Service; + +@interface ArqVerifyCommand : NSObject { + NSString *accessKey; + NSString *secretKey; + NSString *encryptionPassword; + S3Service *s3; +} +- (id)initWithAccessKey:(NSString *)theAccessKey secretKey:(NSString *)theSecretKey encryptionPassword:(NSString *)theEncryptionPassword; +- (BOOL)verifyAll:(NSError **)error; +- (BOOL)verifyS3BucketName:(NSString *)s3BucketName error:(NSError **)error; +- (BOOL)verifyS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error; +- (BOOL)verifyS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID bucketUUID:(NSString *)bucketUUID error:(NSError **)error; +@end diff --git a/ArqVerifyCommand.m b/ArqVerifyCommand.m new file mode 100644 index 0000000..c07ebb8 --- /dev/null +++ b/ArqVerifyCommand.m @@ -0,0 +1,139 @@ +// +// 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 "PackSet.h" + +@interface ArqVerifyCommand (internal) +- (NSArray *)objectSHA1sForS3BucketName:(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 retryOnNetworkError:YES]; + [sap release]; + } + return self; +} +- (void)dealloc { + [accessKey release]; + [secretKey release]; + [encryptionPassword release]; + [s3 release]; + [super dealloc]; +} +- (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) { + if (![self verifyS3BucketName:s3BucketName error:error]) { + 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 domain] isEqualToString:[S3Service serverErrorDomain]] && [myError code] == HTTP_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("verifying 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; + } + NSArray *objectSHA1s = [self objectSHA1sForS3BucketName:s3BucketName computerUUID:computerUUID error:error]; + if (objectSHA1s == nil) { + return NO; + } + for (NSString *s3BucketUUIDPath in s3BucketUUIDPaths) { + NSString *bucketUUID = [s3BucketUUIDPath lastPathComponent]; + printf("verifying 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 + encryptionKey:encryptionPassword] autorelease]; + if (![bucketVerifier verify:error]) { + return NO; + } + } + return YES; +} +- (BOOL)verifyS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID bucketUUID:(NSString *)bucketUUID error:(NSError **)error { + NSArray *objectSHA1s = [self objectSHA1sForS3BucketName:s3BucketName computerUUID:computerUUID error:error]; + if (objectSHA1s == nil) { + return NO; + } + printf("verifying 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 + encryptionKey:encryptionPassword] autorelease]; + if (![bucketVerifier verify:error]) { + return NO; + } + return YES; +} +@end + +@implementation ArqVerifyCommand (internal) +- (NSArray *)objectSHA1sForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error { + 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; + } + printf("loaded %u object SHA1s with prefix %s\n", [objectPaths count], [objectsPrefix UTF8String]); + NSMutableArray *objectSHA1s = [NSMutableArray array]; + for (NSString *objectPath in objectPaths) { + [objectSHA1s addObject:[objectPath lastPathComponent]]; + } + return objectSHA1s; +} +@end diff --git a/PackSet.h b/PackSet.h index 652e974..241099b 100644 --- a/PackSet.h +++ b/PackSet.h @@ -63,4 +63,5 @@ - (NSString *)name; - (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; - (BOOL)containsBlobForSHA1:(NSString *)sha1; +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)blobSHA1; @end diff --git a/PackSet.m b/PackSet.m index 720ec08..dfe88a6 100644 --- a/PackSet.m +++ b/PackSet.m @@ -153,6 +153,13 @@ static double DEFAULT_MAX_REUSABLE_PACK_FILE_SIZE_FRACTION = 0.6; - (BOOL)containsBlobForSHA1:(NSString *)sha1 { return [packIndexEntries objectForKey:sha1] != nil; } +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)blobSHA1 { + PackIndexEntry *pie = [packIndexEntries objectForKey:blobSHA1]; + if (pie == nil) { + return nil; + } + return [pie packSHA1]; +} @end @implementation PackSet (internal) diff --git a/PackSetSet.h b/PackSetSet.h index 6b99946..fb2bd6f 100644 --- a/PackSetSet.h +++ b/PackSetSet.h @@ -48,6 +48,7 @@ computerUUID:(NSString *)theComputerUUID; - (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName error:(NSError **)error; - (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName; +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName; // Sync local cache files to S3 data; reload PackIndexEntries from local cache files. - (BOOL)resetFromS3:(NSError **)error; diff --git a/PackSetSet.m b/PackSetSet.m index 0617704..35b9b5c 100644 --- a/PackSetSet.m +++ b/PackSetSet.m @@ -82,6 +82,15 @@ BOOL contains = [[packSets objectForKey:packSetName] containsBlobForSHA1:sha1]; return contains; } +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName { + NSError *myError = nil; + PackSet *packSet = [self packSetForName:packSetName error:&myError]; + if (packSet == nil) { + HSLogError(@"%@", [myError localizedDescription]); + return nil; + } + return [packSet packSHA1ForPackedBlobSHA1:sha1]; +} - (BOOL)resetFromS3:(NSError **)error { HSLogDebug(@"resetting pack sets from S3"); [packSets removeAllObjects]; diff --git a/S3Fark.h b/S3Fark.h index e8f6c54..273ca17 100644 --- a/S3Fark.h +++ b/S3Fark.h @@ -49,5 +49,6 @@ - (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error; - (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error; - (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly; +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName; - (BOOL)reloadPacksFromS3:(NSError **)error; @end diff --git a/S3Fark.m b/S3Fark.m index 6596850..3fefa63 100644 --- a/S3Fark.m +++ b/S3Fark.m @@ -96,6 +96,9 @@ } return contains; } +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName { + return [packSetSet packSHA1ForPackedBlobSHA1:sha1 packSetName:packSetName]; +} - (BOOL)reloadPacksFromS3:(NSError **)error { NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); return [packSetSet resetFromS3:error]; diff --git a/S3Repo.h b/S3Repo.h index f9d4264..4187527 100644 --- a/S3Repo.h +++ b/S3Repo.h @@ -71,6 +71,7 @@ - (BOOL)tree:(Tree **)tree forSHA1:(NSString *)theSHA1 error:(NSError **)error; - (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly; +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName; - (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error; - (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; - (NSData *)dataForSHA1s:(NSArray *)sha1s error:(NSError **)error; diff --git a/S3Repo.m b/S3Repo.m index eff288c..23edfb9 100644 --- a/S3Repo.m +++ b/S3Repo.m @@ -139,6 +139,9 @@ static NSString *ERROR_DOMAIN = @"S3RepoErrorDomain"; - (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly { return [fark containsBlobForSHA1:sha1 packSetName:packSetName searchPackOnly:searchPackOnly]; } +- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName { + return [fark packSHA1ForPackedBlobSHA1:sha1 packSetName:packSetName]; +} - (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error { NSData *data = [fark dataForSHA1:sha1 packSetName:treesPackSetName searchPackOnly:YES error:error]; if (data == nil) { diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index 398a092..11567cb 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 8DD76F9A0486AA7600D96B5E /* arq_restore.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* arq_restore.m */; settings = {ATTRIBUTES = (); }; }; 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; F805B54D1160D3E6007EC01E /* ArqRestoreCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */; }; F805B7211160D9C2007EC01E /* HSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7201160D9C2007EC01E /* HSLog.m */; }; @@ -64,6 +63,103 @@ F805B8A11160EBAA007EC01E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8A01160EBAA007EC01E /* CoreFoundation.framework */; }; F805B8C21160EC41007EC01E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; }; F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; }; + F83C1A7411CA7C170001958F /* ArqRestoreCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */; }; + F83C1A7511CA7C170001958F /* HSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7201160D9C2007EC01E /* HSLog.m */; }; + F83C1A7611CA7C170001958F /* ArqFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B72F1160DBE9007EC01E /* ArqFolder.m */; }; + F83C1A7711CA7C170001958F /* ArrayNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7421160DCFE007EC01E /* ArrayNode.m */; }; + F83C1A7811CA7C170001958F /* BooleanNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7441160DCFE007EC01E /* BooleanNode.m */; }; + F83C1A7911CA7C170001958F /* DictNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7461160DCFE007EC01E /* DictNode.m */; }; + F83C1A7A11CA7C170001958F /* IntegerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7481160DCFE007EC01E /* IntegerNode.m */; }; + F83C1A7B11CA7C170001958F /* RealNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B74C1160DCFE007EC01E /* RealNode.m */; }; + F83C1A7C11CA7C170001958F /* StringNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B74E1160DCFE007EC01E /* StringNode.m */; }; + F83C1A7D11CA7C170001958F /* XMLPListReader.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7501160DCFE007EC01E /* XMLPListReader.m */; }; + F83C1A7E11CA7C170001958F /* XMLPListWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7521160DCFE007EC01E /* XMLPListWriter.m */; }; + F83C1A7F11CA7C170001958F /* HTTPConnection_S3.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7691160DD60007EC01E /* HTTPConnection_S3.m */; }; + F83C1A8011CA7C170001958F /* NSError_S3.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76B1160DD60007EC01E /* NSError_S3.m */; }; + F83C1A8111CA7C170001958F /* PathReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76D1160DD60007EC01E /* PathReceiver.m */; }; + F83C1A8211CA7C170001958F /* S3AuthorizationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B76F1160DD60007EC01E /* S3AuthorizationParameters.m */; }; + F83C1A8311CA7C170001958F /* S3AuthorizationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7711160DD60007EC01E /* S3AuthorizationProvider.m */; }; + F83C1A8411CA7C170001958F /* S3Lister.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7731160DD60007EC01E /* S3Lister.m */; }; + F83C1A8511CA7C170001958F /* S3ObjectMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7751160DD60007EC01E /* S3ObjectMetadata.m */; }; + F83C1A8611CA7C170001958F /* S3ObjectReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7771160DD60007EC01E /* S3ObjectReceiver.m */; }; + F83C1A8711CA7C170001958F /* S3Request.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B77C1160DD60007EC01E /* S3Request.m */; }; + F83C1A8811CA7C170001958F /* S3Service.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B77E1160DD60007EC01E /* S3Service.m */; }; + F83C1A8911CA7C170001958F /* S3Signature.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7801160DD60007EC01E /* S3Signature.m */; }; + F83C1A8A11CA7C170001958F /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7A81160DEF2007EC01E /* RegexKitLite.m */; }; + F83C1A8B11CA7C170001958F /* Blob.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7B91160E3AF007EC01E /* Blob.m */; }; + F83C1A8C11CA7C170001958F /* HTTPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7CC1160E445007EC01E /* HTTPConnection.m */; }; + F83C1A8D11CA7C170001958F /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7CE1160E445007EC01E /* HTTPRequest.m */; }; + F83C1A8E11CA7C170001958F /* HTTPResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7D01160E445007EC01E /* HTTPResponse.m */; }; + F83C1A8F11CA7C170001958F /* BlobACL.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7D71160E456007EC01E /* BlobACL.m */; }; + F83C1A9011CA7C170001958F /* ServerBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7E01160E48B007EC01E /* ServerBlob.m */; }; + F83C1A9111CA7C170001958F /* RFC2616DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7FA1160E73D007EC01E /* RFC2616DateFormatter.m */; }; + F83C1A9211CA7C170001958F /* RFC822.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7FD1160E764007EC01E /* RFC822.m */; }; + F83C1A9311CA7C170001958F /* InputStreams.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B81A1160E838007EC01E /* InputStreams.m */; }; + F83C1A9411CA7C170001958F /* ChunkedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8221160E857007EC01E /* ChunkedInputStream.m */; }; + F83C1A9511CA7C170001958F /* FixedLengthInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8241160E857007EC01E /* FixedLengthInputStream.m */; }; + F83C1A9611CA7C170001958F /* FDInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8281160E861007EC01E /* FDInputStream.m */; }; + F83C1A9711CA7C170001958F /* FDOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B82A1160E861007EC01E /* FDOutputStream.m */; }; + F83C1A9811CA7C170001958F /* DataInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B82E1160E86E007EC01E /* DataInputStream.m */; }; + F83C1A9911CA7C170001958F /* Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8311160E878007EC01E /* Writer.m */; }; + F83C1A9A11CA7C170001958F /* NSData-InputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83A1160E8DD007EC01E /* NSData-InputStream.m */; }; + F83C1A9B11CA7C170001958F /* StreamPairFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B83E1160E900007EC01E /* StreamPairFactory.m */; }; + F83C1A9C11CA7C170001958F /* Streams.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8411160E90F007EC01E /* Streams.m */; }; + F83C1A9D11CA7C170001958F /* CFStreamPair.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8521160E9B0007EC01E /* CFStreamPair.m */; }; + F83C1A9E11CA7C170001958F /* CFStreamInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8571160E9C9007EC01E /* CFStreamInputStream.m */; }; + F83C1A9F11CA7C170001958F /* CFStreamOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8591160E9C9007EC01E /* CFStreamOutputStream.m */; }; + F83C1AA011CA7C170001958F /* DNS_SDErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B85F1160E9F0007EC01E /* DNS_SDErrors.m */; }; + F83C1AA111CA7C170001958F /* NSXMLNode_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8641160EA15007EC01E /* NSXMLNode_extra.m */; }; + F83C1AA211CA7C170001958F /* NSData-Base64Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B8691160EA83007EC01E /* NSData-Base64Extensions.m */; }; + F83C1AA311CA7C170001958F /* DataInputStreamFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B86E1160EAC1007EC01E /* DataInputStreamFactory.m */; }; + F83C1AA411CA7C170001958F /* Restorer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D677FF1160F26A00CC270E /* Restorer.m */; }; + F83C1AA511CA7C170001958F /* S3Fark.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678141160F4E300CC270E /* S3Fark.m */; }; + F83C1AA611CA7C170001958F /* S3Repo.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678161160F4E300CC270E /* S3Repo.m */; }; + F83C1AA711CA7C170001958F /* SHA1Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6781C1160F4FD00CC270E /* SHA1Hash.m */; }; + F83C1AA811CA7C170001958F /* PackSetSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782A1160F5D000CC270E /* PackSetSet.m */; }; + F83C1AA911CA7C170001958F /* PackSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782C1160F5D000CC270E /* PackSet.m */; }; + F83C1AAA11CA7C170001958F /* ArqUserLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678321160F62E00CC270E /* ArqUserLibrary.m */; }; + F83C1AAB11CA7C170001958F /* PackIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6783B1160F70100CC270E /* PackIndexEntry.m */; }; + F83C1AAC11CA7C170001958F /* DiskPack.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678421160F74A00CC270E /* DiskPack.m */; }; + F83C1AAD11CA7C170001958F /* DiskPackIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678441160F74A00CC270E /* DiskPackIndex.m */; }; + F83C1AAE11CA7C170001958F /* Commit.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6785C1160F7CF00CC270E /* Commit.m */; }; + F83C1AAF11CA7C170001958F /* Tree.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6785E1160F7CF00CC270E /* Tree.m */; }; + F83C1AB011CA7C170001958F /* NSData-Encrypt.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678641160F7FE00CC270E /* NSData-Encrypt.m */; }; + F83C1AB111CA7C170001958F /* OpenSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678691160F81100CC270E /* OpenSSL.m */; }; + F83C1AB211CA7C170001958F /* DecryptedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6786E1160F84600CC270E /* DecryptedInputStream.m */; }; + F83C1AB311CA7C170001958F /* CryptInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678731160F85D00CC270E /* CryptInputStream.m */; }; + F83C1AB411CA7C170001958F /* FileInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678761160F86E00CC270E /* FileInputStream.m */; }; + F83C1AB511CA7C170001958F /* DataIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678791160F8A000CC270E /* DataIO.m */; }; + F83C1AB611CA7C170001958F /* DateIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787B1160F8A000CC270E /* DateIO.m */; }; + F83C1AB711CA7C170001958F /* DoubleIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787D1160F8A000CC270E /* DoubleIO.m */; }; + F83C1AB811CA7C170001958F /* IntegerIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6787F1160F8A000CC270E /* IntegerIO.m */; }; + F83C1AB911CA7C170001958F /* StringIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678811160F8A000CC270E /* StringIO.m */; }; + F83C1ABA11CA7C170001958F /* NSString_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678881160F8CD00CC270E /* NSString_extra.m */; }; + F83C1ABB11CA7C170001958F /* BinarySHA1.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6788B1160F8E500CC270E /* BinarySHA1.m */; }; + F83C1ABC11CA7C170001958F /* NSFileManager_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678991160FA2A00CC270E /* NSFileManager_extra.m */; }; + F83C1ABD11CA7C170001958F /* FileOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6789C1160FA3900CC270E /* FileOutputStream.m */; }; + F83C1ABE11CA7C170001958F /* FileInputStreamFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6789F1160FA4800CC270E /* FileInputStreamFactory.m */; }; + F83C1ABF11CA7C170001958F /* BooleanIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678A41160FA5F00CC270E /* BooleanIO.m */; }; + F83C1AC011CA7C170001958F /* EncryptedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678A71160FA6A00CC270E /* EncryptedInputStream.m */; }; + F83C1AC111CA7C170001958F /* CommitFailedFile.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678AE1160FAD900CC270E /* CommitFailedFile.m */; }; + F83C1AC211CA7C170001958F /* Node.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678B71160FB2100CC270E /* Node.m */; }; + F83C1AC311CA7C170001958F /* FileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67CEA1161363A00CC270E /* FileAttributes.m */; }; + F83C1AC411CA7C170001958F /* XAttrSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67CF11161366100CC270E /* XAttrSet.m */; }; + F83C1AC511CA7C170001958F /* FileACL.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D041161384100CC270E /* FileACL.m */; }; + F83C1AC611CA7C170001958F /* OSStatusDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D061161384100CC270E /* OSStatusDescription.m */; }; + F83C1AC711CA7C170001958F /* RestoreNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67F6F1161443600CC270E /* RestoreNode.m */; }; + F83C1AC811CA7C170001958F /* arq_verify.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1A5F11CA7A6B0001958F /* arq_verify.m */; }; + F83C1AC911CA7C170001958F /* ArqVerifyCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1A6211CA7BD20001958F /* ArqVerifyCommand.m */; }; + F83C1ACB11CA7C170001958F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; + F83C1ACC11CA7C170001958F /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8881160EB39007EC01E /* libcrypto.dylib */; }; + F83C1ACD11CA7C170001958F /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B88A1160EB39007EC01E /* libicucore.dylib */; }; + F83C1ACE11CA7C170001958F /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B88E1160EB45007EC01E /* libssl.dylib */; }; + F83C1ACF11CA7C170001958F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8921160EB4E007EC01E /* SystemConfiguration.framework */; }; + F83C1AD011CA7C170001958F /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8A01160EBAA007EC01E /* CoreFoundation.framework */; }; + F83C1AD111CA7C170001958F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; }; + F83C1AD211CA7C170001958F /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; }; + F83C1AE311CA7C7C0001958F /* arq_restore.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* arq_restore.m */; }; + F83C1D0A11CA929D0001958F /* BucketVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1D0911CA929D0001958F /* BucketVerifier.m */; }; + F83C1D1D11CA95AF0001958F /* BucketVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F83C1D0911CA929D0001958F /* BucketVerifier.m */; }; F8D678001160F26A00CC270E /* Restorer.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D677FF1160F26A00CC270E /* Restorer.m */; }; F8D678171160F4E300CC270E /* S3Fark.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678141160F4E300CC270E /* S3Fark.m */; }; F8D678181160F4E300CC270E /* S3Repo.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678161160F4E300CC270E /* S3Repo.m */; }; @@ -112,6 +208,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + F83C1AD311CA7C170001958F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -235,6 +340,12 @@ F805B8C11160EC41007EC01E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; F805B8C51160EC4E007EC01E /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; F805B8CD1160ECD7007EC01E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; + F83C1A5F11CA7A6B0001958F /* arq_verify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = arq_verify.m; sourceTree = ""; }; + F83C1A6111CA7BD20001958F /* ArqVerifyCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqVerifyCommand.h; sourceTree = ""; }; + F83C1A6211CA7BD20001958F /* ArqVerifyCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqVerifyCommand.m; sourceTree = ""; }; + F83C1AD711CA7C170001958F /* arq_verify */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = arq_verify; sourceTree = BUILT_PRODUCTS_DIR; }; + F83C1D0811CA929D0001958F /* BucketVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BucketVerifier.h; path = s3/BucketVerifier.h; sourceTree = ""; }; + F83C1D0911CA929D0001958F /* BucketVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BucketVerifier.m; path = s3/BucketVerifier.m; sourceTree = ""; }; F8D6763E1160F22800CC270E /* SetNSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetNSError.h; sourceTree = ""; }; F8D677FE1160F26A00CC270E /* Restorer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Restorer.h; sourceTree = ""; }; F8D677FF1160F26A00CC270E /* Restorer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Restorer.m; sourceTree = ""; }; @@ -326,6 +437,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F83C1ACA11CA7C170001958F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F83C1ACB11CA7C170001958F /* Foundation.framework in Frameworks */, + F83C1ACC11CA7C170001958F /* libcrypto.dylib in Frameworks */, + F83C1ACD11CA7C170001958F /* libicucore.dylib in Frameworks */, + F83C1ACE11CA7C170001958F /* libssl.dylib in Frameworks */, + F83C1ACF11CA7C170001958F /* SystemConfiguration.framework in Frameworks */, + F83C1AD011CA7C170001958F /* CoreFoundation.framework in Frameworks */, + F83C1AD111CA7C170001958F /* Security.framework in Frameworks */, + F83C1AD211CA7C170001958F /* CoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -354,6 +480,11 @@ F8D678321160F62E00CC270E /* ArqUserLibrary.m */, F805B54B1160D3E6007EC01E /* ArqRestoreCommand.h */, F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */, + F83C1A5F11CA7A6B0001958F /* arq_verify.m */, + F83C1A6111CA7BD20001958F /* ArqVerifyCommand.h */, + F83C1A6211CA7BD20001958F /* ArqVerifyCommand.m */, + F83C1D0811CA929D0001958F /* BucketVerifier.h */, + F83C1D0911CA929D0001958F /* BucketVerifier.m */, F805B72E1160DBE9007EC01E /* ArqFolder.h */, F805B72F1160DBE9007EC01E /* ArqFolder.m */, F8D6785B1160F7CE00CC270E /* Commit.h */, @@ -414,6 +545,7 @@ isa = PBXGroup; children = ( 8DD76FA10486AA7600D96B5E /* arq_restore */, + F83C1AD711CA7C170001958F /* arq_verify */, ); name = Products; sourceTree = ""; @@ -622,6 +754,24 @@ productReference = 8DD76FA10486AA7600D96B5E /* arq_restore */; productType = "com.apple.product-type.tool"; }; + F83C1A7111CA7C170001958F /* arq_verify */ = { + isa = PBXNativeTarget; + buildConfigurationList = F83C1AD411CA7C170001958F /* Build configuration list for PBXNativeTarget "arq_verify" */; + buildPhases = ( + F83C1A7211CA7C170001958F /* Sources */, + F83C1ACA11CA7C170001958F /* Frameworks */, + F83C1AD311CA7C170001958F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = arq_verify; + productInstallPath = "$(HOME)/bin"; + productName = arq_restore; + productReference = F83C1AD711CA7C170001958F /* arq_verify */; + productType = "com.apple.product-type.tool"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -635,6 +785,7 @@ projectRoot = ""; targets = ( 8DD76F960486AA7600D96B5E /* arq_restore */, + F83C1A7111CA7C170001958F /* arq_verify */, ); }; /* End PBXProject section */ @@ -644,7 +795,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8DD76F9A0486AA7600D96B5E /* arq_restore.m in Sources */, F805B54D1160D3E6007EC01E /* ArqRestoreCommand.m in Sources */, F805B7211160D9C2007EC01E /* HSLog.m in Sources */, F805B7301160DBE9007EC01E /* ArqFolder.m in Sources */, @@ -729,6 +879,102 @@ F8D67D071161384100CC270E /* FileACL.m in Sources */, F8D67D081161384100CC270E /* OSStatusDescription.m in Sources */, F8D67F701161443600CC270E /* RestoreNode.m in Sources */, + F83C1AE311CA7C7C0001958F /* arq_restore.m in Sources */, + F83C1D0A11CA929D0001958F /* BucketVerifier.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F83C1A7211CA7C170001958F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F83C1AC811CA7C170001958F /* arq_verify.m in Sources */, + F83C1A7411CA7C170001958F /* ArqRestoreCommand.m in Sources */, + F83C1A7511CA7C170001958F /* HSLog.m in Sources */, + F83C1A7611CA7C170001958F /* ArqFolder.m in Sources */, + F83C1A7711CA7C170001958F /* ArrayNode.m in Sources */, + F83C1A7811CA7C170001958F /* BooleanNode.m in Sources */, + F83C1A7911CA7C170001958F /* DictNode.m in Sources */, + F83C1A7A11CA7C170001958F /* IntegerNode.m in Sources */, + F83C1A7B11CA7C170001958F /* RealNode.m in Sources */, + F83C1A7C11CA7C170001958F /* StringNode.m in Sources */, + F83C1A7D11CA7C170001958F /* XMLPListReader.m in Sources */, + F83C1A7E11CA7C170001958F /* XMLPListWriter.m in Sources */, + F83C1A7F11CA7C170001958F /* HTTPConnection_S3.m in Sources */, + F83C1A8011CA7C170001958F /* NSError_S3.m in Sources */, + F83C1A8111CA7C170001958F /* PathReceiver.m in Sources */, + F83C1A8211CA7C170001958F /* S3AuthorizationParameters.m in Sources */, + F83C1A8311CA7C170001958F /* S3AuthorizationProvider.m in Sources */, + F83C1A8411CA7C170001958F /* S3Lister.m in Sources */, + F83C1A8511CA7C170001958F /* S3ObjectMetadata.m in Sources */, + F83C1A8611CA7C170001958F /* S3ObjectReceiver.m in Sources */, + F83C1A8711CA7C170001958F /* S3Request.m in Sources */, + F83C1A8811CA7C170001958F /* S3Service.m in Sources */, + F83C1A8911CA7C170001958F /* S3Signature.m in Sources */, + F83C1A8A11CA7C170001958F /* RegexKitLite.m in Sources */, + F83C1A8B11CA7C170001958F /* Blob.m in Sources */, + F83C1A8C11CA7C170001958F /* HTTPConnection.m in Sources */, + F83C1A8D11CA7C170001958F /* HTTPRequest.m in Sources */, + F83C1A8E11CA7C170001958F /* HTTPResponse.m in Sources */, + F83C1A8F11CA7C170001958F /* BlobACL.m in Sources */, + F83C1A9011CA7C170001958F /* ServerBlob.m in Sources */, + F83C1A9111CA7C170001958F /* RFC2616DateFormatter.m in Sources */, + F83C1A9211CA7C170001958F /* RFC822.m in Sources */, + F83C1A9311CA7C170001958F /* InputStreams.m in Sources */, + F83C1A9411CA7C170001958F /* ChunkedInputStream.m in Sources */, + F83C1A9511CA7C170001958F /* FixedLengthInputStream.m in Sources */, + F83C1A9611CA7C170001958F /* FDInputStream.m in Sources */, + F83C1A9711CA7C170001958F /* FDOutputStream.m in Sources */, + F83C1A9811CA7C170001958F /* DataInputStream.m in Sources */, + F83C1A9911CA7C170001958F /* Writer.m in Sources */, + F83C1A9A11CA7C170001958F /* NSData-InputStream.m in Sources */, + F83C1A9B11CA7C170001958F /* StreamPairFactory.m in Sources */, + F83C1A9C11CA7C170001958F /* Streams.m in Sources */, + F83C1A9D11CA7C170001958F /* CFStreamPair.m in Sources */, + F83C1A9E11CA7C170001958F /* CFStreamInputStream.m in Sources */, + F83C1A9F11CA7C170001958F /* CFStreamOutputStream.m in Sources */, + F83C1AA011CA7C170001958F /* DNS_SDErrors.m in Sources */, + F83C1AA111CA7C170001958F /* NSXMLNode_extra.m in Sources */, + F83C1AA211CA7C170001958F /* NSData-Base64Extensions.m in Sources */, + F83C1AA311CA7C170001958F /* DataInputStreamFactory.m in Sources */, + F83C1AA411CA7C170001958F /* Restorer.m in Sources */, + F83C1AA511CA7C170001958F /* S3Fark.m in Sources */, + F83C1AA611CA7C170001958F /* S3Repo.m in Sources */, + F83C1AA711CA7C170001958F /* SHA1Hash.m in Sources */, + F83C1AA811CA7C170001958F /* PackSetSet.m in Sources */, + F83C1AA911CA7C170001958F /* PackSet.m in Sources */, + F83C1AAA11CA7C170001958F /* ArqUserLibrary.m in Sources */, + F83C1AAB11CA7C170001958F /* PackIndexEntry.m in Sources */, + F83C1AAC11CA7C170001958F /* DiskPack.m in Sources */, + F83C1AAD11CA7C170001958F /* DiskPackIndex.m in Sources */, + F83C1AAE11CA7C170001958F /* Commit.m in Sources */, + F83C1AAF11CA7C170001958F /* Tree.m in Sources */, + F83C1AB011CA7C170001958F /* NSData-Encrypt.m in Sources */, + F83C1AB111CA7C170001958F /* OpenSSL.m in Sources */, + F83C1AB211CA7C170001958F /* DecryptedInputStream.m in Sources */, + F83C1AB311CA7C170001958F /* CryptInputStream.m in Sources */, + F83C1AB411CA7C170001958F /* FileInputStream.m in Sources */, + F83C1AB511CA7C170001958F /* DataIO.m in Sources */, + F83C1AB611CA7C170001958F /* DateIO.m in Sources */, + F83C1AB711CA7C170001958F /* DoubleIO.m in Sources */, + F83C1AB811CA7C170001958F /* IntegerIO.m in Sources */, + F83C1AB911CA7C170001958F /* StringIO.m in Sources */, + F83C1ABA11CA7C170001958F /* NSString_extra.m in Sources */, + F83C1ABB11CA7C170001958F /* BinarySHA1.m in Sources */, + F83C1ABC11CA7C170001958F /* NSFileManager_extra.m in Sources */, + F83C1ABD11CA7C170001958F /* FileOutputStream.m in Sources */, + F83C1ABE11CA7C170001958F /* FileInputStreamFactory.m in Sources */, + F83C1ABF11CA7C170001958F /* BooleanIO.m in Sources */, + F83C1AC011CA7C170001958F /* EncryptedInputStream.m in Sources */, + F83C1AC111CA7C170001958F /* CommitFailedFile.m in Sources */, + F83C1AC211CA7C170001958F /* Node.m in Sources */, + F83C1AC311CA7C170001958F /* FileAttributes.m in Sources */, + F83C1AC411CA7C170001958F /* XAttrSet.m in Sources */, + F83C1AC511CA7C170001958F /* FileACL.m in Sources */, + F83C1AC611CA7C170001958F /* OSStatusDescription.m in Sources */, + F83C1AC711CA7C170001958F /* RestoreNode.m in Sources */, + F83C1AC911CA7C170001958F /* ArqVerifyCommand.m in Sources */, + F83C1D1D11CA95AF0001958F /* BucketVerifier.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -798,6 +1044,43 @@ }; name = Release; }; + F83C1AD511CA7C170001958F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = arq_restore_Prefix.pch; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = arq_verify; + SDKROOT = macosx10.5; + }; + name = Debug; + }; + F83C1AD611CA7C170001958F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = arq_restore_Prefix.pch; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = arq_restore; + SDKROOT = macosx10.5; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -819,6 +1102,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F83C1AD411CA7C170001958F /* Build configuration list for PBXNativeTarget "arq_verify" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F83C1AD511CA7C170001958F /* Debug */, + F83C1AD611CA7C170001958F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; diff --git a/arq_verify.m b/arq_verify.m new file mode 100644 index 0000000..df0123e --- /dev/null +++ b/arq_verify.m @@ -0,0 +1,100 @@ +/* + Copyright (c) 2010, Stefan Reitshamer http://www.haystacksoftware.com + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the names of PhotoMinds LLC or Haystack Software, nor the names of + their contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#import +#import "ArqVerifyCommand.h" + +static void printUsage(const char *exeName) { + fprintf(stderr, "usage: %s [s3_bucket_name [computer_uuid [folder_uuid]]]\n", exeName); +} +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + setHSLogLevel(HSLOG_LEVEL_ERROR); + char *exePath = strdup(argv[0]); + char *exeName = basename(exePath); + + char *cAccessKey = getenv("ARQ_ACCESS_KEY"); + if (cAccessKey == NULL) { + fprintf(stderr, "%s: missing ARQ_ACCESS_KEY environment variable\n", exeName); + } + char *cSecretKey = getenv("ARQ_SECRET_KEY"); + if (cSecretKey == NULL) { + fprintf(stderr, "%s: missing ARQ_SECRET_KEY environment variable\n", exeName); + } + char *cEncryptionPassword = getenv("ARQ_ENCRYPTION_PASSWORD"); + if (cEncryptionPassword == NULL) { + fprintf(stderr, "%s: missing ARQ_ENCRYPTION_PASSWORD environment variable\n", exeName); + } + if (cAccessKey == NULL || cSecretKey == NULL || cEncryptionPassword == NULL) { + goto main_error; + } + + NSString *accessKey = [NSString stringWithUTF8String:cAccessKey]; + NSString *secretKey = [NSString stringWithUTF8String:cSecretKey]; + NSString *encryptionPassword = [[NSString alloc] initWithUTF8String:cEncryptionPassword]; + ArqVerifyCommand *cmd = [[[ArqVerifyCommand alloc] initWithAccessKey:accessKey secretKey:secretKey encryptionPassword:encryptionPassword] autorelease]; + NSError *error = nil; + BOOL ret = NO; + if (argc == 1) { + if (![cmd verifyAll:&error]) { + NSLog(@"%@", [error localizedDescription]); + goto main_error; + } + } else if (argc == 2) { + if (!strcmp(argv[1], "-?") || !strcmp(argv[1], "-h")) { + printUsage(exeName); + goto main_error; + } else if (![cmd verifyS3BucketName:[NSString stringWithUTF8String:argv[1]] error:&error]) { + NSLog(@"%@", [error localizedDescription]); + goto main_error; + } + } else if (argc == 3) { + if (![cmd verifyS3BucketName:[NSString stringWithUTF8String:argv[1]] computerUUID:[NSString stringWithUTF8String:argv[2]] error:&error]) { + NSLog(@"%@", [error localizedDescription]); + goto main_error; + } + } else if (argc == 4) { + if (![cmd verifyS3BucketName:[NSString stringWithUTF8String:argv[1]] computerUUID:[NSString stringWithUTF8String:argv[2]] bucketUUID:[NSString stringWithUTF8String:argv[3]] error:&error]) { + NSLog(@"%@", [error localizedDescription]); + goto main_error; + } + } else { + printUsage(exeName); + goto main_error; + } + ret = YES; +main_error: + [pool drain]; + free(exePath); + return ret ? 0 : 1; +} diff --git a/s3/BucketVerifier.h b/s3/BucketVerifier.h new file mode 100644 index 0000000..44170f9 --- /dev/null +++ b/s3/BucketVerifier.h @@ -0,0 +1,25 @@ +// +// BucketVerifier.h +// arq_restore +// +// Created by Stefan Reitshamer on 6/17/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class S3Service; +@class S3Fark; +@class S3Repo; + +@interface BucketVerifier : NSObject { + S3Service *s3; + NSString *s3BucketName; + NSString *computerUUID; + NSString *bucketUUID; + NSArray *objectSHA1s; + S3Fark *fark; + S3Repo *repo; +} +- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID s3ObjectSHA1s:(NSArray *)theObjectSHA1s encryptionKey:(NSString *)encryptionKey; +- (BOOL)verify:(NSError **)error; +@end diff --git a/s3/BucketVerifier.m b/s3/BucketVerifier.m new file mode 100644 index 0000000..a346843 --- /dev/null +++ b/s3/BucketVerifier.m @@ -0,0 +1,144 @@ +// +// BucketVerifier.m +// arq_restore +// +// Created by Stefan Reitshamer on 6/17/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "BucketVerifier.h" +#import "S3Service.h" +#import "S3Fark.h" +#import "S3Repo.h" +#import "Commit.h" +#import "Tree.h" +#import "Node.h" +#import "SetNSError.h" + +@interface BucketVerifier (internal) +- (BOOL)verifyTree:(NSString *)treeSHA1 path:(NSString *)path error:(NSError **)error; +- (BOOL)verify:(NSString *)sha1 error:(NSError **)error; +@end + +@implementation BucketVerifier +- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID s3ObjectSHA1s:(NSArray *)theObjectSHA1s encryptionKey:(NSString *)encryptionKey { + if (self = [super init]) { + s3 = [theS3 retain]; + s3BucketName = [theS3BucketName retain]; + computerUUID = [theComputerUUID retain]; + bucketUUID = [theBucketUUID retain]; + objectSHA1s = [theObjectSHA1s retain]; + fark = [[S3Fark alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID]; + repo = [[S3Repo alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID encrypted:YES encryptionKey:encryptionKey fark:fark ensureCacheIntegrity:YES]; + } + return self; +} +- (void)dealloc { + [s3 release]; + [s3BucketName release]; + [computerUUID release]; + [bucketUUID release]; + [objectSHA1s release]; + [fark release]; + [repo release]; + [super dealloc]; +} +- (BOOL)verify:(NSError **)error { + printf("reloading packs from S3 for s3Bucket %s computerUUID %s bucketUUID %s\n", [s3BucketName UTF8String], [computerUUID UTF8String], [bucketUUID UTF8String]); + if (![fark reloadPacksFromS3:error]) { + return NO; + } + NSString *headSHA1 = nil; + if (![repo localHeadSHA1:&headSHA1 error:error]) { + return NO; + } + if (headSHA1 == nil) { + printf("no head commit for s3Bucket %s computerUUID %s bucketUUID %s\n", [s3BucketName UTF8String], [computerUUID UTF8String], [bucketUUID UTF8String]); + return YES; + } + printf("head commit for s3Bucket %s computerUUID %s bucketUUID %s is %s\n", [s3BucketName UTF8String], [computerUUID UTF8String], [bucketUUID UTF8String], [headSHA1 UTF8String]); + NSString *commitSHA1 = headSHA1; + while (commitSHA1 != nil) { + printf("verifying commit %s bucketUUID %s\n", [commitSHA1 UTF8String], [bucketUUID UTF8String]); + Commit *commit = nil; + if (![repo commit:&commit forSHA1:commitSHA1 error:error]) { + return NO; + } + printf("commit %s's tree is %s\n", [commitSHA1 UTF8String], [[commit treeSHA1] UTF8String]); + if (![self verifyTree:[commit treeSHA1] path:@"/" error:error]) { + return NO; + } + commitSHA1 = [[commit parentCommitSHA1s] anyObject]; + } + return YES; +} +@end + +@implementation BucketVerifier (internal) +- (BOOL)verifyTree:(NSString *)treeSHA1 path:(NSString *)path error:(NSError **)error { + printf("verifying tree %s (path %s)\n", [treeSHA1 UTF8String], [path UTF8String]); + Tree *tree = nil; + if (![repo tree:&tree forSHA1:treeSHA1 error:error]) { + fprintf(stderr, "tree %s not found\n", [treeSHA1 UTF8String]); + return NO; + } + if (![self verify:[tree xattrsSHA1] error:error]) { + fprintf(stderr, "tree %s's xattrsSHA1 %s not found", [treeSHA1 UTF8String], [[tree xattrsSHA1] UTF8String]); + return NO; + } + if (![self verify:[tree aclSHA1] error:error]) { + fprintf(stderr, "tree %s's aclSHA1 %s not found", [treeSHA1 UTF8String], [[tree aclSHA1] UTF8String]); + return NO; + } + for (NSString *childNodeName in [tree childNodeNames]) { + Node *node = [tree childNodeWithName:childNodeName]; + NSArray *dataSHA1s = [node dataSHA1s]; + NSString *childPath = [path stringByAppendingPathComponent:childNodeName]; + if ([node isTree]) { + NSAssert([dataSHA1s count] == 1, ([NSString stringWithFormat:@"tree %@ node %@ must have exactly 1 dataSHA1", treeSHA1, childNodeName])); + if (![self verifyTree:[dataSHA1s objectAtIndex:0] path:childPath error:error]) { + return NO; + } + } else { + printf("verifying node %s\n", [childPath UTF8String]); + for (NSString *dataSHA1 in dataSHA1s) { + if (![self verify:dataSHA1 error:error]) { + HSLogError(@"missing data sha1 %@ for node %@ in tree %@", dataSHA1, childNodeName, treeSHA1); + return NO; + } + } + if (![self verify:[node thumbnailSHA1] error:error]) { + HSLogError(@"missing thumbnail sha1 %@ for node %@ in tree %@", [node thumbnailSHA1], childNodeName, treeSHA1); + return NO; + } + if (![self verify:[node previewSHA1] error:error]) { + HSLogError(@"missing preview sha1 %@ for node %@ in tree %@", [node previewSHA1], childNodeName, treeSHA1); + return NO; + } + if (![self verify:[node xattrsSHA1] error:error]) { + HSLogError(@"missing xattrs sha1 %@ for node %@ in tree %@", [node xattrsSHA1], childNodeName, treeSHA1); + return NO; + } + if (![self verify:[node aclSHA1] error:error]) { + HSLogError(@"missing acl sha1 %@ for node %@ in tree %@", [node aclSHA1], childNodeName, treeSHA1); + return NO; + } + } + } + return YES; +} +- (BOOL)verify:(NSString *)sha1 error:(NSError **)error { + if (sha1 != nil) { + if ([objectSHA1s containsObject:sha1]) { + printf("sha1 %s: blob\n", [sha1 UTF8String]); + } else if ([repo containsBlobForSHA1:sha1 packSetName:[repo blobsPackSetName] searchPackOnly:YES]) { + printf("sha1 %s: pack set %s, packSHA1 %s\n", [sha1 UTF8String], [[repo blobsPackSetName] UTF8String], [[repo packSHA1ForPackedBlobSHA1:sha1 packSetName:[repo blobsPackSetName]] UTF8String]); + } else { + SETNSERROR(@"VerifierErrorDomain", -1, @"sha1 %@ not found", sha1); + return NO; + } + } + return YES; +} + +@end