Merged in many changes from Arq mainline development.

(Sorry for the lame commit comment).
This commit is contained in:
Stefan Reitshamer 2010-08-19 13:25:23 -04:00
parent 9e2d899d8e
commit cbe20c007e
169 changed files with 2234 additions and 2221 deletions

17
AppKeychain.h Normal file
View file

@ -0,0 +1,17 @@
//
// AppKeychain.h
// arq_restore
//
// Created by Stefan Reitshamer on 8/19/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface AppKeychain : NSObject {
}
+ (NSString *)errorDomain;
+ (BOOL)accessKeyID:(NSString **)accessKeyID secretAccessKey:(NSString **)secret error:(NSError **)error;
+ (BOOL)encryptionKey:(NSString **)encryptionKey error:(NSError **)error;
@end

43
AppKeychain.m Normal file
View file

@ -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

26
ArqFark.h Normal file
View file

@ -0,0 +1,26 @@
//
// ArqFark.h
// Arq
//
// Created by Stefan Reitshamer on 6/22/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@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

74
ArqFark.m Normal file
View file

@ -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

29
ArqPackSet.h Normal file
View file

@ -0,0 +1,29 @@
//
// ArqPackSet.h
// Arq
//
// Created by Stefan Reitshamer on 6/22/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@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

195
ArqPackSet.m Normal file
View file

@ -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

37
ArqRepo.h Normal file
View file

@ -0,0 +1,37 @@
//
// ArqRepo.h
// Arq
//
// Created by Stefan Reitshamer on 6/23/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@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

180
ArqRepo.m Normal file
View file

@ -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 <InputStream> 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:@"<ArqRepo: bucketUUID=%@>", bucketUUID];
}
@end

15
ArqRepo_Verifier.h Normal file
View file

@ -0,0 +1,15 @@
//
// ArqRepo_Verifier.h
// arq_restore
//
// Created by Stefan Reitshamer on 8/19/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "ArqRepo.h"
@interface ArqRepo (Verifier)
- (NSString *)blobsPackSetName;
- (BOOL)packSHA1:(NSString **)packSHA1 forPackedBlobSHA1:(NSString *)sha1 error:(NSError **)error;
@end

19
ArqRepo_Verifier.m Normal file
View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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"

View file

@ -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;

View file

@ -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));

17
FarkPath.h Normal file
View file

@ -0,0 +1,17 @@
//
// FarkPath.h
// Arq
//
// Created by Stefan Reitshamer on 6/29/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface FarkPath : NSObject {
}
+ (NSString *)s3PathForS3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID sha1:(NSString *)sha1;
+ (NSString *)s3PathForBucketDataPath:(NSString *)bucketDataPath s3BucketName:(NSString *)s3BucketName computerUUID:(NSString *)computerUUID;
@end

19
FarkPath.m Normal file
View file

@ -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

View file

@ -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;
}

5
Node.h
View file

@ -31,10 +31,7 @@
*/
#import <Cocoa/Cocoa.h>
@class MutableS3Repo;
#import "InputStream.h"
#import "InputStreamFactory.h"
#import "OutputStream.h"
@protocol InputStream;
@interface Node : NSObject {
int treeVersion;

28
Node.m
View file

@ -31,14 +31,11 @@
*/
#include <sys/stat.h>
#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

View file

@ -60,4 +60,9 @@
- (NSString *)objectSHA1 {
return objectSHA1;
}
#pragma mark NSObject
- (NSString *)description {
return [NSString stringWithFormat:@"<PackIndexEntry: packSHA1=%@ offset=%qu dataLength=%qu objectSHA1=%@>", packSHA1, offset, dataLength, objectSHA1];
}
@end

18
PackIndexWriter.h Normal file
View file

@ -0,0 +1,18 @@
//
// PackIndexWriter.h
// Arq
//
// Created by Stefan Reitshamer on 3/3/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class DiskPack;
@interface PackIndexWriter : NSObject {
DiskPack *diskPack;
NSString *destination;
}
- (id)initWithPack:(DiskPack *)theDiskPack destination:(NSString *)theDestination;
- (BOOL)writeIndex:(NSError **)error;
@end

103
PackIndexWriter.m Normal file
View file

@ -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 <OutputStream>)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 <OutputStream>)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

207
PackSet.m
View file

@ -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

View file

@ -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 <Cocoa/Cocoa.h>
@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

View file

@ -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: /<s3bucketname>/<computeruuid>/packsets/<packsetname>/<sha1>.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

View file

@ -32,12 +32,12 @@
#import <Cocoa/Cocoa.h>
@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;

View file

@ -33,8 +33,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#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;
}

112
S3Fark.m
View file

@ -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

View file

@ -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 <Cocoa/Cocoa.h>
@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

294
S3Repo.m
View file

@ -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 <BufferedInputStream> 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 <BufferedInputStream> 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 <InputStream> 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:@"<S3Repo %p bucket %@>", 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

9
Tree.h
View file

@ -32,12 +32,11 @@
#import <Cocoa/Cocoa.h>
#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 <BufferedInputStream>)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

53
Tree.m
View file

@ -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 <BufferedInputStream>)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 <BufferedInputStream>)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 <BufferedInputStream>)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;

View file

@ -39,7 +39,9 @@
}
- (id)initWithPath:(NSString *)thePath error:(NSError **)error;
- (id)initWithBufferedInputStream:(id <BufferedInputStream>)is error:(NSError **)error;
- (Blob *)toBlob;
- (NSUInteger)count;
- (unsigned long long)dataLength;
- (NSArray *)names;
- (BOOL)applyToFile:(NSString *)path error:(NSError **)error;
@end

View file

@ -30,6 +30,7 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/xattr.h>
#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) {

View file

@ -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 = "<group>"; };
F83C1A6211CA7BD20001958F /* ArqVerifyCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqVerifyCommand.m; sourceTree = "<group>"; };
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 = "<group>"; };
F83C1D0911CA929D0001958F /* BucketVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BucketVerifier.m; path = s3/BucketVerifier.m; sourceTree = "<group>"; };
F83C1D0811CA929D0001958F /* BucketVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BucketVerifier.h; sourceTree = "<group>"; };
F83C1D0911CA929D0001958F /* BucketVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BucketVerifier.m; sourceTree = "<group>"; };
F8D6763E1160F22800CC270E /* SetNSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetNSError.h; sourceTree = "<group>"; };
F8D677FE1160F26A00CC270E /* Restorer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Restorer.h; sourceTree = "<group>"; };
F8D677FF1160F26A00CC270E /* Restorer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Restorer.m; sourceTree = "<group>"; };
F8D678131160F4E300CC270E /* S3Fark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Fark.h; sourceTree = "<group>"; };
F8D678141160F4E300CC270E /* S3Fark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Fark.m; sourceTree = "<group>"; };
F8D678151160F4E300CC270E /* S3Repo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3Repo.h; sourceTree = "<group>"; };
F8D678161160F4E300CC270E /* S3Repo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = S3Repo.m; sourceTree = "<group>"; };
F8D6781B1160F4FD00CC270E /* SHA1Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHA1Hash.h; sourceTree = "<group>"; };
F8D6781C1160F4FD00CC270E /* SHA1Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHA1Hash.m; sourceTree = "<group>"; };
F8D678291160F5D000CC270E /* PackSetSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSetSet.h; sourceTree = "<group>"; };
F8D6782A1160F5D000CC270E /* PackSetSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSetSet.m; sourceTree = "<group>"; };
F8D6782B1160F5D000CC270E /* PackSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackSet.h; sourceTree = "<group>"; };
F8D6782C1160F5D000CC270E /* PackSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackSet.m; sourceTree = "<group>"; };
F8D678311160F62E00CC270E /* ArqUserLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqUserLibrary.h; sourceTree = "<group>"; };
F8D678321160F62E00CC270E /* ArqUserLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqUserLibrary.m; sourceTree = "<group>"; };
F8D6783A1160F70100CC270E /* PackIndexEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackIndexEntry.h; sourceTree = "<group>"; };
@ -419,6 +420,24 @@
F8D67D061161384100CC270E /* OSStatusDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSStatusDescription.m; sourceTree = "<group>"; };
F8D67F6E1161443600CC270E /* RestoreNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestoreNode.h; sourceTree = "<group>"; };
F8D67F6F1161443600CC270E /* RestoreNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestoreNode.m; sourceTree = "<group>"; };
F8F4D19B121D77E1002D09C1 /* NSError_extra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSError_extra.h; sourceTree = "<group>"; };
F8F4D19C121D77E1002D09C1 /* NSError_extra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSError_extra.m; sourceTree = "<group>"; };
F8F4D19E121D78AD002D09C1 /* MonitoredInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MonitoredInputStream.h; sourceTree = "<group>"; };
F8F4D19F121D78AD002D09C1 /* MonitoredInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MonitoredInputStream.m; sourceTree = "<group>"; };
F8F4D1AC121D7990002D09C1 /* ArqPackSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqPackSet.h; sourceTree = "<group>"; };
F8F4D1AD121D7990002D09C1 /* ArqPackSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqPackSet.m; sourceTree = "<group>"; };
F8F4D1AE121D7990002D09C1 /* ArqRepo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRepo.h; sourceTree = "<group>"; };
F8F4D1AF121D7990002D09C1 /* ArqRepo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRepo.m; sourceTree = "<group>"; };
F8F4D1C1121D79AC002D09C1 /* ArqFark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqFark.h; sourceTree = "<group>"; };
F8F4D1C2121D79AC002D09C1 /* ArqFark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqFark.m; sourceTree = "<group>"; };
F8F4D1E0121D7BC3002D09C1 /* AppKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppKeychain.h; sourceTree = "<group>"; };
F8F4D1E1121D7BC3002D09C1 /* AppKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppKeychain.m; sourceTree = "<group>"; };
F8F4D1E5121D7DA2002D09C1 /* FarkPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FarkPath.h; sourceTree = "<group>"; };
F8F4D1E6121D7DA2002D09C1 /* FarkPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FarkPath.m; sourceTree = "<group>"; };
F8F4D1FC121D8409002D09C1 /* PackIndexWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PackIndexWriter.h; sourceTree = "<group>"; };
F8F4D1FD121D8409002D09C1 /* PackIndexWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PackIndexWriter.m; sourceTree = "<group>"; };
F8F4D205121D8696002D09C1 /* ArqRepo_Verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqRepo_Verifier.h; sourceTree = "<group>"; };
F8F4D206121D8696002D09C1 /* ArqRepo_Verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqRepo_Verifier.m; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -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;
};

View file

@ -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;

View file

@ -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];

View file

@ -30,7 +30,6 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import "InputStream.h"
@class Blob;

View file

@ -32,6 +32,8 @@
#import "SHA1Hash.h"
#include <openssl/sha.h>
#include <sys/stat.h>
#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 <InputStream> is = [blob newInputStream:self];
id <InputStream> 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;

27
http/CFStreamPair.h Normal file
View file

@ -0,0 +1,27 @@
//
// CFStreamPair.h
// CFN
//
// Created by Stefan Reitshamer on 2/25/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "StreamPair.h"
@class CFStreamInputStream;
@class CFStreamOutputStream;
@interface CFStreamPair : NSObject <StreamPair> {
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

209
http/CFStreamPair.m Normal file
View file

@ -0,0 +1,209 @@
//
// CFStreamPair.m
// CFN
//
// Created by Stefan Reitshamer on 2/25/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#import <SystemConfiguration/SystemConfiguration.h>
#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:@"<CFStreamPair host=%@ ssl=%@>", 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

View file

@ -31,8 +31,9 @@
*/
#import <Cocoa/Cocoa.h>
@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 <InputStream>)bodyStream error:(NSError **)error;
- (int)responseCode;
- (NSString *)responseHeaderForKey:(NSString *)key;
- (NSString *)responseMimeType;
- (NSString *)responseDownloadName;
- (id <InputStream>)newResponseBodyStream:(NSError **)error;
- (id <BufferedInputStream>)newResponseBodyStream:(NSError **)error;
- (NSData *)slurpResponseBody:(NSError **)error;
- (void)setCloseRequested;
@end

View file

@ -81,9 +81,15 @@
[request setRFC822DateHeader];
}
- (BOOL)executeRequest:(NSError **)error {
return [self executeRequestWithBody:nil error:error];
}
- (BOOL)executeRequestWithBody:(id <InputStream>)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 <InputStream>)newResponseBodyStream:(NSError **)error {
- (id <BufferedInputStream>)newResponseBodyStream:(NSError **)error {
return [response newResponseInputStream:streamPair error:error];
}
- (NSData *)slurpResponseBody:(NSError **)error {

View file

@ -31,7 +31,7 @@
*/
#import <Cocoa/Cocoa.h>
@protocol OutputStream;
#import "OutputStream.h"
@class RFC2616DateFormatter;
@interface HTTPRequest : NSObject {

View file

@ -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"];
}

View file

@ -32,7 +32,6 @@
#import <Cocoa/Cocoa.h>
@protocol BufferedInputStream;
@protocol InputStream;
@class FDInputStream;
@interface HTTPResponse : NSObject {
@ -47,5 +46,5 @@
- (NSString *)protocol;
- (NSString *)headerForKey:(NSString *)key;
- (unsigned long long)contentLength;
- (id <InputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error;
- (id <BufferedInputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error;
@end

View file

@ -78,8 +78,8 @@
}
return (unsigned long long)contentLength;
}
- (id <InputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error {
id <InputStream> ret = nil;
- (id <BufferedInputStream>)newResponseInputStream:(id <BufferedInputStream>)underlyingStream error:(NSError **)error {
id <BufferedInputStream> ret = nil;
if ([requestMethod isEqualToString:@"HEAD"] || code == 204) {
ret = [[NSData data] newInputStream];
} else {

17
http/StreamPair.h Normal file
View file

@ -0,0 +1,17 @@
//
// StreamPair.h
// CFN
//
// Created by Stefan Reitshamer on 2/25/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "BufferedInputStream.h"
#import "OutputStream.h"
@protocol StreamPair <BufferedInputStream, OutputStream>
- (void)setCloseRequested;
- (BOOL)isUsable;
@end

22
http/StreamPairFactory.h Normal file
View file

@ -0,0 +1,22 @@
//
// StreamPairFactory.h
// CFN
//
// Created by Stefan Reitshamer on 2/25/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@protocol StreamPair;
@interface StreamPairFactory : NSObject {
NSTimeInterval maxStreamPairLifetime;
NSLock *lock;
NSMutableDictionary *threadMap;
}
+ (StreamPairFactory *)theFactory;
- (void)setMaxStreamPairLifetime:(NSTimeInterval)theMaxLifetime;
- (id <StreamPair>)newStreamPairToHost:(NSString *)theHost useSSL:(BOOL)isUseSSL error:(NSError **)error;
- (void)clear;
@end

153
http/StreamPairFactory.m Normal file
View file

@ -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 <StreamPair>)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 <StreamPair>)newStreamPairToHost:(NSString *)host useSSL:(BOOL)useSSL maxLifeTimeSeconds:(NSTimeInterval)theMaxLifetime error:(NSError **)error {
NSString *key = [NSString stringWithFormat:@"%@:%@", [host lowercaseString], (useSSL ? @"SSL" : @"noSSL")];
id <StreamPair> 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> 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 <StreamPair>)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> 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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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;
}

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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 <InputStream> 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 <InputStream>)theIS cipherName:(NSString *)theCipherName key:(NSString *)theKey error:(NSError **)error;
@end

View file

@ -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 <InputStream>)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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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;
}

View file

@ -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.

View file

@ -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 <InputStream>) newInputStream:(id)sender {
- (id <InputStream>) newInputStream {
return [data newInputStream];
}
- (id <InputStream>) 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:@"<DataISF: %u>", [data length]];
}
@end

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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 {

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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 {

View file

@ -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 <InputStreamFactory> {
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

View file

@ -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 <sys/stat.h>
#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 <InputStream>) newInputStream:(id)sender {
return [[FileInputStream alloc] initWithPath:path length:length];
- (id <InputStream>) newInputStream {
return [[FileInputStream alloc] initWithPath:path offset:offset length:length];
}
- (id <InputStream>) 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:@"<FileISF: %@,length=%qu>", path, length];
}
@end

View file

@ -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

View file

@ -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

View file

@ -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 <Cocoa/Cocoa.h>
#import "InputStream.h"
@class FDInputStream;
@protocol BufferedInputStream;
@interface FixedLengthInputStream : NSObject <InputStream> {
FDInputStream *underlyingStream;
id <BufferedInputStream> underlyingStream;
unsigned long long fixedLength;
unsigned long long totalReceived;
}
- (id)initWithUnderlyingStream:(FDInputStream *)is length:(unsigned long long)theLength;
- (id)initWithUnderlyingStream:(id <BufferedInputStream>)is length:(unsigned long long)theLength;
@end

View file

@ -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 <BufferedInputStream>)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

View file

@ -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 <NSObject>
- (unsigned char *)read:(NSUInteger *)length error:(NSError **)error;
- (NSData *)slurp:(NSError **)error;
- (void)bytesWereNotUsed;
@end

View file

@ -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 <NSObject>
// Returns an object that must be released by the caller.
- (id <InputStream>) newInputStream:(id)sender;
// Returns an object that must be released by the caller.
- (id <InputStream>) newInputStream:(id)sender sourceOffset:(unsigned long long)theOffset sourceLength:(unsigned long long)theLength;
- (id <InputStream>) newInputStream;
@end

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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 <Cocoa/Cocoa.h>
@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 <InputStream> {
id <InputStream> 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 <InputStream>)theUnderlyingStream delegate:(id)theDelegate;
@end
@interface NSObject (MonitoredInputStreamDelegate)
- (BOOL)monitoredInputStream:(MonitoredInputStream *)stream receivedBytes:(unsigned long long)length error:(NSError **)error;
@end

View file

@ -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 <Cocoa/Cocoa.h>
@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 <InputStream>)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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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.

Some files were not shown because too many files have changed in this diff Show more