diff --git a/AppKeychain.h b/AppKeychain.h new file mode 100644 index 0000000..6d2b5b2 --- /dev/null +++ b/AppKeychain.h @@ -0,0 +1,17 @@ +// +// AppKeychain.h +// arq_restore +// +// Created by Stefan Reitshamer on 8/19/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface AppKeychain : NSObject { +} ++ (NSString *)errorDomain; ++ (BOOL)accessKeyID:(NSString **)accessKeyID secretAccessKey:(NSString **)secret error:(NSError **)error; ++ (BOOL)encryptionKey:(NSString **)encryptionKey error:(NSError **)error; +@end diff --git a/AppKeychain.m b/AppKeychain.m new file mode 100644 index 0000000..76eb75a --- /dev/null +++ b/AppKeychain.m @@ -0,0 +1,43 @@ +// +// AppKeychain.m +// arq_restore +// +// Created by Stefan Reitshamer on 8/19/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "AppKeychain.h" +#import "SetNSError.h" +#import "NSErrorCodes.h" + +@implementation AppKeychain ++ (NSString *)errorDomain { + return @"AppKeychainErrorDomain"; +} ++ (BOOL)accessKeyID:(NSString **)accessKeyID secretAccessKey:(NSString **)secret error:(NSError **)error { + char *cAccessKey = getenv("ARQ_ACCESS_KEY"); + if (cAccessKey == NULL) { + SETNSERROR([AppKeychain errorDomain], ERROR_NOT_FOUND, @"ARQ_ACCESS_KEY not found"); + return NO; + } + *accessKeyID = [[NSString alloc] initWithUTF8String:cAccessKey]; + return YES; + char *cSecretKey = getenv("ARQ_SECRET_KEY"); + if (cSecretKey == NULL) { + SETNSERROR([AppKeychain errorDomain], ERROR_NOT_FOUND, @"ARQ_SECRET_KEY not found"); + return NO; + } + *secret = [[NSString alloc] initWithUTF8String:cSecretKey]; + return YES; +} ++ (BOOL)encryptionKey:(NSString **)encryptionKey error:(NSError **)error { + char *cEncryptionPassword = getenv("ARQ_ENCRYPTION_PASSWORD"); + if (cEncryptionPassword != NULL) { + SETNSERROR([AppKeychain errorDomain], ERROR_NOT_FOUND, @"ARQ_ENCRYPTION_PASSWORD not found"); + return NO; + } + *encryptionKey = [[NSString alloc] initWithUTF8String:cEncryptionPassword]; + return YES; +} + +@end diff --git a/ArqFark.h b/ArqFark.h new file mode 100644 index 0000000..8ab764a --- /dev/null +++ b/ArqFark.h @@ -0,0 +1,26 @@ +// +// ArqFark.h +// Arq +// +// Created by Stefan Reitshamer on 6/22/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class S3Service; +@class ServerBlob; + +@interface ArqFark : NSObject { + S3Service *s3; + NSString *s3BucketName; + NSString *computerUUID; + NSThread *creatorThread; +} ++ (NSString *)errorDomain; +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID; +- (NSData *)bucketDataForPath:(NSString *)bucketDataPath error:(NSError **)error; +- (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error; +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; +@end diff --git a/ArqFark.m b/ArqFark.m new file mode 100644 index 0000000..736e790 --- /dev/null +++ b/ArqFark.m @@ -0,0 +1,74 @@ +// +// ArqFark.m +// Arq +// +// Created by Stefan Reitshamer on 6/22/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "ArqFark.h" +#import "ArqPackSet.h" +#import "ServerBlob.h" +#import "NSErrorCodes.h" +#import "S3Service.h" +#import "RegexKitLite.h" +#import "DiskPackIndex.h" +#import "FarkPath.h" +#import "SetNSError.h" +#import "NSError_extra.h" + +#define MAX_RETRIES 10 + +@implementation ArqFark ++ (NSString *)errorDomain { + return @"ArqFarkErrorDomain"; +} +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID { + if (self = [super init]) { + s3 = [theS3 retain]; + s3BucketName = [theS3BucketName retain]; + computerUUID = [theComputerUUID retain]; + creatorThread = [[NSThread currentThread] retain]; + } + return self; +} +- (void)dealloc { + [s3 release]; + [s3BucketName release]; + [computerUUID release]; + [creatorThread release]; + [super dealloc]; +} +- (NSData *)bucketDataForPath:(NSString *)bucketDataPath error:(NSError **)error { + NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); + + NSError *myError = nil; + NSData *data = [s3 dataAtPath:[FarkPath s3PathForBucketDataPath:bucketDataPath s3BucketName:s3BucketName computerUUID:computerUUID] error:&myError]; + if (data == nil) { + if ([myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) { + SETNSERROR([ArqFark errorDomain], ERROR_NOT_FOUND, @"bucket data not found for path %@", bucketDataPath); + } else { + if (error != NULL) { + *error = myError; + } + } + } + return data; +} +- (NSData *)dataForSHA1:(NSString *)sha1 error:(NSError **)error { + ServerBlob *sb = [self newServerBlobForSHA1:sha1 error:error]; + if (sb == nil) { + return nil; + } + NSData *data = [sb slurp:error]; + [sb release]; + return data; +} +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { + NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); + NSString *s3Path = [NSString stringWithFormat:@"/%@/%@/objects/%@", s3BucketName, computerUUID, sha1]; + return [s3 newServerBlobAtPath:s3Path error:error]; +} +@end diff --git a/ArqPackSet.h b/ArqPackSet.h new file mode 100644 index 0000000..9d074d3 --- /dev/null +++ b/ArqPackSet.h @@ -0,0 +1,29 @@ +// +// ArqPackSet.h +// Arq +// +// Created by Stefan Reitshamer on 6/22/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class S3Service; +@class ServerBlob; + +@interface ArqPackSet : NSObject { + S3Service *s3; + NSString *s3BucketName; + NSString *computerUUID; + NSString *packSetName; + NSDictionary *packIndexEntries; +} ++ (NSString *)errorDomain; ++ (BOOL)cachePackSetIndexesForS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID error:(NSError **)error; +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID + packSetName:(NSString *)thePackSetName; +- (NSString *)packSetName; +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; +- (BOOL)packSHA1:(NSString **)packSHA1 forPackedSHA1:(NSString *)packedSHA1 error:(NSError **)error; +@end diff --git a/ArqPackSet.m b/ArqPackSet.m new file mode 100644 index 0000000..3ee789a --- /dev/null +++ b/ArqPackSet.m @@ -0,0 +1,195 @@ +// +// ArqPackSet.m +// Arq +// +// Created by Stefan Reitshamer on 6/22/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "ArqPackSet.h" +#import "S3Service.h" +#import "RegexKitLite.h" +#import "DiskPackIndex.h" +#import "PackIndexEntry.h" +#import "SetNSError.h" +#import "NSErrorCodes.h" +#import "DiskPack.h" +#import "AppKeychain.h" +#import "S3AuthorizationProvider.h" +#import "NSError_extra.h" + +#define MAX_RETRIES (10) + +@interface ArqPackSet (internal) +- (ServerBlob *)newInternalServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; +- (NSDictionary *)doLoadPackIndexEntries:(NSError **)error; +@end + +@implementation ArqPackSet ++ (NSString *)errorDomain { + return @"ArqPackSetErrorDomain"; +} ++ (BOOL)cachePackSetIndexesForS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID error:(NSError **)error { + NSString *accessKeyID; + NSString *secretAccessKey; + if (![AppKeychain accessKeyID:&accessKeyID secretAccessKey:&secretAccessKey error:error]) { + return NO; + } + S3AuthorizationProvider *sap = [[[S3AuthorizationProvider alloc] initWithAccessKey:accessKeyID secretKey:secretAccessKey] autorelease]; + S3Service *theS3 = [[[S3Service alloc] initWithS3AuthorizationProvider:sap useSSL:YES retryOnNetworkError:YES] autorelease]; + NSString *packSetsPrefix = [NSString stringWithFormat:@"/%@/%@/packsets/", theS3BucketName, theComputerUUID]; + NSArray *packPaths = [theS3 pathsWithPrefix:packSetsPrefix error:error]; + if (packPaths == nil) { + return NO; + } + for (NSString *packPath in packPaths) { + NSString *pattern = [NSString stringWithFormat:@"/%@/%@/packsets/([^/]+)/(\\w+)\\.pack", theS3BucketName, theComputerUUID]; + NSRange sha1Range = [packPath rangeOfRegex:pattern capture:2]; + if (sha1Range.location != NSNotFound) { + NSString *packSHA1 = [packPath substringWithRange:sha1Range]; + NSString *thePackSetName = [packPath substringWithRange:[packPath rangeOfRegex:pattern capture:1]]; + DiskPackIndex *dpi = [[[DiskPackIndex alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName packSHA1:packSHA1] autorelease]; + if (![dpi makeLocal:error]) { + return NO; + } + } + } + return YES; +} + +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID + packSetName:(NSString *)thePackSetName { + if (self = [super init]) { + s3 = [theS3 retain]; + s3BucketName = [theS3BucketName retain]; + computerUUID = [theComputerUUID retain]; + packSetName = [thePackSetName retain]; + } + return self; +} +- (void)dealloc { + [s3 release]; + [s3BucketName release]; + [computerUUID release]; + [packSetName release]; + [packIndexEntries release]; + [super dealloc]; +} +- (NSString *)packSetName { + return packSetName; +} +- (BOOL)packSHA1:(NSString **)packSHA1 forPackedSHA1:(NSString *)packedSHA1 error:(NSError **)error { + if (packIndexEntries == nil) { + NSDictionary *entries = [self doLoadPackIndexEntries:error]; + if (entries == nil) { + return NO; + } + packIndexEntries = [entries retain]; + } + *packSHA1 = nil; + PackIndexEntry *pie = [packIndexEntries objectForKey:packedSHA1]; + if (pie != nil) { + *packSHA1 = [pie packSHA1]; + } + return YES; +} +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { + ServerBlob *sb = nil; + NSError *myError = nil; + NSUInteger i = 0; + for (i = 0; i < MAX_RETRIES; i++) { + sb = [self newInternalServerBlobForSHA1:sha1 error:&myError]; + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE]) { + HSLogInfo(@"pack index entry not resolvable; reloading pack index entries from disk cache"); + [packIndexEntries release]; + packIndexEntries = nil; + } else { + break; + } + } else { + break; + } + } + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE]) { + SETNSERROR([ArqPackSet errorDomain], ERROR_NOT_FOUND, @"failed %u times to load blob for sha1 %@ from pack set %@", i, sha1, packSetName); + } else if (error != NULL) { + *error = myError; + } + } + return sb; +} +@end + +@implementation ArqPackSet (internal) +- (ServerBlob *)newInternalServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { + if (packIndexEntries == nil) { + NSDictionary *entries = [self doLoadPackIndexEntries:error]; + if (entries == nil) { + return nil; + } + packIndexEntries = [entries retain]; + } + PackIndexEntry *pie = [packIndexEntries objectForKey:sha1]; + if (pie == nil) { + SETNSERROR([ArqPackSet errorDomain], ERROR_NOT_FOUND, @"sha1 %@ not found in pack set %@", sha1, packSetName); + return NO; + } + DiskPack *diskPack = [[DiskPack alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:[pie packSHA1]]; + ServerBlob *sb = nil; + do { + NSError *myError = nil; + if (![diskPack makeLocal:&myError]) { + NSString *msg = [NSString stringWithFormat:@"error making disk pack %@ (pack set %@, computerUUID %@, s3BucketName %@) containing sha1 %@ local: %@", [pie packSHA1], packSetName, computerUUID, s3BucketName, sha1, [myError localizedDescription]]; + HSLogError(@"%@", msg); + SETNSERROR([ArqPackSet errorDomain], ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE, @"%@", msg); + break; + } + sb = [diskPack newServerBlobForObjectAtOffset:[pie offset] error:&myError]; + if (sb == nil) { + SETNSERROR([ArqPackSet errorDomain], ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE, @"error reading sha1 %@ from disk pack %@ (pack set %@, computerUUID %@, s3BucketName %@): %@", sha1, [pie packSHA1], packSetName, computerUUID, s3BucketName, [myError localizedDescription]); + break; + } + } while(0); + [diskPack release]; + return sb; +} +- (NSDictionary *)doLoadPackIndexEntries:(NSError **)error { + NSMutableDictionary *entries = [NSMutableDictionary dictionary]; + NSString *packSHA1Prefix = [NSString stringWithFormat:@"/%@/%@/packsets/%@/", s3BucketName, computerUUID, packSetName]; + NSArray *packSHA1Paths = [s3 pathsWithPrefix:packSHA1Prefix error:error]; + if (packSHA1Paths == nil) { + return nil; + } + for (NSString *packSHA1Path in packSHA1Paths) { + NSRange sha1Range = [packSHA1Path rangeOfRegex:@"/(\\w+)\\.pack$" capture:1]; + if (sha1Range.location != NSNotFound) { + NSString *packSHA1 = [packSHA1Path substringWithRange:sha1Range]; + BOOL ret = NO; + DiskPackIndex *index = [[DiskPackIndex alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1]; + do { + if (![index makeLocal:error]) { + break; + } + NSArray *pies = [index allPackIndexEntries:error]; + if (pies == nil) { + break; + } + HSLogDebug(@"found %u entries in s3 pack sha1 %@ packset %@ computer %@ s3bucket %@", [pies count], packSHA1, packSetName, computerUUID, s3BucketName); + for (PackIndexEntry *pie in pies) { + [entries setObject:pie forKey:[pie objectSHA1]]; + } + ret = YES; + } while (0); + [index release]; + if (!ret) { + return nil; + } + } + } + return entries; +} +@end diff --git a/ArqRepo.h b/ArqRepo.h new file mode 100644 index 0000000..c41d98f --- /dev/null +++ b/ArqRepo.h @@ -0,0 +1,37 @@ +// +// ArqRepo.h +// Arq +// +// Created by Stefan Reitshamer on 6/23/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class S3Service; +@class ArqFark; +@class ArqPackSet; +@class Commit; +@class Tree; +@class ServerBlob; + +@interface ArqRepo : NSObject { + NSString *bucketUUID; + NSString *encryptionKey; + ArqFark *arqFark; + ArqPackSet *treesPackSet; + ArqPackSet *blobsPackSet; +} ++ (NSString *)errorDomain; +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID + bucketUUID:(NSString *)theBucketUUID + encryptionKey:(NSString *)theEncryptionKey; + +- (NSString *)headSHA1:(NSError **)error; +- (Commit *)commitForSHA1:(NSString *)theSHA1 error:(NSError **)error; +- (Tree *)treeForSHA1:(NSString *)theSHA1 error:(NSError **)error; +- (NSData *)blobDataForSHA1:(NSString *)sha1 error:(NSError **)error; +- (NSData *)blobDataForSHA1s:(NSArray *)sha1s error:(NSError **)error; +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; +@end diff --git a/ArqRepo.m b/ArqRepo.m new file mode 100644 index 0000000..8660aca --- /dev/null +++ b/ArqRepo.m @@ -0,0 +1,180 @@ +// +// ArqRepo.m +// Arq +// +// Created by Stefan Reitshamer on 6/23/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "ArqRepo.h" +#import "ArqFark.h" +#import "ArqPackSet.h" +#import "Commit.h" +#import "Tree.h" +#import "NSErrorCodes.h" +#import "ServerBlob.h" +#import "DataInputStream.h" +#import "DecryptedInputStream.h" +#import "NSData-Encrypt.h" +#import "SetNSError.h" +#import "NSError_extra.h" + +@implementation ArqRepo ++ (NSString *)errorDomain { + return @"ArqRepoErrorDomain"; +} +- (id)initWithS3Service:(S3Service *)theS3 + s3BucketName:(NSString *)theS3BucketName + computerUUID:(NSString *)theComputerUUID + bucketUUID:(NSString *)theBucketUUID + encryptionKey:(NSString *)theEncryptionKey { + if (self = [super init]) { + bucketUUID = [theBucketUUID retain]; + encryptionKey = [theEncryptionKey retain]; + arqFark = [[ArqFark alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID]; + treesPackSet = [[ArqPackSet alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:[theBucketUUID stringByAppendingString:@"-trees"]]; + blobsPackSet = [[ArqPackSet alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:[theBucketUUID stringByAppendingString:@"-blobs"]]; + } + return self; +} +- (void)dealloc { + [bucketUUID release]; + [encryptionKey release]; + [arqFark release]; + [treesPackSet release]; + [blobsPackSet release]; + [super dealloc]; +} +- (NSString *)headSHA1:(NSError **)error { + NSString *bucketDataPath = [NSString stringWithFormat:@"/%@/refs/heads/master", bucketUUID]; + NSError *myError = nil; + NSData *data = [arqFark bucketDataForPath:bucketDataPath error:&myError]; + if (data == nil) { + if ([myError isErrorWithDomain:[ArqFark errorDomain] code:ERROR_NOT_FOUND]) { + SETNSERROR([ArqRepo errorDomain], ERROR_NOT_FOUND, @"no head for bucketUUID %@", bucketUUID); + } else { + if (error != NULL) { + *error = myError; + } + } + return nil; + } + return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; +} +- (Commit *)commitForSHA1:(NSString *)theSHA1 error:(NSError **)error { + NSError *myError = nil; + ServerBlob *sb = [treesPackSet newServerBlobForSHA1:theSHA1 error:&myError]; + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_NOT_FOUND]) { + HSLogDebug(@"commit %@ not found in pack set %@", theSHA1, [treesPackSet packSetName]); + SETNSERROR([ArqRepo errorDomain], ERROR_NOT_FOUND, @"commit not found for sha1 %@", theSHA1); + } else { + HSLogError(@"commit not found for %@: %@", theSHA1, [myError localizedDescription]); + if (error != NULL) { + *error = myError; + } + } + return nil; + } + NSData *data = [[sb slurp:error] decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; + [sb release]; + if (data == nil) { + return nil; + } + DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; + Commit *commit = [[[Commit alloc] initWithBufferedInputStream:dis error:error] autorelease]; + [dis release]; + return commit; +} + +// Returns NO if commit not found: +- (Tree *)treeForSHA1:(NSString *)theSHA1 error:(NSError **)error { + NSError *myError = nil; + ServerBlob *sb = [treesPackSet newServerBlobForSHA1:theSHA1 error:error]; + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_NOT_FOUND]) { + HSLogDebug(@"tree %@ not found in pack set %@", theSHA1, [treesPackSet packSetName]); + SETNSERROR([ArqRepo errorDomain], ERROR_NOT_FOUND, @"commit not found for sha1 %@", theSHA1); + } else { + HSLogError(@"tree not found for %@: %@", theSHA1, [myError localizedDescription]); + if (error != NULL) { + *error = myError; + } + } + return nil; + } + NSData *data = [[sb slurp:error] decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; + [sb release]; + if (data == nil) { + return nil; + } + DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; + Tree *tree = [[[Tree alloc] initWithBufferedInputStream:dis error:error] autorelease]; + [dis release]; + return tree; +} +- (NSData *)blobDataForSHA1:(NSString *)sha1 error:(NSError **)error { + ServerBlob *sb = [self newServerBlobForSHA1:sha1 error:error]; + if (sb == nil) { + return nil; + } + NSData *data = [sb slurp:error]; + [sb release]; + return data; +} +- (NSData *)blobDataForSHA1s:(NSArray *)sha1s error:(NSError **)error { + //FIXME: This is very inefficient! + NSMutableData *ret = [NSMutableData data]; + for (NSString *sha1 in sha1s) { + NSData *data = [self blobDataForSHA1:sha1 error:error]; + if (data == nil) { + return nil; + } + [ret appendData:data]; + } + return ret; +} +- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { + NSError *myError = nil; + ServerBlob *sb = [blobsPackSet newServerBlobForSHA1:sha1 error:&myError]; + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_NOT_FOUND]) { + HSLogTrace(@"sha1 %@ not found in pack set %@; looking in S3", sha1, [blobsPackSet packSetName]); + sb = [arqFark newServerBlobForSHA1:sha1 error:&myError]; + if (sb == nil) { + if ([myError isErrorWithDomain:[ArqFark errorDomain] code:ERROR_NOT_FOUND]) { + SETNSERROR([ArqRepo errorDomain], ERROR_NOT_FOUND, @"sha1 %@ not found", sha1); + } else { + if (error != NULL) { + *error = myError; + } + } + } + } else { + HSLogError(@"error trying to read from pack set: %@", [myError localizedDescription]); + if (error != NULL) { + *error = myError; + } + } + } + if (sb != nil) { + id is = [sb newInputStream]; + NSString *mimeType = [sb mimeType]; + NSString *downloadName = [sb downloadName]; + [sb autorelease]; + sb = nil; + DecryptedInputStream *dis = [[DecryptedInputStream alloc] initWithInputStream:is cipherName:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; + [is release]; + if (dis != nil) { + sb = [[ServerBlob alloc] initWithInputStream:dis mimeType:mimeType downloadName:downloadName]; + [dis release]; + } + } + return sb; +} + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", bucketUUID]; +} +@end diff --git a/ArqRepo_Verifier.h b/ArqRepo_Verifier.h new file mode 100644 index 0000000..ce27bd6 --- /dev/null +++ b/ArqRepo_Verifier.h @@ -0,0 +1,15 @@ +// +// ArqRepo_Verifier.h +// arq_restore +// +// Created by Stefan Reitshamer on 8/19/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +#import "ArqRepo.h" + +@interface ArqRepo (Verifier) +- (NSString *)blobsPackSetName; +- (BOOL)packSHA1:(NSString **)packSHA1 forPackedBlobSHA1:(NSString *)sha1 error:(NSError **)error; +@end diff --git a/ArqRepo_Verifier.m b/ArqRepo_Verifier.m new file mode 100644 index 0000000..036d935 --- /dev/null +++ b/ArqRepo_Verifier.m @@ -0,0 +1,19 @@ +// +// ArqRepo_Verifier.m +// arq_restore +// +// Created by Stefan Reitshamer on 8/19/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "ArqRepo_Verifier.h" +#import "ArqPackSet.h" + +@implementation ArqRepo (Verifier) +- (NSString *)blobsPackSetName { + return [blobsPackSet packSetName]; +} +- (BOOL)packSHA1:(NSString **)packSHA1 forPackedBlobSHA1:(NSString *)sha1 error:(NSError **)error { + return [blobsPackSet packSHA1:packSHA1 forPackedSHA1:sha1 error:error]; +} +@end diff --git a/ArqRestoreCommand.m b/ArqRestoreCommand.m index 8600382..f295bde 100644 --- a/ArqRestoreCommand.m +++ b/ArqRestoreCommand.m @@ -39,7 +39,8 @@ #import "ArqFolder.h" #import "HTTP.h" #import "Restorer.h" - +#import "NSErrorCodes.h" +#import "NSError_extra.h" @interface ArqRestoreCommand (internal) - (BOOL)printArqFolders:(NSError **)error; @@ -123,7 +124,7 @@ 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) { + if ([myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) { // Skip. } else { if (error != NULL) { diff --git a/ArqVerifyCommand.m b/ArqVerifyCommand.m index 4d8d516..6f93ac2 100644 --- a/ArqVerifyCommand.m +++ b/ArqVerifyCommand.m @@ -12,7 +12,8 @@ #import "HTTP.h" #import "RegexKitLite.h" #import "BucketVerifier.h" -#import "PackSet.h" +#import "NSError_extra.h" +#import "NSErrorCodes.h" @interface ArqVerifyCommand (internal) - (NSArray *)objectSHA1sForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID error:(NSError **)error; @@ -64,7 +65,7 @@ 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) { + if ([myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) { // Skip. printf("no computer UUIDs found in bucket %s\n", [s3BucketName UTF8String]); return YES; diff --git a/Commit.m b/Commit.m index 9da6397..21f1942 100644 --- a/Commit.m +++ b/Commit.m @@ -34,7 +34,6 @@ #import "DateIO.h" #import "StringIO.h" #import "Commit.h" -#import "Blob.h" #import "DataInputStream.h" #import "RegexKitLite.h" #import "SetNSError.h" diff --git a/DiskPack.m b/DiskPack.m index 5e93103..abff2ea 100644 --- a/DiskPack.m +++ b/DiskPack.m @@ -36,7 +36,6 @@ #import "FDInputStream.h" #import "StringIO.h" #import "IntegerIO.h" -#import "PackSetSet.h" #import "ServerBlob.h" #import "NSFileManager_extra.h" #import "S3Service.h" @@ -44,12 +43,11 @@ #import "FileInputStream.h" #import "FileOutputStream.h" #import "Streams.h" -#import "PackSet.h" #import "S3ObjectReceiver.h" #import "S3ObjectMetadata.h" #import "PackIndexEntry.h" #import "SHA1Hash.h" - +#import "ArqUserLibrary.h" @interface DiskPack (internal) - (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error; @@ -58,10 +56,10 @@ @implementation DiskPack + (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { - return [NSString stringWithFormat:@"%@/%@.pack", [PackSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName], thePackSHA1]; + return [NSString stringWithFormat:@"/%@/%@/packsets/%@/%@.pack", theS3BucketName, theComputerUUID, thePackSetName, thePackSHA1]; } + (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { - return [NSString stringWithFormat:@"%@/%@/%@.pack", [PackSet localPathWithComputerUUID:theComputerUUID packSetName:thePackSetName], [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]]; + return [NSString stringWithFormat:@"%@/%@/packsets/%@/%@/%@.pack", [ArqUserLibrary arqCachesPath], theComputerUUID, thePackSetName, [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]]; } - (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { if (self = [super init]) { @@ -90,8 +88,14 @@ BOOL ret = NO; if (![fm fileExistsAtPath:localPath]) { HSLogDebug(@"packset %@: making pack %@ local", packSetName, packSHA1); - ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:error]; - if (sb != nil) { + NSError *myError = nil; + ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:&myError]; + if (sb == nil) { + HSLogError(@"error getting S3 pack %@: %@", s3Path, [myError localizedDescription]); + if (error != NULL) { + *error = myError; + } + } else { unsigned long long bytesWritten; ret = [self savePack:sb bytesWritten:&bytesWritten error:error]; [sb release]; @@ -148,25 +152,12 @@ *length = st.st_size; return YES; } -- (BOOL)copyToPath:(NSString *)dest error:(NSError **)error { - NSFileManager *fm = [NSFileManager defaultManager]; - if ([fm fileExistsAtPath:dest] && ![fm removeItemAtPath:dest error:error]) { - HSLogError(@"error removing old mutable pack at %@", dest); - return NO; - } - if (![fm ensureParentPathExistsForPath:dest error:error] || ![fm copyItemAtPath:localPath toPath:dest error:error]) { - HSLogError(@"error copying pack %@ to %@", localPath, dest); - return NO; - } - HSLogDebug(@"copied %@ to %@", localPath, dest); - return YES; -} - (NSArray *)sortedPackIndexEntries:(NSError **)error { unsigned long long length; if (![self fileLength:&length error:error]) { return NO; } - FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath length:length]; + FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath offset:0 length:length]; NSArray *ret = [self sortedPackIndexEntriesFromStream:fis error:error]; [fis release]; return ret; diff --git a/DiskPackIndex.m b/DiskPackIndex.m index a10e1d2..241d42f 100644 --- a/DiskPackIndex.m +++ b/DiskPackIndex.m @@ -40,18 +40,16 @@ #import "PackIndexEntry.h" #import "NSErrorCodes.h" #import "S3Service.h" -#import "PackSetSet.h" #import "FileOutputStream.h" #import "Streams.h" #import "NSFileManager_extra.h" #import "ServerBlob.h" -#import "PackSet.h" #import "S3ObjectReceiver.h" #import "DiskPack.h" #import "BlobACL.h" #import "FileInputStreamFactory.h" +#import "PackIndexWriter.h" #import "ArqUserLibrary.h" -#import "Blob.h" typedef struct index_object { uint64_t nbo_offset; @@ -76,10 +74,10 @@ typedef struct pack_index { @implementation DiskPackIndex + (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { - return [NSString stringWithFormat:@"%@/%@.index", [PackSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName], thePackSHA1]; + return [NSString stringWithFormat:@"/%@/%@/packsets/%@/%@.index", theS3BucketName, theComputerUUID, thePackSetName, thePackSHA1]; } + (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { - return [NSString stringWithFormat:@"%@/%@/%@.index", [PackSet localPathWithComputerUUID:theComputerUUID packSetName:thePackSetName], [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]]; + return [NSString stringWithFormat:@"%@/%@/packsets/%@/%@/%@.index", [ArqUserLibrary arqCachesPath], theComputerUUID, thePackSetName, [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]]; } - (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 { if (self = [super init]) { @@ -109,12 +107,12 @@ typedef struct pack_index { if (![fm fileExistsAtPath:localPath]) { HSLogDebug(@"packset %@: making pack index %@ local", packSetName, packSHA1); ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:error]; - if (sb != nil) { + if (sb == nil) { + ret = NO; + } else { unsigned long long bytesWritten; ret = [self savePackIndex:sb bytesWritten:&bytesWritten error:error]; [sb release]; - } else { - HSLogError(@"failed to read pack index from S3 path %@", s3Path); } } else { ret = YES; @@ -143,12 +141,13 @@ typedef struct pack_index { uint32_t count = OSSwapBigToHostInt32(the_pack_index->nbo_fanout[255]); index_object *indexObjects = &(the_pack_index->first_index_object); for (uint32_t i = 0; i < count; i++) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; uint64_t offset = OSSwapBigToHostInt64(indexObjects[i].nbo_offset); uint64_t dataLength = OSSwapBigToHostInt64(indexObjects[i].nbo_datalength); NSString *objectSHA1 = [NSString hexStringWithBytes:indexObjects[i].sha1 length:20]; - PackIndexEntry *pie = [[PackIndexEntry alloc] initWithPackSHA1:packSHA1 offset:offset dataLength:dataLength objectSHA1:objectSHA1]; + PackIndexEntry *pie = [[[PackIndexEntry alloc] initWithPackSHA1:packSHA1 offset:offset dataLength:dataLength objectSHA1:objectSHA1] autorelease]; [ret addObject:pie]; - [pie release]; + [pool drain]; } if (munmap(the_pack_index, st.st_size) == -1) { HSLogError(@"munmap: %s", strerror(errno)); diff --git a/FarkPath.h b/FarkPath.h new file mode 100644 index 0000000..0cd41d9 --- /dev/null +++ b/FarkPath.h @@ -0,0 +1,17 @@ +// +// FarkPath.h +// Arq +// +// Created by Stefan Reitshamer on 6/29/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface FarkPath : NSObject { + +} ++ (NSString *)s3PathForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID sha1:(NSString *)sha1; ++ (NSString *)s3PathForBucketDataPath:(NSString *)bucketDataPath s3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID; +@end diff --git a/FarkPath.m b/FarkPath.m new file mode 100644 index 0000000..9be0fa3 --- /dev/null +++ b/FarkPath.m @@ -0,0 +1,19 @@ +// +// FarkPath.m +// Arq +// +// Created by Stefan Reitshamer on 6/29/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "FarkPath.h" + + +@implementation FarkPath ++ (NSString *)s3PathForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID sha1:(NSString *)sha1 { + return [NSString stringWithFormat:@"/%@/%@/objects/%@", s3BucketName, computerUUID, sha1]; +} ++ (NSString *)s3PathForBucketDataPath:(NSString *)bucketDataPath s3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID { + return [[NSString stringWithFormat:@"/%@/%@/bucketdata", s3BucketName, computerUUID] stringByAppendingPathComponent:bucketDataPath]; +} +@end diff --git a/FileAttributes.m b/FileAttributes.m index 76c9ac3..f28e4b5 100644 --- a/FileAttributes.m +++ b/FileAttributes.m @@ -114,7 +114,7 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir cPath = [path fileSystemRepresentation]; memcpy(&st, theStat, sizeof(st)); targetExists = YES; - if ((st.st_mode & S_IFLNK) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { struct stat targetSt; int ret = stat(cPath, &targetSt); if (ret == -1 && errno == ENOENT) { @@ -125,7 +125,7 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir FSRef fsRef; Boolean isDirectory; OSStatus oss = 0; - if ((st.st_mode & S_IFLNK) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); } else { oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); @@ -254,22 +254,22 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir return finderFileCreator; } - (BOOL)isExtensionHidden { - return (st.st_flags & UF_HIDDEN) == UF_HIDDEN; + return st.st_flags & UF_HIDDEN; } - (BOOL)isFifo { - return (st.st_mode & S_IFIFO) == S_IFIFO; + return S_ISFIFO(st.st_mode); } - (BOOL)isDevice { - return (st.st_mode & S_IFCHR) == S_IFCHR || (st.st_mode & S_IFBLK) == S_IFBLK; + return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode); } - (BOOL)isSymbolicLink { - return (st.st_mode & S_IFLNK) == S_IFLNK; + return S_ISLNK(st.st_mode); } - (BOOL)isRegularFile { - return (st.st_mode & S_IFREG) == S_IFREG; + return S_ISREG(st.st_mode); } - (BOOL)isSocket { - return (st.st_mode & S_IFSOCK) == S_IFSOCK; + return S_ISSOCK(st.st_mode); } - (int)st_dev { return st.st_dev; @@ -311,7 +311,7 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir FSRef fsRef; Boolean isDirectory; OSStatus oss = 0; - if ((st.st_mode & S_IFLNK) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); } else { oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); @@ -391,7 +391,7 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir FSRef fsRef; Boolean isDirectory; OSStatus oss = 0; - if ((st.st_mode & S_IFLNK) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); } else { oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); @@ -432,7 +432,7 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir FSRef fsRef; Boolean isDirectory; OSStatus oss = 0; - if ((st.st_mode & S_IFLNK) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { oss = SymlinkPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); } else { oss = FSPathMakeRef((UInt8*)cPath, &fsRef, &isDirectory); @@ -485,11 +485,12 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir } - (BOOL)applyUID:(int)uid gid:(int)gid error:(NSError **)error { if (uid != st.st_uid || gid != st.st_gid) { - HSLogTrace(@"lchown(%s, %d, %d)", cPath, uid, gid); if (lchown(cPath, uid, gid) == -1) { + HSLogError(@"lchown failed"); SETNSERROR(@"UnixErrorDomain", errno, @"lchown: %s", strerror(errno)); return NO; } + HSLogDebug(@"lchown(%s, %d, %d); euid=%d", cPath, uid, gid, geteuid()); st.st_uid = uid; st.st_gid = gid; } @@ -497,26 +498,26 @@ static OSStatus SymlinkPathMakeRef(const UInt8 *path, FSRef *ref, Boolean *isDir } - (BOOL)applyMode:(int)mode error:(NSError **)error { if (mode != st.st_mode) { - if ((st.st_mode & S_IFDIR) == S_IFDIR) { - HSLogTrace(@"chmod(%s, %d)", cPath, mode); + if (S_ISDIR(st.st_mode)) { int ret = chmod(cPath, mode); if (ret == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"Setting permissions on %@: %s", path, strerror(errno)); return NO; } + HSLogDebug(@"chmod(%s, 0%6o)", cPath, mode); } else { int fd = open(cPath, O_RDWR|O_SYMLINK); if (fd == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno)); return NO; } - HSLogTrace(@"fchmod symlink (%s, %d)", cPath, mode); int ret = fchmod(fd, mode); close(fd); if (ret == -1) { SETNSERROR(@"UnixErrorDomain", errno, @"Setting permissions on %@: %s", path, strerror(errno)); return NO; } + HSLogDebug(@"fchmod(%s, 0%6o)", cPath, mode); } st.st_mode = mode; } diff --git a/Node.h b/Node.h index e5883e7..cea1d1a 100644 --- a/Node.h +++ b/Node.h @@ -31,10 +31,7 @@ */ #import -@class MutableS3Repo; -#import "InputStream.h" -#import "InputStreamFactory.h" -#import "OutputStream.h" +@protocol InputStream; @interface Node : NSObject { int treeVersion; diff --git a/Node.m b/Node.m index 982fd1f..431aa24 100644 --- a/Node.m +++ b/Node.m @@ -31,14 +31,11 @@ */ #include +#import "Node.h" #import "BooleanIO.h" #import "IntegerIO.h" #import "StringIO.h" -#import "Node.h" -#import "InputStream.h" -#import "Blob.h" -#import "SetNSError.h" -#import "Tree.h" +#import "BufferedInputStream.h" @implementation Node @synthesize isTree, dataSize, thumbnailSHA1, previewSHA1, xattrsSHA1, xattrsSize, aclSHA1, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, finderFileType, finderFileCreator, isFileExtensionHidden, treeVersion, st_rdev; @@ -117,7 +114,16 @@ [finderFileCreator release]; [super dealloc]; } - +- (NSString *)treeSHA1 { + NSAssert(isTree, @"must be a Tree"); + return [dataSHA1s objectAtIndex:0]; +} +- (NSArray *)dataSHA1s { + return dataSHA1s; +} +- (BOOL)dataMatchesStatData:(struct stat *)st { + return (st->st_mtimespec.tv_sec == mtime_sec && st->st_mtimespec.tv_nsec == mtime_nsec && st->st_size == dataSize); +} - (void)writeToData:(NSMutableData *)data { [BooleanIO write:isTree to:data]; [IntegerIO writeInt32:(int32_t)[dataSHA1s count] to:data]; @@ -152,14 +158,4 @@ [IntegerIO writeInt64:st_blocks to:data]; [IntegerIO writeUInt32:st_blksize to:data]; } -- (BOOL)dataMatchesStatData:(struct stat *)st { - return (st->st_mtimespec.tv_sec == mtime_sec && st->st_mtimespec.tv_nsec == mtime_nsec & st->st_size == dataSize); -} -- (NSString *)treeSHA1 { - NSAssert(isTree, @"must be a Tree"); - return [dataSHA1s objectAtIndex:0]; -} -- (NSArray *)dataSHA1s { - return dataSHA1s; -} @end diff --git a/PackIndexEntry.m b/PackIndexEntry.m index 6e1fc74..2c478b6 100644 --- a/PackIndexEntry.m +++ b/PackIndexEntry.m @@ -60,4 +60,9 @@ - (NSString *)objectSHA1 { return objectSHA1; } + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", packSHA1, offset, dataLength, objectSHA1]; +} @end diff --git a/PackIndexWriter.h b/PackIndexWriter.h new file mode 100644 index 0000000..5f1c320 --- /dev/null +++ b/PackIndexWriter.h @@ -0,0 +1,18 @@ +// +// PackIndexWriter.h +// Arq +// +// Created by Stefan Reitshamer on 3/3/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@class DiskPack; + +@interface PackIndexWriter : NSObject { + DiskPack *diskPack; + NSString *destination; +} +- (id)initWithPack:(DiskPack *)theDiskPack destination:(NSString *)theDestination; +- (BOOL)writeIndex:(NSError **)error; +@end diff --git a/PackIndexWriter.m b/PackIndexWriter.m new file mode 100644 index 0000000..70bc90d --- /dev/null +++ b/PackIndexWriter.m @@ -0,0 +1,103 @@ +// +// PackIndexWriter.m +// Arq +// +// Created by Stefan Reitshamer on 3/3/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "PackIndexWriter.h" +#import "DiskPack.h" +#import "FileInputStream.h" +#import "FileOutputStream.h" +#import "IntegerIO.h" +#import "StringIO.h" +#import "SetNSError.h" +#import "SHA1Hash.h" +#import "NSString_extra.h" +#import "PackIndexEntry.h" + +@interface PackIndexWriter (internal) +- (BOOL)writeEntries:(NSArray *)entries toStream:(id )os error:(NSError **)error; +@end + +@implementation PackIndexWriter +- (id)initWithPack:(DiskPack *)theDiskPack destination:(NSString *)theDestination { + if (self = [super init]) { + diskPack = [theDiskPack retain]; + destination = [theDestination copy]; + } + return self; +} +- (void)dealloc { + [diskPack release]; + [destination release]; + [super dealloc]; +} +- (BOOL)writeIndex:(NSError **)error { + NSArray *entries = [diskPack sortedPackIndexEntries:error]; + if (entries == nil) { + return NO; + } + FileOutputStream *fos = [[FileOutputStream alloc] initWithPath:destination append:NO]; + BOOL ret = [self writeEntries:entries toStream:fos error:error]; + [fos release]; + if (!ret) { + return NO; + } + NSString *indexSHA1 = [SHA1Hash hashFile:destination error:error]; + NSData *sha1Data = [indexSHA1 hexStringToData]; + fos = [[FileOutputStream alloc] initWithPath:destination append:YES]; + ret = [fos write:[sha1Data bytes] length:[sha1Data length] error:error]; + [fos release]; + return ret; +} +@end + +@implementation PackIndexWriter (internal) +- (BOOL)writeEntries:(NSArray *)entries toStream:(id )os error:(NSError **)error { + // Write header to index. + if (![IntegerIO writeUInt32:0xff744f63 to:os error:error]) { // Magic number. + return NO; + } + if (![IntegerIO writeUInt32:0x00000002 to:os error:error]) { // Version 2. + return NO; + } + unsigned int firstByte = 0; + NSUInteger index = 0; + for (index = 0; index < [entries count]; index++) { + PackIndexEntry *pie = [entries objectAtIndex:index]; + NSData *sha1Hex = [[pie objectSHA1] hexStringToData]; + unsigned char myFirstByte = ((unsigned char *)[sha1Hex bytes])[0]; + while ((unsigned int)myFirstByte > firstByte) { + if (![IntegerIO writeUInt32:index to:os error:error]) { + return NO; + } + firstByte++; + } + } + while (firstByte <= 0xff) { + if (![IntegerIO writeUInt32:index to:os error:error]) { + return NO; + } + firstByte++; + } + for (index = 0; index < [entries count]; index++) { + PackIndexEntry *pie = [entries objectAtIndex:index]; + if (![IntegerIO writeUInt64:[pie offset] to:os error:error] + || ![IntegerIO writeUInt64:[pie dataLength] to:os error:error]) { + return NO; + } + // Write sha1 to index. + NSData *sha1Data = [[pie objectSHA1] hexStringToData]; + if (![os write:[sha1Data bytes] length:[sha1Data length] error:error]) { + break; + } + // Write 4 bytes (for alignment) to index. + if (![IntegerIO writeUInt32:0 to:os error:error]) { + return NO; + } + } + return YES; +} +@end diff --git a/PackSet.m b/PackSet.m deleted file mode 100644 index dfe88a6..0000000 --- a/PackSet.m +++ /dev/null @@ -1,207 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import "PackSet.h" -#import "S3Service.h" -#import "SHA1Hash.h" -#import "S3ObjectReceiver.h" -#import "SetNSError.h" -#import "DataInputStream.h" -#import "PackSetSet.h" -#import "NSData-InputStream.h" -#import "ServerBlob.h" -#import "NSErrorCodes.h" -#import "DiskPackIndex.h" -#import "PackIndexEntry.h" -#import "DiskPack.h" -#import "RegexKitLite.h" -#import "HTTP.h" - -static unsigned long long DEFAULT_MAX_PACK_FILE_SIZE_MB = 10; -static unsigned long long DEFAULT_MAX_PACK_ITEM_SIZE_BYTES = 65536; -static double DEFAULT_MAX_REUSABLE_PACK_FILE_SIZE_FRACTION = 0.6; - - - -@interface PackSet (internal) -+ (unsigned long long)maxReusablePackFileSizeBytes; -- (BOOL)loadPackIndexEntries:(NSString *)packSHA1 totalDataSize:(unsigned long long *)totalDataSize error:(NSError **)error; -- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 error:(NSError **)error; -- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 inPackSHA1:(NSString *)packSHA1 error:(NSError **)error; -@end - -@implementation PackSet -+ (NSString *)errorDomain { - return @"PackSetErrorDomain"; -} -+ (unsigned long long)maxPackFileSizeMB { - return DEFAULT_MAX_PACK_FILE_SIZE_MB; -} -+ (unsigned long long)maxPackItemSizeBytes { - return DEFAULT_MAX_PACK_ITEM_SIZE_BYTES; -} -+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName { - return [NSString stringWithFormat:@"%@/%@", [PackSetSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID], [thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; -} -+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName { - return [NSString stringWithFormat:@"%@/%@", [PackSetSet localPathWithComputerUUID:theComputerUUID], [thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; -} -- (id)initWithName:(NSString *)thePackSetName - s3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID - keepPacksLocal:(BOOL)isKeepPacksLocal - packSHA1s:(NSArray *)thePackSHA1s - error:(NSError **)error { - if (self = [super init]) { - packSetName = [thePackSetName copy]; - s3 = [theS3 retain]; - s3BucketName = [theS3BucketName copy]; - computerUUID = [theComputerUUID copy]; - keepPacksLocal = isKeepPacksLocal; - escapedPackSetName = [[thePackSetName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] copy]; - packSetDir = [[PackSet localPathWithComputerUUID:theComputerUUID packSetName:packSetName] retain]; - packSHA1s = [[NSMutableSet alloc] initWithArray:thePackSHA1s]; - packIndexEntries = [[NSMutableDictionary alloc] init]; - for (NSString *packSHA1 in packSHA1s) { - unsigned long long totalDataSize = 0; - if (![self loadPackIndexEntries:packSHA1 totalDataSize:&totalDataSize error:error]) { - [self release]; - return nil; - } - if (totalDataSize < [PackSet maxReusablePackFileSizeBytes] && currentPackSHA1 == nil) { - currentPackSHA1 = [packSHA1 copy]; - } - } - } - return self; -} -- (void)dealloc { - [packSetName release]; - [escapedPackSetName release]; - [packSetDir release]; - [s3 release]; - [s3BucketName release]; - [computerUUID release]; - [packSHA1s release]; - [packIndexEntries release]; - [currentPackSHA1 release]; - [super dealloc]; -} -- (NSString *)name { - return packSetName; -} -- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { - HSLogTrace(@"packset %@ looking for SHA1 %@", packSetName, sha1); - NSError *myError = nil; - PackIndexEntry *entry = [self packIndexEntryForObjectSHA1:sha1 error:&myError]; - if (entry == nil && [myError code] != ERROR_NOT_FOUND) { - if (error != NULL) { - *error = myError; - } - HSLogError(@"error reading pack index entry for %@ from pack set %@: %@", sha1, packSetName, [myError localizedDescription]); - return nil; - } - if (entry != nil) { - NSError *myError = nil; - DiskPack *diskPack = [[DiskPack alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:[entry packSHA1]]; - if (![diskPack makeLocal:&myError]) { - [diskPack release]; - if ([[myError domain] isEqualToString:[S3Service errorDomain]] && [myError code] == HTTP_NOT_FOUND) { - SETNSERROR(@"PackSetErrorDomain", ERROR_NOT_FOUND, @"pack %@ not found in S3: %@", [entry packSHA1], [myError localizedDescription]); - } else if (error != NULL) { - *error = myError; - } - return nil; - } - ServerBlob *sb = [diskPack newServerBlobForObjectAtOffset:[entry offset] error:error]; - [diskPack release]; - return sb; - } - SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"sha1 %@ not found", sha1); - return nil; -} -- (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) -+ (unsigned long long)maxReusablePackFileSizeBytes { - return (unsigned long long)((double)([PackSet maxPackFileSizeMB] * 1000000) * DEFAULT_MAX_REUSABLE_PACK_FILE_SIZE_FRACTION); -} -- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 error:(NSError **)error { - PackIndexEntry *pie = [packIndexEntries objectForKey:objectSHA1]; - if (pie == nil) { - SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"pie not found for %@", objectSHA1); - } - return pie; -} -- (PackIndexEntry *)packIndexEntryForObjectSHA1:(NSString *)objectSHA1 inPackSHA1:(NSString *)packSHA1 error:(NSError **)error { - PackIndexEntry *pie = [packIndexEntries objectForKey:objectSHA1]; - if (pie == nil) { - SETNSERROR(@"PackErrorDomain", ERROR_NOT_FOUND, @"pie not found for %@", objectSHA1); - } - return pie; -} -- (BOOL)loadPackIndexEntries:(NSString *)packSHA1 totalDataSize:(unsigned long long *)totalDataSize error:(NSError **)error { - *totalDataSize = 0; - DiskPackIndex *index = [[DiskPackIndex alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID packSetName:packSetName packSHA1:packSHA1]; - BOOL ret = NO; - do { - if (![index makeLocal:error]) { - break; - } - NSArray *pies = [index allPackIndexEntries:error]; - if (pies == nil) { - break; - } - for (PackIndexEntry *pie in pies) { - [packIndexEntries setObject:pie forKey:[pie objectSHA1]]; - unsigned long long dataEndOffset = [pie offset] + [pie dataLength]; - if (dataEndOffset > *totalDataSize) { - *totalDataSize = dataEndOffset; - } - } - ret = YES; - } while (0); - [index release]; - return ret; -} -@end diff --git a/PackSetSet.h b/PackSetSet.h deleted file mode 100644 index e180f40..0000000 --- a/PackSetSet.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import -@class ServerBlob; -@class S3Service; -@class Blob; - -@interface PackSetSet : NSObject { - S3Service *s3; - NSString *s3BucketName; - NSString *computerUUID; - NSMutableDictionary *packSets; -} -+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID; -+ (NSString *)localPathWithComputerUUID:(NSString *)computerUUID; -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - 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. -- (NSArray *)resetFromS3:(NSError **)error; -@end diff --git a/PackSetSet.m b/PackSetSet.m deleted file mode 100644 index 4e48d5f..0000000 --- a/PackSetSet.m +++ /dev/null @@ -1,237 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import "PackSetSet.h" -#import "PackSet.h" -#import "SetNSError.h" -#import "NSErrorCodes.h" -#import "S3Service.h" -#import "ArqUserLibrary.h" -#import "RegexKitLite.h" - -@interface PackSetSet (internal) -- (NSDictionary *)packSHA1sByPackSetNameFromS3:(NSError **)error; -- (NSMutableSet *)diskPackSetNames:(NSError **)error; -- (PackSet *)packSetForName:(NSString *)packSetName error:(NSError **)error; -- (NSArray *)cachedPackSHA1sForPackSet:(NSString *)packSetName error:(NSError **)error; -@end - -@implementation PackSetSet -+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID { - return [NSString stringWithFormat:@"/%@/%@/packsets", theS3BucketName, theComputerUUID]; -} -+ (NSString *)localPathWithComputerUUID:(NSString *)computerUUID { - return [NSString stringWithFormat:@"%@/%@/packsets", [ArqUserLibrary arqCachesPath], computerUUID]; -} - -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID { - if (self = [super init]) { - s3 = [theS3 retain]; - s3BucketName = [theS3BucketName copy]; - computerUUID = [theComputerUUID copy]; - packSets = [[NSMutableDictionary alloc] init]; - } - return self; -} -- (void)dealloc { - [s3 release]; - [s3BucketName release]; - [computerUUID release]; - [packSets release]; - [super dealloc]; -} -- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName error:(NSError **)error { - PackSet *packSet = [self packSetForName:packSetName error:error]; - if (packSet == nil) { - return nil; - } - return [packSet newServerBlobForSHA1:sha1 error:error]; -} -- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName { - 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]; -} -- (NSArray *)resetFromS3:(NSError **)error { - HSLogDebug(@"resetting pack sets from S3"); - [packSets removeAllObjects]; - NSDictionary *s3PackSHA1sByPackSetName = [self packSHA1sByPackSetNameFromS3:error]; - if (s3PackSHA1sByPackSetName == nil) { - return nil; - } - - // - // Remove disk pack sets that don't exist in S3. - // - NSMutableSet *diskPackSetNames = [self diskPackSetNames:error]; - if (diskPackSetNames == nil) { - return nil; - } - NSMutableSet *s3PackSetNames = [NSMutableSet setWithArray:[s3PackSHA1sByPackSetName allKeys]]; - [diskPackSetNames minusSet:s3PackSetNames]; - for (NSString *bogusDiskPackSetName in diskPackSetNames) { - NSString *packSetPath = [PackSet localPathWithComputerUUID:computerUUID packSetName:bogusDiskPackSetName]; - HSLogDebug(@"removing local pack set that doesn't exist in S3: %@", packSetPath); - if (![[NSFileManager defaultManager] removeItemAtPath:packSetPath error:error]) { - return nil; - } - } - - // - // Create PackSets, make index files local, and load PackIndexEntries into memory. - // - for (NSString *s3PackSetName in [s3PackSHA1sByPackSetName allKeys]) { - NSArray *packSHA1s = [s3PackSHA1sByPackSetName objectForKey:s3PackSetName]; - PackSet *packSet = [[[PackSet alloc] initWithName:s3PackSetName - s3Service:s3 - s3BucketName:s3BucketName - computerUUID:computerUUID - keepPacksLocal:[s3PackSetName hasSuffix:@"-trees"] - packSHA1s:packSHA1s error:error] autorelease]; - if (packSet == nil) { - return nil; - } - [packSets setObject:packSet forKey:s3PackSetName]; - } - NSMutableArray *ret = [NSMutableArray array]; - for (NSArray *sha1s in [s3PackSHA1sByPackSetName allValues]) { - [ret addObjectsFromArray:sha1s]; - } - return ret; -} -@end -@implementation PackSetSet (internal) -- (NSDictionary *)packSHA1sByPackSetNameFromS3:(NSError **)error { - NSMutableDictionary *packSHA1sByPackSetName = [NSMutableDictionary dictionary]; - NSString *packSetPrefix = [PackSet s3PathWithS3BucketName:s3BucketName computerUUID:computerUUID packSetName:@""]; - NSArray *s3Paths = [s3 pathsWithPrefix:packSetPrefix error:error]; - if (s3Paths == nil) { - return nil; - } - // Format: ///packsets//.pack - NSString *pattern = [NSString stringWithFormat:@"^%@([^/]+)/(.+)\\.pack$", packSetPrefix]; - for (NSString *s3Path in s3Paths) { - NSRange packSetNameRange = [s3Path rangeOfRegex:pattern capture:1]; - NSRange sha1Range = [s3Path rangeOfRegex:pattern capture:2]; - if (packSetNameRange.location != NSNotFound && sha1Range.location != NSNotFound) { - NSString *escapedPackSetName = [s3Path substringWithRange:packSetNameRange]; - NSString *packSetName = [escapedPackSetName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSString *packSHA1 = [s3Path substringWithRange:sha1Range]; - NSMutableArray *packSHA1s = [packSHA1sByPackSetName objectForKey:packSetName]; - if (packSHA1s == nil) { - packSHA1s = [NSMutableArray array]; - [packSHA1sByPackSetName setObject:packSHA1s forKey:packSetName]; - } - [packSHA1s addObject:packSHA1]; - } - } - return packSHA1sByPackSetName; -} -- (NSMutableSet *)diskPackSetNames:(NSError **)error { - NSMutableSet *diskPackSetNames = [NSMutableSet set]; - NSString *packSetsDir = [PackSetSet localPathWithComputerUUID:computerUUID]; - if ([[NSFileManager defaultManager] fileExistsAtPath:packSetsDir]) { - NSArray *packSetNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:packSetsDir error:error]; - if (packSetNames == nil) { - return nil; - } - for (NSString *packSetName in packSetNames) { - if (![packSetName hasPrefix:@"."]) { - [diskPackSetNames addObject:packSetName]; - } - } - } - return diskPackSetNames; -} -- (PackSet *)packSetForName:(NSString *)packSetName error:(NSError **)error { - PackSet *packSet = [packSets objectForKey:packSetName]; - if (packSet == nil) { - NSError *myError; - NSArray *packSHA1s = [self cachedPackSHA1sForPackSet:packSetName error:&myError]; - if (packSHA1s == nil) { - HSLogError(@"error reading cached pack sets: %@", [myError localizedDescription]); - packSHA1s = [NSArray array]; - } - packSet = [[PackSet alloc] initWithName:packSetName - s3Service:s3 - s3BucketName:s3BucketName - computerUUID:computerUUID - keepPacksLocal:[packSetName hasSuffix:@"-trees"] - packSHA1s:packSHA1s - error:error]; - if (packSet == nil) { - return nil; - } - [packSets setObject:packSet forKey:packSetName]; - [packSet release]; - } - return packSet; -} -- (NSArray *)cachedPackSHA1sForPackSet:(NSString *)packSetName error:(NSError **)error { - NSString *packSetDir = [PackSet localPathWithComputerUUID:computerUUID packSetName:packSetName]; - NSMutableArray *ret = [NSMutableArray array]; - BOOL isDir = NO; - if ([[NSFileManager defaultManager] fileExistsAtPath:packSetDir isDirectory:&isDir] && isDir) { - NSArray *dirNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:packSetDir error:error]; - if (dirNames == nil) { - return nil; - } - for (NSString *dirName in dirNames) { - NSString *dir = [packSetDir stringByAppendingPathComponent:dirName]; - if ([[NSFileManager defaultManager] fileExistsAtPath:dir isDirectory:&isDir] && isDir) { - NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:error]; - if (fileNames == nil) { - return nil; - } - for (NSString *fileName in fileNames) { - NSRange sha1Range = [fileName rangeOfRegex:@"^(.+)\\.index$" capture:1]; - if (sha1Range.location != NSNotFound) { - NSString *sha1 = [dirName stringByAppendingString:[fileName substringWithRange:sha1Range]]; - [ret addObject:sha1]; - } - } - } - } - } - return ret; -} -@end diff --git a/Restorer.h b/Restorer.h index 2452bb9..95d2136 100644 --- a/Restorer.h +++ b/Restorer.h @@ -32,12 +32,12 @@ #import @class S3Service; -@class S3Fark; -@class S3Repo; +@class ArqFark; +@class ArqRepo; @interface Restorer : NSObject { - S3Fark *fark; - S3Repo *repo; + ArqFark *fark; + ArqRepo *repo; NSString *bucketName; NSString *rootPath; NSMutableArray *restoreNodes; diff --git a/Restorer.m b/Restorer.m index 367e6c8..090fb3b 100644 --- a/Restorer.m +++ b/Restorer.m @@ -33,8 +33,8 @@ #include #include #import "Restorer.h" -#import "S3Fark.h" -#import "S3Repo.h" +#import "ArqFark.h" +#import "ArqRepo.h" #import "SetNSError.h" #import "Tree.h" #import "Node.h" @@ -65,8 +65,8 @@ @implementation Restorer - (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID bucketName:(NSString *)theBucketName encryptionKey:(NSString *)theEncryptionKey { if (self = [super init]) { - fark = [[S3Fark alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID]; - repo = [[S3Repo alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID bucketUUID:theBucketUUID encrypted:YES encryptionKey:theEncryptionKey fark:fark ensureCacheIntegrity:NO]; + fark = [[ArqFark alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID]; + repo = [[ArqRepo alloc] initWithS3Service:theS3 s3BucketName:theS3BucketName computerUUID:theComputerUUID bucketUUID:theBucketUUID encryptionKey:theEncryptionKey]; bucketName = [theBucketName copy]; rootPath = [[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:theBucketName] copy]; restoreNodes = [[NSMutableArray alloc] init]; @@ -88,23 +88,20 @@ SETNSERROR(@"RestorerErrorDomain", -1, @"%@ already exists", rootPath); return NO; } - if (![fark reloadPacksFromS3:error]) { - return NO; - } if (![[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:nil error:error]) { HSLogError(@"failed to create directory %@", rootPath); return NO; } - NSString *headSHA1 = nil; - if (![repo localHeadSHA1:&headSHA1 error:error]) { + NSString *headSHA1 = [repo headSHA1:error]; + if (headSHA1 == nil) { return NO; } if (headSHA1 == nil) { SETNSERROR(@"RestorerErrorDomain", -1, @"no backup found"); return NO; } - Commit *head = nil; - if (![repo commit:&head forSHA1:headSHA1 error:error]) { + Commit *head = [repo commitForSHA1:headSHA1 error:error]; + if (head == nil) { return NO; } if (![self addRestoreNodesForTreeSHA1:[head treeSHA1] relativePath:@"" error:error]) { @@ -130,8 +127,8 @@ @implementation Restorer (internal) - (BOOL)addRestoreNodesForTreeSHA1:(NSString *)treeSHA1 relativePath:(NSString *)relativePath error:(NSError **)error { - Tree *tree = nil; - if (![repo tree:&tree forSHA1:treeSHA1 error:error]) { + Tree *tree = [repo treeForSHA1:treeSHA1 error:error]; + if (tree == nil) { return NO; } RestoreNode *treeRN = [[RestoreNode alloc] initWithTree:tree nodeName:nil relativePath:relativePath]; @@ -330,7 +327,7 @@ } HSLogTrace(@"%qu bytes -> %@", [node dataSize], path); if (([node mode] & S_IFLNK) == S_IFLNK) { - NSData *data = [repo dataForSHA1s:[node dataSHA1s] error:error]; + NSData *data = [repo blobDataForSHA1s:[node dataSHA1s] error:error]; if (data == nil) { HSLogError(@"error getting data for %@", [node dataSHA1s]); return NO; @@ -435,7 +432,7 @@ } - (BOOL)applyACLSHA1:(NSString *)aclSHA1 toFileAttributes:(FileAttributes *)fa error:(NSError **)error { if (aclSHA1 != nil) { - NSData *data = [repo dataForSHA1:aclSHA1 error:error]; + NSData *data = [repo blobDataForSHA1:aclSHA1 error:error]; if (data == nil) { return NO; } @@ -448,7 +445,7 @@ } - (BOOL)applyXAttrsSHA1:(NSString *)xattrsSHA1 toFile:(NSString *)path error:(NSError **)error { if (xattrsSHA1 != nil) { - NSData *xattrsData = [repo dataForSHA1:xattrsSHA1 error:error]; + NSData *xattrsData = [repo blobDataForSHA1:xattrsSHA1 error:error]; if (xattrsData == nil) { return NO; } diff --git a/S3Fark.m b/S3Fark.m deleted file mode 100644 index 1d92856..0000000 --- a/S3Fark.m +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import "S3Service.h" -#import "S3Fark.h" -#import "SetNSError.h" -#import "NSErrorCodes.h" -#import "PackSetSet.h" -#import "ServerBlob.h" - -@interface S3Fark (internal) -- (NSString *)pathForSHA1:(NSString *)sha1; -@end - -@implementation S3Fark -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID { - if (self = [super init]) { - s3 = [theS3 retain]; - s3BucketName = [theS3BucketName copy]; - computerUUID = [theComputerUUID copy]; - creatorThread = [[NSThread currentThread] retain]; - packSetSet = [[PackSetSet alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID]; - } - return self; -} -- (void)dealloc { - [s3 release]; - [s3BucketName release]; - [computerUUID release]; - [creatorThread release]; - [packSetSet release]; - [super dealloc]; -} -- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error { - ServerBlob *sb = [self newServerBlobForSHA1:sha1 packSetName:packSetName searchPackOnly:searchPackOnly error:error]; - if (sb == nil) { - return nil; - } - NSData *data = [sb slurp:error]; - [sb release]; - return data; -} -- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly error:(NSError **)error { - NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); - NSError *myError = nil; - ServerBlob *sb = [packSetSet newServerBlobForSHA1:sha1 packSetName:packSetName error:&myError]; - if (sb == nil) { - if ([myError code] != ERROR_NOT_FOUND) { - HSLogError(@"error reading sha1 %@ from packSetSet: %@", sha1, [myError localizedDescription]); - } - if (error != NULL) { - *error = myError; - } - if (!searchPackOnly) { - sb = [s3 newServerBlobAtPath:[self pathForSHA1:sha1] error:error]; - } - } - return sb; -} -- (BOOL)containsBlobForSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName searchPackOnly:(BOOL)searchPackOnly { - NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); - BOOL contains = [packSetSet containsBlobForSHA1:sha1 packSetName:packSetName]; - if (!contains && !searchPackOnly) { - contains = [s3 containsBlobAtPath:[self pathForSHA1:sha1]]; - } - return contains; -} -- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)sha1 packSetName:(NSString *)packSetName { - return [packSetSet packSHA1ForPackedBlobSHA1:sha1 packSetName:packSetName]; -} -- (NSArray *)reloadPacksFromS3:(NSError **)error { - NSAssert([NSThread currentThread] == creatorThread, @"must be on same thread!"); - return [packSetSet resetFromS3:error]; -} -@end - -@implementation S3Fark (internal) -- (NSString *)pathForSHA1:(NSString *)sha1 { - return [NSString stringWithFormat:@"/%@/%@/objects/%@", s3BucketName, computerUUID, sha1]; -} -@end diff --git a/S3Repo.h b/S3Repo.h deleted file mode 100644 index 4187527..0000000 --- a/S3Repo.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import -@class S3Service; -@class S3Fark; -@class Commit; -@class Tree; -@class Blob; -@class ServerBlob; - - -@interface S3Repo : NSObject { - S3Service *s3; - NSString *s3BucketName; - NSString *computerUUID; - NSString *bucketUUID; - S3Fark *fark; - BOOL encrypted; - NSString *encryptionKey; - BOOL ensureCacheIntegrity; - NSString *treesPackSetName; - NSString *blobsPackSetName; -} -+ (NSString *)errorDomain; - -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID - bucketUUID:(NSString *)theBucketUUID - encrypted:(BOOL)isEncrypted - encryptionKey:(NSString *)theEncryptionKey - fark:(S3Fark *)theFark - ensureCacheIntegrity:(BOOL)ensure; - -- (BOOL)localHeadSHA1:(NSString **)localHeadSHA1 error:(NSError **)error; - -// Returns NO if commit not found: -- (BOOL)commit:(Commit **)commit forSHA1:(NSString *)theSHA1 error:(NSError **)error; - -// Returns NO if commit not found: -- (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; -- (BOOL)commonAncestorCommitSHA1:(NSString **)ancestorSHA1 forCommitSHA1:(NSString *)commit0SHA1 andCommitSHA1:(NSString *)commit1SHA1 error:(NSError **)error; -- (BOOL)is:(BOOL *)isAncestor commitSHA1:(NSString *)descendantSHA1 ancestorOfCommitSHA1:(NSString *)sha1 error:(NSError **)error; -- (NSString *)localHeadS3Path; -- (BOOL)isEncrypted; -- (NSString *)blobsPackSetName; -- (NSSet *)packSetNames; -@end diff --git a/S3Repo.m b/S3Repo.m deleted file mode 100644 index 23edfb9..0000000 --- a/S3Repo.m +++ /dev/null @@ -1,294 +0,0 @@ -/* - Copyright (c) 2009, 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. - */ - -#import "ServerBlob.h" -#import "S3Repo.h" -#import "S3Service.h" -#import "S3Fark.h" -#import "SetNSError.h" -#import "Tree.h" -#import "Commit.h" -#import "NSData-Encrypt.h" -#import "DecryptedInputStream.h" -#import "InputStreams.h" -#import "NSErrorCodes.h" -#import "NSData-InputStream.h" -#import "DataInputStream.h" -#import "HTTP.h" - -@interface S3Repo (internal) -- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName error:(NSError **)error; -- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName searchPackOnly:(BOOL)packOnly error:(NSError **)error; -- (BOOL)ancestorCommitSHA1sForCommitSHA1:(NSString *)commitSHA1 outArray:(NSMutableArray *)arr error:(NSError **)error; -- (BOOL)commitSHA1:(NSString **)sha1 fromSHA1:(NSString *)fromSHA1 before:(NSDate *)date error:(NSError **)error; -@end - -static NSString *ERROR_DOMAIN = @"S3RepoErrorDomain"; - -@implementation S3Repo -+ (NSString *)errorDomain { - return ERROR_DOMAIN; -} -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID - bucketUUID:(NSString *)theBucketUUID - encrypted:(BOOL)isEncrypted - encryptionKey:(NSString *)theEncryptionKey - fark:(S3Fark *)theFark - ensureCacheIntegrity:(BOOL)ensure { - if (self = [super init]) { - s3 = [theS3 retain]; - s3BucketName = [theS3BucketName copy]; - computerUUID = [theComputerUUID copy]; - bucketUUID = [theBucketUUID copy]; - encrypted = isEncrypted; - encryptionKey = [theEncryptionKey copy]; - fark = [theFark retain]; - ensureCacheIntegrity = ensure; - treesPackSetName = [[NSString alloc] initWithFormat:@"%@-trees", bucketUUID]; - blobsPackSetName = [[NSString alloc] initWithFormat:@"%@-blobs", bucketUUID]; - } - return self; -} -- (void)dealloc { - [s3 release]; - [s3BucketName release]; - [computerUUID release]; - [bucketUUID release]; - [encryptionKey release]; - [fark release]; - [treesPackSetName release]; - [blobsPackSetName release]; - [super dealloc]; -} -- (BOOL)localHeadSHA1:(NSString **)headSHA1 error:(NSError **)error { - *headSHA1 = nil; - NSError *myError; - NSData *headSHA1Data = [s3 dataAtPath:[self localHeadS3Path] error:&myError]; - if (headSHA1Data == nil && !([[myError domain] isEqualToString:[S3Service serverErrorDomain]] && [myError code] == HTTP_NOT_FOUND)) { - if (error != NULL) { - *error = myError; - } - return NO; - } - if (headSHA1Data != nil) { - *headSHA1 = [[[NSString alloc] initWithData:headSHA1Data encoding:NSUTF8StringEncoding] autorelease]; - } - return YES; -} -- (BOOL)commit:(Commit **)commit forSHA1:(NSString *)theSHA1 error:(NSError **)error { - *commit = nil; - NSData *data = [self dataForSHA1:theSHA1 packSetName:treesPackSetName searchPackOnly:YES error:error]; - if (data == nil) { - HSLogDebug(@"commit data not found for %@", theSHA1); - return NO; - } - id is = [data newInputStream]; - *commit = [[[Commit alloc] initWithBufferedInputStream:is error:error] autorelease]; - [is release]; - if (*commit == nil) { - return NO; - } - return YES; -} -- (BOOL)tree:(Tree **)tree forSHA1:(NSString *)theSHA1 error:(NSError **)error { - *tree = nil; - NSData *data = [self dataForSHA1:theSHA1 packSetName:treesPackSetName searchPackOnly:YES error:error]; - if (data == nil) { - HSLogDebug(@"tree data not found for %@", theSHA1); - return NO; - } - id is = [data newInputStream]; - *tree = [[[Tree alloc] initWithBufferedInputStream:is error:error] autorelease]; - [is release]; - if (*tree == nil) { - return NO; - } - return YES; -} -- (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) { - data = [fark dataForSHA1:sha1 packSetName:blobsPackSetName searchPackOnly:NO error:error]; - } - if (data != nil && encrypted) { - data = [data decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; - } - return data; -} -- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error { - ServerBlob *sb = [fark newServerBlobForSHA1:sha1 packSetName:treesPackSetName searchPackOnly:NO error:error]; - if (sb == nil) { - sb = [fark newServerBlobForSHA1:sha1 packSetName:blobsPackSetName searchPackOnly:NO error:error]; - } - if (sb != nil && encrypted) { - id is = [sb newInputStream]; - NSString *mimeType = [sb mimeType]; - NSString *downloadName = [sb downloadName]; - [sb release]; - DecryptedInputStream *dis = [[DecryptedInputStream alloc] initWithInputStream:is cipherName:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; - [is release]; - if (dis == nil) { - return NO; - } - sb = [[ServerBlob alloc] initWithInputStream:dis mimeType:mimeType downloadName:downloadName]; - [dis release]; - } - return sb; -} -- (NSData *)dataForSHA1s:(NSArray *)sha1s error:(NSError **)error { - NSMutableData *data = [NSMutableData data]; - for (NSString *sha1 in sha1s) { - ServerBlob *sb = [self newServerBlobForSHA1:sha1 error:error]; - if (sb == nil) { - return NO; - } - NSData *blobData = [sb slurp:error]; - [sb release]; - if (blobData == nil) { - return NO; - } - //FIXME: Get rid of this extra copying of data. - [data appendData:blobData]; - } - return data; -} -- (BOOL)commonAncestorCommitSHA1:(NSString **)ancestorSHA1 forCommitSHA1:(NSString *)commit0SHA1 andCommitSHA1:(NSString *)commit1SHA1 error:(NSError **)error { - //FIXME: This is slow and memory-intensive! - NSMutableArray *commit0ParentSHA1s = [[[NSMutableArray alloc] initWithObjects:commit0SHA1, nil] autorelease]; - NSMutableArray *commit1ParentSHA1s = [[[NSMutableArray alloc] initWithObjects:commit1SHA1, nil] autorelease]; - if (![self ancestorCommitSHA1sForCommitSHA1:commit0SHA1 outArray:commit0ParentSHA1s error:error]) { - return NO; - } - if (![self ancestorCommitSHA1sForCommitSHA1:commit1SHA1 outArray:commit1ParentSHA1s error:error]) { - return NO; - } - for (NSString *parent in commit1ParentSHA1s) { - if ([commit0ParentSHA1s containsObject:parent]) { - *ancestorSHA1 = parent; - break; - } - } - return YES; -} -- (BOOL)is:(BOOL *)isAncestor commitSHA1:(NSString *)ancestorCommitSHA1 ancestorOfCommitSHA1:(NSString *)sha1 error:(NSError **)error { - *isAncestor = NO; - if ([ancestorCommitSHA1 isEqualToString:sha1]) { - return YES; - } - //TODO: Get rid of recursion in this method: - Commit *commit = nil; - if (![self commit:&commit forSHA1:sha1 error:error]) { - return NO; - } - for (NSString *parentCommitSHA1 in [commit parentCommitSHA1s]) { - if (![self is:isAncestor commitSHA1:parentCommitSHA1 ancestorOfCommitSHA1:ancestorCommitSHA1 error:error]) { - return NO; - } - if (*isAncestor) { - return YES; - } - } - return YES; -} -- (NSString *)localHeadS3Path { - return [NSString stringWithFormat:@"/%@/%@/bucketdata/%@/refs/heads/master", s3BucketName, computerUUID, bucketUUID]; -} -- (BOOL)isEncrypted { - return encrypted; -} -- (NSString *)blobsPackSetName { - return blobsPackSetName; -} -- (NSSet *)packSetNames { - return [NSSet setWithObjects:blobsPackSetName, treesPackSetName, nil]; -} - -#pragma mark NSObject protocol -- (NSString *)description { - return [NSString stringWithFormat:@"", self, bucketUUID]; -} -@end - -@implementation S3Repo (internal) -- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName error:(NSError **)error { - return [self dataForSHA1:sha1 packSetName:thePackSetName searchPackOnly:NO error:error]; -} -- (NSData *)dataForSHA1:(NSString *)sha1 packSetName:(NSString *)thePackSetName searchPackOnly:(BOOL)packOnly error:(NSError **)error { - NSData *data = [fark dataForSHA1:sha1 packSetName:thePackSetName searchPackOnly:packOnly error:error]; - if (data != nil && encrypted) { - data = [data decryptWithCipher:ARQ_DEFAULT_CIPHER_NAME key:encryptionKey error:error]; - } - return data; -} -- (BOOL)ancestorCommitSHA1sForCommitSHA1:(NSString *)commitSHA1 outArray:(NSMutableArray *)arr error:(NSError **)error { - Commit *commit = nil; - if (![self commit:&commit forSHA1:commitSHA1 error:error]) { - return NO; - } - for (NSString *parentCommitSHA1 in [commit parentCommitSHA1s]) { - [arr addObject:parentCommitSHA1]; - if (![self ancestorCommitSHA1sForCommitSHA1:parentCommitSHA1 outArray:arr error:error]) { - return NO; - } - } - return YES; -} -- (BOOL)commitSHA1:(NSString **)sha1 fromSHA1:(NSString *)fromSHA1 before:(NSDate *)date error:(NSError **)error { - HSLogDebug(@"looking for Commit before %@", [date description]); - *sha1 = nil; - for (;;) { - Commit *commit = nil; - if (![self commit:&commit forSHA1:fromSHA1 error:error]) { - return NO; - } - NSDate *creationDate = [commit creationDate]; - if ([date earlierDate:creationDate] == creationDate) { - *sha1 = [[fromSHA1 retain] autorelease]; - HSLogDebug(@"returning Commit SHA1 %@: creationDate=%@", *sha1, creationDate); - break; - } - if ([[commit parentCommitSHA1s] count] == 0) { - break; - } - fromSHA1 = [[[commit parentCommitSHA1s] allObjects] objectAtIndex:0]; - } - return YES; -} -@end diff --git a/Tree.h b/Tree.h index 9dce379..b3a6e79 100644 --- a/Tree.h +++ b/Tree.h @@ -32,12 +32,11 @@ #import #import "Blob.h" -#import "BufferedInputStream.h" -#import "OutputStream.h" +@protocol BufferedInputStream; @class Node; -@class MutableS3Repo; #define CURRENT_TREE_VERSION 10 +#define TREE_HEADER_LENGTH (8) @interface Tree : NSObject { int treeVersion; @@ -64,11 +63,12 @@ uint32_t st_blksize; NSMutableDictionary *nodes; } -- (id)init; ++ (NSString *)errorDomain; - (id)initWithBufferedInputStream:(id )is error:(NSError **)error; - (NSArray *)childNodeNames; - (Node *)childNodeWithName:(NSString *)name; - (BOOL)containsNodeNamed:(NSString *)name; +- (Blob *)toBlob; @property(readonly,copy) NSString *xattrsSHA1; @property(readonly) unsigned long long xattrsSize; @@ -89,4 +89,5 @@ @property(readonly) long long createTime_nsec; @property(readonly) uint32_t st_nlink; @property(readonly) int st_ino; + @end diff --git a/Tree.m b/Tree.m index d6650fe..6e904b0 100644 --- a/Tree.m +++ b/Tree.m @@ -40,9 +40,6 @@ #import "SetNSError.h" #import "RegexKitLite.h" #import "NSErrorCodes.h" -#import "Streams.h" - -#define HEADER_LENGTH (8) @interface Tree (internal) - (BOOL)readHeader:(id )is error:(NSError **)error; @@ -52,11 +49,8 @@ @synthesize xattrsSHA1, xattrsSize, aclSHA1, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, treeVersion, st_rdev; @synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino; -- (id)init { - if (self = [super init]) { - nodes = [[NSMutableDictionary alloc] init]; - } - return self; ++ (NSString *)errorDomain { + return @"TreeErrorDomain"; } - (id)initWithBufferedInputStream:(id )is error:(NSError **)error { if (self = [super init]) { @@ -130,15 +124,52 @@ initDone: - (BOOL)containsNodeNamed:(NSString *)name { return [nodes objectForKey:name] != nil; } +- (Blob *)toBlob { + NSMutableData *data = [[NSMutableData alloc] init]; + char header[TREE_HEADER_LENGTH + 1]; + sprintf(header, "TreeV%03d", CURRENT_TREE_VERSION); + [data appendBytes:header length:TREE_HEADER_LENGTH]; + [StringIO write:xattrsSHA1 to:data]; + [IntegerIO writeUInt64:xattrsSize to:data]; + [StringIO write:aclSHA1 to:data]; + [IntegerIO writeInt32:uid to:data]; + [IntegerIO writeInt32:gid to:data]; + [IntegerIO writeInt32:mode to:data]; + [IntegerIO writeInt64:mtime_sec to:data]; + [IntegerIO writeInt64:mtime_nsec to:data]; + [IntegerIO writeInt64:flags to:data]; + [IntegerIO writeInt32:finderFlags to:data]; + [IntegerIO writeInt32:extendedFinderFlags to:data]; + [IntegerIO writeInt32:st_dev to:data]; + [IntegerIO writeInt32:st_ino to:data]; + [IntegerIO writeUInt32:st_nlink to:data]; + [IntegerIO writeInt32:st_rdev to:data]; + [IntegerIO writeInt64:ctime_sec to:data]; + [IntegerIO writeInt64:ctime_nsec to:data]; + [IntegerIO writeInt64:st_blocks to:data]; + [IntegerIO writeUInt32:st_blksize to:data]; + + [IntegerIO writeUInt32:(uint32_t)[nodes count] to:data]; + NSMutableArray *nodeNames = [NSMutableArray arrayWithArray:[nodes allKeys]]; + [nodeNames sortUsingSelector:@selector(compare:)]; + for (NSString *nodeName in nodeNames) { + [StringIO write:nodeName to:data]; + Node *node = [nodes objectForKey:nodeName]; + [node writeToData:data]; + } + Blob *ret =[[[Blob alloc] initWithData:data mimeType:@"binary/octet-stream" downloadName:@"Tree"] autorelease]; + [data release]; + return ret; +} @end @implementation Tree (internal) - (BOOL)readHeader:(id )is error:(NSError **)error { - unsigned char *headerBytes = [is readExactly:HEADER_LENGTH error:error]; + unsigned char *headerBytes = [is readExactly:TREE_HEADER_LENGTH error:error]; if (headerBytes == NULL) { return NO; } - NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; + NSString *header = [[[NSString alloc] initWithBytes:headerBytes length:TREE_HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; NSRange versionRange = [header rangeOfRegex:@"^TreeV(\\d{3})$" capture:1]; treeVersion = 0; if (versionRange.location != NSNotFound) { @@ -148,7 +179,7 @@ initDone: [nf release]; } if (treeVersion != CURRENT_TREE_VERSION) { - SETNSERROR(@"TreeErrorDomain", ERROR_INVALID_OBJECT_VERSION, @"invalid Tree header: %@", header); + SETNSERROR([Tree errorDomain], ERROR_INVALID_OBJECT_VERSION, @"invalid Tree header: %@", header); return NO; } return YES; diff --git a/XAttrSet.h b/XAttrSet.h index 31889a2..4eb7c90 100644 --- a/XAttrSet.h +++ b/XAttrSet.h @@ -39,7 +39,9 @@ } - (id)initWithPath:(NSString *)thePath error:(NSError **)error; - (id)initWithBufferedInputStream:(id )is error:(NSError **)error; +- (Blob *)toBlob; - (NSUInteger)count; +- (unsigned long long)dataLength; - (NSArray *)names; - (BOOL)applyToFile:(NSString *)path error:(NSError **)error; @end diff --git a/XAttrSet.m b/XAttrSet.m index 3702aeb..0c99ae4 100644 --- a/XAttrSet.m +++ b/XAttrSet.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #import "XAttrSet.h" #import "StringIO.h" @@ -40,6 +41,7 @@ #import "SetNSError.h" #import "NSErrorCodes.h" #import "Streams.h" +#import "NSError_extra.h" #define HEADER_LENGTH (12) @@ -52,9 +54,17 @@ - (id)initWithPath:(NSString *)thePath error:(NSError **)error { if (self = [super init]) { xattrs = [[NSMutableDictionary alloc] init]; - if (![self loadFromPath:thePath error:error]) { - [self release]; - self = nil; + NSError *myError = nil; + if (![self loadFromPath:thePath error:&myError]) { + if ([myError isErrorWithDomain:@"UnixErrorDomain" code:EPERM]) { + HSLogDebug(@"%@ doesn't support extended attributes; skipping", thePath); + } else { + if (error != NULL) { + *error = myError; + } + [self release]; + self = nil; + } } } return self; @@ -73,9 +83,30 @@ [xattrs release]; [super dealloc]; } +- (Blob *)toBlob { + NSMutableData *data = [[NSMutableData alloc] init]; + [data appendBytes:"XAttrSetV002" length:HEADER_LENGTH]; + uint64_t count = (uint64_t)[xattrs count]; + [IntegerIO writeUInt64:count to:data]; + for (NSString *name in [xattrs allKeys]) { + [StringIO write:name to:data]; + [DataIO write:[xattrs objectForKey:name] to:data]; + } + Blob *ret = [[[Blob alloc] initWithData:data mimeType:@"binary/octet-stream" downloadName:@"xattrset"] autorelease]; + [data release]; + return ret; +} - (NSUInteger)count { return [xattrs count]; } +- (unsigned long long)dataLength { + unsigned long long total = 0; + for (NSString *key in [xattrs allKeys]) { + NSData *value = [xattrs objectForKey:key]; + total += [value length]; + } + return total; +} - (NSArray *)names { return [xattrs allKeys]; } @@ -109,15 +140,12 @@ @implementation XAttrSet (internal) - (BOOL)loadFromPath:(NSString *)thePath error:(NSError **)error { - NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:thePath error:error]; - if (attribs == nil) { + struct stat st; + if (lstat([thePath fileSystemRepresentation], &st) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"lstat(%@): %s", thePath, strerror(errno)); return NO; } - NSString *fileType = [attribs objectForKey:NSFileType]; - if (![fileType isEqualToString:NSFileTypeSocket] - && ![fileType isEqualToString:NSFileTypeBlockSpecial] - && ![fileType isEqualToString:NSFileTypeCharacterSpecial] - && ![fileType isEqualToString:NSFileTypeUnknown]) { + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) { const char *path = [thePath fileSystemRepresentation]; ssize_t xattrsize = listxattr(path, NULL, 0, XATTR_NOFOLLOW); if (xattrsize == -1) { diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index c0e406c..f7c4efe 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -112,11 +112,7 @@ 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 */; }; @@ -161,11 +157,7 @@ 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 */; }; F8D6781D1160F4FD00CC270E /* SHA1Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6781C1160F4FD00CC270E /* SHA1Hash.m */; }; - F8D6782D1160F5D000CC270E /* PackSetSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782A1160F5D000CC270E /* PackSetSet.m */; }; - F8D6782E1160F5D000CC270E /* PackSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6782C1160F5D000CC270E /* PackSet.m */; }; F8D678331160F62E00CC270E /* ArqUserLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678321160F62E00CC270E /* ArqUserLibrary.m */; }; F8D6783C1160F70100CC270E /* PackIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D6783B1160F70100CC270E /* PackIndexEntry.m */; }; F8D678451160F74A00CC270E /* DiskPack.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D678421160F74A00CC270E /* DiskPack.m */; }; @@ -196,6 +188,23 @@ F8D67D071161384100CC270E /* FileACL.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D041161384100CC270E /* FileACL.m */; }; F8D67D081161384100CC270E /* OSStatusDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67D061161384100CC270E /* OSStatusDescription.m */; }; F8D67F701161443600CC270E /* RestoreNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D67F6F1161443600CC270E /* RestoreNode.m */; }; + F8F4D19D121D77E1002D09C1 /* NSError_extra.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D19C121D77E1002D09C1 /* NSError_extra.m */; }; + F8F4D1A0121D78AD002D09C1 /* MonitoredInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */; }; + F8F4D1B0121D7990002D09C1 /* ArqPackSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1AD121D7990002D09C1 /* ArqPackSet.m */; }; + F8F4D1B1121D7990002D09C1 /* ArqRepo.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1AF121D7990002D09C1 /* ArqRepo.m */; }; + F8F4D1C3121D79AC002D09C1 /* ArqFark.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1C2121D79AC002D09C1 /* ArqFark.m */; }; + F8F4D1E2121D7BC3002D09C1 /* AppKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */; }; + F8F4D1E7121D7DA2002D09C1 /* FarkPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */; }; + F8F4D1FE121D8409002D09C1 /* PackIndexWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */; }; + F8F4D207121D8696002D09C1 /* ArqRepo_Verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */; }; + F8F4D59A121D9FA7002D09C1 /* MonitoredInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */; }; + F8F4D59B121D9FAD002D09C1 /* ArqFark.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1C2121D79AC002D09C1 /* ArqFark.m */; }; + F8F4D59C121D9FAE002D09C1 /* ArqPackSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1AD121D7990002D09C1 /* ArqPackSet.m */; }; + F8F4D59D121D9FAF002D09C1 /* ArqRepo.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1AF121D7990002D09C1 /* ArqRepo.m */; }; + F8F4D59E121D9FAF002D09C1 /* ArqRepo_Verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */; }; + F8F4D5A1121D9FC2002D09C1 /* AppKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */; }; + F8F4D5A2121D9FC9002D09C1 /* FarkPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */; }; + F8F4D5A3121D9FCF002D09C1 /* PackIndexWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -344,21 +353,13 @@ 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 = ""; }; + F83C1D0811CA929D0001958F /* BucketVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BucketVerifier.h; sourceTree = ""; }; + F83C1D0911CA929D0001958F /* BucketVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = 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 = ""; }; - F8D678131160F4E300CC270E /* S3Fark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Fark.h; sourceTree = ""; }; - F8D678141160F4E300CC270E /* S3Fark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Fark.m; sourceTree = ""; }; - F8D678151160F4E300CC270E /* S3Repo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Repo.h; sourceTree = ""; }; - F8D678161160F4E300CC270E /* S3Repo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Repo.m; sourceTree = ""; }; F8D6781B1160F4FD00CC270E /* SHA1Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHA1Hash.h; sourceTree = ""; }; F8D6781C1160F4FD00CC270E /* SHA1Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHA1Hash.m; sourceTree = ""; }; - F8D678291160F5D000CC270E /* PackSetSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSetSet.h; sourceTree = ""; }; - F8D6782A1160F5D000CC270E /* PackSetSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSetSet.m; sourceTree = ""; }; - F8D6782B1160F5D000CC270E /* PackSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSet.h; sourceTree = ""; }; - F8D6782C1160F5D000CC270E /* PackSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSet.m; sourceTree = ""; }; F8D678311160F62E00CC270E /* ArqUserLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqUserLibrary.h; sourceTree = ""; }; F8D678321160F62E00CC270E /* ArqUserLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqUserLibrary.m; sourceTree = ""; }; F8D6783A1160F70100CC270E /* PackIndexEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackIndexEntry.h; sourceTree = ""; }; @@ -419,6 +420,24 @@ F8D67D061161384100CC270E /* OSStatusDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSStatusDescription.m; sourceTree = ""; }; F8D67F6E1161443600CC270E /* RestoreNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoreNode.h; sourceTree = ""; }; F8D67F6F1161443600CC270E /* RestoreNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoreNode.m; sourceTree = ""; }; + F8F4D19B121D77E1002D09C1 /* NSError_extra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSError_extra.h; sourceTree = ""; }; + F8F4D19C121D77E1002D09C1 /* NSError_extra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSError_extra.m; sourceTree = ""; }; + F8F4D19E121D78AD002D09C1 /* MonitoredInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MonitoredInputStream.h; sourceTree = ""; }; + F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MonitoredInputStream.m; sourceTree = ""; }; + F8F4D1AC121D7990002D09C1 /* ArqPackSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqPackSet.h; sourceTree = ""; }; + F8F4D1AD121D7990002D09C1 /* ArqPackSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqPackSet.m; sourceTree = ""; }; + F8F4D1AE121D7990002D09C1 /* ArqRepo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRepo.h; sourceTree = ""; }; + F8F4D1AF121D7990002D09C1 /* ArqRepo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRepo.m; sourceTree = ""; }; + F8F4D1C1121D79AC002D09C1 /* ArqFark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqFark.h; sourceTree = ""; }; + F8F4D1C2121D79AC002D09C1 /* ArqFark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqFark.m; sourceTree = ""; }; + F8F4D1E0121D7BC3002D09C1 /* AppKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppKeychain.h; sourceTree = ""; }; + F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppKeychain.m; sourceTree = ""; }; + F8F4D1E5121D7DA2002D09C1 /* FarkPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FarkPath.h; sourceTree = ""; }; + F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FarkPath.m; sourceTree = ""; }; + F8F4D1FC121D8409002D09C1 /* PackIndexWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackIndexWriter.h; sourceTree = ""; }; + F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackIndexWriter.m; sourceTree = ""; }; + F8F4D205121D8696002D09C1 /* ArqRepo_Verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRepo_Verifier.h; sourceTree = ""; }; + F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRepo_Verifier.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -458,6 +477,8 @@ 08FB7794FE84155DC02AAC07 /* arq_restore */ = { isa = PBXGroup; children = ( + F8F4D1FC121D8409002D09C1 /* PackIndexWriter.h */, + F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */, 08FB7795FE84155DC02AAC07 /* Source */, 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, 1AB674ADFE9D54B511CA2CBB /* Products */, @@ -476,6 +497,16 @@ F805B7651160DD60007EC01E /* s3 */, 32A70AAB03705E1F00C91783 /* arq_restore_Prefix.pch */, 08FB7796FE84155DC02AAC07 /* arq_restore.m */, + F8F4D1C1121D79AC002D09C1 /* ArqFark.h */, + F8F4D1C2121D79AC002D09C1 /* ArqFark.m */, + F8F4D1E5121D7DA2002D09C1 /* FarkPath.h */, + F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */, + F8F4D1AC121D7990002D09C1 /* ArqPackSet.h */, + F8F4D1AD121D7990002D09C1 /* ArqPackSet.m */, + F8F4D1AE121D7990002D09C1 /* ArqRepo.h */, + F8F4D1AF121D7990002D09C1 /* ArqRepo.m */, + F8F4D205121D8696002D09C1 /* ArqRepo_Verifier.h */, + F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */, F8D678311160F62E00CC270E /* ArqUserLibrary.h */, F8D678321160F62E00CC270E /* ArqUserLibrary.m */, F805B54B1160D3E6007EC01E /* ArqRestoreCommand.h */, @@ -483,8 +514,6 @@ 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 */, @@ -501,24 +530,18 @@ F8D678B71160FB2100CC270E /* Node.m */, F8D6783A1160F70100CC270E /* PackIndexEntry.h */, F8D6783B1160F70100CC270E /* PackIndexEntry.m */, - F8D678291160F5D000CC270E /* PackSetSet.h */, - F8D6782A1160F5D000CC270E /* PackSetSet.m */, - F8D6782B1160F5D000CC270E /* PackSet.h */, - F8D6782C1160F5D000CC270E /* PackSet.m */, F8D677FE1160F26A00CC270E /* Restorer.h */, F8D677FF1160F26A00CC270E /* Restorer.m */, F8D67F6E1161443600CC270E /* RestoreNode.h */, F8D67F6F1161443600CC270E /* RestoreNode.m */, - F8D678131160F4E300CC270E /* S3Fark.h */, - F8D678141160F4E300CC270E /* S3Fark.m */, - F8D678151160F4E300CC270E /* S3Repo.h */, - F8D678161160F4E300CC270E /* S3Repo.m */, F8D6785D1160F7CF00CC270E /* Tree.h */, F8D6785E1160F7CF00CC270E /* Tree.m */, F8D67CE91161363A00CC270E /* FileAttributes.h */, F8D67CEA1161363A00CC270E /* FileAttributes.m */, F8D67CF01161366100CC270E /* XAttrSet.h */, F8D67CF11161366100CC270E /* XAttrSet.m */, + F8F4D1E0121D7BC3002D09C1 /* AppKeychain.h */, + F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */, ); name = Source; sourceTree = ""; @@ -578,6 +601,8 @@ F805B7651160DD60007EC01E /* s3 */ = { isa = PBXGroup; children = ( + F83C1D0811CA929D0001958F /* BucketVerifier.h */, + F83C1D0911CA929D0001958F /* BucketVerifier.m */, F805B7681160DD60007EC01E /* HTTPConnection_S3.h */, F805B7691160DD60007EC01E /* HTTPConnection_S3.m */, F805B76A1160DD60007EC01E /* NSError_S3.h */, @@ -608,6 +633,8 @@ F805B7A61160DEF2007EC01E /* shared */ = { isa = PBXGroup; children = ( + F8F4D19B121D77E1002D09C1 /* NSError_extra.h */, + F8F4D19C121D77E1002D09C1 /* NSError_extra.m */, F8D6788A1160F8E500CC270E /* BinarySHA1.h */, F8D6788B1160F8E500CC270E /* BinarySHA1.m */, F805B7D61160E456007EC01E /* BlobACL.h */, @@ -655,6 +682,8 @@ F805B8081160E7A1007EC01E /* io */ = { isa = PBXGroup; children = ( + F8F4D19E121D78AD002D09C1 /* MonitoredInputStream.h */, + F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */, F8D678A31160FA5F00CC270E /* BooleanIO.h */, F8D678A41160FA5F00CC270E /* BooleanIO.m */, F805B8331160E882007EC01E /* BufferedInputStream.h */, @@ -844,11 +873,7 @@ F805B86A1160EA83007EC01E /* NSData-Base64Extensions.m in Sources */, F805B86F1160EAC1007EC01E /* DataInputStreamFactory.m in Sources */, F8D678001160F26A00CC270E /* Restorer.m in Sources */, - F8D678171160F4E300CC270E /* S3Fark.m in Sources */, - F8D678181160F4E300CC270E /* S3Repo.m in Sources */, F8D6781D1160F4FD00CC270E /* SHA1Hash.m in Sources */, - F8D6782D1160F5D000CC270E /* PackSetSet.m in Sources */, - F8D6782E1160F5D000CC270E /* PackSet.m in Sources */, F8D678331160F62E00CC270E /* ArqUserLibrary.m in Sources */, F8D6783C1160F70100CC270E /* PackIndexEntry.m in Sources */, F8D678451160F74A00CC270E /* DiskPack.m in Sources */, @@ -881,6 +906,15 @@ F8D67F701161443600CC270E /* RestoreNode.m in Sources */, F83C1AE311CA7C7C0001958F /* arq_restore.m in Sources */, F83C1D0A11CA929D0001958F /* BucketVerifier.m in Sources */, + F8F4D19D121D77E1002D09C1 /* NSError_extra.m in Sources */, + F8F4D1A0121D78AD002D09C1 /* MonitoredInputStream.m in Sources */, + F8F4D1B0121D7990002D09C1 /* ArqPackSet.m in Sources */, + F8F4D1B1121D7990002D09C1 /* ArqRepo.m in Sources */, + F8F4D1C3121D79AC002D09C1 /* ArqFark.m in Sources */, + F8F4D1E2121D7BC3002D09C1 /* AppKeychain.m in Sources */, + F8F4D1E7121D7DA2002D09C1 /* FarkPath.m in Sources */, + F8F4D1FE121D8409002D09C1 /* PackIndexWriter.m in Sources */, + F8F4D207121D8696002D09C1 /* ArqRepo_Verifier.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -938,11 +972,7 @@ 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 */, @@ -975,6 +1005,14 @@ F83C1AC711CA7C170001958F /* RestoreNode.m in Sources */, F83C1AC911CA7C170001958F /* ArqVerifyCommand.m in Sources */, F83C1D1D11CA95AF0001958F /* BucketVerifier.m in Sources */, + F8F4D59A121D9FA7002D09C1 /* MonitoredInputStream.m in Sources */, + F8F4D59B121D9FAD002D09C1 /* ArqFark.m in Sources */, + F8F4D59C121D9FAE002D09C1 /* ArqPackSet.m in Sources */, + F8F4D59D121D9FAF002D09C1 /* ArqRepo.m in Sources */, + F8F4D59E121D9FAF002D09C1 /* ArqRepo_Verifier.m in Sources */, + F8F4D5A1121D9FC2002D09C1 /* AppKeychain.m in Sources */, + F8F4D5A2121D9FC9002D09C1 /* FarkPath.m in Sources */, + F8F4D5A3121D9FCF002D09C1 /* PackIndexWriter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/crypto/NSData-Encrypt.h b/crypto/NSData-Encrypt.h index 176eafc..95653f6 100644 --- a/crypto/NSData-Encrypt.h +++ b/crypto/NSData-Encrypt.h @@ -35,6 +35,8 @@ #define ARQ_DEFAULT_CIPHER_NAME @"aes256" @interface NSData (Encrypt) ++ (NSString *)encryptErrorDomain; ++ (NSString *)decryptErrorDomain; - (NSData *)encryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error; - (NSData *)decryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error; diff --git a/crypto/NSData-Encrypt.m b/crypto/NSData-Encrypt.m index ea898c5..7cf60c1 100644 --- a/crypto/NSData-Encrypt.m +++ b/crypto/NSData-Encrypt.m @@ -57,7 +57,7 @@ if ([data length] > 0) { NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; if ([keyData length] > EVP_MAX_KEY_LENGTH) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH); + SETNSERROR([NSData encryptErrorDomain], -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH); break; } if (![OpenSSL initializeSSL:error]) { @@ -65,7 +65,7 @@ } cipher = EVP_get_cipherbyname([cipherName UTF8String]); if (!cipher) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"failed to load %@ cipher: %@", cipherName, [OpenSSL errorMessage]); + SETNSERROR([NSData encryptErrorDomain], -1, @"failed to load %@ cipher: %@", cipherName, [OpenSSL errorMessage]); break; } @@ -96,7 +96,7 @@ } if (!EVP_EncryptInit(&cipherContext, cipher, evp_key, iv)) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData encryptErrorDomain], -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]); return nil; } EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH); @@ -106,12 +106,12 @@ int outlen; if (!EVP_EncryptUpdate(&cipherContext, outbuf, &outlen, [data bytes], [data length])) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptUpdate: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData encryptErrorDomain], -1, @"EVP_EncryptUpdate: %@", [OpenSSL errorMessage]); return nil; } int templen; if (!EVP_EncryptFinal(&cipherContext, outbuf + outlen, &templen)) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptFinal: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData encryptErrorDomain], -1, @"EVP_EncryptFinal: %@", [OpenSSL errorMessage]); return nil; } outlen += templen; @@ -134,7 +134,7 @@ } if (!EVP_DecryptInit(&cipherContext, cipher, evp_key, iv)) { - SETNSERROR(@"NSDataDecryptErrorDomain", -1, @"EVP_DecryptInit: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData decryptErrorDomain], -1, @"EVP_DecryptInit: %@", [OpenSSL errorMessage]); return nil; } EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH); @@ -151,12 +151,12 @@ int outlen; if (!EVP_DecryptUpdate(&cipherContext, outbuf, &outlen, input, inlen)) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_DecryptUpdate: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData decryptErrorDomain], -1, @"EVP_DecryptUpdate: %@", [OpenSSL errorMessage]); return nil; } int templen; if (!EVP_DecryptFinal(&cipherContext, outbuf + outlen, &templen)) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_DecryptFinal: %@", [OpenSSL errorMessage]); + SETNSERROR([NSData decryptErrorDomain], -1, @"EVP_DecryptFinal: %@", [OpenSSL errorMessage]); return nil; } outlen += templen; @@ -167,6 +167,12 @@ @end @implementation NSData (Encrypt) ++ (NSString *)encryptErrorDomain { + return @"NSDataEncryptErrorDomain"; +} ++ (NSString *)decryptErrorDomain { + return @"NSDataDecryptErrorDomain"; +} - (NSData *)encryptWithCipher:(NSString *)cipherName key:(NSString *)key error:(NSError **)error { NSData *ret = nil; Crypter *crypter = [[Crypter alloc] initWithCipher:cipherName key:key data:self error:error]; diff --git a/crypto/SHA1Hash.h b/crypto/SHA1Hash.h index b7a8825..ba0480a 100644 --- a/crypto/SHA1Hash.h +++ b/crypto/SHA1Hash.h @@ -30,7 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #import #import "InputStream.h" @class Blob; diff --git a/crypto/SHA1Hash.m b/crypto/SHA1Hash.m index e73e52b..ef20bea 100644 --- a/crypto/SHA1Hash.m +++ b/crypto/SHA1Hash.m @@ -32,6 +32,8 @@ #import "SHA1Hash.h" #include +#include +#import "SetNSError.h" #import "FileInputStream.h" #import "NSErrorCodes.h" #import "Blob.h" @@ -62,7 +64,7 @@ static NSString *digest2String(unsigned char *digest) { return digest2String(md); } + (NSString *)hashBlob:(Blob *)blob blobLength:(unsigned long long *)blobLength error:(NSError **)error { - id is = [blob newInputStream:self]; + id is = [[blob inputStreamFactory] newInputStream]; if (is == nil) { return nil; } @@ -71,11 +73,13 @@ static NSString *digest2String(unsigned char *digest) { return sha1; } + (NSString *)hashFile:(NSString *)path error:(NSError **)error { - NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:error]; - if (attribs == nil) { + struct stat st; + if (lstat([path fileSystemRepresentation], &st) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"lstat(%@): %s", path, strerror(errno)); return NO; } - FileInputStream *fis = [[FileInputStream alloc] initWithPath:path length:[[attribs objectForKey:NSFileSize] unsignedLongLongValue]]; + unsigned long long length = (unsigned long long)st.st_size; + FileInputStream *fis = [[FileInputStream alloc] initWithPath:path offset:0 length:length]; NSString *sha1 = [SHA1Hash hashStream:fis error:error]; [fis release]; return sha1; diff --git a/http/CFStreamPair.h b/http/CFStreamPair.h new file mode 100644 index 0000000..53c9e5a --- /dev/null +++ b/http/CFStreamPair.h @@ -0,0 +1,27 @@ +// +// CFStreamPair.h +// CFN +// +// Created by Stefan Reitshamer on 2/25/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +#import "StreamPair.h" +@class CFStreamInputStream; +@class CFStreamOutputStream; + +@interface CFStreamPair : NSObject { + NSString *description; + CFStreamInputStream *is; + CFStreamOutputStream *os; + NSTimeInterval createTime; + NSTimeInterval maxLifetime; + BOOL closeRequested; + +} ++ (NSString *)errorDomain; ++ (NSError *)NSErrorWithNetworkError:(CFErrorRef)err; +- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime; + +@end diff --git a/http/CFStreamPair.m b/http/CFStreamPair.m new file mode 100644 index 0000000..1ef86f3 --- /dev/null +++ b/http/CFStreamPair.m @@ -0,0 +1,209 @@ +// +// CFStreamPair.m +// CFN +// +// Created by Stefan Reitshamer on 2/25/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#include +#include +#include +#import +#import "CFStreamPair.h" +#import "CFStreamInputStream.h" +#import "CFStreamOutputStream.h" +#import "DNS_SDErrors.h" + +static uint32_t HTTP_PORT = 80; +static uint32_t HTTPS_PORT = 443; + +@implementation CFStreamPair ++ (NSString *)errorDomain { + return @"CFStreamPairErrorDomain"; +} ++ (NSError *)NSErrorWithNetworkError:(CFErrorRef)err { + NSString *localizedDescription = @"Network error"; + NSString *domain = (NSString *)CFErrorGetDomain(err); + CFIndex code = CFErrorGetCode(err); + CFDictionaryRef userInfo = CFErrorCopyUserInfo(err); + if ([domain isEqualToString:(NSString *)kCFErrorDomainCFNetwork]) { + if (code == kCFHostErrorHostNotFound) { + localizedDescription = @"host not found"; + } else if (code == kCFHostErrorUnknown) { + int gaiCode = 0; + if (CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(userInfo, kCFGetAddrInfoFailureKey), kCFNumberIntType, &gaiCode)) { + HSLogDebug(@"Host lookup error: %s", gai_strerror(gaiCode)); + localizedDescription = @"Could not connect to the Internet"; + } + } else if (code == kCFSOCKSErrorUnknownClientVersion) { + localizedDescription = @"Unknown SOCKS client version"; + } else if (code == kCFSOCKSErrorUnsupportedServerVersion) { + localizedDescription = [NSString stringWithFormat:@"Unsupported SOCKS server version (server requested version %@)", (NSString *)CFDictionaryGetValue(userInfo, kCFSOCKSVersionKey)]; + } else if (code == kCFSOCKS4ErrorRequestFailed) { + localizedDescription = @"SOCKS4 request rejected or failed"; + } else if (code == kCFSOCKS4ErrorIdentdFailed) { + localizedDescription = @"SOCKS4 server cannot connect to identd on the client"; + } else if (code == kCFSOCKS4ErrorIdConflict) { + localizedDescription = @"SOCKS4 client and identd report different user IDs"; + } else if (code == kCFSOCKS4ErrorUnknownStatusCode) { + localizedDescription = [NSString stringWithFormat:@"SOCKS4 error %@", (NSString *)CFDictionaryGetValue(userInfo, kCFSOCKSStatusCodeKey)]; + } else if (code == kCFSOCKS5ErrorBadState) { + localizedDescription = @"SOCKS5 bad state"; + } else if (code == kCFSOCKS5ErrorBadResponseAddr) { + localizedDescription = @"SOCKS5 bad credentials"; + } else if (code == kCFSOCKS5ErrorBadCredentials) { + localizedDescription = @"SOCKS5 unsupported negotiation method"; + } else if (code == kCFSOCKS5ErrorUnsupportedNegotiationMethod) { + localizedDescription = @"SOCKS5 unsupported negotiation method"; + } else if (code == kCFSOCKS5ErrorNoAcceptableMethod) { + localizedDescription = @"SOCKS5 no acceptable method"; + } else if (code == kCFNetServiceErrorUnknown) { + localizedDescription = @"Unknown Net Services error"; + } else if (code == kCFNetServiceErrorCollision) { + localizedDescription = @"Net Services: collision"; + } else if (code == kCFNetServiceErrorNotFound) { + localizedDescription = @"Net Services: not found"; + } else if (code == kCFNetServiceErrorInProgress) { + localizedDescription = @"Net Services: in progress"; + } else if (code == kCFNetServiceErrorBadArgument) { + localizedDescription = @"Net Services: bad argument"; + } else if (code == kCFNetServiceErrorCancel) { + localizedDescription = @"Net Services: cancelled"; + } else if (code == kCFNetServiceErrorInvalid) { + localizedDescription = @"Net Services: invalid"; + } else if (code == kCFNetServiceErrorTimeout) { + localizedDescription = @"Net Services timeout"; + } else if (code == kCFNetServiceErrorDNSServiceFailure) { + localizedDescription = @"Net Services DNS failure"; + int dns_sdCode = 0; + if (CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(userInfo, kCFDNSServiceFailureKey), kCFNumberIntType, &dns_sdCode)) { + localizedDescription = [NSString stringWithFormat:@"Net Services DNS failure: %@", [DNS_SDErrors descriptionForDNS_SDError:dns_sdCode]]; + } + } else if (code == kCFFTPErrorUnexpectedStatusCode) { + localizedDescription = [NSString stringWithFormat:@"FTP error %@", (NSString *)CFDictionaryGetValue(userInfo, kCFFTPStatusCodeKey)]; + } else if (code == kCFErrorHTTPAuthenticationTypeUnsupported) { + localizedDescription = @"HTTP authentication type unsupported"; + } else if (code == kCFErrorHTTPBadCredentials) { + localizedDescription = @"bad HTTP credentials"; + } else if (code == kCFErrorHTTPConnectionLost) { + localizedDescription = @"HTTP connection lost"; + } else if (code == kCFErrorHTTPParseFailure) { + localizedDescription = @"HTTP parse failure"; + } else if (code == kCFErrorHTTPRedirectionLoopDetected) { + localizedDescription = @"HTTP redirection loop detected"; + } else if (code == kCFErrorHTTPBadURL) { + localizedDescription = @"bad HTTP URL"; + } else if (code == kCFErrorHTTPProxyConnectionFailure) { + localizedDescription = @"HTTP proxy connection failure"; + } else if (code == kCFErrorHTTPBadProxyCredentials) { + localizedDescription = @"bad HTTP proxy credentials"; + } else if (code == kCFErrorPACFileError) { + localizedDescription = @"HTTP PAC file error"; + } + } else if ([domain isEqualToString:@"NSPOSIXErrorDomain"] && code == ENOTCONN) { + localizedDescription = @"Lost connection to the Internet"; + } else { + localizedDescription = [(NSString *)CFErrorCopyDescription(err) autorelease]; + } + CFRelease(userInfo); + return [NSError errorWithDomain:[CFStreamPair errorDomain] code:code userInfo:[NSDictionary dictionaryWithObjectsAndKeys:localizedDescription, NSLocalizedDescriptionKey, nil]]; +} +- (id)initWithHost:(NSString *)theHost useSSL:(BOOL)isUseSSL maxLifetime:(NSTimeInterval)theMaxLifetime { + if (self = [super init]) { + description = [[NSString alloc] initWithFormat:@"", theHost, (isUseSSL ? @"YES" : @"NO")]; + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)theHost, (isUseSSL ? HTTPS_PORT : HTTP_PORT), &readStream, &writeStream); + if (isUseSSL) { + NSDictionary *sslProperties = [NSDictionary dictionaryWithObjectsAndKeys: + (NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel, + kCFBooleanTrue, kCFStreamSSLAllowsExpiredCertificates, + kCFBooleanTrue, kCFStreamSSLAllowsExpiredRoots, + kCFBooleanTrue, kCFStreamSSLAllowsAnyRoot, + kCFBooleanFalse, kCFStreamSSLValidatesCertificateChain, + kCFNull, kCFStreamSSLPeerName, + nil]; + + CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, sslProperties); + CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, sslProperties); + } + NSDictionary *proxyDict = (NSDictionary *)SCDynamicStoreCopyProxies(NULL); + if ([proxyDict objectForKey:(NSString *)kCFStreamPropertyHTTPProxyHost] != nil) { + CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxyDict); + CFWriteStreamSetProperty(writeStream, kCFStreamPropertyHTTPProxy, proxyDict); + } + if ([proxyDict objectForKey:(NSString *)kCFStreamPropertySOCKSProxyHost] != nil && [proxyDict objectForKey:(NSString *)kCFStreamPropertySOCKSProxyPort] != nil) { + CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, proxyDict); + CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, proxyDict); + } + [proxyDict release]; + + is = [[CFStreamInputStream alloc] initWithCFReadStream:readStream]; + os = [[CFStreamOutputStream alloc] initWithCFWriteStream:writeStream]; + CFRelease(readStream); + CFRelease(writeStream); + createTime = [NSDate timeIntervalSinceReferenceDate]; + maxLifetime = theMaxLifetime; + } + return self; +} +- (void)dealloc { + [description release]; + [is release]; + [os release]; + [super dealloc]; +} +- (void)setCloseRequested { + closeRequested = YES; +} +- (BOOL)isUsable { + if (closeRequested) { + HSLogTrace(@"%@ close requested; not reusing", self); + return NO; + } + if (([NSDate timeIntervalSinceReferenceDate] - createTime) > maxLifetime) { + HSLogTrace(@"%@ > %f seconds old; not reusing", self, maxLifetime); + return NO; + } + return YES; +} + +#pragma mark BufferedInputStream +- (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error { + return [is readExactly:exactLength error:error]; +} +- (unsigned char *)readMaximum:(NSUInteger)maximum length:(NSUInteger *)length error:(NSError **)error { + return [is readMaximum:maximum length:length error:error]; +} +- (uint64_t)bytesReceived { + return [is bytesReceived]; +} +- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { + return [is read:length error:error]; +} +- (NSData *)slurp:(NSError **)error { + return [is slurp:error]; +} + +#pragma mark OutputStream +- (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error { + NSError *myError = nil; + BOOL ret = [os write:buf length:len error:&myError]; + if (error != NULL) { + *error = myError; + } + if (!ret && [[myError domain] isEqualToString:@"UnixErrorDomain"] && [myError code] == EPIPE) { + HSLogError(@"broken pipe"); //FIXME: This may not work with CFStream stuff. + } + return ret; +} +- (unsigned long long)bytesWritten { + return [os bytesWritten]; +} + +#pragma mark NSObject +- (NSString *)description { + return description; +} +@end diff --git a/http/HTTPConnection.h b/http/HTTPConnection.h index 54f3f14..f801a56 100644 --- a/http/HTTPConnection.h +++ b/http/HTTPConnection.h @@ -31,8 +31,9 @@ */ #import -@protocol InputStream; +#import "InputStream.h" @protocol StreamPair; +@protocol BufferedInputStream; @class HTTPRequest; @class HTTPResponse; @@ -49,11 +50,12 @@ - (void)setRequestContentDispositionHeader:(NSString *)downloadName; - (void)setRFC822DateRequestHeader; - (BOOL)executeRequest:(NSError **)error; +- (BOOL)executeRequestWithBody:(id )bodyStream error:(NSError **)error; - (int)responseCode; - (NSString *)responseHeaderForKey:(NSString *)key; - (NSString *)responseMimeType; - (NSString *)responseDownloadName; -- (id )newResponseBodyStream:(NSError **)error; +- (id )newResponseBodyStream:(NSError **)error; - (NSData *)slurpResponseBody:(NSError **)error; - (void)setCloseRequested; @end diff --git a/http/HTTPConnection.m b/http/HTTPConnection.m index aa83303..7049471 100644 --- a/http/HTTPConnection.m +++ b/http/HTTPConnection.m @@ -81,9 +81,15 @@ [request setRFC822DateHeader]; } - (BOOL)executeRequest:(NSError **)error { + return [self executeRequestWithBody:nil error:error]; +} +- (BOOL)executeRequestWithBody:(id )bodyStream error:(NSError **)error { if (![request write:streamPair error:error]) { return NO; } + if (bodyStream != nil && ![Streams transferFrom:bodyStream to:streamPair error:error]) { + return NO; + } if (![response readHead:streamPair requestMethod:[request method] error:error]) { return NO; } @@ -117,7 +123,7 @@ } return downloadName; } -- (id )newResponseBodyStream:(NSError **)error { +- (id )newResponseBodyStream:(NSError **)error { return [response newResponseInputStream:streamPair error:error]; } - (NSData *)slurpResponseBody:(NSError **)error { diff --git a/http/HTTPRequest.h b/http/HTTPRequest.h index 5a2df30..ce98269 100644 --- a/http/HTTPRequest.h +++ b/http/HTTPRequest.h @@ -31,7 +31,7 @@ */ #import -@protocol OutputStream; +#import "OutputStream.h" @class RFC2616DateFormatter; @interface HTTPRequest : NSObject { diff --git a/http/HTTPRequest.m b/http/HTTPRequest.m index 37356d1..d22442f 100644 --- a/http/HTTPRequest.m +++ b/http/HTTPRequest.m @@ -73,6 +73,7 @@ - (void)setContentDispositionHeader:(NSString *)downloadName { if (downloadName != nil) { NSString *encodedFilename = [NSString stringWithFormat:@"\"%@\"", [downloadName stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\\\""]]; + encodedFilename = [encodedFilename stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; NSString *contentDisposition = [NSString stringWithFormat:@"attachment;filename=%@", encodedFilename]; [self setHeader:contentDisposition forKey:@"Content-Disposition"]; } diff --git a/http/HTTPResponse.h b/http/HTTPResponse.h index c237aa6..e617ac1 100644 --- a/http/HTTPResponse.h +++ b/http/HTTPResponse.h @@ -32,7 +32,6 @@ #import @protocol BufferedInputStream; -@protocol InputStream; @class FDInputStream; @interface HTTPResponse : NSObject { @@ -47,5 +46,5 @@ - (NSString *)protocol; - (NSString *)headerForKey:(NSString *)key; - (unsigned long long)contentLength; -- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error; +- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error; @end diff --git a/http/HTTPResponse.m b/http/HTTPResponse.m index 8998906..76fba97 100644 --- a/http/HTTPResponse.m +++ b/http/HTTPResponse.m @@ -78,8 +78,8 @@ } return (unsigned long long)contentLength; } -- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error { - id ret = nil; +- (id )newResponseInputStream:(id )underlyingStream error:(NSError **)error { + id ret = nil; if ([requestMethod isEqualToString:@"HEAD"] || code == 204) { ret = [[NSData data] newInputStream]; } else { diff --git a/http/StreamPair.h b/http/StreamPair.h new file mode 100644 index 0000000..2892527 --- /dev/null +++ b/http/StreamPair.h @@ -0,0 +1,17 @@ +// +// StreamPair.h +// CFN +// +// Created by Stefan Reitshamer on 2/25/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +#import "BufferedInputStream.h" +#import "OutputStream.h" + +@protocol StreamPair +- (void)setCloseRequested; +- (BOOL)isUsable; + +@end diff --git a/http/StreamPairFactory.h b/http/StreamPairFactory.h new file mode 100644 index 0000000..62941b1 --- /dev/null +++ b/http/StreamPairFactory.h @@ -0,0 +1,22 @@ +// +// StreamPairFactory.h +// CFN +// +// Created by Stefan Reitshamer on 2/25/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +@protocol StreamPair; + +@interface StreamPairFactory : NSObject { + NSTimeInterval maxStreamPairLifetime; + NSLock *lock; + NSMutableDictionary *threadMap; + +} ++ (StreamPairFactory *)theFactory; +- (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime; +- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error; +- (void)clear; +@end diff --git a/http/StreamPairFactory.m b/http/StreamPairFactory.m new file mode 100644 index 0000000..72779c9 --- /dev/null +++ b/http/StreamPairFactory.m @@ -0,0 +1,153 @@ +// +// StreamPairFactory.m +// CFN +// +// Created by Stefan Reitshamer on 2/25/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "StreamPairFactory.h" +#import "CFStreamPair.h" + +static StreamPairFactory *theFactory = nil; + +#define DEFAULT_MAX_STREAM_PAIR_LIFETIME_SECONDS (60) +#define CLEANUP_THREAD_SLEEP_SECONDS (5) + +@interface StreamPairMap : NSObject { + NSMutableDictionary *streamPairs; +} +- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error; +- (void)dropUnusableStreamPairs; +@end + +@implementation StreamPairMap +- (id)init { + if (self = [super init]) { + streamPairs = [[NSMutableDictionary alloc] init]; + } + return self; +} +- (void)dealloc { + [streamPairs release]; + [super dealloc]; +} +- (id )newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error { + NSString *key = [NSString stringWithFormat:@"%@:%@", [host lowercaseString], (useSSL ? @"SSL" : @"noSSL")]; + id streamPair = [streamPairs objectForKey:key]; + if (streamPair != nil) { + if (![streamPair isUsable]) { + [streamPairs removeObjectForKey:key]; + streamPair = nil; + } else { + [streamPair retain]; + } + } + if (streamPair == nil) { + streamPair = [[CFStreamPair alloc] initWithHost:host useSSL:useSSL maxLifetime:theMaxLifetime]; + [streamPairs setObject:streamPair forKey:key]; + } + return streamPair; +} +- (void)dropUnusableStreamPairs { + NSMutableArray *keysToDrop = [NSMutableArray array]; + for (NSString *key in streamPairs) { + id streamPair = [streamPairs objectForKey:key]; + if (![streamPair isUsable]) { + [keysToDrop addObject:key]; + } + } + [streamPairs removeObjectsForKeys:keysToDrop]; +} +@end + + + +@implementation StreamPairFactory ++ (StreamPairFactory *)theFactory { + if (theFactory == nil) { + theFactory = [[super allocWithZone:NULL] init]; + } + return theFactory; +} + +/* Singleton recipe: */ ++ (id)allocWithZone:(NSZone *)zone { + return [[StreamPairFactory theFactory] retain]; +} +- (id)copyWithZone:(NSZone *)zone { + return self; +} +- (id)retain { + return self; +} +- (NSUInteger)retainCount { + return NSUIntegerMax; //denotes an object that cannot be released +} +- (void)release { + //do nothing +} +- (id)autorelease { + return self; +} + +- (id)init { + if (self = [super init]) { + lock = [[NSLock alloc] init]; + [lock setName:@"SocketFactory lock"]; + threadMap = [[NSMutableDictionary alloc] init]; + maxStreamPairLifetime = DEFAULT_MAX_STREAM_PAIR_LIFETIME_SECONDS; + [NSThread detachNewThreadSelector:@selector(dropUnusableSockets) toTarget:self withObject:nil]; + } + return self; +} +- (void)dealloc { + [lock release]; + [threadMap release]; + [super dealloc]; +} +- (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime { + maxStreamPairLifetime = theMaxLifetime; +} +- (id )newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error { + void *pthreadPtr = pthread_self(); +#ifdef __LP64__ + NSNumber *threadID = [NSNumber numberWithUnsignedLongLong:(uint64_t)pthreadPtr]; +#else + NSNumber *threadID = [NSNumber numberWithUnsignedLong:(uint32_t)pthreadPtr]; +#endif + [lock lock]; + StreamPairMap *map = [threadMap objectForKey:threadID]; + if (map == nil) { + map = [[StreamPairMap alloc] init]; + [threadMap setObject:map forKey:threadID]; + [map release]; + } + id streamPair = [map newStreamPairToHost:theHost useSSL:isUseSSL maxLifeTimeSeconds:maxStreamPairLifetime error:error]; + [lock unlock]; + return streamPair; +} +- (void)clear { + [lock lock]; + [threadMap removeAllObjects]; + [lock unlock]; +} + +#pragma mark cleanup thread +- (void)dropUnusableSockets { + for (;;) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @try { + [NSThread sleepForTimeInterval:CLEANUP_THREAD_SLEEP_SECONDS]; + [lock lock]; + for (StreamPairMap *map in [threadMap allValues]) { + [map dropUnusableStreamPairs]; + } + [lock unlock]; + } @catch(NSException *e) { + HSLogError(@"unexpected exception in StreamPairFactory cleanup thread: %@", [e description]); + } + [pool drain]; + } +} +@end diff --git a/io/BooleanIO.h b/io/BooleanIO.h index 08eb4ee..3ac826f 100644 --- a/io/BooleanIO.h +++ b/io/BooleanIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/BooleanIO.m b/io/BooleanIO.m index 44b04e3..45c4218 100644 --- a/io/BooleanIO.m +++ b/io/BooleanIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/BufferedInputStream.h b/io/BufferedInputStream.h index 8ca9f49..a60650d 100644 --- a/io/BufferedInputStream.h +++ b/io/BufferedInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/CFStreamInputStream.h b/io/CFStreamInputStream.h index d833af3..039c4a3 100644 --- a/io/CFStreamInputStream.h +++ b/io/CFStreamInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/CFStreamInputStream.m b/io/CFStreamInputStream.m index 7212345..5c8a3a7 100644 --- a/io/CFStreamInputStream.m +++ b/io/CFStreamInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -128,8 +128,6 @@ bytesReceived += (uint64_t)index; return buf; } -- (void)bytesWereNotUsed { -} - (uint64_t)bytesReceived { return bytesReceived; } diff --git a/io/CFStreamOutputStream.h b/io/CFStreamOutputStream.h index 60c0fc8..a6ce164 100644 --- a/io/CFStreamOutputStream.h +++ b/io/CFStreamOutputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/CFStreamOutputStream.m b/io/CFStreamOutputStream.m index 65d32cf..1896528 100644 --- a/io/CFStreamOutputStream.m +++ b/io/CFStreamOutputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/ChunkedInputStream.h b/io/ChunkedInputStream.h index a2fc02a..f075470 100644 --- a/io/ChunkedInputStream.h +++ b/io/ChunkedInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/ChunkedInputStream.m b/io/ChunkedInputStream.m index 434146d..4514e8b 100644 --- a/io/ChunkedInputStream.m +++ b/io/ChunkedInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -87,6 +87,4 @@ - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; } -- (void)bytesWereNotUsed { -} @end diff --git a/io/CryptInputStream.h b/io/CryptInputStream.h index bd8ea02..10ba4b1 100644 --- a/io/CryptInputStream.h +++ b/io/CryptInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -43,7 +43,6 @@ typedef int (*CryptFinalFunc)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl CryptUpdateFunc cryptUpdate; CryptFinalFunc cryptFinal; id is; - NSUInteger totalInBytesRecvd; unsigned char *outBuf; NSUInteger outBufLen; const EVP_CIPHER *cipher; @@ -54,5 +53,6 @@ typedef int (*CryptFinalFunc)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl BOOL initialized; BOOL finalized; } ++ (NSString *)errorDomain; - (id)initWithCryptInitFunc:(void *)theCryptInit cryptUpdateFunc:(void *)theCryptUpdate cryptFinalFunc:(void *)theCryptFinal inputStream:(id )theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error; @end diff --git a/io/CryptInputStream.m b/io/CryptInputStream.m index 269ec93..3e42ee1 100644 --- a/io/CryptInputStream.m +++ b/io/CryptInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -36,11 +36,12 @@ #import "InputStreams.h" #import "NSErrorCodes.h" -@interface CryptInputStream (internal) -- (unsigned char *)readAtLeastBlockSize:(NSUInteger *)length error:(NSError **)error; -@end +#define MY_BUF_SIZE (4096) @implementation CryptInputStream ++ (NSString *)errorDomain { + return @"CryptInputStreamErrorDomain"; +} - (id)initWithCryptInitFunc:(void *)theCryptInit cryptUpdateFunc:(void *)theCryptUpdate cryptFinalFunc:(void *)theCryptFinal inputStream:(id )theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error { if (self = [super init]) { cryptInit = (CryptInitFunc)theCryptInit; @@ -51,7 +52,7 @@ is = [theIS retain]; NSData *keyData = [theKey dataUsingEncoding:NSUTF8StringEncoding]; if ([keyData length] > EVP_MAX_KEY_LENGTH) { - SETNSERROR(@"EncryptedInputStreamErrorDomain", -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH); + SETNSERROR([CryptInputStream errorDomain], -1, @"encryption key must be less than or equal to %d bytes", EVP_MAX_KEY_LENGTH); break; } if (![OpenSSL initializeSSL:error]) { @@ -59,18 +60,20 @@ } cipher = EVP_get_cipherbyname([theCipherName UTF8String]); if (!cipher) { - SETNSERROR(@"EncryptedInputStreamErrorDomain", -1, @"failed to load %@ cipher: %@", theCipherName, [OpenSSL errorMessage]); + SETNSERROR([CryptInputStream errorDomain], -1, @"failed to load %@ cipher: %@", theCipherName, [OpenSSL errorMessage]); break; } evp_key[0] = 0; EVP_BytesToKey(cipher, EVP_md5(), NULL, [keyData bytes], [keyData length], 1, evp_key, iv); EVP_CIPHER_CTX_init(&cipherContext); if (!(*cryptInit)(&cipherContext, cipher, evp_key, iv)) { - SETNSERROR(@"NSDataEncryptErrorDomain", -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]); + SETNSERROR([CryptInputStream errorDomain], -1, @"EVP_EncryptInit: %@", [OpenSSL errorMessage]); break; } EVP_CIPHER_CTX_set_key_length(&cipherContext, EVP_MAX_KEY_LENGTH); blockSize = (unsigned long long)EVP_CIPHER_CTX_block_size(&cipherContext); + outBufLen = MY_BUF_SIZE + blockSize - 1; + outBuf = (unsigned char *)malloc(outBufLen); initialized = YES; ret = YES; } while(0); @@ -99,89 +102,63 @@ } - (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { if (finalized) { - SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"already finalized"); + SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); return NULL; } NSUInteger inLen = 0; + NSError *myError = nil; + unsigned char *inBuf = [is read:&inLen error:&myError]; int outLen = 0; - NSError *myError; - unsigned char *inBuf = [self readAtLeastBlockSize:&inLen error:&myError]; - if (inBuf == NULL && [myError code] != ERROR_EOF) { - if (error != NULL) { - *error = myError; - } - return NULL; - } - NSUInteger neededBufLen = inLen + blockSize; - if (outBufLen < neededBufLen) { - if (outBuf == NULL) { - outBuf = (unsigned char *)malloc(neededBufLen); + if (inBuf == NULL) { + if ([myError code] == ERROR_EOF) { + finalized = YES; + if (!(cryptFinal)(&cipherContext, outBuf, &outLen)) { + SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt final: %@", [OpenSSL errorMessage]); + return NULL; + } + if (outLen == 0) { + // Don't return 0 bytes and make the caller have to call again. + SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); + return NULL; + } + *length = (NSUInteger)outLen; + return outBuf; } else { - outBuf = (unsigned char *)realloc(outBuf, neededBufLen); - } - outBufLen = neededBufLen; - } - if (inBuf != NULL) { - NSAssert(inLen > 0, @"expected more than 0 input bytes"); - totalInBytesRecvd += inLen; - if (!(*cryptUpdate)(&cipherContext, outBuf, &outLen, inBuf, inLen)) { - SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt update: %@", [OpenSSL errorMessage]); + if (error != NULL) { + *error = myError; + } return NULL; } - NSAssert(outLen < outBufLen, @"can't receive more bytes than outBufLen from EVP_EncryptUpdate"); + } + NSUInteger needed = inLen + blockSize - 1; + if (outBufLen < needed) { + outBuf = (unsigned char *)realloc(outBuf, needed); + if (outBuf == NULL) { + SETNSERROR(@"MallocErrorDomain", -1, @"malloc failed"); + return NULL; + } + outBufLen = needed; + } + if (!(*cryptUpdate)(&cipherContext, outBuf, &outLen, inBuf, inLen)) { + SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt update: %@", [OpenSSL errorMessage]); + return NULL; } if (outLen == 0) { finalized = YES; - if (totalInBytesRecvd > 0 && !(*cryptFinal)(&cipherContext, outBuf, &outLen)) { + if (!(cryptFinal)(&cipherContext, outBuf, &outLen)) { SETNSERROR(@"OpenSSLErrorDomain", -1, @"crypt final: %@", [OpenSSL errorMessage]); return NULL; } - NSAssert(outLen < outBufLen, @"can't receive more bytes than outBufLen from EVP_EncryptFinal"); + if (outLen == 0) { + // Don't return 0 bytes and make the caller have to call again. + SETNSERROR([CryptInputStream errorDomain], ERROR_EOF, @"EOF"); + return NULL; + } } - if (outLen == 0) { - SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on encrypted input stream"); - return NULL; - } - NSAssert(outLen > 0, @"outLen must be greater than 0"); *length = (NSUInteger)outLen; return outBuf; } - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; } -- (void)bytesWereNotUsed { -} - -@end -@implementation CryptInputStream (internal) -- (unsigned char *)readAtLeastBlockSize:(NSUInteger *)length error:(NSError **)error { - NSMutableData *data = [NSMutableData data]; - while ([data length] < blockSize) { - NSUInteger recvd = 0; - NSError *myError = nil; - unsigned char *buf = [is read:&recvd error:&myError]; - if (buf == NULL) { - if ([myError code] != ERROR_EOF) { - if (error != NULL) { - *error = myError; - } - return NULL; - } - break; - } - if (recvd > blockSize && [data length] == 0) { - // Short-circuit to avoid a buffer copy. - *length = recvd; - return buf; - } - [data appendBytes:buf length:recvd]; - } - if ([data length] == 0) { - SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF"); - return NULL; - } - NSAssert([data length] > 0, @"must have received some bytes"); - *length = [data length]; - return [data mutableBytes]; -} @end diff --git a/io/DataIO.h b/io/DataIO.h index 655d016..f57ddee 100644 --- a/io/DataIO.h +++ b/io/DataIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DataIO.m b/io/DataIO.m index 28b05dd..a763c64 100644 --- a/io/DataIO.m +++ b/io/DataIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DataInputStream.h b/io/DataInputStream.h index 9d3d9ef..74cec4e 100644 --- a/io/DataInputStream.h +++ b/io/DataInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DataInputStream.m b/io/DataInputStream.m index 26601c4..0b56377 100644 --- a/io/DataInputStream.m +++ b/io/DataInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -93,8 +93,6 @@ pos = [data length]; return ret; } -- (void)bytesWereNotUsed { -} - (uint64_t)bytesReceived { return (uint64_t)pos; } diff --git a/io/DataInputStreamFactory.h b/io/DataInputStreamFactory.h index 8c03b73..9cd7c03 100644 --- a/io/DataInputStreamFactory.h +++ b/io/DataInputStreamFactory.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DataInputStreamFactory.m b/io/DataInputStreamFactory.m index 83c9a18..cb57f56 100644 --- a/io/DataInputStreamFactory.m +++ b/io/DataInputStreamFactory.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #import "DataInputStreamFactory.h" #import "DataInputStream.h" #import "NSData-InputStream.h" @@ -47,10 +48,12 @@ } #pragma mark InputStreamFactory protocol -- (id ) newInputStream:(id)sender { +- (id ) newInputStream { return [data newInputStream]; } -- (id ) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength { - return [[DataInputStream alloc] initWithData:data offset:theOffset length:theLength]; + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", [data length]]; } @end diff --git a/io/DateIO.h b/io/DateIO.h index 61b843e..bed2cbf 100644 --- a/io/DateIO.h +++ b/io/DateIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DateIO.m b/io/DateIO.m index 4fbc4e8..6849037 100644 --- a/io/DateIO.m +++ b/io/DateIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DecryptedInputStream.h b/io/DecryptedInputStream.h index 6a5af39..6932e16 100644 --- a/io/DecryptedInputStream.h +++ b/io/DecryptedInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DecryptedInputStream.m b/io/DecryptedInputStream.m index 300ce22..6af686c 100644 --- a/io/DecryptedInputStream.m +++ b/io/DecryptedInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DoubleIO.h b/io/DoubleIO.h index 34d6004..e5732d6 100644 --- a/io/DoubleIO.h +++ b/io/DoubleIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/DoubleIO.m b/io/DoubleIO.m index b372b7e..836f015 100644 --- a/io/DoubleIO.m +++ b/io/DoubleIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/EncryptedInputStream.h b/io/EncryptedInputStream.h index 4503e26..fb80002 100644 --- a/io/EncryptedInputStream.h +++ b/io/EncryptedInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/EncryptedInputStream.m b/io/EncryptedInputStream.m index d70b2b0..4f92282 100644 --- a/io/EncryptedInputStream.m +++ b/io/EncryptedInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/FDInputStream.h b/io/FDInputStream.h index d08f0ee..eef8848 100644 --- a/io/FDInputStream.h +++ b/io/FDInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/FDInputStream.m b/io/FDInputStream.m index 77defd6..b07c9c2 100644 --- a/io/FDInputStream.m +++ b/io/FDInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -105,8 +105,6 @@ read_again: - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; } -- (void)bytesWereNotUsed { -} #pragma mark BufferedInputStream protocol - (unsigned char *)readExactly:(NSUInteger)exactLength error:(NSError **)error { diff --git a/io/FDOutputStream.h b/io/FDOutputStream.h index 4b1927e..5029231 100644 --- a/io/FDOutputStream.h +++ b/io/FDOutputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/FDOutputStream.m b/io/FDOutputStream.m index 361b5c0..7691b2f 100644 --- a/io/FDOutputStream.m +++ b/io/FDOutputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/FileInputStream.h b/io/FileInputStream.h index 4756d14..899a058 100644 --- a/io/FileInputStream.h +++ b/io/FileInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -41,6 +41,5 @@ unsigned char *buf; uint64_t bytesReceived; } -- (id)initWithPath:(NSString *)thePath length:(unsigned long long)theLength; - (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength; @end diff --git a/io/FileInputStream.m b/io/FileInputStream.m index e608577..bfc94ba 100644 --- a/io/FileInputStream.m +++ b/io/FileInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -42,15 +42,6 @@ @end @implementation FileInputStream -- (id)initWithPath:(NSString *)thePath length:(unsigned long long)theLength { - if (self = [super init]) { - fd = -1; - path = [thePath retain]; - fileLength = theLength; - buf = (unsigned char *)malloc(MY_BUF_SIZE); - } - return self; -} - (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength { if (self = [super init]) { fd = -1; @@ -142,8 +133,6 @@ read_again: - (uint64_t)bytesReceived { return bytesReceived; } -- (void)bytesWereNotUsed { -} #pragma mark NSObject protocol - (NSString *)description { diff --git a/io/FileInputStreamFactory.h b/io/FileInputStreamFactory.h index fa811c1..fb5abe1 100644 --- a/io/FileInputStreamFactory.h +++ b/io/FileInputStreamFactory.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -35,7 +35,9 @@ @interface FileInputStreamFactory : NSObject { NSString *path; + unsigned long long offset; unsigned long long length; } +- (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength; - (id)initWithPath:(NSString *)thePath error:(NSError **)error; @end diff --git a/io/FileInputStreamFactory.m b/io/FileInputStreamFactory.m index 3991883..b217862 100644 --- a/io/FileInputStreamFactory.m +++ b/io/FileInputStreamFactory.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -30,29 +30,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #import "FileInputStreamFactory.h" #import "FileInputStream.h" +#import "SetNSError.h" @implementation FileInputStreamFactory -- (id)initWithPath:(NSString *)thePath error:(NSError **)error { +- (id)initWithPath:(NSString *)thePath offset:(unsigned long long)theOffset length:(unsigned long long)theLength { if (self = [super init]) { path = [thePath copy]; - NSDictionary *attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:error]; - if (attribs == nil) { - return nil; - } - length = [[attribs objectForKey:NSFileSize] unsignedLongLongValue]; + offset = theOffset; + length = theLength; } return self; } +- (id)initWithPath:(NSString *)thePath error:(NSError **)error { + struct stat st; + if (lstat([thePath fileSystemRepresentation], &st) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"lstat(%@): %s", path, strerror(errno)); + return nil; + } + return [self initWithPath:thePath offset:0 length:(unsigned long long)st.st_size]; +} - (void)dealloc { [path release]; [super dealloc]; } -- (id ) newInputStream:(id)sender { - return [[FileInputStream alloc] initWithPath:path length:length]; +- (id ) newInputStream { + return [[FileInputStream alloc] initWithPath:path offset:offset length:length]; } -- (id ) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength { - return [[FileInputStream alloc] initWithPath:path offset:theOffset length:length]; + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", path, length]; } @end diff --git a/io/FileOutputStream.h b/io/FileOutputStream.h index 1a41eab..79f49b9 100644 --- a/io/FileOutputStream.h +++ b/io/FileOutputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -41,4 +41,6 @@ } - (id)initWithPath:(NSString *)thePath append:(BOOL)isAppend; - (void)close; +- (BOOL)seekTo:(unsigned long long)offset error:(NSError **)error; +- (NSString *)path; @end diff --git a/io/FileOutputStream.m b/io/FileOutputStream.m index 500acf5..b8284df 100644 --- a/io/FileOutputStream.m +++ b/io/FileOutputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -33,6 +33,10 @@ #import "FileOutputStream.h" #import "SetNSError.h" +@interface FileOutputStream (internal) +- (BOOL)open:(NSError **)error; +@end + @implementation FileOutputStream - (id)initWithPath:(NSString *)thePath append:(BOOL)isAppend { if (self = [super init]) { @@ -55,20 +59,24 @@ fd = -1; } } +- (BOOL)seekTo:(unsigned long long)offset error:(NSError **)error { + if (fd == -1 && ![self open:error]) { + return NO; + } + if (lseek(fd, (off_t)offset, SEEK_SET) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"lseek(%@, %qu): %s", path, offset, strerror(errno)); + return NO; + } + return YES; +} +- (NSString *)path { + return path; +} + +#pragma mark OutputStream - (BOOL)write:(const unsigned char *)buf length:(NSUInteger)len error:(NSError **)error { - if (fd == -1) { - int oflag = O_WRONLY|O_CREAT; - if (append) { - oflag |= O_APPEND; - } else { - oflag |= O_TRUNC; - } - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - fd = open([path fileSystemRepresentation], oflag, mode); - if (fd == -1) { - SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno)); - return NO; - } + if (fd == -1 && ![self open:error]) { + return NO; } int ret = 0; NSUInteger written = 0; @@ -90,3 +98,21 @@ return bytesWritten; } @end + +@implementation FileOutputStream (internal) +- (BOOL)open:(NSError **)error { + int oflag = O_WRONLY|O_CREAT; + if (append) { + oflag |= O_APPEND; + } else { + oflag |= O_TRUNC; + } + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + fd = open([path fileSystemRepresentation], oflag, mode); + if (fd == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno)); + return NO; + } + return YES; +} +@end diff --git a/io/FixedLengthInputStream.h b/io/FixedLengthInputStream.h index 7aafc13..81672c1 100644 --- a/io/FixedLengthInputStream.h +++ b/io/FixedLengthInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -32,12 +32,12 @@ #import #import "InputStream.h" -@class FDInputStream; +@protocol BufferedInputStream; @interface FixedLengthInputStream : NSObject { - FDInputStream *underlyingStream; + id underlyingStream; unsigned long long fixedLength; unsigned long long totalReceived; } -- (id)initWithUnderlyingStream:(FDInputStream *)is length:(unsigned long long)theLength; +- (id)initWithUnderlyingStream:(id )is length:(unsigned long long)theLength; @end diff --git a/io/FixedLengthInputStream.m b/io/FixedLengthInputStream.m index 4280c51..a73e88f 100644 --- a/io/FixedLengthInputStream.m +++ b/io/FixedLengthInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -37,7 +37,7 @@ #import "FDInputStream.h" @implementation FixedLengthInputStream -- (id)initWithUnderlyingStream:(FDInputStream *)is length:(unsigned long long)theLength { +- (id)initWithUnderlyingStream:(id )is length:(unsigned long long)theLength { if (self = [super init]) { underlyingStream = [is retain]; fixedLength = theLength; @@ -54,8 +54,17 @@ SETNSERROR(@"StreamsErrorDomain", ERROR_EOF, @"EOF on fixed length input stream"); return NULL; } - unsigned char *buf = [underlyingStream readMaximum:maximum length:length error:error]; + NSError *myError = nil; + unsigned char *buf = [underlyingStream readMaximum:maximum length:length error:&myError]; if (buf == NULL) { + if ([myError code] == ERROR_EOF) { + HSLogError(@"unexpected EOF when only %qu of %qu bytes received", totalReceived, fixedLength); + SETNSERROR(@"StreamsErrorDomain", -1, @"unexpected EOF when only %qu of %qu bytes received", totalReceived, fixedLength); + return NULL; + } + if (error != NULL) { + *error = myError; + } return NULL; } totalReceived += (unsigned long long)(*length); @@ -64,6 +73,4 @@ - (NSData *)slurp:(NSError **)error { return [InputStreams slurp:self error:error]; } -- (void)bytesWereNotUsed { -} @end diff --git a/io/InputStream.h b/io/InputStream.h index ec31666..5520e49 100644 --- a/io/InputStream.h +++ b/io/InputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -36,5 +36,4 @@ @protocol InputStream - (unsigned char *)read:(NSUInteger *)length error:(NSError **)error; - (NSData *)slurp:(NSError **)error; -- (void)bytesWereNotUsed; @end diff --git a/io/InputStreamFactory.h b/io/InputStreamFactory.h index eba7b54..73c489c 100644 --- a/io/InputStreamFactory.h +++ b/io/InputStreamFactory.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -36,8 +36,5 @@ @protocol InputStreamFactory // Returns an object that must be released by the caller. -- (id ) newInputStream:(id)sender; - -// Returns an object that must be released by the caller. -- (id ) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength; +- (id ) newInputStream; @end diff --git a/io/InputStreams.h b/io/InputStreams.h index 90da808..e8db3c1 100644 --- a/io/InputStreams.h +++ b/io/InputStreams.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/InputStreams.m b/io/InputStreams.m index b3f31c0..b996049 100644 --- a/io/InputStreams.m +++ b/io/InputStreams.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/IntegerIO.h b/io/IntegerIO.h index baf7e57..326793e 100644 --- a/io/IntegerIO.h +++ b/io/IntegerIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/IntegerIO.m b/io/IntegerIO.m index 8b4b7ce..3c28006 100644 --- a/io/IntegerIO.m +++ b/io/IntegerIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/S3Fark.h b/io/MonitoredInputStream.h similarity index 60% rename from S3Fark.h rename to io/MonitoredInputStream.h index e426a43..d5c6110 100644 --- a/S3Fark.h +++ b/io/MonitoredInputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -31,24 +31,16 @@ */ #import -@class S3Service; -@class Blob; -@class ServerBlob; -@class PackSetSet; +#import "InputStream.h" -@interface S3Fark : NSObject { - S3Service *s3; - NSString *s3BucketName; - NSString *computerUUID; - NSThread *creatorThread; - PackSetSet *packSetSet; +@interface MonitoredInputStream : NSObject { + id underlyingStream; + unsigned long long bytesReceived; + id delegate; } -- (id)initWithS3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID; -- (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; -- (NSArray *)reloadPacksFromS3:(NSError **)error; +- (id)initWithUnderlyingStream:(id )theUnderlyingStream delegate:(id)theDelegate; +@end + +@interface NSObject (MonitoredInputStreamDelegate) +- (BOOL)monitoredInputStream:(MonitoredInputStream *)stream receivedBytes:(unsigned long long)length error:(NSError **)error; @end diff --git a/PackSet.h b/io/MonitoredInputStream.m similarity index 50% rename from PackSet.h rename to io/MonitoredInputStream.m index 241099b..cdf13f9 100644 --- a/PackSet.h +++ b/io/MonitoredInputStream.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -30,38 +30,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import -@class Blob; -@class S3Service; -@class ServerBlob; +#import "MonitoredInputStream.h" +#import "SetNSError.h" -@interface PackSet : NSObject { - NSString *packSetName; - NSString *escapedPackSetName; - NSString *packSetDir; - S3Service *s3; - NSString *s3BucketName; - NSString *computerUUID; - BOOL keepPacksLocal; - NSMutableSet *packSHA1s; - NSString *currentPackSHA1; - NSMutableDictionary *packIndexEntries; +@implementation MonitoredInputStream +- (id)initWithUnderlyingStream:(id )theUnderlyingStream delegate:(id)theDelegate { + if (self = [super init]) { + underlyingStream = [theUnderlyingStream retain]; + delegate = [theDelegate retain]; + NSNotification *notif = [NSNotification notificationWithName:@"MonitoredInputStreamCreated" object:nil]; + [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notif waitUntilDone:NO]; + } + return self; +} +- (void)dealloc { + [underlyingStream release]; + [delegate release]; + [super dealloc]; +} +- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error { + unsigned char *buf = [underlyingStream read:length error:error]; + if (buf != NULL) { + if (![delegate monitoredInputStream:self receivedBytes:*length error:error]) { + return NULL; + } + bytesReceived += (unsigned long long)*length; + } + return buf; +} +- (NSData *)slurp:(NSError **)error { + NSData *data = [underlyingStream slurp:error]; + if (data != nil) { + if (![delegate monitoredInputStream:self receivedBytes:(unsigned long long)[data length] error:error]) { + data = nil; + } + bytesReceived += (unsigned long long)[data length]; + } + return data; } -+ (NSString *)errorDomain; -+ (unsigned long long)maxPackFileSizeMB; -+ (unsigned long long)maxPackItemSizeBytes; -+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName; -+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName; - -- (id)initWithName:(NSString *)thePackSetName - s3Service:(S3Service *)theS3 - s3BucketName:(NSString *)theS3BucketName - computerUUID:(NSString *)theComputerUUID - keepPacksLocal:(BOOL)isKeepPacksLocal - packSHA1s:(NSArray *)thePackSHA1s - error:(NSError **)error; -- (NSString *)name; -- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error; -- (BOOL)containsBlobForSHA1:(NSString *)sha1; -- (NSString *)packSHA1ForPackedBlobSHA1:(NSString *)blobSHA1; @end diff --git a/io/NSFileManager_extra.h b/io/NSFileManager_extra.h index be5a05e..c83fc5f 100644 --- a/io/NSFileManager_extra.h +++ b/io/NSFileManager_extra.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -35,4 +35,5 @@ @interface NSFileManager (extra) - (BOOL)ensureParentPathExistsForPath:(NSString *)path error:(NSError **)error; +- (BOOL)touchFileAtPath:(NSString *)path error:(NSError **)error; @end diff --git a/io/NSFileManager_extra.m b/io/NSFileManager_extra.m index 267860c..5f54abf 100644 --- a/io/NSFileManager_extra.m +++ b/io/NSFileManager_extra.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -52,4 +52,27 @@ } return YES; } +- (BOOL)touchFileAtPath:(NSString *)path error:(NSError **)error { + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + time_t theTime = time(NULL); + struct timespec spec; + spec.tv_sec = theTime; + spec.tv_nsec = 0; + struct timeval timevals[2]; + TIMESPEC_TO_TIMEVAL(&(timevals[0]), &spec); + TIMESPEC_TO_TIMEVAL(&(timevals[1]), &spec); + if (utimes([path fileSystemRepresentation], timevals) == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"utimes(%@): %s", path, strerror(errno)); + return NO; + } + } else { + int fd = open([path fileSystemRepresentation], O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); + if (fd == -1) { + SETNSERROR(@"UnixErrorDomain", errno, @"%s", strerror(errno)); + return NO; + } + close(fd); + } + return YES; +} @end diff --git a/io/OutputStream.h b/io/OutputStream.h index 39db911..69943c4 100644 --- a/io/OutputStream.h +++ b/io/OutputStream.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/StringIO.h b/io/StringIO.h index ab1abb6..c030a71 100644 --- a/io/StringIO.h +++ b/io/StringIO.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/StringIO.m b/io/StringIO.m index 7bc1979..d730759 100644 --- a/io/StringIO.m +++ b/io/StringIO.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/Writer.h b/io/Writer.h index a27bf41..34e4bc7 100644 --- a/io/Writer.h +++ b/io/Writer.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/io/Writer.m b/io/Writer.m index f0211ef..cce0768 100644 --- a/io/Writer.m +++ b/io/Writer.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/plist/ArrayNode.h b/plist/ArrayNode.h index 5aeb318..c734f00 100644 --- a/plist/ArrayNode.h +++ b/plist/ArrayNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// ArrayNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import #import "PListNode.h" diff --git a/plist/ArrayNode.m b/plist/ArrayNode.m index 4c7fa21..b3b4db6 100644 --- a/plist/ArrayNode.m +++ b/plist/ArrayNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// ArrayNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "DictNode.h" #import "ArrayNode.h" diff --git a/plist/BooleanNode.h b/plist/BooleanNode.h index 87d1a21..e6d7026 100644 --- a/plist/BooleanNode.h +++ b/plist/BooleanNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// BooleanNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import #import "PListNode.h" diff --git a/plist/BooleanNode.m b/plist/BooleanNode.m index 113ef54..4a7c8c7 100644 --- a/plist/BooleanNode.m +++ b/plist/BooleanNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// BooleanNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "PListNodeType.h" #import "BooleanNode.h" diff --git a/plist/DictNode.h b/plist/DictNode.h index 0162b45..1743d36 100644 --- a/plist/DictNode.h +++ b/plist/DictNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// DictNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import @class ArrayNode; diff --git a/plist/DictNode.m b/plist/DictNode.m index dcfb6b5..4a5a737 100644 --- a/plist/DictNode.m +++ b/plist/DictNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// DictNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "PListNode.h" #import "PListNodeType.h" diff --git a/plist/IntegerNode.h b/plist/IntegerNode.h index 267764c..c8a26d7 100644 --- a/plist/IntegerNode.h +++ b/plist/IntegerNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// IntegerNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import #import "PListNode.h" diff --git a/plist/IntegerNode.m b/plist/IntegerNode.m index 7cc82ea..ad60e4c 100644 --- a/plist/IntegerNode.m +++ b/plist/IntegerNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// IntegerNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "PListNode.h" #import "PListNodeType.h" diff --git a/plist/PListNode.h b/plist/PListNode.h index 6fe7f08..24a2648 100644 --- a/plist/PListNode.h +++ b/plist/PListNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// PListNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import diff --git a/plist/PListNodeType.h b/plist/PListNodeType.h index 944b543..bc0afcc 100644 --- a/plist/PListNodeType.h +++ b/plist/PListNodeType.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// PListNodeType.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// enum { PLN_INTEGER = 1, diff --git a/plist/RealNode.h b/plist/RealNode.h index 11e9b52..054ab29 100644 --- a/plist/RealNode.h +++ b/plist/RealNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// RealNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import #import "PListNode.h" diff --git a/plist/RealNode.m b/plist/RealNode.m index 1eab58d..2186186 100644 --- a/plist/RealNode.m +++ b/plist/RealNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// RealNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "PListNodeType.h" #import "RealNode.h" diff --git a/plist/StringNode.h b/plist/StringNode.h index 04efe4d..aa616a0 100644 --- a/plist/StringNode.h +++ b/plist/StringNode.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// StringNode.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import #import "PListNode.h" diff --git a/plist/StringNode.m b/plist/StringNode.m index 94612a0..75be934 100644 --- a/plist/StringNode.m +++ b/plist/StringNode.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// StringNode.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "PListNodeType.h" #import "StringNode.h" diff --git a/plist/XMLPListReader.h b/plist/XMLPListReader.h index 8e6ca3a..b92e946 100644 --- a/plist/XMLPListReader.h +++ b/plist/XMLPListReader.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// XMLPListReader.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import @class DictNode; diff --git a/plist/XMLPListReader.m b/plist/XMLPListReader.m index 39d1538..408cec7 100644 --- a/plist/XMLPListReader.m +++ b/plist/XMLPListReader.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// XMLPListReader.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "ArrayNode.h" #import "BooleanNode.h" diff --git a/plist/XMLPListWriter.h b/plist/XMLPListWriter.h index 4150b18..6aa2af6 100644 --- a/plist/XMLPListWriter.h +++ b/plist/XMLPListWriter.h @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// XMLPListWriter.h +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import @class DictNode; diff --git a/plist/XMLPListWriter.m b/plist/XMLPListWriter.m index 386a6cd..c450c0b 100644 --- a/plist/XMLPListWriter.m +++ b/plist/XMLPListWriter.m @@ -1,34 +1,10 @@ -/* - Copyright (c) 2009, 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. - */ +// +// XMLPListWriter.m +// Backup +// +// Created by Stefan Reitshamer on 4/13/09. +// Copyright 2009 PhotoMinds LLC. All rights reserved. +// #import "ArrayNode.h" #import "BooleanNode.h" diff --git a/s3/BucketVerifier.m b/s3/BucketVerifier.m index 13a2ec9..60c05e0 100644 --- a/s3/BucketVerifier.m +++ b/s3/BucketVerifier.m @@ -8,12 +8,15 @@ #import "BucketVerifier.h" #import "S3Service.h" -#import "S3Fark.h" -#import "S3Repo.h" +#import "ArqFark.h" +#import "ArqRepo.h" +#import "ArqRepo_Verifier.h" #import "Commit.h" #import "Tree.h" #import "Node.h" #import "SetNSError.h" +#import "NSError_extra.h" +#import "NSErrorCodes.h" @interface BucketVerifier (internal) - (BOOL)verifyTree:(NSString *)treeSHA1 path:(NSString *)path error:(NSError **)error; @@ -28,8 +31,8 @@ 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]; + fark = [[ArqFark alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID]; + repo = [[ArqRepo alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID encryptionKey:encryptionKey]; } return self; } @@ -44,38 +47,35 @@ [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]); - NSArray *s3PackSHA1s = [fark reloadPacksFromS3:error]; - if (s3PackSHA1s == nil) { - return NO; - } - printf("S3 packs found for computer UUID %s:\n", [computerUUID UTF8String]); - for (NSString *s3PackSHA1 in s3PackSHA1s) { - printf("S3 pack SHA1: %s\n", [s3PackSHA1 UTF8String]); - } + printf("verifying all objects exist for commits in %s\n", [bucketUUID UTF8String]); - 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]; - } + NSError *myError = nil; + NSString *headSHA1 = [repo headSHA1:&myError]; + if (headSHA1 == nil) { + if ([myError isErrorWithDomain:[ArqRepo errorDomain] code:ERROR_NOT_FOUND]) { + printf("no head commit for s3Bucket %s computerUUID %s bucketUUID %s is %s\n", [s3BucketName UTF8String], [computerUUID UTF8String], [bucketUUID UTF8String], [headSHA1 UTF8String]); + } else { + if (error != NULL) { + *error = myError; + } + return NO; + } + } else { + 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 = [repo commitForSHA1:commitSHA1 error:error]; + if (commit == nil) { + 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 @@ -83,8 +83,8 @@ @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]) { + Tree *tree = [repo treeForSHA1:treeSHA1 error:error]; + if (tree == nil) { fprintf(stderr, "tree %s not found\n", [treeSHA1 UTF8String]); return NO; } @@ -153,14 +153,19 @@ } - (BOOL)verify:(NSString *)sha1 error:(NSError **)error { if (sha1 != nil) { - if ([objectSHA1s containsObject:sha1]) { + NSString *packSHA1 = nil; + if (![repo packSHA1:&packSHA1 forPackedBlobSHA1:sha1 error:error]) { + return NO; + } + if (packSHA1 != nil) { + printf("sha1 %s: pack set %s, packSHA1 %s\n", [sha1 UTF8String], [[repo blobsPackSetName] UTF8String], [packSHA1 UTF8String]); + } else { + if (![objectSHA1s containsObject:sha1]) { + SETNSERROR(@"VerifierErrorDomain", ERROR_NOT_FOUND, @"sha1 %@ not found in blobs packset or objects", sha1); + return NO; + } 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; } diff --git a/s3/HTTPConnection_S3.h b/s3/HTTPConnection_S3.h index e37023a..de65410 100644 --- a/s3/HTTPConnection_S3.h +++ b/s3/HTTPConnection_S3.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/HTTPConnection_S3.m b/s3/HTTPConnection_S3.m index 34d78e1..08ce7f3 100644 --- a/s3/HTTPConnection_S3.m +++ b/s3/HTTPConnection_S3.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/NSError_S3.h b/s3/NSError_S3.h index f059cf1..d6327db 100644 --- a/s3/NSError_S3.h +++ b/s3/NSError_S3.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/NSError_S3.m b/s3/NSError_S3.m index 331c3c9..f55a0c7 100644 --- a/s3/NSError_S3.m +++ b/s3/NSError_S3.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/PathReceiver.h b/s3/PathReceiver.h index 2b61106..a237bff 100644 --- a/s3/PathReceiver.h +++ b/s3/PathReceiver.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/PathReceiver.m b/s3/PathReceiver.m index b3a03a8..07c5c27 100644 --- a/s3/PathReceiver.m +++ b/s3/PathReceiver.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3AuthorizationParameters.h b/s3/S3AuthorizationParameters.h index e5c405b..d1492fc 100644 --- a/s3/S3AuthorizationParameters.h +++ b/s3/S3AuthorizationParameters.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3AuthorizationParameters.m b/s3/S3AuthorizationParameters.m index 02a8ad0..b4e33f2 100644 --- a/s3/S3AuthorizationParameters.m +++ b/s3/S3AuthorizationParameters.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3AuthorizationProvider.h b/s3/S3AuthorizationProvider.h index a6c9dc3..00ffb78 100644 --- a/s3/S3AuthorizationProvider.h +++ b/s3/S3AuthorizationProvider.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3AuthorizationProvider.m b/s3/S3AuthorizationProvider.m index 67c5373..92fa156 100644 --- a/s3/S3AuthorizationProvider.m +++ b/s3/S3AuthorizationProvider.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3Lister.h b/s3/S3Lister.h index 656c405..1005ea0 100644 --- a/s3/S3Lister.h +++ b/s3/S3Lister.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3Lister.m b/s3/S3Lister.m index ba0efe4..2d37209 100644 --- a/s3/S3Lister.m +++ b/s3/S3Lister.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -41,7 +41,6 @@ #import "HTTP.h" #import "S3Service.h" #import "S3Request.h" -#import "ServerBlob.h" @interface S3Lister (internal) - (BOOL)getWithMax:(int)max error:(NSError **)error; @@ -76,8 +75,11 @@ *error = nil; } BOOL ret = YES; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSAutoreleasePool *pool = nil; while (isTruncated) { + [pool drain]; + pool = [[NSAutoreleasePool alloc] init]; if (maxRequested < 0) { if (![self getWithMax:-1 error:error]) { ret = NO; @@ -90,11 +92,12 @@ } } } - if (!ret && error != NULL) { + + if (error != NULL) { [*error retain]; } [pool drain]; - if (!ret && error != NULL) { + if (error != NULL) { [*error autorelease]; } return ret; diff --git a/s3/S3ObjectMetadata.h b/s3/S3ObjectMetadata.h index 9f9e594..275b3e3 100644 --- a/s3/S3ObjectMetadata.h +++ b/s3/S3ObjectMetadata.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -37,10 +37,12 @@ NSString *path; NSDate *lastModified; long size; + NSString *storageClass; } - (id)initWithS3BucketName:(NSString *)s3BucketName node:(NSXMLNode *)node error:(NSError **)error; - +- (id)initWithPath:(NSString *)thePath lastModified:(NSDate *)theLastModified size:(long)theSize storageClass:(NSString *)theStorageClass; - (NSString *)path; - (NSDate *)lastModified; - (long)size; +- (NSString *)storageClass; @end diff --git a/s3/S3ObjectMetadata.m b/s3/S3ObjectMetadata.m index 44222bc..707c28c 100644 --- a/s3/S3ObjectMetadata.m +++ b/s3/S3ObjectMetadata.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -60,9 +60,14 @@ goto init_error; } NSXMLNode *sizeNode = [nodes objectAtIndex:0]; - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease]; size = [[numberFormatter numberFromString:[sizeNode stringValue]] longValue]; - [numberFormatter release]; + nodes = [node nodesForXPath:@"StorageClass" error:error]; + if (!nodes) { + goto init_error; + } + NSXMLNode *storageClassNode = [nodes objectAtIndex:0]; + storageClass = [[storageClassNode stringValue] retain]; goto init_done; init_error: [self release]; @@ -70,18 +75,28 @@ goto init_done; } init_done: - if (self == nil && error != NULL) { + if (error != NULL) { [*error retain]; } [pool drain]; - if (self == nil && error != NULL) { + if (error != NULL) { [*error autorelease]; } return self; } +- (id)initWithPath:(NSString *)thePath lastModified:(NSDate *)theLastModified size:(long)theSize storageClass:(NSString *)theStorageClass { + if (self = [super init]) { + path = [thePath retain]; + lastModified = [theLastModified retain]; + size = theSize; + storageClass = [theStorageClass retain]; + } + return self; +} - (void)dealloc { [path release]; [lastModified release]; + [storageClass release]; [super dealloc]; } - (NSString *)path { @@ -93,5 +108,7 @@ init_done: - (long)size { return size; } - +- (NSString *)storageClass { + return storageClass; +} @end diff --git a/s3/S3ObjectReceiver.h b/s3/S3ObjectReceiver.h index 5226549..e3736ca 100644 --- a/s3/S3ObjectReceiver.h +++ b/s3/S3ObjectReceiver.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3ObjectReceiver.m b/s3/S3ObjectReceiver.m index bb917d2..80a1108 100644 --- a/s3/S3ObjectReceiver.m +++ b/s3/S3ObjectReceiver.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3Owner.h b/s3/S3Owner.h deleted file mode 100644 index 3aaed98..0000000 --- a/s3/S3Owner.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// S3Owner.h -// Backup -// -// Created by Stefan Reitshamer on 4/12/09. -// Copyright 2009 PhotoMinds LLC. All rights reserved. -// - -#import - - -@interface S3Owner : NSObject { - NSString *displayName; - NSString *idString; -} -- (id)initWithDisplayName:(NSString *)dn idString:(NSString *)ids; -- (NSString *)displayName; -- (NSString *)idString; -@end diff --git a/s3/S3Owner.m b/s3/S3Owner.m deleted file mode 100644 index 5036785..0000000 --- a/s3/S3Owner.m +++ /dev/null @@ -1,26 +0,0 @@ -// -// S3Owner.m -// Backup -// -// Created by Stefan Reitshamer on 4/12/09. -// Copyright 2009 PhotoMinds LLC. All rights reserved. -// - -#import "S3Owner.h" - - -@implementation S3Owner -- (id)initWithDisplayName:(NSString *)dn idString:(NSString *)ids { - if (self = [super init]) { - displayName = [dn copy]; - idString = [ids copy]; - } - return self; -} -- (NSString *)displayName { - return displayName; -} -- (NSString *)idString { - return idString; -} -@end diff --git a/s3/S3Receiver.h b/s3/S3Receiver.h index 9cba0a3..5ab4f2a 100644 --- a/s3/S3Receiver.h +++ b/s3/S3Receiver.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3Request.h b/s3/S3Request.h index 295dc8e..5a49959 100644 --- a/s3/S3Request.h +++ b/s3/S3Request.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -33,6 +33,7 @@ #import @class S3AuthorizationProvider; @class ServerBlob; +@class Blob; @interface S3Request : NSObject { NSString *method; @@ -42,10 +43,22 @@ S3AuthorizationProvider *sap; BOOL withSSL; BOOL retryOnNetworkError; + Blob *blob; uint64_t length; NSString *virtualHost; NSString *virtualPath; + NSMutableDictionary *extraHeaders; + id delegate; + unsigned long long bytesUploaded; } - (id)initWithMethod:(NSString *)theMethod path:(NSString *)thePath queryString:(NSString *)theQueryString authorizationProvider:(S3AuthorizationProvider *)theSAP useSSL:(BOOL)ssl retryOnNetworkError:(BOOL)retry; +- (void)setDelegate:(id)theDelegate; +- (void)setBlob:(Blob *)theBlob length:(uint64_t)theLength; +- (void)setHeader:(NSString *)value forKey:(NSString *)key; - (ServerBlob *)newServerBlob:(NSError **)error; @end + +@interface NSObject (S3RequestDelegate) +- (BOOL)s3Request:(S3Request *)s3r willUploadBytes:(unsigned long long)theLength error:(NSError **)error; +- (void)s3Request:(S3Request *)s3r bytesFailedToUpload:(unsigned long long)theLength; +@end diff --git a/s3/S3Request.m b/s3/S3Request.m index da2b0e7..975fb24 100644 --- a/s3/S3Request.m +++ b/s3/S3Request.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -35,12 +35,14 @@ #import "HTTPConnection.h" #import "HTTPConnection_S3.h" #import "ServerBlob.h" -#import "Blob.h" #import "S3Service.h" #import "NSXMLNode_extra.h" #import "SetNSError.h" #import "CFStreamPair.h" #import "RegexKitLite.h" +#import "NSErrorCodes.h" +#import "MonitoredInputStream.h" +#import "NSError_extra.h" #define INITIAL_RETRY_SLEEP (0.5) #define RETRY_SLEEP_GROWTH_FACTOR (1.5) @@ -48,6 +50,7 @@ @interface S3Request (internal) - (ServerBlob *)newServerBlobOnce:(NSError **)error; +- (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response; @end @implementation S3Request @@ -63,6 +66,7 @@ sap = [theSAP retain]; withSSL = ssl; retryOnNetworkError = retry; + extraHeaders = [[NSMutableDictionary alloc] init]; } return self; } @@ -71,10 +75,29 @@ [path release]; [queryString release]; [sap release]; + [blob release]; [virtualHost release]; [virtualPath release]; + [extraHeaders release]; + [delegate release]; [super dealloc]; } +- (void)setDelegate:(id)theDelegate { + if (delegate != theDelegate) { + [delegate release]; + delegate = [theDelegate retain]; + } +} +- (void)setBlob:(Blob *)theBlob length:(uint64_t)theLength { + if (blob != theBlob) { + [blob release]; + blob = [theBlob retain]; + } + length = theLength; +} +- (void)setHeader:(NSString *)value forKey:(NSString *)key { + [extraHeaders setObject:value forKey:key]; +} - (ServerBlob *)newServerBlob:(NSError **)error { [virtualHost release]; virtualHost = nil; @@ -88,33 +111,33 @@ NSString *pattern = @"^/([^/]+)(.+)$"; NSRange s3BucketRange = [path rangeOfRegex:pattern capture:1]; if (s3BucketRange.location == NSNotFound) { - SETNSERROR(@"S3ServiceErrorDomain", -1, @"invalid path-style path -- missing s3 bucket name"); + SETNSERROR([S3Service errorDomain], -1, @"invalid path-style path -- missing s3 bucket name"); return nil; } NSRange pathRange = [path rangeOfRegex:pattern capture:2]; if (pathRange.location == NSNotFound) { - SETNSERROR(@"S3ServiceErrorDomain", -1, @"invalid path-style path -- missing path"); + SETNSERROR([S3Service errorDomain], -1, @"invalid path-style path -- missing path"); return nil; } virtualHost = [[[path substringWithRange:s3BucketRange] stringByAppendingString:@".s3.amazonaws.com"] retain]; virtualPath = [[path substringWithRange:pathRange] retain]; } - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *pool = nil; NSTimeInterval sleepTime = INITIAL_RETRY_SLEEP; ServerBlob *sb = nil; NSError *myError = nil; for (;;) { + [pool drain]; + pool = [[NSAutoreleasePool alloc] init]; myError = nil; - NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init]; sb = [self newServerBlobOnce:&myError]; [myError retain]; - [pool2 drain]; [myError autorelease]; if (sb != nil) { break; } BOOL needSleep = NO; - if ([[myError domain] isEqualToString:[S3Service serverErrorDomain]]) { + if ([[myError domain] isEqualToString:[S3Service amazonErrorDomain]]) { NSString *amazonErrorCode = [[myError userInfo] objectForKey:@"Code"]; if ([myError code] == HTTP_INTERNAL_SERVER_ERROR) { HSLogInfo(@"S3 returned %u; retrying", HTTP_INTERNAL_SERVER_ERROR); @@ -134,6 +157,9 @@ } break; } + } else if ([myError isErrorWithDomain:@"UnixErrorDomain" code:ETIMEDOUT]) { + HSLogDebug(@"timeout error (retrying): %@", [myError description]); + needSleep = YES; } else if ([[myError domain] isEqualToString:[CFStreamPair errorDomain]] && retryOnNetworkError) { HSLogDebug(@"network error (retrying): %@", [myError localizedDescription]); needSleep = YES; @@ -152,8 +178,11 @@ [pool drain]; [myError autorelease]; if (sb == nil && error != NULL) { - NSAssert(myError != nil, @"myError must be set"); - *error = myError; + if (myError != nil) { + *error = myError; + } else { + SETNSERROR([S3Service errorDomain], -1, @"unknown error reading %@", path); + } } return sb; } @@ -169,68 +198,119 @@ [conn setRequestHostHeader]; [conn setRFC822DateRequestHeader]; [conn setRequestKeepAliveHeader]; + if (blob != nil) { + if ([blob mimeType] != nil) { + [conn setRequestHeader:[blob mimeType] forKey:@"Content-Type"]; + } + if ([blob downloadName] != nil) { + [conn setRequestContentDispositionHeader:[blob downloadName]]; + } + [conn setRequestHeader:[NSString stringWithFormat:@"%qu", length] forKey:@"Content-Length"]; + } + for (NSString *headerKey in [extraHeaders allKeys]) { + [conn setRequestHeader:[extraHeaders objectForKey:headerKey] forKey:headerKey]; + } [conn setAuthorizationRequestHeaderUsingProvider:sap s3BucketName:s3BucketName]; - BOOL execRet = [conn executeRequest:error]; + id requestBody = nil; + if (blob != nil) { + requestBody = [[[blob inputStreamFactory] newInputStream] autorelease]; + if (delegate != nil) { + requestBody = [[[MonitoredInputStream alloc] initWithUnderlyingStream:requestBody delegate:self] autorelease]; + } + bytesUploaded = 0; + } + BOOL execRet = [conn executeRequestWithBody:requestBody error:error]; if (!execRet) { return nil; } ServerBlob *ret = nil; int code = [conn responseCode]; if (code >= 200 && code <= 299) { - id bodyStream = [conn newResponseBodyStream:error]; + id bodyStream = [conn newResponseBodyStream:error]; if (bodyStream == nil) { return nil; } ret = [[ServerBlob alloc] initWithInputStream:bodyStream mimeType:[conn responseMimeType] downloadName:[conn responseDownloadName]]; [bodyStream release]; - } else { - if (code >= 400 && code != HTTP_NOT_FOUND) { - HSLogDebug(@"S3 HTTP response code was %d; requesting close on connection", code); - [conn setCloseRequested]; - } - NSData *response = [conn slurpResponseBody:error]; - if (response == nil) { - return nil; - } - NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithData:response options:0 error:error]; - HSLogTrace(@"%@", [xmlDoc description]); - if (xmlDoc == nil) { - return nil; - } - NSXMLElement *rootElement = [xmlDoc rootElement]; - NSArray *errorNodes = [rootElement nodesForXPath:@"//Error" error:error]; - if (errorNodes == nil) { - [xmlDoc release]; - return nil; - } else if ([errorNodes count] == 0) { - HSLogWarn(@"missing Error node in S3 XML response"); - SETNSERROR([S3Service errorDomain], code, @"S3 error"); - [xmlDoc release]; - return nil; - } else { - if ([errorNodes count] > 1) { - HSLogWarn(@"ignoring additional S3 errors"); - } - NSXMLNode *errorNode = [errorNodes objectAtIndex:0]; - NSString *errorCode = [[errorNode childNodeNamed:@"Code"] stringValue]; - NSString *errorMessage = [[errorNode childNodeNamed:@"Message"] stringValue]; - if (code == HTTP_NOT_FOUND) { - errorMessage = [NSString stringWithFormat:@"%@ not found", virtualPath]; - } - NSString *endpoint = (code == HTTP_MOVED_TEMPORARILY) ? [[errorNode childNodeNamed:@"Endpoint"] stringValue] : nil; - NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: - (errorCode != nil ? errorCode : @""), @"Code", - (errorMessage != nil ? errorMessage : @""), @"Message", - (errorMessage != nil ? errorMessage : @""), NSLocalizedDescriptionKey, - (endpoint != nil ? endpoint : @""), @"Endpoint", - nil]; - NSError *myError = [NSError errorWithDomain:[S3Service serverErrorDomain] code:code userInfo:userInfo]; - if (error != NULL) { - *error = myError; - } - } - [xmlDoc release]; + return ret; } - return ret; + + if ([delegate respondsToSelector:@selector(s3Request:bytesFailedToUpload:)]) { + [delegate s3Request:self bytesFailedToUpload:bytesUploaded]; + } + if (code >= 400 && code != HTTP_NOT_FOUND) { + HSLogDebug(@"S3 HTTP response code was %d; requesting close on connection", code); + [conn setCloseRequested]; + } + NSData *response = [conn slurpResponseBody:error]; + if (response == nil) { + return nil; + } + + if (code == HTTP_NOT_FOUND) { + SETNSERROR([S3Service errorDomain], ERROR_NOT_FOUND, @"%@ not found", path); + return nil; + } + + NSError *amazonError = nil; + [self setError:&amazonError withHTTPResponseCode:code responseData:response]; + HSLogError(@"S3 error: %@", [amazonError description]); + if (error != NULL) { + *error = amazonError; + } + return nil; +} +- (void)setError:(NSError **)error withHTTPResponseCode:(int)code responseData:(NSData *)response { + NSAssert(error != NULL, @"NSError **error must not be NULL"); + NSString *errorXML = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding] autorelease]; + HSLogDebug(@"amazon HTTP error code=%d; xml=%@", code, errorXML); + NSError *xmlError = nil; + NSXMLDocument *xmlDoc = [[[NSXMLDocument alloc] initWithData:response options:0 error:&xmlError] autorelease]; + if (xmlDoc == nil) { + HSLogError(@"error parsing Amazon error XML: %@", [xmlError localizedDescription]); + SETNSERROR([S3Service errorDomain], code, @"Amazon error (failed to parse XML); xml=%@", errorXML); + return; + } + + HSLogTrace(@"error XML: %@", [xmlDoc description]); + NSXMLElement *rootElement = [xmlDoc rootElement]; + NSArray *errorNodes = [rootElement nodesForXPath:@"//Error" error:&xmlError]; + + if (errorNodes == nil) { + HSLogError(@"error finding Error node in Amazon error XML: %@", [xmlError localizedDescription]); + SETNSERROR([S3Service errorDomain], code, @"Amazon error (failed to parse Error node in XML); xml=%@", errorXML); + return; + } + + if ([errorNodes count] == 0) { + HSLogWarn(@"missing Error node in S3 XML response %@", errorXML); + SETNSERROR([S3Service errorDomain], code, @"Amazon error (no Error node found); xml=%@", errorXML); + return; + } + + if ([errorNodes count] > 1) { + HSLogWarn(@"ignoring additional S3 errors"); + } + NSXMLNode *errorNode = [errorNodes objectAtIndex:0]; + NSString *errorCode = [[errorNode childNodeNamed:@"Code"] stringValue]; + NSString *errorMessage = [[errorNode childNodeNamed:@"Message"] stringValue]; + NSString *endpoint = (code == HTTP_MOVED_TEMPORARILY) ? [[errorNode childNodeNamed:@"Endpoint"] stringValue] : nil; + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + (errorCode != nil ? errorCode : @""), @"Code", + (errorMessage != nil ? errorMessage : @""), @"Message", + (errorMessage != nil ? errorMessage : @""), NSLocalizedDescriptionKey, + (endpoint != nil ? endpoint : @""), @"Endpoint", + nil]; + NSError *myError = [NSError errorWithDomain:[S3Service amazonErrorDomain] code:code userInfo:userInfo]; + *error = myError; +} + +#pragma mark MonitoredInputStream +- (BOOL)monitoredInputStream:(MonitoredInputStream *)stream receivedBytes:(unsigned long long)theLength error:(NSError **)error { + bytesUploaded += theLength; + if (delegate != nil && ![delegate s3Request:self willUploadBytes:theLength error:error]) { + return NO; + } + return YES; } @end diff --git a/s3/S3Service.h b/s3/S3Service.h index 9128482..05527dc 100644 --- a/s3/S3Service.h +++ b/s3/S3Service.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -32,10 +32,15 @@ #import #import "S3Receiver.h" -@class Blob; +#import "Blob.h" +#import "InputStream.h" @class S3AuthorizationProvider; @class ServerBlob; +#define S3_INITIAL_RETRY_SLEEP (0.5) +#define S3_RETRY_SLEEP_GROWTH_FACTOR (1.5) +#define S3_MAX_RETRY (5) + enum { BUCKET_REGION_US_STANDARD = 0, BUCKET_REGION_US_WEST = 1, @@ -49,10 +54,11 @@ enum { BOOL retryOnNetworkError; } + (NSString *)errorDomain; -+ (NSString *)serverErrorDomain; ++ (NSString *)amazonErrorDomain; + (NSString *)displayNameForBucketRegion:(int)region; + (NSString *)s3BucketNameForAccessKeyID:(NSString *)theAccessKeyId region:(int)s3BucketRegion; + (NSArray *)s3BucketNamesForAccessKeyID:(NSString *)theAccessKeyId; ++ (int)s3BucketRegionForS3BucketName:(NSString *)s3BucketName; - (id)initWithS3AuthorizationProvider:(S3AuthorizationProvider *)theSAP useSSL:(BOOL)useSSL retryOnNetworkError:(BOOL)retry; - (NSArray *)s3BucketNames:(NSError **)error; - (BOOL)s3BucketExists:(NSString *)s3BucketName; @@ -61,12 +67,11 @@ enum { - (NSArray *)objectsWithPrefix:(NSString *)prefix error:(NSError **)error; - (BOOL)listObjectsWithPrefix:(NSString *)prefix receiver:(id )receiver error:(NSError **)error; - (BOOL)listObjectsWithMax:(int)maxResults prefix:(NSString *)prefix receiver:(id )receiver error:(NSError **)error; -- (BOOL)containsBlobAtPath:(NSString *)path; +- (BOOL)containsBlob:(BOOL *)contains atPath:(NSString *)path error:(NSError **)error; - (NSData *)dataAtPath:(NSString *)path error:(NSError **)error; - (ServerBlob *)newServerBlobAtPath:(NSString *)path error:(NSError **)error; - - (NSArray *)commonPrefixesForPathPrefix:(NSString *)prefix delimiter:(NSString *)delimiter error:(NSError **)error; @end diff --git a/s3/S3Service.m b/s3/S3Service.m index 8e7fa81..e9315da 100644 --- a/s3/S3Service.m +++ b/s3/S3Service.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import "BlobACL.h" +#import "InputStream.h" #import "Blob.h" #import "RegexKitLite.h" #import "NSError_S3.h" @@ -39,12 +41,15 @@ #import "S3Service.h" #import "PathReceiver.h" #import "SetNSError.h" +#import "DataInputStream.h" #import "HTTPResponse.h" #import "HTTPRequest.h" #import "HTTP.h" +#import "Streams.h" #import "S3ObjectReceiver.h" #import "ServerBlob.h" #import "NSErrorCodes.h" +#import "NSData-InputStream.h" #import "S3Request.h" /* @@ -60,19 +65,19 @@ + (NSString *)errorDomain { return @"S3ServiceErrorDomain"; } -+ (NSString *)serverErrorDomain { - return @"S3ServiceServerErrorDomain"; ++ (NSString *)amazonErrorDomain { + return @"AmazonErrorDomain"; } + (NSString *)displayNameForBucketRegion:(int)region { switch (region) { case BUCKET_REGION_US_STANDARD: return @"US Standard"; case BUCKET_REGION_US_WEST: - return @"US West"; + return @"US-West (Northern California)"; case BUCKET_REGION_AP_SOUTHEAST_1: return @"Asia Pacific (Singapore)"; case BUCKET_REGION_EU: - return @"EU"; + return @"EU (Ireland)"; } NSAssert(NO, @"invalid S3 bucket region"); return nil; @@ -88,6 +93,16 @@ } return [NSString stringWithFormat:@"%@.com.haystacksoftware.arq%@", [theAccessKeyID lowercaseString], regionSuffix]; } ++ (int)s3BucketRegionForS3BucketName:(NSString *)s3BucketName { + if ([s3BucketName hasSuffix:@"com.haystacksoftware.arq.eu"]) { + return BUCKET_REGION_EU; + } else if ([s3BucketName hasSuffix:@"com.haystacksoftware.arq.ap-southeast-1"]) { + return BUCKET_REGION_AP_SOUTHEAST_1; + } else if ([s3BucketName hasSuffix:@"com.haystacksoftware.arq.us-west-1"]) { + return BUCKET_REGION_US_WEST; + } + return BUCKET_REGION_US_STANDARD; +} + (NSArray *)s3BucketNamesForAccessKeyID:(NSString *)theAccessKeyId { return [NSArray arrayWithObjects: [S3Service s3BucketNameForAccessKeyID:theAccessKeyId region:BUCKET_REGION_US_STANDARD], @@ -155,16 +170,14 @@ S3Lister *lister = [[[S3Lister alloc] initWithS3AuthorizationProvider:sap useSSL:useSSL retryOnNetworkError:retryOnNetworkError max:maxResults prefix:prefix receiver:receiver] autorelease]; return lister && [lister listObjects:error]; } -- (BOOL)containsBlobAtPath:(NSString *)path { - NSError *error = nil; +- (BOOL)containsBlob:(BOOL *)contains atPath:(NSString *)path error:(NSError **)error { + *contains = NO; PathReceiver *rec = [[PathReceiver alloc] init]; S3Lister *lister = [[S3Lister alloc] initWithS3AuthorizationProvider:sap useSSL:useSSL retryOnNetworkError:retryOnNetworkError max:-1 prefix:path receiver:rec]; - BOOL ret = [lister listObjects:&error]; - if (!ret) { - HSLogError(@"listObjects(%@): %@", path, [error localizedDescription]); - } else { - ret = [[rec paths] containsObject:path]; - HSLogDebug(@"S3 path %@ %@", path, (ret ? @"exists" : @"does not exist")); + BOOL ret = [lister listObjects:error]; + if (ret) { + *contains = [[rec paths] containsObject:path]; + HSLogDebug(@"S3 path %@ %@", path, ((*contains) ? @"exists" : @"does not exist")); } [lister release]; [rec release]; diff --git a/s3/S3Signature.h b/s3/S3Signature.h index 445b4ca..f5a4431 100644 --- a/s3/S3Signature.h +++ b/s3/S3Signature.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/s3/S3Signature.m b/s3/S3Signature.m index c78035c..6a655ec 100644 --- a/s3/S3Signature.m +++ b/s3/S3Signature.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/Blob.h b/shared/Blob.h index d3bd296..6419b6d 100644 --- a/shared/Blob.h +++ b/shared/Blob.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -36,20 +36,12 @@ @interface Blob : NSObject { NSString *mimeType; NSString *downloadName; - BOOL gzipped; id inputStreamFactory; - BOOL entireSource; - unsigned long long sourceOffset; - unsigned long long sourceLength; } - (id)initWithInputStreamFactory:(id )theFactory mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName; -- (id)initWithInputStreamFactory:(id )theFactory sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName; - (id)initWithData:(NSData *)theData mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName; -- (id)initWithGzippedData:(NSData *)theData mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName; - (NSString *)mimeType; - (NSString *)downloadName; -- (BOOL)gzipped; -- (id )newInputStream:(id)sender; +- (id )inputStreamFactory; - (NSData *)slurp:(NSError **)error; -- (NSData *)slurp:(id)sender error:(NSError **)error; @end diff --git a/shared/Blob.m b/shared/Blob.m index 63d34ae..a2a6b97 100644 --- a/shared/Blob.m +++ b/shared/Blob.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -38,21 +38,7 @@ if (self = [super init]) { mimeType = [theMimeType copy]; downloadName = [theDownloadName copy]; - gzipped = NO; inputStreamFactory = [theFactory retain]; - entireSource = YES; - } - return self; -} -- (id)initWithInputStreamFactory:(id )theFactory sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName { - if (self = [super init]) { - mimeType = [theMimeType copy]; - downloadName = [theDownloadName copy]; - gzipped = NO; - inputStreamFactory = [theFactory retain]; - entireSource = NO; - sourceOffset = theOffset; - sourceLength = theLength; } return self; } @@ -60,19 +46,7 @@ if (self = [super init]) { mimeType = [theMimeType copy]; downloadName = [theDownloadName copy]; - gzipped = NO; inputStreamFactory = [[DataInputStreamFactory alloc] initWithData:theData]; - entireSource = YES; - } - return self; -} -- (id)initWithGzippedData:(NSData *)theData mimeType:(NSString *)theMimeType downloadName:(NSString *)theDownloadName { - if (self = [super init]) { - mimeType = [theMimeType copy]; - downloadName = [theDownloadName copy]; - gzipped = YES; - inputStreamFactory = [[DataInputStreamFactory alloc] initWithData:theData]; - entireSource = YES; } return self; } @@ -88,25 +62,18 @@ - (NSString *)downloadName { return downloadName; } -- (BOOL)gzipped { - return gzipped; -} -- (id )newInputStream:(id)sender { - id is = nil; - if (entireSource) { - is = [inputStreamFactory newInputStream:sender]; - } else { - is = [inputStreamFactory newInputStream:sender sourceOffset:sourceOffset sourceLength:sourceLength]; - } - return is; +- (id )inputStreamFactory { + return inputStreamFactory; } - (NSData *)slurp:(NSError **)error { - return [self slurp:self error:error]; -} -- (NSData *)slurp:(id)sender error:(NSError **)error { - id is = [self newInputStream:sender]; + id is = [inputStreamFactory newInputStream]; NSData *data = [is slurp:error]; [is release]; return data; } + +#pragma mark NSObject +- (NSString *)description { + return [NSString stringWithFormat:@"", [inputStreamFactory description]]; +} @end diff --git a/shared/BlobACL.h b/shared/BlobACL.h index c4224ea..d97f710 100644 --- a/shared/BlobACL.h +++ b/shared/BlobACL.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/BlobACL.m b/shared/BlobACL.m index f6c1437..0ea7dcf 100644 --- a/shared/BlobACL.m +++ b/shared/BlobACL.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/DNS_SDErrors.h b/shared/DNS_SDErrors.h index 7d3cea3..e38b3ee 100644 --- a/shared/DNS_SDErrors.h +++ b/shared/DNS_SDErrors.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/DNS_SDErrors.m b/shared/DNS_SDErrors.m index 9b479e6..8756090 100644 --- a/shared/DNS_SDErrors.m +++ b/shared/DNS_SDErrors.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/FileACL.h b/shared/FileACL.h index 2848b99..195c651 100644 --- a/shared/FileACL.h +++ b/shared/FileACL.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/FileACL.m b/shared/FileACL.m index d6f6349..e88e473 100644 --- a/shared/FileACL.m +++ b/shared/FileACL.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -71,7 +71,7 @@ return NO; } int ret = 0; - if ((st.st_mode & S_IFMT) == S_IFLNK) { + if (S_ISLNK(st.st_mode)) { ret = acl_set_link_np(pathChars, ACL_TYPE_EXTENDED, acl); } else { ret = acl_set_file(pathChars, ACL_TYPE_EXTENDED, acl); diff --git a/shared/NSErrorCodes.h b/shared/NSErrorCodes.h index 997589f..3e610f8 100644 --- a/shared/NSErrorCodes.h +++ b/shared/NSErrorCodes.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -35,3 +35,7 @@ #define ERROR_EOF -4 #define ERROR_NOT_LICENSED -5 #define ERROR_BUCKET_CONFIGURATION_CHANGED -6 +#define ERROR_ABORT_REQUESTED -7 +#define ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE (-8) +#define ERROR_COULD_NOT_CONNECT_TO_AGENT (-9) +#define ERROR_AGENT_COMMUNICATION (-10) \ No newline at end of file diff --git a/shared/NSError_extra.h b/shared/NSError_extra.h new file mode 100644 index 0000000..2f59ae1 --- /dev/null +++ b/shared/NSError_extra.h @@ -0,0 +1,13 @@ +// +// NSError_extra.h +// Arq +// +// Created by Stefan Reitshamer on 6/29/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import + +@interface NSError (extra) +- (BOOL)isErrorWithDomain:(NSString *)theDomain code:(int)theCode; +@end diff --git a/shared/NSError_extra.m b/shared/NSError_extra.m new file mode 100644 index 0000000..323975b --- /dev/null +++ b/shared/NSError_extra.m @@ -0,0 +1,16 @@ +// +// NSError_extra.m +// Arq +// +// Created by Stefan Reitshamer on 6/29/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "NSError_extra.h" + + +@implementation NSError (extra) +- (BOOL)isErrorWithDomain:(NSString *)theDomain code:(int)theCode { + return [self code] == theCode && [[self domain] isEqualToString:theDomain]; +} +@end diff --git a/shared/NSString_extra.h b/shared/NSString_extra.h index cc2ecb1..e95d02e 100644 --- a/shared/NSString_extra.h +++ b/shared/NSString_extra.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/NSString_extra.m b/shared/NSString_extra.m index 1ed1ee6..bf4dba3 100644 --- a/shared/NSString_extra.m +++ b/shared/NSString_extra.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/NSXMLNode_extra.h b/shared/NSXMLNode_extra.h index 96c40fa..84582d5 100644 --- a/shared/NSXMLNode_extra.h +++ b/shared/NSXMLNode_extra.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/NSXMLNode_extra.m b/shared/NSXMLNode_extra.m index 11119e8..069caf4 100644 --- a/shared/NSXMLNode_extra.m +++ b/shared/NSXMLNode_extra.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/OSStatusDescription.h b/shared/OSStatusDescription.h index fba5486..a5e74ab 100644 --- a/shared/OSStatusDescription.h +++ b/shared/OSStatusDescription.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/OSStatusDescription.m b/shared/OSStatusDescription.m index 84c49a1..d0d8fa5 100644 --- a/shared/OSStatusDescription.m +++ b/shared/OSStatusDescription.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/RFC822.h b/shared/RFC822.h index c5ffc5b..fdbe94c 100644 --- a/shared/RFC822.h +++ b/shared/RFC822.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/RFC822.m b/shared/RFC822.m index f9d32c3..cf730d0 100644 --- a/shared/RFC822.m +++ b/shared/RFC822.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. @@ -33,13 +33,14 @@ #import "RFC822.h" #import "SetNSError.h" #import "RegexKitLite.h" +#import "S3Service.h" #define FMT822 (@"^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{3})Z$") @implementation RFC822 + (NSDate *)dateFromString:(NSString *)dateString error:(NSError **)error { if ([dateString rangeOfRegex:FMT822].location == NSNotFound) { - SETNSERROR(@"S3ErrorDomain", -1, @"invalid date '%@'", dateString); + SETNSERROR([S3Service amazonErrorDomain], -1, @"invalid date '%@'", dateString); return nil; } return [NSCalendarDate dateWithYear:[[dateString stringByMatching:FMT822 capture:1] intValue] diff --git a/shared/ServerBlob.h b/shared/ServerBlob.h index dd66fe1..e7158a1 100644 --- a/shared/ServerBlob.h +++ b/shared/ServerBlob.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/ServerBlob.m b/shared/ServerBlob.m index 0533a9f..ddb3dca 100644 --- a/shared/ServerBlob.m +++ b/shared/ServerBlob.m @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved. diff --git a/shared/SetNSError.h b/shared/SetNSError.h index e9b1ae7..abd55a7 100644 --- a/shared/SetNSError.h +++ b/shared/SetNSError.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009, Stefan Reitshamer http://www.haystacksoftware.com + Copyright (c) 2009-2010, Stefan Reitshamer http://www.haystacksoftware.com All rights reserved.