mirror of
https://github.com/samsonjs/arq_restore.git
synced 2026-03-25 09:25:53 +00:00
212 lines
8 KiB
Objective-C
212 lines
8 KiB
Objective-C
//
|
|
// 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;
|
|
- (BOOL)loadPackIndexEntries:(NSError **)error;
|
|
- (NSDictionary *)doLoadPackIndexEntries:(NSError **)error;
|
|
@end
|
|
|
|
@implementation ArqPackSet
|
|
+ (NSString *)errorDomain {
|
|
return @"ArqPackSetErrorDomain";
|
|
}
|
|
|
|
- (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;
|
|
}
|
|
- (ServerBlob *)newServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error {
|
|
ServerBlob *sb = nil;
|
|
NSError *myError = nil;
|
|
NSUInteger i = 0;
|
|
NSAutoreleasePool *pool = nil;
|
|
for (i = 0; i < MAX_RETRIES; i++) {
|
|
[pool drain];
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
myError = nil;
|
|
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;
|
|
}
|
|
}
|
|
[myError retain];
|
|
[pool drain];
|
|
[myError autorelease];
|
|
if (sb == nil) {
|
|
if ([myError isErrorWithDomain:[ArqPackSet errorDomain] code:ERROR_PACK_INDEX_ENTRY_NOT_RESOLVABLE]) {
|
|
SETNSERROR([ArqPackSet errorDomain], ERROR_NOT_FOUND, @"failed %lu times to load blob for sha1 %@ from pack set %@", i, sha1, packSetName);
|
|
} else if (error != NULL) {
|
|
*error = myError;
|
|
}
|
|
}
|
|
return sb;
|
|
}
|
|
- (BOOL)containsBlob:(BOOL *)contains forSHA1:(NSString *)sha1 packSHA1:(NSString **)packSHA1 error:(NSError **)error {
|
|
if (packIndexEntries == nil && ![self loadPackIndexEntries:error]) {
|
|
return NO;
|
|
}
|
|
PackIndexEntry *pie = [packIndexEntries objectForKey:sha1];
|
|
*contains = (pie != nil);
|
|
if (pie != nil) {
|
|
*packSHA1 = [pie packSHA1];
|
|
}
|
|
return YES;
|
|
}
|
|
@end
|
|
|
|
@implementation ArqPackSet (internal)
|
|
- (ServerBlob *)newInternalServerBlobForSHA1:(NSString *)sha1 error:(NSError **)error {
|
|
if (packIndexEntries == nil && ![self loadPackIndexEntries:error]) {
|
|
return nil;
|
|
}
|
|
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]
|
|
targetUID:getuid()
|
|
targetGID:getgid()];
|
|
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;
|
|
}
|
|
- (BOOL)loadPackIndexEntries:(NSError **)error {
|
|
BOOL ret = YES;
|
|
NSAutoreleasePool *pool = nil;
|
|
for (;;) {
|
|
[pool drain];
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
NSError *myError = nil;
|
|
NSDictionary *entries = [self doLoadPackIndexEntries:&myError];
|
|
if (entries != nil) {
|
|
packIndexEntries = [entries retain];
|
|
break;
|
|
}
|
|
if ([myError code] != ERROR_NOT_FOUND) {
|
|
if (error != NULL) {
|
|
*error = myError;
|
|
}
|
|
ret = NO;
|
|
break;
|
|
}
|
|
// If it's a not-found error, it can be because Arq Agent replaced a pack with another one between when we got
|
|
// the S3 list and when we tried to make them local.
|
|
HSLogDebug(@"error loading pack index entries (retrying): %@", myError);
|
|
}
|
|
if (!ret && error != NULL) {
|
|
[*error retain];
|
|
}
|
|
[pool drain];
|
|
if (!ret && error != NULL) {
|
|
[*error autorelease];
|
|
}
|
|
return ret;
|
|
}
|
|
- (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
|
|
targetUID:getuid()
|
|
targetGID:getgid()];
|
|
do {
|
|
if (![index makeLocal:error]) {
|
|
break;
|
|
}
|
|
NSArray *pies = [index allPackIndexEntries:error];
|
|
if (pies == nil) {
|
|
break;
|
|
}
|
|
HSLogTrace(@"found %lu 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
|