From 20bf814d1728fd7ee8ea2bc68507197cfa5eadca Mon Sep 17 00:00:00 2001 From: Stefan Reitshamer Date: Sat, 9 Mar 2013 08:46:38 -0500 Subject: [PATCH] Updated to Commit version 9. --- ArqRepo.m | 8 +- ArqRestoreCommand.m | 36 +++-- BlobKey.h | 22 ++- BlobKey.m | 79 ++++++++++- BlobKeyIO.h | 17 +++ BlobKeyIO.m | 54 ++++++++ Commit.h | 27 ++-- Commit.m | 145 +++++++++++--------- Node.h | 21 ++- Node.m | 141 ++++++++++--------- ReflogEntry.m | 8 +- Restorer.m | 30 ++--- Tree.h | 14 +- Tree.m | 187 +++++++++++++++++--------- arq_restore.xcodeproj/project.pbxproj | 16 +++ arq_restore_Prefix.pch | 2 + io/CryptoKey.m | 5 + shared/NSErrorCodes.h | 1 + storage/StorageType.h | 5 + 19 files changed, 543 insertions(+), 275 deletions(-) create mode 100644 BlobKeyIO.h create mode 100644 BlobKeyIO.m create mode 100644 storage/StorageType.h diff --git a/ArqRepo.m b/ArqRepo.m index 4522d49..7a38968 100644 --- a/ArqRepo.m +++ b/ArqRepo.m @@ -22,6 +22,8 @@ #import "CryptoKey.h" #import "BlobKey.h" #import "Encryption.h" +#import "NSData-GZip.h" + @implementation ArqRepo + (NSString *)errorDomain { @@ -92,7 +94,7 @@ stretch = [sha1 characterAtIndex:40] == 'Y'; sha1 = [sha1 substringToIndex:40]; } - return [[[BlobKey alloc] initWithSHA1:sha1 stretchEncryptionKey:stretch] autorelease]; + return [[[BlobKey alloc] initWithSHA1:sha1 storageType:StorageTypeS3 stretchEncryptionKey:stretch compressed:NO] autorelease]; } - (Commit *)commitForBlobKey:(BlobKey *)commitBlobKey error:(NSError **)error { NSError *myError = nil; @@ -152,7 +154,9 @@ if (data == nil) { return nil; } - + if ([blobKey compressed]) { + data = [data gzipInflate]; + } DataInputStream *dis = [[DataInputStream alloc] initWithData:data]; BufferedInputStream *bis = [[BufferedInputStream alloc] initWithUnderlyingStream:dis]; Tree *tree = [[[Tree alloc] initWithBufferedInputStream:bis error:error] autorelease]; diff --git a/ArqRestoreCommand.m b/ArqRestoreCommand.m index 8c29de7..c81498a 100644 --- a/ArqRestoreCommand.m +++ b/ArqRestoreCommand.m @@ -49,6 +49,9 @@ #import "CryptoKey.h" +#define BUCKET_PLIST_SALT "BucketPL" + + @interface ArqRestoreCommand (internal) - (BOOL)printArqFolders:(NSError **)error; - (BOOL)processPath:(NSError **)error; @@ -169,12 +172,7 @@ } if (length >= 9 && !strncmp([data bytes], "encrypted", length)) { NSData *encryptedData = [data subdataWithRange:NSMakeRange(9, [data length] - 9)]; - ArqSalt *arqSalt = [[[ArqSalt alloc] initWithAccessKeyID:accessKey secretAccessKey:secretKey s3BucketName:s3BucketName computerUUID:computerUUID] autorelease]; - NSData *salt = [arqSalt salt:error]; - if (salt == nil) { - return NO; - } - CryptoKey *cryptoKey = [[[CryptoKey alloc] initWithPassword:encryptionPassword salt:salt error:error] autorelease]; + CryptoKey *cryptoKey = [[[CryptoKey alloc] initWithPassword:encryptionPassword salt:[NSData dataWithBytes:BUCKET_PLIST_SALT length:8] error:error] autorelease]; if (cryptoKey == nil) { return NO; } @@ -217,24 +215,12 @@ NSString *computerUUID = [path substringWithRange:computerUUIDRange]; NSString *bucketUUID = [path substringWithRange:bucketUUIDRange]; - NSError *saltError = nil; - ArqSalt *arqSalt = [[[ArqSalt alloc] initWithAccessKeyID:accessKey secretAccessKey:secretKey s3BucketName:s3BucketName computerUUID:computerUUID] autorelease]; - NSData *salt = [arqSalt salt:&saltError]; - if (salt == nil) { - if ([saltError code] != ERROR_NOT_FOUND) { - if (error != NULL) { - *error = saltError; - } - return NO; - } - } - NSString *bucketName = @"(unknown)"; NSData *data = [s3 dataAtPath:path error:NULL]; if (data != nil) { if (!strncmp([data bytes], "encrypted", 9)) { data = [data subdataWithRange:NSMakeRange(9, [data length] - 9)]; - CryptoKey *cryptoKey = [[[CryptoKey alloc] initWithPassword:encryptionPassword salt:salt error:error] autorelease]; + CryptoKey *cryptoKey = [[[CryptoKey alloc] initWithPassword:encryptionPassword salt:[NSData dataWithBytes:BUCKET_PLIST_SALT length:8] error:error] autorelease]; if (cryptoKey == nil) { return NO; } @@ -257,6 +243,18 @@ uac = [[[UserAndComputer alloc] initWithXMLData:uacData error:&uacError] autorelease]; } + NSError *saltError = nil; + ArqSalt *arqSalt = [[[ArqSalt alloc] initWithAccessKeyID:accessKey secretAccessKey:secretKey s3BucketName:s3BucketName computerUUID:computerUUID] autorelease]; + NSData *salt = [arqSalt salt:&saltError]; + if (salt == nil) { + if ([saltError code] != ERROR_NOT_FOUND) { + if (error != NULL) { + *error = saltError; + } + return NO; + } + } + ArqRepo *repo = [[[ArqRepo alloc] initWithS3Service:s3 s3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID encryptionPassword:encryptionPassword salt:salt error:error] autorelease]; if (repo == nil) { return NO; diff --git a/BlobKey.h b/BlobKey.h index 6f59b66..dcaea16 100644 --- a/BlobKey.h +++ b/BlobKey.h @@ -1,19 +1,33 @@ // // BlobKey.h -// Arq // // Created by Stefan Reitshamer on 6/27/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. +// Copyright 2011 Haystack Software. All rights reserved. // - +#import "StorageType.h" +@class BufferedInputStream; @interface BlobKey : NSObject { + StorageType storageType; + NSString *archiveId; + uint64_t archiveSize; + NSDate *archiveUploadedDate; NSString *sha1; BOOL stretchEncryptionKey; + BOOL compressed; } -- (id)initWithSHA1:(NSString *)theSHA1 stretchEncryptionKey:(BOOL)isStretchedKey; +- (id)initWithSHA1:(NSString *)theSHA1 archiveId:(NSString *)theArchiveId archiveSize:(uint64_t)theArchiveSize archiveUploadedDate:(NSDate *)theArchiveUploadedDate compressed:(BOOL)isCompressed; +- (id)initWithSHA1:(NSString *)theSHA1 storageType:(StorageType)theStorageType stretchEncryptionKey:(BOOL)isStretchedKey compressed:(BOOL)isCompressed; +- (id)initWithStorageType:(StorageType)theStorageType archiveId:(NSString *)theArchiveId archiveSize:(uint64_t)theArchiveSize archiveUploadedDate:(NSDate *)theArchiveUploadedDate sha1:(NSString *)theSHA1 stretchEncryptionKey:(BOOL)isStretchedKey compressed:(BOOL)isCompressed; + +- (StorageType)storageType; +- (NSString *)archiveId; +- (uint64_t)archiveSize; +- (NSDate *)archiveUploadedDate; - (NSString *)sha1; - (BOOL)stretchEncryptionKey; +- (BOOL)compressed; +- (BOOL)isEqualToBlobKey:(BlobKey *)other; @end diff --git a/BlobKey.m b/BlobKey.m index 74b3756..b9ec643 100644 --- a/BlobKey.m +++ b/BlobKey.m @@ -1,53 +1,118 @@ // // BlobKey.m -// Arq // // Created by Stefan Reitshamer on 6/27/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. +// Copyright 2011 Haystack Software. All rights reserved. // #import "BlobKey.h" +#import "BufferedInputStream.h" +#import "StringIO.h" +#import "BooleanIO.h" +#import "IntegerIO.h" +#import "NSObject_extra.h" @implementation BlobKey -- (id)initWithSHA1:(NSString *)theSHA1 stretchEncryptionKey:(BOOL)isStretchedKey { +- (id)initWithSHA1:(NSString *)theSHA1 archiveId:(NSString *)theArchiveId archiveSize:(uint64_t)theArchiveSize archiveUploadedDate:(NSDate *)theArchiveUploadedDate compressed:(BOOL)isCompressed { if (self = [super init]) { + storageType = StorageTypeGlacier; + sha1 = [theSHA1 retain]; + archiveId = [theArchiveId retain]; + archiveSize = theArchiveSize; + archiveUploadedDate = [theArchiveUploadedDate retain]; + compressed = isCompressed; + } + return self; +} +- (id)initWithSHA1:(NSString *)theSHA1 storageType:(StorageType)theStorageType stretchEncryptionKey:(BOOL)isStretchedKey compressed:(BOOL)isCompressed { + if (self = [super init]) { + storageType = theStorageType; sha1 = [theSHA1 retain]; stretchEncryptionKey = isStretchedKey; + compressed = isCompressed; + } + return self; +} +- (id)initWithStorageType:(StorageType)theStorageType archiveId:(NSString *)theArchiveId archiveSize:(uint64_t)theArchiveSize archiveUploadedDate:(NSDate *)theArchiveUploadedDate sha1:(NSString *)theSHA1 stretchEncryptionKey:(BOOL)isStretchedKey compressed:(BOOL)isCompressed { + if (self = [super init]) { + storageType = theStorageType; + archiveId = [theArchiveId retain]; + archiveSize = theArchiveSize; + archiveUploadedDate = [theArchiveUploadedDate retain]; + sha1 = [theSHA1 retain]; + stretchEncryptionKey = isStretchedKey; + compressed = isCompressed; } return self; } - (void)dealloc { + [archiveId release]; + [archiveUploadedDate release]; [sha1 release]; [super dealloc]; } +- (StorageType)storageType { + return storageType; +} +- (NSString *)archiveId { + return archiveId; +} +- (uint64_t)archiveSize { + return archiveSize; +} +- (NSDate *)archiveUploadedDate { + return archiveUploadedDate; +} - (NSString *)sha1 { return sha1; } - (BOOL)stretchEncryptionKey { return stretchEncryptionKey; } +- (BOOL)compressed { + return compressed; +} +- (BOOL)isEqualToBlobKey:(BlobKey *)other { + if (![[other sha1] isEqualToString:sha1]) { + return NO; + } + if (stretchEncryptionKey != [other stretchEncryptionKey]) { + return NO; + } + return YES; +} #pragma mark NSCopying - (id)copyWithZone:(NSZone *)zone { - return [[BlobKey alloc] initWithSHA1:sha1 stretchEncryptionKey:stretchEncryptionKey]; + return [[BlobKey alloc] initWithStorageType:storageType archiveId:archiveId archiveSize:archiveSize archiveUploadedDate:archiveUploadedDate sha1:sha1 stretchEncryptionKey:stretchEncryptionKey compressed:compressed]; } #pragma mark NSObject - (NSString *)description { - return [NSString stringWithFormat:@"", sha1, (stretchEncryptionKey ? @"YES" : @"NO")]; + if (storageType == StorageTypeS3) { + return [NSString stringWithFormat:@"", sha1, (stretchEncryptionKey ? @"YES" : @"NO"), (compressed ? @"YES" : @"NO")]; + } + return [NSString stringWithFormat:@"", archiveId, archiveUploadedDate, (stretchEncryptionKey ? @"YES" : @"NO"), (compressed ? @"YES" : @"NO")]; } - (BOOL)isEqual:(id)anObject { if (![anObject isKindOfClass:[BlobKey class]]) { return NO; } BlobKey *other = (BlobKey *)anObject; - return [[other sha1] isEqualToString:sha1] && [other stretchEncryptionKey] == stretchEncryptionKey; + + return [NSObject equalObjects:sha1 and:[other sha1]] + && stretchEncryptionKey == [other stretchEncryptionKey] + && storageType == [other storageType] + && [NSObject equalObjects:archiveId and:[other archiveId]] + && archiveSize == [other archiveSize] + && [NSObject equalObjects:archiveUploadedDate and:[other archiveUploadedDate]] + && compressed == [other compressed]; } - (NSUInteger)hash { - return [sha1 hash] + (stretchEncryptionKey ? 1 : 0); + return [sha1 hash] + (stretchEncryptionKey ? 1 : 0) + (compressed ? 1 : 0); } @end diff --git a/BlobKeyIO.h b/BlobKeyIO.h new file mode 100644 index 0000000..8129174 --- /dev/null +++ b/BlobKeyIO.h @@ -0,0 +1,17 @@ +// +// BlobKeyIO.h +// +// Created by Stefan Reitshamer on 9/14/12. +// +// + +@class BufferedInputStream; +@class BlobKey; + + +@interface BlobKeyIO : NSObject { + +} ++ (void)write:(BlobKey *)theBlobKey to:(NSMutableData *)data; ++ (BOOL)read:(BlobKey **)theBlobKey from:(BufferedInputStream *)is treeVersion:(int)theTreeVersion compressed:(BOOL)isCompressed error:(NSError **)error; +@end diff --git a/BlobKeyIO.m b/BlobKeyIO.m new file mode 100644 index 0000000..342dfe2 --- /dev/null +++ b/BlobKeyIO.m @@ -0,0 +1,54 @@ +// +// BlobKeyIO.m +// +// Created by Stefan Reitshamer on 9/14/12. +// +// + +#import "BlobKeyIO.h" +#import "BooleanIO.h" +#import "StringIO.h" +#import "IntegerIO.h" +#import "StorageType.h" +#import "BlobKey.h" +#import "DateIO.h" + + +@implementation BlobKeyIO ++ (void)write:(BlobKey *)theBlobKey to:(NSMutableData *)data { + [StringIO write:[theBlobKey sha1] to:data]; + [BooleanIO write:[theBlobKey stretchEncryptionKey] to:data]; + [IntegerIO writeUInt32:(uint32_t)[theBlobKey storageType] to:data]; + [StringIO write:[theBlobKey archiveId] to:data]; + [IntegerIO writeUInt64:[theBlobKey archiveSize] to:data]; + [DateIO write:[theBlobKey archiveUploadedDate] to:data]; +} ++ (BOOL)read:(BlobKey **)theBlobKey from:(BufferedInputStream *)is treeVersion:(int)theTreeVersion compressed:(BOOL)isCompressed error:(NSError **)error { + NSString *dataSHA1; + BOOL stretchEncryptionKey = NO; + StorageType storageType = StorageTypeS3; + NSString *archiveId = nil; + uint64_t archiveSize = 0; + NSDate *archiveUploadedDate = nil; + + if (![StringIO read:&dataSHA1 from:is error:error]) { + [self release]; + return NO; + } + if (theTreeVersion >= 14 && ![BooleanIO read:&stretchEncryptionKey from:is error:error]) { + [self release]; + return NO; + } + if (theTreeVersion >= 17) { + if (![IntegerIO readUInt32:&storageType from:is error:error] + || ![StringIO read:&archiveId from:is error:error] + || ![IntegerIO readUInt64:&archiveSize from:is error:error] + || ![DateIO read:&archiveUploadedDate from:is error:error]) { + [self release]; + return NO; + } + } + *theBlobKey = [[[BlobKey alloc] initWithStorageType:storageType archiveId:archiveId archiveSize:archiveSize archiveUploadedDate:archiveUploadedDate sha1:dataSHA1 stretchEncryptionKey:stretchEncryptionKey compressed:isCompressed] autorelease]; + return YES; +} +@end diff --git a/Commit.h b/Commit.h index 3af0f57..5d21549 100644 --- a/Commit.h +++ b/Commit.h @@ -11,47 +11,50 @@ #import "BufferedInputStream.h" @class BlobKey; -#define CURRENT_COMMIT_VERSION 7 +#define CURRENT_COMMIT_VERSION 9 @interface Commit : NSObject { int commitVersion; NSString *_author; NSString *_comment; - NSMutableSet *_parentCommitBlobKeys; + BlobKey *_parentCommitBlobKey; BlobKey *_treeBlobKey; NSString *_location; NSString *_computer; - BlobKey *_mergeCommonAncestorCommitBlobKey; NSDate *_creationDate; NSArray *_commitFailedFiles; + BOOL _hasMissingNodes; + BOOL _isComplete; NSData *_bucketXMLData; } + (NSString *)errorDomain; - (id)initWithCommit:(Commit *)commit parentCommitBlobKey:(BlobKey *)parentCommitBlobKey; -- (id) initWithAuthor:(NSString *)theAuthor - comment:(NSString *)theComment - parentCommitBlobKeys:(NSSet *)theParentCommitBlobKeys +- (id) initWithAuthor:(NSString *)theAuthor + comment:(NSString *)theComment + parentCommitBlobKey:(BlobKey *)theParentCommitBlobKey treeBlobKey:(BlobKey *)theTreeBlobKey location:(NSString *)theLocation - mergeCommonAncestorCommitBlobKey:(BlobKey *)theMergeCommonAncestorCommitBlobKey + creationDate:(NSDate *)theCreationDate commitFailedFiles:(NSArray *)theCommitFailedFiles + hasMissingNodes:(BOOL)theHasMissingNodes + isComplete:(BOOL)theIsComplete bucketXMLData:(NSData *)theBucketXMLData; - (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error; +@property(readonly) int commitVersion; @property(readonly,copy) NSString *author; @property(readonly,copy) NSString *comment; @property(readonly,copy) BlobKey *treeBlobKey; -@property(readonly,retain) NSSet *parentCommitBlobKeys; +@property(readonly,retain) BlobKey *parentCommitBlobKey; @property(readonly,copy) NSString *location; @property(readonly,copy) NSString *computer; -@property(readonly,copy) BlobKey *mergeCommonAncestorCommitBlobKey; @property(readonly,retain) NSDate *creationDate; @property(readonly,retain) NSArray *commitFailedFiles; +@property(readonly) BOOL hasMissingNodes; +@property(readonly) BOOL isComplete; @property(readonly, retain) NSData *bucketXMLData; -- (NSNumber *)isMergeCommit; -- (Blob *)toBlob; - +- (NSData *)toData; @end diff --git a/Commit.m b/Commit.m index e39d7a8..927ab72 100644 --- a/Commit.m +++ b/Commit.m @@ -13,8 +13,7 @@ #import "Blob.h" #import "DataInputStream.h" #import "RegexKitLite.h" -#import "SetNSError.h" -#import "NSErrorCodes.h" +#import "StorageType.h" #import "CommitFailedFile.h" #import "BufferedInputStream.h" #import "BooleanIO.h" @@ -33,15 +32,17 @@ } -@synthesize author = _author, -comment = _comment, -treeBlobKey = _treeBlobKey, -parentCommitBlobKeys = _parentCommitBlobKeys, -location = _location, +@synthesize commitVersion, +author = _author, +comment = _comment, +treeBlobKey = _treeBlobKey, +parentCommitBlobKey = _parentCommitBlobKey, +location = _location, computer = _computer, -mergeCommonAncestorCommitBlobKey = _mergeCommonAncestorCommitBlobKey, creationDate = _creationDate, commitFailedFiles = _commitFailedFiles, +hasMissingNodes = _hasMissingNodes, +isComplete = _isComplete, bucketXMLData = _bucketXMLData; @@ -49,34 +50,34 @@ bucketXMLData = _bucketXMLData; if (self = [super init]) { _author = [[theCommit author] copy]; _comment = [[theCommit comment] copy]; - if (theParentBlobKey != nil) { - _parentCommitBlobKeys = [[NSMutableSet alloc] initWithObjects:theParentBlobKey, nil]; - } else { - _parentCommitBlobKeys = [[NSMutableSet alloc] init]; - } + _parentCommitBlobKey = [theParentBlobKey retain]; _treeBlobKey = [[theCommit treeBlobKey] copy]; _location = [[theCommit location] copy]; _computer = [[theCommit computer] copy]; - _mergeCommonAncestorCommitBlobKey = nil; _creationDate = [[theCommit creationDate] copy]; _commitFailedFiles = [[theCommit commitFailedFiles] copy]; + _hasMissingNodes = [theCommit hasMissingNodes]; + _isComplete = [theCommit isComplete]; _bucketXMLData = [[theCommit bucketXMLData] copy]; } return self; } -- (id) initWithAuthor:(NSString *)theAuthor - comment:(NSString *)theComment - parentCommitBlobKeys:(NSSet *)theParentCommitBlobKeys +- (id) initWithAuthor:(NSString *)theAuthor + comment:(NSString *)theComment + parentCommitBlobKey:(BlobKey *)theParentCommitBlobKey treeBlobKey:(BlobKey *)theTreeBlobKey location:(NSString *)theLocation - mergeCommonAncestorCommitBlobKey:(BlobKey *)theMergeCommonAncestorCommitBlobKey - commitFailedFiles:(NSArray *)theCommitFailedFiles + creationDate:(NSDate *)theCreationDate + commitFailedFiles:(NSArray *)theCommitFailedFiles + hasMissingNodes:(BOOL)theHasMissingNodes + isComplete:(BOOL)theIsComplete bucketXMLData:(NSData *)theBucketXMLData { if (self = [super init]) { + commitVersion = CURRENT_COMMIT_VERSION; _author = [theAuthor copy]; _comment = [theComment copy]; - _parentCommitBlobKeys = [[NSMutableSet alloc] initWithSet:theParentCommitBlobKeys]; + _parentCommitBlobKey = [theParentCommitBlobKey retain]; _treeBlobKey = [theTreeBlobKey retain]; _location = [theLocation copy]; NSRange computerRange = [_location rangeOfRegex:@"^file://([^/]+)/" capture:1]; @@ -86,16 +87,16 @@ bucketXMLData = _bucketXMLData; _computer = @""; } [_computer retain]; - _mergeCommonAncestorCommitBlobKey = [theMergeCommonAncestorCommitBlobKey retain]; - _creationDate = [[NSDate alloc] init]; + _creationDate = [theCreationDate retain]; _commitFailedFiles = [theCommitFailedFiles copy]; + _hasMissingNodes = theHasMissingNodes; + _isComplete = theIsComplete; _bucketXMLData = [theBucketXMLData copy]; } return self; } - (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { - _parentCommitBlobKeys = [[NSMutableSet alloc] init]; if (![self readHeader:is error:error]) { goto init_error; } @@ -124,9 +125,11 @@ bucketXMLData = _bucketXMLData; goto init_error; } } - BlobKey *parentBlobKey = [[BlobKey alloc] initWithSHA1:key stretchEncryptionKey:cryptoKeyStretched]; - [_parentCommitBlobKeys addObject:parentBlobKey]; - [parentBlobKey release]; + if (_parentCommitBlobKey != nil) { + HSLogError(@"IGNORING EXTRA PARENT COMMIT BLOB KEY!"); + } else { + _parentCommitBlobKey = [[BlobKey alloc] initWithSHA1:key storageType:StorageTypeS3 stretchEncryptionKey:cryptoKeyStretched compressed:NO]; + } } NSString *treeSHA1 = nil; @@ -139,7 +142,13 @@ bucketXMLData = _bucketXMLData; goto init_error; } } - _treeBlobKey = [[BlobKey alloc] initWithSHA1:treeSHA1 stretchEncryptionKey:treeStretchedKey]; + BOOL treeIsCompressed = NO; + if (commitVersion >= 8) { + if (![BooleanIO read:&treeIsCompressed from:is error:error]) { + goto init_error; + } + } + _treeBlobKey = [[BlobKey alloc] initWithSHA1:treeSHA1 storageType:StorageTypeS3 stretchEncryptionKey:treeStretchedKey compressed:treeIsCompressed]; if (![StringIO read:&_location from:is error:error]) { goto init_error; @@ -154,18 +163,21 @@ bucketXMLData = _bucketXMLData; } [_computer retain]; - NSString *mergeCommonAncestorCommitSHA1 = nil; - BOOL mergeCommonAncestorCommitStretchedKey = NO; - if (![StringIO read:&mergeCommonAncestorCommitSHA1 from:is error:error]) { - goto init_error; - } - if (commitVersion >= 4) { - if (![BooleanIO read:&mergeCommonAncestorCommitStretchedKey from:is error:error]) { + // Removed mergeCommonAncestorCommitBlobKey in Commit version 8. It was never used. + if (commitVersion < 8) { + NSString *mergeCommonAncestorCommitSHA1 = nil; + BOOL mergeCommonAncestorCommitStretchedKey = NO; + if (![StringIO read:&mergeCommonAncestorCommitSHA1 from:is error:error]) { goto init_error; } - } - if (mergeCommonAncestorCommitSHA1 != nil) { - _mergeCommonAncestorCommitBlobKey = [[BlobKey alloc] initWithSHA1:mergeCommonAncestorCommitSHA1 stretchEncryptionKey:mergeCommonAncestorCommitStretchedKey]; + if (commitVersion >= 4) { + if (![BooleanIO read:&mergeCommonAncestorCommitStretchedKey from:is error:error]) { + goto init_error; + } + } + // if (mergeCommonAncestorCommitSHA1 != nil) { + // _mergeCommonAncestorCommitBlobKey = [[BlobKey alloc] initWithSHA1:mergeCommonAncestorCommitSHA1 stretchEncryptionKey:mergeCommonAncestorCommitStretchedKey]; + // } } if (![DateIO read:&_creationDate from:is error:error]) { @@ -189,6 +201,18 @@ bucketXMLData = _bucketXMLData; _commitFailedFiles = [commitFailedFiles retain]; } + if (commitVersion >= 8) { + if (![BooleanIO read:&_hasMissingNodes from:is error:error]) { + goto init_error; + } + } + if (commitVersion >= 9) { + if (![BooleanIO read:&_isComplete from:is error:error]) { + goto init_error; + } + } else { + _isComplete = YES; + } if (commitVersion >= 5) { if (![DataIO read:&_bucketXMLData from:is error:error]) { goto init_error; @@ -208,53 +232,48 @@ init_done: - (void)dealloc { [_author release]; [_comment release]; - [_parentCommitBlobKeys release]; + [_parentCommitBlobKey release]; [_treeBlobKey release]; [_location release]; [_computer release]; - [_mergeCommonAncestorCommitBlobKey release]; [_creationDate release]; [_commitFailedFiles release]; [_bucketXMLData release]; [super dealloc]; } -- (NSNumber *)isMergeCommit { - return [NSNumber numberWithBool:([_parentCommitBlobKeys count] > 1)]; -} -- (Blob *)toBlob { - Blob *ret = nil; - NSMutableData *data = [[NSMutableData alloc] init]; +- (NSData *)toData { + NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; char header[HEADER_LENGTH + 1]; sprintf(header, "CommitV%03d", CURRENT_COMMIT_VERSION); [data appendBytes:header length:HEADER_LENGTH]; [StringIO write:_author to:data]; [StringIO write:_comment to:data]; - uint64_t parentCommitBlobKeysCount = (uint64_t)[_parentCommitBlobKeys count]; - [IntegerIO writeUInt64:parentCommitBlobKeysCount to:data]; - for (BlobKey *parentCommitBlobKey in _parentCommitBlobKeys) { - [StringIO write:[parentCommitBlobKey sha1] to:data]; - [BooleanIO write:[parentCommitBlobKey stretchEncryptionKey] to:data]; + if (_parentCommitBlobKey == nil) { + [IntegerIO writeUInt64:0 to:data]; + } else { + [IntegerIO writeUInt64:1 to:data]; + [StringIO write:[_parentCommitBlobKey sha1] to:data]; + [BooleanIO write:[_parentCommitBlobKey stretchEncryptionKey] to:data]; } [StringIO write:[_treeBlobKey sha1] to:data]; [BooleanIO write:[_treeBlobKey stretchEncryptionKey] to:data]; + [BooleanIO write:[_treeBlobKey compressed] to:data]; [StringIO write:_location to:data]; - [StringIO write:[_mergeCommonAncestorCommitBlobKey sha1] to:data]; - [BooleanIO write:[_mergeCommonAncestorCommitBlobKey stretchEncryptionKey] to:data]; [DateIO write:_creationDate to:data]; uint64_t commitFailedFilesCount = (uint64_t)[_commitFailedFiles count]; [IntegerIO writeUInt64:commitFailedFilesCount to:data]; for (CommitFailedFile *cff in _commitFailedFiles) { [cff writeTo:data]; } + [BooleanIO write:_hasMissingNodes to:data]; + [BooleanIO write:_isComplete to:data]; [DataIO write:_bucketXMLData to:data]; - ret = [[[Blob alloc] initWithData:data mimeType:@"binary/octet-stream" downloadName:@"commit" dataDescription:@"commit"] autorelease]; - [data release]; - return ret; + return data; } #pragma mark NSObject - (NSString *)description { - return [NSString stringWithFormat:@"", _creationDate, _treeBlobKey, _parentCommitBlobKeys]; + return [NSString stringWithFormat:@"", _creationDate, _treeBlobKey, _parentCommitBlobKey, (_isComplete ? @"YES" : @"NO"), (_hasMissingNodes ? @"YES" : @"NO")]; } @end @@ -266,16 +285,14 @@ init_done: goto readHeader_error; } NSString *header = [[[NSString alloc] initWithBytes:buf length:HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; - NSRange versionRange = [header rangeOfRegex:@"^CommitV(\\d{3})$" capture:1]; - commitVersion = 0; - if (versionRange.location != NSNotFound) { - NSNumberFormatter *nf = [[NSNumberFormatter alloc] init]; - NSNumber *number = [nf numberFromString:[header substringWithRange:versionRange]]; - commitVersion = [number intValue]; - [nf release]; + if (![header hasPrefix:@"CommitV"] || [header length] < 8) { + HSLogDebug(@"current Commit version: %d", CURRENT_COMMIT_VERSION); + SETNSERROR([Commit errorDomain], ERROR_INVALID_COMMIT_HEADER, @"invalid header %@", header); + goto readHeader_error; } + commitVersion = [[header substringFromIndex:7] intValue]; if (commitVersion > CURRENT_COMMIT_VERSION || commitVersion < 2) { - SETNSERROR([Commit errorDomain], ERROR_INVALID_OBJECT_VERSION, @"invalid Commit header"); + SETNSERROR([Commit errorDomain], ERROR_INVALID_OBJECT_VERSION, @"invalid Commit version %d", commitVersion); goto readHeader_error; } ret = YES; diff --git a/Node.h b/Node.h index 507f1c0..a1b5c12 100644 --- a/Node.h +++ b/Node.h @@ -10,19 +10,17 @@ @protocol InputStream; @class BlobKey; +@class BufferedInputStream; + @interface Node : NSObject { int treeVersion; BOOL isTree; + BOOL treeContainsMissingItems; unsigned long long uncompressedDataSize; - BOOL dataAreCompressed; NSMutableArray *dataBlobKeys; - BlobKey *thumbnailBlobKey; - BlobKey *previewBlobKey; - BOOL xattrsAreCompressed; BlobKey *xattrsBlobKey; unsigned long long xattrsSize; - BOOL aclIsCompressed; BlobKey *aclBlobKey; int uid; int gid; @@ -46,22 +44,20 @@ int64_t st_blocks; uint32_t st_blksize; } -- (id)initWithInputStream:(id )is treeVersion:(int)theTreeVersion error:(NSError **)error; +- (id)initWithInputStream:(BufferedInputStream *)is treeVersion:(int)theTreeVersion error:(NSError **)error; - (void)writeToData:(NSMutableData *)data; -- (BOOL)dataMatchesStatData:(struct stat *)st; +- (BOOL)dataMatchesStat:(struct stat *)st; +- (BOOL)ctimeMatchesStat:(struct stat *)st; @property(readonly) BOOL isTree; +@property(readonly) BOOL treeContainsMissingItems; @property(readonly,copy) BlobKey *treeBlobKey; -@property(readonly) BOOL dataAreCompressed; @property(readonly,copy) NSArray *dataBlobKeys; @property(readonly) unsigned long long uncompressedDataSize; -@property(readonly,copy) BlobKey *thumbnailBlobKey; -@property(readonly,copy) BlobKey *previewBlobKey; -@property(readonly) BOOL xattrsAreCompressed; +@property(readonly) unsigned long long aggregateGlacierArchiveSize; @property(readonly,copy) BlobKey *xattrsBlobKey; @property(readonly) unsigned long long xattrsSize; -@property(readonly) BOOL aclIsCompressed; @property(readonly,copy) BlobKey *aclBlobKey; @property(readonly) int uid; @property(readonly) int gid; @@ -85,5 +81,4 @@ @property(readonly) int st_ino; @property(readonly) int64_t st_blocks; @property(readonly) uint32_t st_blksize; -- (uint64_t)sizeOnDisk; @end diff --git a/Node.m b/Node.m index 64e4758..cda603d 100644 --- a/Node.m +++ b/Node.m @@ -14,23 +14,37 @@ #import "BufferedInputStream.h" #import "BlobKey.h" #import "NSObject_extra.h" +#import "Tree.h" +#import "BlobKeyIO.h" + @implementation Node -@synthesize isTree, uncompressedDataSize, thumbnailBlobKey, previewBlobKey, xattrsBlobKey, xattrsSize, aclBlobKey, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, finderFileType, finderFileCreator, isFileExtensionHidden, st_dev, treeVersion, st_rdev; +@synthesize isTree, treeContainsMissingItems, uncompressedDataSize, xattrsBlobKey, xattrsSize, aclBlobKey, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, finderFileType, finderFileCreator, isFileExtensionHidden, st_dev, treeVersion, st_rdev; @synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino, st_blocks, st_blksize; @dynamic treeBlobKey, dataBlobKeys; -@synthesize dataAreCompressed, xattrsAreCompressed, aclIsCompressed; +@dynamic aggregateGlacierArchiveSize; + - (id)initWithInputStream:(BufferedInputStream *)is treeVersion:(int)theTreeVersion error:(NSError **)error { if (self = [super init]) { treeVersion = theTreeVersion; dataBlobKeys = [[NSMutableArray alloc] init]; - + if (![BooleanIO read:&isTree from:is error:error]) { [self release]; return nil; } + if (theTreeVersion >= 18) { + if (![BooleanIO read:&treeContainsMissingItems from:is error:error]) { + [self release]; + return nil; + } + } + + BOOL dataAreCompressed = NO; + BOOL xattrsAreCompressed = NO; + BOOL aclIsCompressed = NO; if (treeVersion >= 12) { if (![BooleanIO read:&dataAreCompressed from:is error:error] || ![BooleanIO read:&xattrsAreCompressed from:is error:error] @@ -46,38 +60,32 @@ return nil; } for (int i = 0; i < dataBlobKeysCount; i++) { - NSString *dataSHA1; - BOOL stretchEncryptionKey = NO; - if (![StringIO read:&dataSHA1 from:is error:error]) { - [self release]; - return nil; - } - if (treeVersion >= 14 && ![BooleanIO read:&stretchEncryptionKey from:is error:error]) { + BlobKey *dataBlobKey = nil; + if (![BlobKeyIO read:&dataBlobKey from:is treeVersion:treeVersion compressed:dataAreCompressed error:error]) { [self release]; return nil; } - BlobKey *bk = [[BlobKey alloc] initWithSHA1:dataSHA1 stretchEncryptionKey:stretchEncryptionKey]; - [dataBlobKeys addObject:bk]; - [bk release]; + [dataBlobKeys addObject:dataBlobKey]; } - NSString *thumbnailSHA1 = nil; - BOOL thumbnailStretchedKey = NO; - NSString *previewSHA1 = nil; - BOOL previewStretchedKey = NO; - NSString *xattrsSHA1 = nil; - BOOL xattrsStretchedKey = NO; - NSString *aclSHA1 = nil; - BOOL aclStretchedKey = NO; - BOOL ret = [IntegerIO readUInt64:&uncompressedDataSize from:is error:error] - && [StringIO read:&thumbnailSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&thumbnailStretchedKey from:is error:error]) - && [StringIO read:&previewSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&previewStretchedKey from:is error:error]) - && [StringIO read:&xattrsSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&xattrsStretchedKey from:is error:error]) + if (![IntegerIO readUInt64:&uncompressedDataSize from:is error:error]) { + [self release]; + return nil; + } + + // As of Tree version 18 thumbnailBlobKey and previewBlobKey have been removed. They were never used. + if (theTreeVersion < 18) { + BlobKey *theThumbnailBlobKey = nil; + BlobKey *thePreviewBlobKey = nil; + if (![BlobKeyIO read:&theThumbnailBlobKey from:is treeVersion:treeVersion compressed:NO error:error] + || ![BlobKeyIO read:&thePreviewBlobKey from:is treeVersion:treeVersion compressed:NO error:error]) { + [self release]; + return nil; + } + } + + BOOL ret = [BlobKeyIO read:&xattrsBlobKey from:is treeVersion:treeVersion compressed:xattrsAreCompressed error:error] && [IntegerIO readUInt64:&xattrsSize from:is error:error] - && [StringIO read:&aclSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&aclStretchedKey from:is error:error]) + && [BlobKeyIO read:&aclBlobKey from:is treeVersion:treeVersion compressed:aclIsCompressed error:error] && [IntegerIO readInt32:&uid from:is error:error] && [IntegerIO readInt32:&gid from:is error:error] && [IntegerIO readInt32:&mode from:is error:error] @@ -99,31 +107,30 @@ && [IntegerIO readInt64:&createTime_nsec from:is error:error] && [IntegerIO readInt64:&st_blocks from:is error:error] && [IntegerIO readUInt32:&st_blksize from:is error:error]; + [xattrsBlobKey retain]; + [aclBlobKey retain]; [finderFileType retain]; [finderFileCreator retain]; if (!ret) { [self release]; return nil; } - if (thumbnailSHA1 != nil) { - thumbnailBlobKey = [[BlobKey alloc] initWithSHA1:thumbnailSHA1 stretchEncryptionKey:thumbnailStretchedKey]; + + // If any BlobKey has a nil sha1, drop it. + + if ([xattrsBlobKey sha1] == nil) { + [xattrsBlobKey release]; + xattrsBlobKey = nil; } - if (previewSHA1 != nil) { - previewBlobKey = [[BlobKey alloc] initWithSHA1:previewSHA1 stretchEncryptionKey:previewStretchedKey]; - } - if (xattrsSHA1 != nil) { - xattrsBlobKey = [[BlobKey alloc] initWithSHA1:xattrsSHA1 stretchEncryptionKey:xattrsStretchedKey]; - } - if (aclSHA1 != nil) { - aclBlobKey = [[BlobKey alloc] initWithSHA1:aclSHA1 stretchEncryptionKey:aclStretchedKey]; + if ([aclBlobKey sha1] == nil) { + [aclBlobKey release]; + aclBlobKey = nil; } } return self; } - (void)dealloc { [dataBlobKeys release]; - [thumbnailBlobKey release]; - [previewBlobKey release]; [xattrsBlobKey release]; [aclBlobKey release]; [finderFileType release]; @@ -137,29 +144,38 @@ - (NSArray *)dataBlobKeys { return dataBlobKeys; } -- (BOOL)dataMatchesStatData:(struct stat *)st { - return (st->st_mtimespec.tv_sec == mtime_sec && st->st_mtimespec.tv_nsec == mtime_nsec && st->st_size == uncompressedDataSize); +- (uint64_t)aggregateGlacierArchiveSize { + uint64_t ret = 0; + ret += [aclBlobKey archiveSize]; + ret += [xattrsBlobKey archiveSize]; + for (BlobKey *dataBlobKey in dataBlobKeys) { + ret += [dataBlobKey archiveSize]; + } + return ret; +} + +- (BOOL)dataMatchesStat:(struct stat *)st { + return st->st_mtimespec.tv_sec == mtime_sec && st->st_mtimespec.tv_nsec == mtime_nsec && st->st_size == uncompressedDataSize; +} +- (BOOL)ctimeMatchesStat:(struct stat *)st { + return st->st_ctimespec.tv_sec == ctime_sec && st->st_ctimespec.tv_nsec == ctime_nsec; } - (void)writeToData:(NSMutableData *)data { [BooleanIO write:isTree to:data]; + [BooleanIO write:treeContainsMissingItems to:data]; + BOOL dataAreCompressed = [dataBlobKeys count] == 0 ? NO : [[dataBlobKeys objectAtIndex:0] compressed]; [BooleanIO write:dataAreCompressed to:data]; - [BooleanIO write:xattrsAreCompressed to:data]; - [BooleanIO write:aclIsCompressed to:data]; + [BooleanIO write:[xattrsBlobKey compressed] to:data]; + [BooleanIO write:[aclBlobKey compressed] to:data]; [IntegerIO writeInt32:(int32_t)[dataBlobKeys count] to:data]; for (BlobKey *dataBlobKey in dataBlobKeys) { - [StringIO write:[dataBlobKey sha1] to:data]; - [BooleanIO write:[dataBlobKey stretchEncryptionKey] to:data]; + NSAssert([dataBlobKey compressed] == dataAreCompressed, @"all dataBlobKeys must have same compressed flag value"); + [BlobKeyIO write:dataBlobKey to:data]; } [IntegerIO writeUInt64:uncompressedDataSize to:data]; - [StringIO write:[thumbnailBlobKey sha1] to:data]; - [BooleanIO write:[thumbnailBlobKey stretchEncryptionKey] to:data]; - [StringIO write:[previewBlobKey sha1] to:data]; - [BooleanIO write:[previewBlobKey stretchEncryptionKey] to:data]; - [StringIO write:[xattrsBlobKey sha1] to:data]; - [BooleanIO write:[xattrsBlobKey stretchEncryptionKey] to:data]; + [BlobKeyIO write:xattrsBlobKey to:data]; [IntegerIO writeUInt64:xattrsSize to:data]; - [StringIO write:[aclBlobKey sha1] to:data]; - [BooleanIO write:[aclBlobKey stretchEncryptionKey] to:data]; + [BlobKeyIO write:aclBlobKey to:data]; [IntegerIO writeInt32:uid to:data]; [IntegerIO writeInt32:gid to:data]; [IntegerIO writeInt32:mode to:data]; @@ -182,9 +198,7 @@ [IntegerIO writeInt64:st_blocks to:data]; [IntegerIO writeUInt32:st_blksize to:data]; } -- (uint64_t)sizeOnDisk { - return (uint64_t)st_blocks * (uint64_t)512; -} + #pragma mark NSObject - (BOOL)isEqual:(id)object { @@ -192,17 +206,12 @@ return NO; } Node *other = (Node *)object; - return treeVersion == [other treeVersion] + return treeVersion == [other treeVersion] && isTree == [other isTree] && uncompressedDataSize == [other uncompressedDataSize] - && dataAreCompressed == [other dataAreCompressed] && [dataBlobKeys isEqualToArray:[other dataBlobKeys]] - && [NSObject equalObjects:thumbnailBlobKey and:[other thumbnailBlobKey]] - && [NSObject equalObjects:previewBlobKey and:[other previewBlobKey]] - && xattrsAreCompressed == [other xattrsAreCompressed] && [NSObject equalObjects:xattrsBlobKey and:[other xattrsBlobKey]] && xattrsSize == [other xattrsSize] - && aclIsCompressed == [other aclIsCompressed] && [NSObject equalObjects:aclBlobKey and:[other aclBlobKey]] && uid == [other uid] && gid == [other gid] @@ -226,6 +235,6 @@ && st_blksize == [other st_blksize]; } - (NSUInteger)hash { - return (NSUInteger)treeVersion + (dataAreCompressed ? 1 : 0) + [dataBlobKeys hash]; + return (NSUInteger)treeVersion + [dataBlobKeys hash]; } @end diff --git a/ReflogEntry.m b/ReflogEntry.m index 24ab650..a3cf941 100644 --- a/ReflogEntry.m +++ b/ReflogEntry.m @@ -29,10 +29,14 @@ return nil; } oldHeadBlobKey = [[BlobKey alloc] initWithSHA1:[[dictNode stringNodeForKey:@"oldHeadSHA1"] stringValue] - stretchEncryptionKey:[[dictNode booleanNodeForKey:@"oldHeadStretchKey"] booleanValue]]; + storageType:StorageTypeS3 + stretchEncryptionKey:[[dictNode booleanNodeForKey:@"oldHeadStretchKey"] booleanValue] + compressed:NO]; newHeadBlobKey = [[BlobKey alloc] initWithSHA1:[[dictNode stringNodeForKey:@"newHeadSHA1"] stringValue] - stretchEncryptionKey:[[dictNode booleanNodeForKey:@"newHeadStretchKey"] booleanValue]]; + storageType:StorageTypeS3 + stretchEncryptionKey:[[dictNode booleanNodeForKey:@"newHeadStretchKey"] booleanValue] + compressed:NO]; } return self; } diff --git a/Restorer.m b/Restorer.m index db6b348..d098f4e 100644 --- a/Restorer.m +++ b/Restorer.m @@ -69,9 +69,9 @@ - (BOOL)applyTree:(Tree *)tree toPath:(NSString *)restorePath error:(NSError **)error; - (BOOL)applyNode:(Node *)node toPath:(NSString *)restorePath error:(NSError **)error; - (BOOL)createFile:(Node *)node atPath:(NSString *)path error:(NSError **)error; -- (BOOL)createFileAtPath:(NSString *)path fromBlobKeys:(NSArray *)dataBlobKeys uncompress:(BOOL)uncompress error:(NSError **)error; -- (BOOL)appendBlobForBlobKey:(BlobKey *)theBlobKey uncompress:(BOOL)uncompress to:(FileOutputStream *)fos error:(NSError **)error; -- (BOOL)doAppendBlobForBlobKey:(BlobKey *)theBlobKey uncompress:(BOOL)uncompress to:(BufferedOutputStream *)bos error:(NSError **)error; +- (BOOL)createFileAtPath:(NSString *)path fromBlobKeys:(NSArray *)dataBlobKeys error:(NSError **)error; +- (BOOL)appendBlobForBlobKey:(BlobKey *)theBlobKey to:(FileOutputStream *)fos error:(NSError **)error; +- (BOOL)doAppendBlobForBlobKey:(BlobKey *)theBlobKey to:(BufferedOutputStream *)bos error:(NSError **)error; - (BOOL)createSymLink:(Node *)node path:(NSString *)symLinkFile target:(NSString *)target error:(NSError **)error; - (BOOL)applyACLBlobKey:(BlobKey *)aclBlobKey uncompress:(BOOL)uncompress toPath:(NSString *)path error:(NSError **)error; - (BOOL)applyXAttrsBlobKey:(BlobKey *)xattrsBlobKey uncompress:(BOOL)uncompress toFile:(NSString *)path error:(NSError **)error; @@ -117,13 +117,13 @@ Commit *commit = nil; NSError *myError = nil; if (commitSHA1 != nil) { - commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 stretchEncryptionKey:YES] autorelease]; + commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 storageType:StorageTypeS3 stretchEncryptionKey:YES compressed:NO] autorelease]; commit = [repo commitForBlobKey:commitBlobKey error:&myError]; if (commit == nil) { HSLogError(@"error attempting to read commit for %@", commitBlobKey); // Try without stretched encryption key. - commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 stretchEncryptionKey:NO] autorelease]; + commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 storageType:StorageTypeS3 stretchEncryptionKey:NO compressed:NO] autorelease]; commit = [repo commitForBlobKey:commitBlobKey error:&myError]; if (commit == nil) { HSLogError(@"error attempting to read commit for %@", commitBlobKey); @@ -452,10 +452,10 @@ if (!fa) { return NO; } - if (![self applyXAttrsBlobKey:[node xattrsBlobKey] uncompress:[node xattrsAreCompressed] toFile:path error:error]) { + if (![self applyXAttrsBlobKey:[node xattrsBlobKey] uncompress:[[node xattrsBlobKey] compressed] toFile:path error:error]) { return NO; } - if (![self applyACLBlobKey:[node aclBlobKey] uncompress:[node aclIsCompressed] toPath:path error:error]) { + if (![self applyACLBlobKey:[node aclBlobKey] uncompress:[[node aclBlobKey] compressed] toPath:path error:error]) { return NO; } if (!S_ISFIFO([node mode])) { @@ -503,7 +503,7 @@ HSLogError(@"error getting data for %@", dataBlobKey); return NO; } - if ([node dataAreCompressed]) { + if ([dataBlobKey compressed]) { blobData = [blobData gzipInflate]; } [data appendData:blobData]; @@ -514,7 +514,7 @@ return NO; } } else if ([node uncompressedDataSize] > 0) { - if (![self createFileAtPath:path fromBlobKeys:[node dataBlobKeys] uncompress:[node dataAreCompressed] error:error]) { + if (![self createFileAtPath:path fromBlobKeys:[node dataBlobKeys] error:error]) { NSError *myError = nil; if ([[NSFileManager defaultManager] fileExistsAtPath:path] && ![[NSFileManager defaultManager] removeItemAtPath:path error:&myError]) { HSLogError(@"error deleting incorrectly-restored file %@: %@", path, myError); @@ -535,12 +535,12 @@ HSLogDetail(@"restored %@", path); return YES; } -- (BOOL)createFileAtPath:(NSString *)path fromBlobKeys:(NSArray *)dataBlobKeys uncompress:(BOOL)uncompress error:(NSError **)error { +- (BOOL)createFileAtPath:(NSString *)path fromBlobKeys:(NSArray *)dataBlobKeys error:(NSError **)error { FileOutputStream *fos = [[FileOutputStream alloc] initWithPath:path append:NO]; BOOL ret = YES; writtenToCurrentFile = 0; for (BlobKey *dataBlobKey in dataBlobKeys) { - if (![self appendBlobForBlobKey:dataBlobKey uncompress:uncompress to:fos error:error]) { + if (![self appendBlobForBlobKey:dataBlobKey to:fos error:error]) { ret = NO; break; } @@ -548,7 +548,7 @@ [fos release]; return ret; } -- (BOOL)appendBlobForBlobKey:(BlobKey *)theBlobKey uncompress:(BOOL)uncompress to:(FileOutputStream *)fos error:(NSError **)error { +- (BOOL)appendBlobForBlobKey:(BlobKey *)theBlobKey to:(FileOutputStream *)fos error:(NSError **)error { BOOL ret = NO; NSError *myError = nil; NSAutoreleasePool *pool = nil; @@ -558,7 +558,7 @@ [pool drain]; pool = [[NSAutoreleasePool alloc] init]; BufferedOutputStream *bos = [[[BufferedOutputStream alloc] initWithUnderlyingOutputStream:fos] autorelease]; - if ([self doAppendBlobForBlobKey:theBlobKey uncompress:uncompress to:bos error:&myError] && [bos flush:&myError]) { + if ([self doAppendBlobForBlobKey:theBlobKey to:bos error:&myError] && [bos flush:&myError]) { ret = YES; break; } @@ -589,13 +589,13 @@ } return ret; } -- (BOOL)doAppendBlobForBlobKey:(BlobKey *)theBlobKey uncompress:(BOOL)uncompress to:(BufferedOutputStream *)bos error:(NSError **)error { +- (BOOL)doAppendBlobForBlobKey:(BlobKey *)theBlobKey to:(BufferedOutputStream *)bos error:(NSError **)error { ServerBlob *sb = [[repo newServerBlobForBlobKey:theBlobKey error:error] autorelease]; if (sb == nil) { return NO; } id is = [[sb newInputStream] autorelease]; - if (uncompress) { + if ([theBlobKey compressed]) { is = [[[GunzipInputStream alloc] initWithUnderlyingStream:is] autorelease]; } HSLogDebug(@"writing %@ to %@", is, bos); diff --git a/Tree.h b/Tree.h index 3fe73e4..b76b990 100644 --- a/Tree.h +++ b/Tree.h @@ -6,13 +6,14 @@ // Copyright 2009 PhotoMinds LLC. All rights reserved. // +#include #import "Blob.h" @class BufferedInputStream; @class Node; @class BlobKey; -#define CURRENT_TREE_VERSION 15 +#define CURRENT_TREE_VERSION 18 #define TREE_HEADER_LENGTH (8) @interface Tree : NSObject { @@ -40,7 +41,7 @@ int64_t createTime_nsec; int64_t st_blocks; uint32_t st_blksize; - uint64_t aggregateSizeOnDisk; + NSMutableDictionary *missingNodes; NSMutableDictionary *nodes; } + (NSString *)errorDomain; @@ -49,7 +50,12 @@ - (Node *)childNodeWithName:(NSString *)name; - (BOOL)containsNodeNamed:(NSString *)name; - (NSDictionary *)nodes; -- (Blob *)toBlob; +- (BOOL)containsMissingItems; +- (NSArray *)missingChildNodeNames; +- (Node *)missingChildNodeWithName:(NSString *)name; +- (NSDictionary *)missingNodes; +- (NSData *)toData; +- (BOOL)ctimeMatchesStat:(struct stat *)st; @property(readonly) BOOL xattrsAreCompressed; @property(readonly,copy) BlobKey *xattrsBlobKey; @@ -75,5 +81,5 @@ @property(readonly) int st_ino; @property(readonly) int64_t st_blocks; @property(readonly) uint32_t st_blksize; -@property(readonly) uint64_t aggregateSizeOnDisk; +@property(readonly) uint64_t aggregateUncompressedDataSize; @end diff --git a/Tree.m b/Tree.m index b5e3ef6..f6ee8e6 100644 --- a/Tree.m +++ b/Tree.m @@ -6,6 +6,7 @@ // Copyright 2009 PhotoMinds LLC. All rights reserved. // +#include #import "StringIO.h" #import "IntegerIO.h" #import "BooleanIO.h" @@ -13,14 +14,14 @@ #import "Tree.h" #import "Blob.h" #import "DataInputStream.h" -#import "SetNSError.h" #import "RegexKitLite.h" -#import "NSErrorCodes.h" #import "BufferedInputStream.h" #import "NSData-Gzip.h" #import "GunzipInputStream.h" #import "BlobKey.h" #import "NSObject_extra.h" +#import "BlobKeyIO.h" + @interface Tree (internal) - (BOOL)readHeader:(BufferedInputStream *)is error:(NSError **)error; @@ -29,7 +30,8 @@ @implementation Tree @synthesize xattrsAreCompressed, xattrsBlobKey, xattrsSize, aclIsCompressed, aclBlobKey, uid, gid, mode, mtime_sec, mtime_nsec, flags, finderFlags, extendedFinderFlags, st_dev, treeVersion, st_rdev; @synthesize ctime_sec, ctime_nsec, createTime_sec, createTime_nsec, st_nlink, st_ino, st_blocks, st_blksize; -@synthesize aggregateSizeOnDisk; +@dynamic aggregateUncompressedDataSize; + + (NSString *)errorDomain { return @"TreeErrorDomain"; @@ -37,11 +39,15 @@ - (id)init { if (self = [super init]) { nodes = [[NSMutableDictionary alloc] init]; + missingNodes = [[NSMutableDictionary alloc] init]; } return self; } - (id)initWithBufferedInputStream:(BufferedInputStream *)is error:(NSError **)error { if (self = [super init]) { + nodes = [[NSMutableDictionary alloc] init]; + missingNodes = [[NSMutableDictionary alloc] init]; + if (![self readHeader:is error:error]) { [self release]; return nil; @@ -54,15 +60,9 @@ } } - NSString *xattrsSHA1 = nil; - BOOL xattrsStretchedKey = NO; - NSString *aclSHA1 = nil; - BOOL aclStretchedKey = NO; - BOOL ret = [StringIO read:&xattrsSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&xattrsStretchedKey from:is error:error]) + BOOL ret = [BlobKeyIO read:&xattrsBlobKey from:is treeVersion:treeVersion compressed:xattrsAreCompressed error:error] && [IntegerIO readUInt64:&xattrsSize from:is error:error] - &&[StringIO read:&aclSHA1 from:is error:error] - && (treeVersion < 14 || [BooleanIO read:&aclStretchedKey from:is error:error]) + && [BlobKeyIO read:&aclBlobKey from:is treeVersion:treeVersion compressed:aclIsCompressed error:error] && [IntegerIO readInt32:&uid from:is error:error] && [IntegerIO readInt32:&gid from:is error:error] && [IntegerIO readInt32:&mode from:is error:error] @@ -79,18 +79,23 @@ && [IntegerIO readInt64:&ctime_nsec from:is error:error] && [IntegerIO readInt64:&st_blocks from:is error:error] && [IntegerIO readUInt32:&st_blksize from:is error:error]; + [xattrsBlobKey retain]; + [aclBlobKey retain]; if (!ret) { goto initError; } - if (xattrsSHA1 != nil) { - xattrsBlobKey = [[BlobKey alloc] initWithSHA1:xattrsSHA1 stretchEncryptionKey:xattrsStretchedKey]; + if ([xattrsBlobKey sha1] == nil) { + [xattrsBlobKey release]; + xattrsBlobKey = nil; } - if (aclSHA1 != nil) { - aclBlobKey = [[BlobKey alloc] initWithSHA1:aclSHA1 stretchEncryptionKey:aclStretchedKey]; + if ([aclBlobKey sha1] == nil) { + [aclBlobKey release]; + aclBlobKey = nil; } - if (treeVersion >= 11) { - if (![IntegerIO readUInt64:&aggregateSizeOnDisk from:is error:error]) { + if (treeVersion >= 11 && treeVersion <= 16) { + uint64_t unusedAggregateSizeOnDisk; + if (![IntegerIO readUInt64:&unusedAggregateSizeOnDisk from:is error:error]) { goto initError; } } @@ -99,14 +104,31 @@ || ![IntegerIO readInt64:&createTime_nsec from:is error:error]) { goto initError; } - } + } - unsigned int nodeCount; + if (treeVersion >= 18) { + uint32_t missingNodeCount; + if (![IntegerIO readUInt32:&missingNodeCount from:is error:error]) { + goto initError; + } + for (uint32_t i = 0; i < missingNodeCount; i++) { + NSString *missingNodeName = nil; + if (![StringIO read:&missingNodeName from:is error:error]) { + goto initError; + } + Node *node = [[[Node alloc] initWithInputStream:is treeVersion:treeVersion error:error] autorelease]; + if (node == nil) { + goto initError; + } + [missingNodes setObject:node forKey:missingNodeName]; + } + } + + uint32_t nodeCount; if (![IntegerIO readUInt32:&nodeCount from:is error:error]) { goto initError; } - nodes = [[NSMutableDictionary alloc] init]; - for (unsigned int i = 0; i < nodeCount; i++) { + for (uint32_t i = 0; i < nodeCount; i++) { NSString *nodeName; if (![StringIO read:&nodeName from:is error:error]) { goto initError; @@ -130,6 +152,7 @@ initDone: [xattrsBlobKey release]; [aclBlobKey release]; [nodes release]; + [missingNodes release]; [super dealloc]; } - (NSArray *)childNodeNames { @@ -141,18 +164,36 @@ initDone: - (BOOL)containsNodeNamed:(NSString *)name { return [nodes objectForKey:name] != nil; } +- (BOOL)containsMissingItems { + if ([missingNodes count] > 0) { + return YES; + } + for (Node *node in [nodes allValues]) { + if ([node isTree] && [node treeContainsMissingItems]) { + return YES; + } + } + return NO; +} +- (NSArray *)missingChildNodeNames { + return [missingNodes allKeys]; +} +- (Node *)missingChildNodeWithName:(NSString *)name { + return [missingNodes objectForKey:name]; +} - (NSDictionary *)nodes { return nodes; } -- (Blob *)toBlob { - NSMutableData *data = [[NSMutableData alloc] init]; +- (NSDictionary *)missingNodes { + return missingNodes; +} +- (NSData *)toData { + NSMutableData *data = [[[NSMutableData alloc] init] autorelease]; [BooleanIO write:xattrsAreCompressed to:data]; [BooleanIO write:aclIsCompressed to:data]; - [StringIO write:[xattrsBlobKey sha1] to:data]; - [BooleanIO write:[xattrsBlobKey stretchEncryptionKey] to:data]; + [BlobKeyIO write:xattrsBlobKey to:data]; [IntegerIO writeUInt64:xattrsSize to:data]; - [StringIO write:[aclBlobKey sha1] to:data]; - [BooleanIO write:[aclBlobKey stretchEncryptionKey] to:data]; + [BlobKeyIO write:aclBlobKey to:data]; [IntegerIO writeInt32:uid to:data]; [IntegerIO writeInt32:gid to:data]; [IntegerIO writeInt32:mode to:data]; @@ -169,7 +210,14 @@ initDone: [IntegerIO writeInt64:ctime_nsec to:data]; [IntegerIO writeInt64:st_blocks to:data]; [IntegerIO writeUInt32:st_blksize to:data]; - [IntegerIO writeUInt64:aggregateSizeOnDisk to:data]; + [IntegerIO writeInt64:createTime_sec to:data]; + [IntegerIO writeInt64:createTime_nsec to:data]; + + [IntegerIO writeUInt32:(uint32_t)[missingNodes count] to:data]; + for (NSString *missingNodeName in [missingNodes allKeys]) { + [StringIO write:missingNodeName to:data]; + [[missingNodes objectForKey:missingNodeName] writeToData:data]; + } [IntegerIO writeUInt32:(uint32_t)[nodes count] to:data]; NSMutableArray *nodeNames = [NSMutableArray arrayWithArray:[nodes allKeys]]; @@ -182,14 +230,21 @@ initDone: char header[TREE_HEADER_LENGTH + 1]; sprintf(header, "TreeV%03d", CURRENT_TREE_VERSION); - NSMutableData *completeData = [[NSMutableData alloc] init]; + NSMutableData *completeData = [[[NSMutableData alloc] init] autorelease]; [completeData appendBytes:header length:TREE_HEADER_LENGTH]; - - [completeData appendBytes:[data bytes] length:[data length]]; - Blob *ret =[[[Blob alloc] initWithData:completeData mimeType:@"binary/octet-stream" downloadName:@"Tree" dataDescription:@"tree"] autorelease]; - [completeData release]; - [data release]; + [completeData appendBytes:[data bytes] length:[data length]]; + return completeData; +} +- (BOOL)ctimeMatchesStat:(struct stat *)st { + return st->st_ctimespec.tv_sec == ctime_sec && st->st_ctimespec.tv_nsec == ctime_nsec; +} +- (uint64_t)aggregateUncompressedDataSize { + //FIXME: This doesn't include the size of the ACL. + uint64_t ret = xattrsSize; + for (Node *node in [nodes allValues]) { + ret += [node uncompressedDataSize]; + } return ret; } @@ -199,32 +254,33 @@ initDone: return NO; } Tree *other = (Tree *)object; - return treeVersion == [other treeVersion] - && xattrsAreCompressed == [other xattrsAreCompressed] - && [NSObject equalObjects:xattrsBlobKey and:[other xattrsBlobKey]] - && xattrsSize == [other xattrsSize] - && aclIsCompressed == [other aclIsCompressed] - && [NSObject equalObjects:aclBlobKey and:[other aclBlobKey]] - && uid == [other uid] - && gid == [other gid] - && mode == [other mode] - && mtime_sec == [other mtime_sec] - && mtime_nsec == [other mtime_nsec] - && flags == [other flags] - && finderFlags == [other finderFlags] - && extendedFinderFlags == [other extendedFinderFlags] - && st_dev == [other st_dev] - && st_ino == [other st_ino] - && st_nlink == [other st_nlink] - && st_rdev == [other st_rdev] - && ctime_sec == [other ctime_sec] - && ctime_nsec == [other ctime_nsec] - && createTime_sec == [other createTime_sec] - && createTime_nsec == [other createTime_nsec] - && st_blocks == [other st_blocks] - && st_blksize == [other st_blksize] - && aggregateSizeOnDisk == [other aggregateSizeOnDisk] - && [nodes isEqual:[other nodes]]; + BOOL ret = (treeVersion == [other treeVersion] + && xattrsAreCompressed == [other xattrsAreCompressed] + && [NSObject equalObjects:xattrsBlobKey and:[other xattrsBlobKey]] + && xattrsSize == [other xattrsSize] + && aclIsCompressed == [other aclIsCompressed] + && [NSObject equalObjects:aclBlobKey and:[other aclBlobKey]] + && uid == [other uid] + && gid == [other gid] + && mode == [other mode] + && mtime_sec == [other mtime_sec] + && mtime_nsec == [other mtime_nsec] + && flags == [other flags] + && finderFlags == [other finderFlags] + && extendedFinderFlags == [other extendedFinderFlags] + && st_dev == [other st_dev] + && st_ino == [other st_ino] + && st_nlink == [other st_nlink] + && st_rdev == [other st_rdev] + && ctime_sec == [other ctime_sec] + && ctime_nsec == [other ctime_nsec] + && createTime_sec == [other createTime_sec] + && createTime_nsec == [other createTime_nsec] + && [missingNodes isEqual:[other missingNodes]] + && st_blocks == [other st_blocks] + && st_blksize == [other st_blksize] + && [nodes isEqual:[other nodes]]); + return ret; } - (NSUInteger)hash { return (NSUInteger)treeVersion + [nodes hash]; @@ -239,14 +295,11 @@ initDone: goto readHeader_error; } NSString *header = [[[NSString alloc] initWithBytes:buf length:TREE_HEADER_LENGTH encoding:NSASCIIStringEncoding] autorelease]; - NSRange versionRange = [header rangeOfRegex:@"^TreeV(\\d{3})$" capture:1]; - treeVersion = 0; - if (versionRange.location != NSNotFound) { - NSNumberFormatter *nf = [[NSNumberFormatter alloc] init]; - NSNumber *number = [nf numberFromString:[header substringWithRange:versionRange]]; - treeVersion = [number intValue]; - [nf release]; + if (![header hasPrefix:@"TreeV"] || [header length] < 6) { + SETNSERROR([Tree errorDomain], ERROR_INVALID_OBJECT_VERSION, @"invalid Tree header: %@", header); + goto readHeader_error; } + treeVersion = [[header substringFromIndex:5] intValue]; if (treeVersion < 10) { SETNSERROR([Tree errorDomain], ERROR_INVALID_OBJECT_VERSION, @"invalid Tree header: %@", header); goto readHeader_error; diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index 6aaa844..ad3e261 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ F805B8C21160EC41007EC01E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; }; F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; }; F81426D714541A6C00D7E50A /* BackupSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F81426D614541A6C00D7E50A /* BackupSet.m */; }; + F8146BEB16EB70EE006AD471 /* BlobKeyIO.m in Sources */ = {isa = PBXBuildFile; fileRef = F8146BEA16EB70EE006AD471 /* BlobKeyIO.m */; }; F8373E0F14794D01005AFBE6 /* ReflogEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F8373E0E14794D01005AFBE6 /* ReflogEntry.m */; }; F8373E5A147A8DEC005AFBE6 /* ReflogPrinter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8373E59147A8DEC005AFBE6 /* ReflogPrinter.m */; }; F83C1A7411CA7C170001958F /* ArqRestoreCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */; }; @@ -363,6 +364,9 @@ F805B8CD1160ECD7007EC01E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; F81426D514541A6C00D7E50A /* BackupSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackupSet.h; sourceTree = ""; }; F81426D614541A6C00D7E50A /* BackupSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackupSet.m; sourceTree = ""; }; + F8146BE816EB7054006AD471 /* StorageType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StorageType.h; path = storage/StorageType.h; sourceTree = ""; }; + F8146BE916EB70EE006AD471 /* BlobKeyIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobKeyIO.h; sourceTree = ""; }; + F8146BEA16EB70EE006AD471 /* BlobKeyIO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlobKeyIO.m; sourceTree = ""; }; F8373E0D14794D01005AFBE6 /* ReflogEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflogEntry.h; sourceTree = ""; }; F8373E0E14794D01005AFBE6 /* ReflogEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReflogEntry.m; sourceTree = ""; }; F8373E58147A8DEC005AFBE6 /* ReflogPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflogPrinter.h; sourceTree = ""; }; @@ -577,6 +581,7 @@ F89A1EB613FAC3750071D321 /* repo */, F805B7651160DD60007EC01E /* s3 */, F805B7A61160DEF2007EC01E /* shared */, + F8146BE716EB704A006AD471 /* storage */, F8F4D1E0121D7BC3002D09C1 /* AppKeychain.h */, F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */, F805B54B1160D3E6007EC01E /* ArqRestoreCommand.h */, @@ -588,6 +593,8 @@ 32A70AAB03705E1F00C91783 /* arq_restore_Prefix.pch */, F89A1F4313FAC6C40071D321 /* BlobKey.h */, F89A1F4413FAC6C40071D321 /* BlobKey.m */, + F8146BE916EB70EE006AD471 /* BlobKeyIO.h */, + F8146BEA16EB70EE006AD471 /* BlobKeyIO.m */, F83C1D0811CA929D0001958F /* BucketVerifier.h */, F83C1D0911CA929D0001958F /* BucketVerifier.m */, F8D67F6E1161443600CC270E /* RestoreNode.h */, @@ -856,6 +863,14 @@ path = crypto; sourceTree = ""; }; + F8146BE716EB704A006AD471 /* storage */ = { + isa = PBXGroup; + children = ( + F8146BE816EB7054006AD471 /* StorageType.h */, + ); + name = storage; + sourceTree = ""; + }; F89A1EB613FAC3750071D321 /* repo */ = { isa = PBXGroup; children = ( @@ -1074,6 +1089,7 @@ F841670515E279BB00B6ECED /* Sysctl.m in Sources */, F841670B15E279DE00B6ECED /* DNS_SDErrors.m in Sources */, F841672615E27A5200B6ECED /* RemoteS3Signer.m in Sources */, + F8146BEB16EB70EE006AD471 /* BlobKeyIO.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/arq_restore_Prefix.pch b/arq_restore_Prefix.pch index 75556a0..76e4b43 100644 --- a/arq_restore_Prefix.pch +++ b/arq_restore_Prefix.pch @@ -1,4 +1,6 @@ #ifdef __OBJC__ #import #import "HSLog.h" +#import "SetNSError.h" +#import "NSErrorCodes.h" #endif diff --git a/io/CryptoKey.m b/io/CryptoKey.m index 4e28ade..edded61 100644 --- a/io/CryptoKey.m +++ b/io/CryptoKey.m @@ -28,6 +28,11 @@ [self release]; return nil; } + if ([thePassword length] == 0) { + SETNSERROR([Encryption errorDomain], -1, @"missing encryption password"); + [self release]; + return nil; + } if (theSalt != nil && [theSalt length] != 8) { SETNSERROR([Encryption errorDomain], -1, @"salt must be 8 bytes or nil"); [self release]; diff --git a/shared/NSErrorCodes.h b/shared/NSErrorCodes.h index c9515ed..ac8aaad 100644 --- a/shared/NSErrorCodes.h +++ b/shared/NSErrorCodes.h @@ -48,3 +48,4 @@ #define ERROR_RRS_NOT_FOUND (-17) #define ERROR_INVALID_FILE (-18) #define ERROR_DELAYS_IN_S3_EVENTUAL_CONSISTENCY (-19) +#define ERROR_INVALID_COMMIT_HEADER (-20) diff --git a/storage/StorageType.h b/storage/StorageType.h new file mode 100644 index 0000000..1a480bd --- /dev/null +++ b/storage/StorageType.h @@ -0,0 +1,5 @@ +enum { + StorageTypeS3 = 1, + StorageTypeGlacier = 2 +}; +typedef uint32_t StorageType;