mirror of
https://github.com/samsonjs/arq_restore.git
synced 2026-03-25 09:25:53 +00:00
Merged in many changes from Arq mainline development.
(Sorry for the lame commit comment).
This commit is contained in:
parent
9e2d899d8e
commit
cbe20c007e
169 changed files with 2234 additions and 2221 deletions
17
AppKeychain.h
Normal file
17
AppKeychain.h
Normal 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
43
AppKeychain.m
Normal 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
26
ArqFark.h
Normal 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
74
ArqFark.m
Normal 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
29
ArqPackSet.h
Normal 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
195
ArqPackSet.m
Normal 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
37
ArqRepo.h
Normal 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
180
ArqRepo.m
Normal 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
15
ArqRepo_Verifier.h
Normal 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
19
ArqRepo_Verifier.m
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
1
Commit.m
1
Commit.m
|
|
@ -34,7 +34,6 @@
|
|||
#import "DateIO.h"
|
||||
#import "StringIO.h"
|
||||
#import "Commit.h"
|
||||
#import "Blob.h"
|
||||
#import "DataInputStream.h"
|
||||
#import "RegexKitLite.h"
|
||||
#import "SetNSError.h"
|
||||
|
|
|
|||
33
DiskPack.m
33
DiskPack.m
|
|
@ -36,7 +36,6 @@
|
|||
#import "FDInputStream.h"
|
||||
#import "StringIO.h"
|
||||
#import "IntegerIO.h"
|
||||
#import "PackSetSet.h"
|
||||
#import "ServerBlob.h"
|
||||
#import "NSFileManager_extra.h"
|
||||
#import "S3Service.h"
|
||||
|
|
@ -44,12 +43,11 @@
|
|||
#import "FileInputStream.h"
|
||||
#import "FileOutputStream.h"
|
||||
#import "Streams.h"
|
||||
#import "PackSet.h"
|
||||
#import "S3ObjectReceiver.h"
|
||||
#import "S3ObjectMetadata.h"
|
||||
#import "PackIndexEntry.h"
|
||||
#import "SHA1Hash.h"
|
||||
|
||||
#import "ArqUserLibrary.h"
|
||||
|
||||
@interface DiskPack (internal)
|
||||
- (BOOL)savePack:(ServerBlob *)sb bytesWritten:(unsigned long long *)written error:(NSError **)error;
|
||||
|
|
@ -58,10 +56,10 @@
|
|||
|
||||
@implementation DiskPack
|
||||
+ (NSString *)s3PathWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
|
||||
return [NSString stringWithFormat:@"%@/%@.pack", [PackSet s3PathWithS3BucketName:theS3BucketName computerUUID:theComputerUUID packSetName:thePackSetName], thePackSHA1];
|
||||
return [NSString stringWithFormat:@"/%@/%@/packsets/%@/%@.pack", theS3BucketName, theComputerUUID, thePackSetName, thePackSHA1];
|
||||
}
|
||||
+ (NSString *)localPathWithComputerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
|
||||
return [NSString stringWithFormat:@"%@/%@/%@.pack", [PackSet localPathWithComputerUUID:theComputerUUID packSetName:thePackSetName], [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]];
|
||||
return [NSString stringWithFormat:@"%@/%@/packsets/%@/%@/%@.pack", [ArqUserLibrary arqCachesPath], theComputerUUID, thePackSetName, [thePackSHA1 substringToIndex:2], [thePackSHA1 substringFromIndex:2]];
|
||||
}
|
||||
- (id)initWithS3Service:(S3Service *)theS3 s3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID packSetName:(NSString *)thePackSetName packSHA1:(NSString *)thePackSHA1 {
|
||||
if (self = [super init]) {
|
||||
|
|
@ -90,8 +88,14 @@
|
|||
BOOL ret = NO;
|
||||
if (![fm fileExistsAtPath:localPath]) {
|
||||
HSLogDebug(@"packset %@: making pack %@ local", packSetName, packSHA1);
|
||||
ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:error];
|
||||
if (sb != nil) {
|
||||
NSError *myError = nil;
|
||||
ServerBlob *sb = [s3 newServerBlobAtPath:s3Path error:&myError];
|
||||
if (sb == nil) {
|
||||
HSLogError(@"error getting S3 pack %@: %@", s3Path, [myError localizedDescription]);
|
||||
if (error != NULL) {
|
||||
*error = myError;
|
||||
}
|
||||
} else {
|
||||
unsigned long long bytesWritten;
|
||||
ret = [self savePack:sb bytesWritten:&bytesWritten error:error];
|
||||
[sb release];
|
||||
|
|
@ -148,25 +152,12 @@
|
|||
*length = st.st_size;
|
||||
return YES;
|
||||
}
|
||||
- (BOOL)copyToPath:(NSString *)dest error:(NSError **)error {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if ([fm fileExistsAtPath:dest] && ![fm removeItemAtPath:dest error:error]) {
|
||||
HSLogError(@"error removing old mutable pack at %@", dest);
|
||||
return NO;
|
||||
}
|
||||
if (![fm ensureParentPathExistsForPath:dest error:error] || ![fm copyItemAtPath:localPath toPath:dest error:error]) {
|
||||
HSLogError(@"error copying pack %@ to %@", localPath, dest);
|
||||
return NO;
|
||||
}
|
||||
HSLogDebug(@"copied %@ to %@", localPath, dest);
|
||||
return YES;
|
||||
}
|
||||
- (NSArray *)sortedPackIndexEntries:(NSError **)error {
|
||||
unsigned long long length;
|
||||
if (![self fileLength:&length error:error]) {
|
||||
return NO;
|
||||
}
|
||||
FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath length:length];
|
||||
FileInputStream *fis = [[FileInputStream alloc] initWithPath:localPath offset:0 length:length];
|
||||
NSArray *ret = [self sortedPackIndexEntriesFromStream:fis error:error];
|
||||
[fis release];
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -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
17
FarkPath.h
Normal 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
19
FarkPath.m
Normal 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
|
||||
|
|
@ -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
5
Node.h
|
|
@ -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
28
Node.m
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
18
PackIndexWriter.h
Normal 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
103
PackIndexWriter.m
Normal 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
207
PackSet.m
|
|
@ -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
|
||||
55
PackSetSet.h
55
PackSetSet.h
|
|
@ -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
|
||||
237
PackSetSet.m
237
PackSetSet.m
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
29
Restorer.m
29
Restorer.m
|
|
@ -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
112
S3Fark.m
|
|
@ -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
|
||||
84
S3Repo.h
84
S3Repo.h
|
|
@ -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
294
S3Repo.m
|
|
@ -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
9
Tree.h
|
|
@ -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
53
Tree.m
|
|
@ -40,9 +40,6 @@
|
|||
#import "SetNSError.h"
|
||||
#import "RegexKitLite.h"
|
||||
#import "NSErrorCodes.h"
|
||||
#import "Streams.h"
|
||||
|
||||
#define HEADER_LENGTH (8)
|
||||
|
||||
@interface Tree (internal)
|
||||
- (BOOL)readHeader:(id <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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
48
XAttrSet.m
48
XAttrSet.m
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "InputStream.h"
|
||||
@class Blob;
|
||||
|
|
|
|||
|
|
@ -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
27
http/CFStreamPair.h
Normal 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
209
http/CFStreamPair.m
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@protocol OutputStream;
|
||||
#import "OutputStream.h"
|
||||
@class RFC2616DateFormatter;
|
||||
|
||||
@interface HTTPRequest : NSObject {
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
17
http/StreamPair.h
Normal 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
22
http/StreamPairFactory.h
Normal 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
153
http/StreamPairFactory.m
Normal 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
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue