From cbe20c007e7b11e35de0a10768cb54b08418aa36 Mon Sep 17 00:00:00 2001 From: Stefan Reitshamer Date: Thu, 19 Aug 2010 13:25:23 -0400 Subject: [PATCH] Merged in many changes from Arq mainline development. (Sorry for the lame commit comment). --- AppKeychain.h | 17 ++ AppKeychain.m | 43 ++++ ArqFark.h | 26 +++ ArqFark.m | 74 +++++++ ArqPackSet.h | 29 +++ ArqPackSet.m | 195 ++++++++++++++++ ArqRepo.h | 37 ++++ ArqRepo.m | 180 +++++++++++++++ ArqRepo_Verifier.h | 15 ++ ArqRepo_Verifier.m | 19 ++ ArqRestoreCommand.m | 5 +- ArqVerifyCommand.m | 5 +- Commit.m | 1 - DiskPack.m | 33 +-- DiskPackIndex.m | 19 +- FarkPath.h | 17 ++ FarkPath.m | 19 ++ FileAttributes.m | 31 +-- Node.h | 5 +- Node.m | 28 +-- PackIndexEntry.m | 5 + PackIndexWriter.h | 18 ++ PackIndexWriter.m | 103 +++++++++ PackSet.m | 207 ----------------- PackSetSet.h | 55 ----- PackSetSet.m | 237 -------------------- Restorer.h | 8 +- Restorer.m | 29 ++- S3Fark.m | 112 ---------- S3Repo.h | 84 ------- S3Repo.m | 294 ------------------------- Tree.h | 9 +- Tree.m | 53 ++++- XAttrSet.h | 2 + XAttrSet.m | 48 +++- arq_restore.xcodeproj/project.pbxproj | 110 ++++++--- crypto/NSData-Encrypt.h | 2 + crypto/NSData-Encrypt.m | 22 +- crypto/SHA1Hash.h | 1 - crypto/SHA1Hash.m | 12 +- http/CFStreamPair.h | 27 +++ http/CFStreamPair.m | 209 ++++++++++++++++++ http/HTTPConnection.h | 6 +- http/HTTPConnection.m | 8 +- http/HTTPRequest.h | 2 +- http/HTTPRequest.m | 1 + http/HTTPResponse.h | 3 +- http/HTTPResponse.m | 4 +- http/StreamPair.h | 17 ++ http/StreamPairFactory.h | 22 ++ http/StreamPairFactory.m | 153 +++++++++++++ io/BooleanIO.h | 2 +- io/BooleanIO.m | 2 +- io/BufferedInputStream.h | 2 +- io/CFStreamInputStream.h | 2 +- io/CFStreamInputStream.m | 4 +- io/CFStreamOutputStream.h | 2 +- io/CFStreamOutputStream.m | 2 +- io/ChunkedInputStream.h | 2 +- io/ChunkedInputStream.m | 4 +- io/CryptInputStream.h | 4 +- io/CryptInputStream.m | 121 +++++----- io/DataIO.h | 2 +- io/DataIO.m | 2 +- io/DataInputStream.h | 2 +- io/DataInputStream.m | 4 +- io/DataInputStreamFactory.h | 2 +- io/DataInputStreamFactory.m | 11 +- io/DateIO.h | 2 +- io/DateIO.m | 2 +- io/DecryptedInputStream.h | 2 +- io/DecryptedInputStream.m | 2 +- io/DoubleIO.h | 2 +- io/DoubleIO.m | 2 +- io/EncryptedInputStream.h | 2 +- io/EncryptedInputStream.m | 2 +- io/FDInputStream.h | 2 +- io/FDInputStream.m | 4 +- io/FDOutputStream.h | 2 +- io/FDOutputStream.m | 2 +- io/FileInputStream.h | 3 +- io/FileInputStream.m | 13 +- io/FileInputStreamFactory.h | 4 +- io/FileInputStreamFactory.m | 31 ++- io/FileOutputStream.h | 4 +- io/FileOutputStream.m | 54 +++-- io/FixedLengthInputStream.h | 8 +- io/FixedLengthInputStream.m | 17 +- io/InputStream.h | 3 +- io/InputStreamFactory.h | 7 +- io/InputStreams.h | 2 +- io/InputStreams.m | 2 +- io/IntegerIO.h | 2 +- io/IntegerIO.m | 2 +- S3Fark.h => io/MonitoredInputStream.h | 30 +-- PackSet.h => io/MonitoredInputStream.m | 70 +++--- io/NSFileManager_extra.h | 3 +- io/NSFileManager_extra.m | 25 ++- io/OutputStream.h | 2 +- io/StringIO.h | 2 +- io/StringIO.m | 2 +- io/Writer.h | 2 +- io/Writer.m | 2 +- plist/ArrayNode.h | 38 +--- plist/ArrayNode.m | 38 +--- plist/BooleanNode.h | 38 +--- plist/BooleanNode.m | 38 +--- plist/DictNode.h | 38 +--- plist/DictNode.m | 38 +--- plist/IntegerNode.h | 38 +--- plist/IntegerNode.m | 38 +--- plist/PListNode.h | 38 +--- plist/PListNodeType.h | 38 +--- plist/RealNode.h | 38 +--- plist/RealNode.m | 38 +--- plist/StringNode.h | 38 +--- plist/StringNode.m | 38 +--- plist/XMLPListReader.h | 38 +--- plist/XMLPListReader.m | 38 +--- plist/XMLPListWriter.h | 38 +--- plist/XMLPListWriter.m | 38 +--- s3/BucketVerifier.m | 93 ++++---- s3/HTTPConnection_S3.h | 2 +- s3/HTTPConnection_S3.m | 2 +- s3/NSError_S3.h | 2 +- s3/NSError_S3.m | 2 +- s3/PathReceiver.h | 2 +- s3/PathReceiver.m | 2 +- s3/S3AuthorizationParameters.h | 2 +- s3/S3AuthorizationParameters.m | 2 +- s3/S3AuthorizationProvider.h | 2 +- s3/S3AuthorizationProvider.m | 2 +- s3/S3Lister.h | 2 +- s3/S3Lister.m | 13 +- s3/S3ObjectMetadata.h | 6 +- s3/S3ObjectMetadata.m | 29 ++- s3/S3ObjectReceiver.h | 2 +- s3/S3ObjectReceiver.m | 2 +- s3/S3Owner.h | 19 -- s3/S3Owner.m | 26 --- s3/S3Receiver.h | 2 +- s3/S3Request.h | 15 +- s3/S3Request.m | 200 ++++++++++++----- s3/S3Service.h | 15 +- s3/S3Service.m | 39 ++-- s3/S3Signature.h | 2 +- s3/S3Signature.m | 2 +- shared/Blob.h | 12 +- shared/Blob.m | 51 +---- shared/BlobACL.h | 2 +- shared/BlobACL.m | 2 +- shared/DNS_SDErrors.h | 2 +- shared/DNS_SDErrors.m | 2 +- shared/FileACL.h | 2 +- shared/FileACL.m | 4 +- shared/NSErrorCodes.h | 6 +- shared/NSError_extra.h | 13 ++ shared/NSError_extra.m | 16 ++ shared/NSString_extra.h | 2 +- shared/NSString_extra.m | 2 +- shared/NSXMLNode_extra.h | 2 +- shared/NSXMLNode_extra.m | 2 +- shared/OSStatusDescription.h | 2 +- shared/OSStatusDescription.m | 2 +- shared/RFC822.h | 2 +- shared/RFC822.m | 5 +- shared/ServerBlob.h | 2 +- shared/ServerBlob.m | 2 +- shared/SetNSError.h | 2 +- 169 files changed, 2234 insertions(+), 2221 deletions(-) create mode 100644 AppKeychain.h create mode 100644 AppKeychain.m create mode 100644 ArqFark.h create mode 100644 ArqFark.m create mode 100644 ArqPackSet.h create mode 100644 ArqPackSet.m create mode 100644 ArqRepo.h create mode 100644 ArqRepo.m create mode 100644 ArqRepo_Verifier.h create mode 100644 ArqRepo_Verifier.m create mode 100644 FarkPath.h create mode 100644 FarkPath.m create mode 100644 PackIndexWriter.h create mode 100644 PackIndexWriter.m delete mode 100644 PackSet.m delete mode 100644 PackSetSet.h delete mode 100644 PackSetSet.m delete mode 100644 S3Fark.m delete mode 100644 S3Repo.h delete mode 100644 S3Repo.m create mode 100644 http/CFStreamPair.h create mode 100644 http/CFStreamPair.m create mode 100644 http/StreamPair.h create mode 100644 http/StreamPairFactory.h create mode 100644 http/StreamPairFactory.m rename S3Fark.h => io/MonitoredInputStream.h (60%) rename PackSet.h => io/MonitoredInputStream.m (50%) delete mode 100644 s3/S3Owner.h delete mode 100644 s3/S3Owner.m create mode 100644 shared/NSError_extra.h create mode 100644 shared/NSError_extra.m 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.