mirror of
https://github.com/samsonjs/arq_restore.git
synced 2026-03-25 09:25:53 +00:00
check every S3 bucket for Arq data, not just ones with pre-defined names
This commit is contained in:
parent
68ef02f1ac
commit
23ba1cc792
4 changed files with 233 additions and 44 deletions
|
|
@ -43,6 +43,7 @@
|
|||
#import "UserAndComputer.h"
|
||||
#import "ArqSalt.h"
|
||||
#import "ArqRepo.h"
|
||||
#import "BackupSet.h"
|
||||
|
||||
@interface ArqRestoreCommand (internal)
|
||||
- (BOOL)printArqFolders:(NSError **)error;
|
||||
|
|
@ -119,56 +120,43 @@
|
|||
if (![self validateS3Keys:error]) {
|
||||
return NO;
|
||||
}
|
||||
for (NSString *s3BucketName in [S3Service s3BucketNamesForAccessKeyID:accessKey]) {
|
||||
|
||||
NSArray *backupSets = [BackupSet allBackupSetsForAccessKeyID:accessKey secretAccessKey:secretKey error:error];
|
||||
if (backupSets == nil) {
|
||||
return NO;
|
||||
}
|
||||
for (BackupSet *backupSet in backupSets) {
|
||||
NSString *s3BucketName = [backupSet s3BucketName];
|
||||
NSString *computerUUID = [backupSet computerUUID];
|
||||
UserAndComputer *uac = [backupSet userAndComputer];
|
||||
printf("S3 bucket: %s", [s3BucketName UTF8String]);
|
||||
NSString *computerUUIDPrefix = [NSString stringWithFormat:@"/%@/", s3BucketName];
|
||||
NSError *myError = nil;
|
||||
NSArray *computerUUIDs = [s3 commonPrefixesForPathPrefix:computerUUIDPrefix delimiter:@"/" error:&myError];
|
||||
if (computerUUIDs == nil && ![myError isErrorWithDomain:[S3Service errorDomain] code:ERROR_NOT_FOUND]) {
|
||||
if (error != NULL) {
|
||||
*error = myError;
|
||||
}
|
||||
if (uac != nil) {
|
||||
printf(" %s (%s)", [[uac computerName] UTF8String], [[uac userName] UTF8String]);
|
||||
} else {
|
||||
printf(" (unknown computer)");
|
||||
}
|
||||
printf(" UUID %s", [computerUUID UTF8String]);
|
||||
NSString *computerBucketsPrefix = [NSString stringWithFormat:@"/%@/%@/buckets", s3BucketName, computerUUID];
|
||||
NSArray *s3BucketUUIDPaths = [s3 pathsWithPrefix:computerBucketsPrefix error:error];
|
||||
if (s3BucketUUIDPaths == nil) {
|
||||
return NO;
|
||||
}
|
||||
if ([computerUUIDs count] == 0) {
|
||||
printf(" (no computers found)");
|
||||
if ([s3BucketUUIDPaths count] == 0) {
|
||||
printf(" (no folders found)");
|
||||
}
|
||||
printf("\n");
|
||||
for (NSString *computerUUID in computerUUIDs) {
|
||||
NSString *computerInfoPath = [NSString stringWithFormat:@"/%@/%@/computerinfo", s3BucketName, computerUUID];
|
||||
NSError *uacError = nil;
|
||||
NSData *uacData = [s3 dataAtPath:computerInfoPath error:&uacError];
|
||||
UserAndComputer *uac = nil;
|
||||
if (uacData != nil) {
|
||||
uac = [[[UserAndComputer alloc] initWithXMLData:uacData error:&uacError] autorelease];
|
||||
printf(" %s (%s)", [[uac computerName] UTF8String], [[uac userName] UTF8String]);
|
||||
} else {
|
||||
printf(" (unknown computer)");
|
||||
}
|
||||
printf(" UUID %s", [computerUUID UTF8String]);
|
||||
NSString *computerUUIDPath = [computerUUIDPrefix stringByAppendingPathComponent:computerUUID];
|
||||
NSString *computerBucketsPrefix = [computerUUIDPath stringByAppendingPathComponent:@"buckets"];
|
||||
NSArray *s3BucketUUIDPaths = [s3 pathsWithPrefix:computerBucketsPrefix error:error];
|
||||
if (s3BucketUUIDPaths == nil) {
|
||||
for (NSString *uuidPath in s3BucketUUIDPaths) {
|
||||
NSData *data = [s3 dataAtPath:uuidPath error:error];
|
||||
if (data == nil) {
|
||||
return NO;
|
||||
}
|
||||
if ([s3BucketUUIDPaths count] == 0) {
|
||||
printf(" (no folders found)");
|
||||
}
|
||||
printf("\n");
|
||||
for (NSString *uuidPath in s3BucketUUIDPaths) {
|
||||
NSData *data = [s3 dataAtPath:uuidPath error:error];
|
||||
if (data == nil) {
|
||||
return NO;
|
||||
}
|
||||
DictNode *plist = [DictNode dictNodeWithXMLData:data error:error];
|
||||
if (plist == nil) {
|
||||
return NO;
|
||||
}
|
||||
printf(" %s\n", [[[plist stringNodeForKey:@"LocalPath"] stringValue] UTF8String]);
|
||||
printf(" UUID: %s\n", [[uuidPath lastPathComponent] UTF8String]);
|
||||
printf(" restore command: arq_restore %s\n", [uuidPath UTF8String]);
|
||||
DictNode *plist = [DictNode dictNodeWithXMLData:data error:error];
|
||||
if (plist == nil) {
|
||||
return NO;
|
||||
}
|
||||
printf(" %s\n", [[[plist stringNodeForKey:@"LocalPath"] stringValue] UTF8String]);
|
||||
printf(" UUID: %s\n", [[uuidPath lastPathComponent] UTF8String]);
|
||||
printf(" restore command: arq_restore %s\n", [uuidPath UTF8String]);
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
|
|
|
|||
53
BackupSet.h
Normal file
53
BackupSet.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright (c) 2010, 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 UserAndComputer;
|
||||
|
||||
@interface BackupSet : NSObject {
|
||||
NSString *accessKeyID;
|
||||
NSString *secretAccessKey;
|
||||
NSString *s3BucketName;
|
||||
NSString *computerUUID;
|
||||
UserAndComputer *uac;
|
||||
}
|
||||
+ (NSArray *)allBackupSetsForAccessKeyID:(NSString *)theAccessKeyID secretAccessKey:(NSString *)theSecretAccessKey error:(NSError **)error;
|
||||
|
||||
- (id)initWithAccessKeyID:(NSString *)theAccessKeyID
|
||||
secretAccessKey:(NSString *)theSecretAccessKey
|
||||
s3BucketName:(NSString *)theS3BucketName
|
||||
computerUUID:(NSString *)theComputerUUID
|
||||
userAndComputer:(UserAndComputer *)theUAC;
|
||||
- (NSString *)s3BucketName;
|
||||
- (NSString *)computerUUID;
|
||||
- (UserAndComputer *)userAndComputer;
|
||||
@end
|
||||
134
BackupSet.m
Normal file
134
BackupSet.m
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Copyright (c) 2010, 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 "BackupSet.h"
|
||||
#import "S3AuthorizationProvider.h"
|
||||
#import "S3Service.h"
|
||||
#import "UserAndComputer.h"
|
||||
#import "NSErrorCodes.h"
|
||||
#import "NSData-Encrypt.h"
|
||||
#import "CryptoKey.h"
|
||||
#import "RegexKitLite.h"
|
||||
#import "ArqRepo.h"
|
||||
#import "BlobKey.h"
|
||||
#import "SetNSError.h"
|
||||
#import "Commit.h"
|
||||
#import "S3ObjectMetadata.h"
|
||||
#import "ArqSalt.h"
|
||||
|
||||
@implementation BackupSet
|
||||
+ (NSArray *)allBackupSetsForAccessKeyID:(NSString *)theAccessKeyID secretAccessKey:(NSString *)theSecretAccessKey error:(NSError **)error {
|
||||
S3AuthorizationProvider *sap = [[[S3AuthorizationProvider alloc] initWithAccessKey:theAccessKeyID secretKey:theSecretAccessKey] autorelease];
|
||||
S3Service *s3 = [[[S3Service alloc] initWithS3AuthorizationProvider:sap useSSL:YES retryOnTransientError:NO] autorelease];
|
||||
NSArray *s3BucketNames = [s3 s3BucketNames:error];
|
||||
if (s3BucketNames == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSMutableArray *ret = [NSMutableArray array];
|
||||
for (NSString *theS3BucketName in s3BucketNames) {
|
||||
if ([theS3BucketName rangeOfString:@"-com-haystacksoftware-arq"].location == NSNotFound
|
||||
&& [theS3BucketName rangeOfString:@".com.haystacksoftware.arq"].location == NSNotFound) {
|
||||
HSLogDebug(@"skipping bucket %@", theS3BucketName);
|
||||
} else {
|
||||
NSString *queryPrefix = [NSString stringWithFormat:@"/%@/", [theS3BucketName lowercaseString]];
|
||||
NSArray *theS3ComputerUUIDs = [s3 commonPrefixesForPathPrefix:queryPrefix delimiter:@"/" error:error];
|
||||
if (theS3ComputerUUIDs == nil) {
|
||||
return nil;
|
||||
}
|
||||
for (NSString *s3ComputerUUID in theS3ComputerUUIDs) {
|
||||
NSString *computerInfoPath = [NSString stringWithFormat:@"/%@/%@/computerinfo", theS3BucketName, s3ComputerUUID];
|
||||
NSError *uacError = nil;
|
||||
NSData *uacData = [s3 dataAtPath:computerInfoPath error:&uacError];
|
||||
UserAndComputer *uac = nil;
|
||||
if (uacData != nil) {
|
||||
uac = [[[UserAndComputer alloc] initWithXMLData:uacData error:&uacError] autorelease];
|
||||
if (uac == nil) {
|
||||
HSLogError(@"error parsing UserAndComputer data: %@", uacError);
|
||||
}
|
||||
}
|
||||
BackupSet *backupSet = [[[BackupSet alloc] initWithAccessKeyID:theAccessKeyID
|
||||
secretAccessKey:theSecretAccessKey
|
||||
s3BucketName:theS3BucketName
|
||||
computerUUID:s3ComputerUUID
|
||||
userAndComputer:uac] autorelease];
|
||||
[ret addObject:backupSet];
|
||||
}
|
||||
}
|
||||
}
|
||||
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"description" ascending:YES] autorelease];
|
||||
[ret sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (id)initWithAccessKeyID:(NSString *)theAccessKeyID
|
||||
secretAccessKey:(NSString *)theSecretAccessKey
|
||||
s3BucketName:(NSString *)theS3BucketName
|
||||
computerUUID:(NSString *)theComputerUUID
|
||||
userAndComputer:(UserAndComputer *)theUAC {
|
||||
if (self = [super init]) {
|
||||
accessKeyID = [theAccessKeyID retain];
|
||||
secretAccessKey = [theSecretAccessKey retain];
|
||||
s3BucketName = [theS3BucketName retain];
|
||||
computerUUID = [theComputerUUID retain];
|
||||
uac = [theUAC retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
[accessKeyID release];
|
||||
[secretAccessKey release];
|
||||
[s3BucketName release];
|
||||
[computerUUID release];
|
||||
[uac release];
|
||||
[super dealloc];
|
||||
}
|
||||
- (NSString *)s3BucketName {
|
||||
return s3BucketName;
|
||||
}
|
||||
- (NSString *)computerUUID {
|
||||
return computerUUID;
|
||||
}
|
||||
- (UserAndComputer *)userAndComputer {
|
||||
return uac;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark NSObject
|
||||
- (NSString *)description {
|
||||
NSString *bucketRegion = [S3Service displayNameForBucketRegion:[S3Service s3BucketRegionForS3BucketName:s3BucketName]];
|
||||
if (uac != nil) {
|
||||
return [NSString stringWithFormat:@"%@ (%@) : %@ (%@)", [uac computerName], [uac userName], bucketRegion, computerUUID];
|
||||
}
|
||||
return [NSString stringWithFormat:@"unknown computer : %@ (%@)", bucketRegion, computerUUID];
|
||||
|
||||
}
|
||||
@end
|
||||
|
|
@ -50,6 +50,7 @@
|
|||
F805B8A11160EBAA007EC01E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8A01160EBAA007EC01E /* CoreFoundation.framework */; };
|
||||
F805B8C21160EC41007EC01E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; };
|
||||
F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; };
|
||||
F81426D714541A6C00D7E50A /* BackupSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F81426D614541A6C00D7E50A /* BackupSet.m */; };
|
||||
F83C1A7411CA7C170001958F /* ArqRestoreCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B54C1160D3E6007EC01E /* ArqRestoreCommand.m */; };
|
||||
F83C1A7711CA7C170001958F /* ArrayNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7421160DCFE007EC01E /* ArrayNode.m */; };
|
||||
F83C1A7811CA7C170001958F /* BooleanNode.m in Sources */ = {isa = PBXBuildFile; fileRef = F805B7441160DCFE007EC01E /* BooleanNode.m */; };
|
||||
|
|
@ -336,6 +337,8 @@
|
|||
F805B8C11160EC41007EC01E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
F805B8C51160EC4E007EC01E /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
F805B8CD1160ECD7007EC01E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
|
||||
F81426D514541A6C00D7E50A /* BackupSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackupSet.h; sourceTree = "<group>"; };
|
||||
F81426D614541A6C00D7E50A /* BackupSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackupSet.m; sourceTree = "<group>"; };
|
||||
F83C1A5F11CA7A6B0001958F /* arq_verify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = arq_verify.m; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
|
@ -544,6 +547,8 @@
|
|||
F8F4D67D121DA542002D09C1 /* UserAndComputer.m */,
|
||||
F89A1F3C13FAC6820071D321 /* UserLibrary_Arq.h */,
|
||||
F89A1F3D13FAC6820071D321 /* UserLibrary_Arq.m */,
|
||||
F81426D514541A6C00D7E50A /* BackupSet.h */,
|
||||
F81426D614541A6C00D7E50A /* BackupSet.m */,
|
||||
);
|
||||
name = Source;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -974,6 +979,7 @@
|
|||
F89A200B13FADAD70071D321 /* ArqSalt.m in Sources */,
|
||||
F89A205413FAE2DA0071D321 /* LocalS3Signer.m in Sources */,
|
||||
F89A205A13FAE3010071D321 /* DataOutputStream.m in Sources */,
|
||||
F81426D714541A6C00D7E50A /* BackupSet.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -1088,7 +1094,11 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
ARCHS = (
|
||||
x86_64,
|
||||
i386,
|
||||
ppc,
|
||||
);
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
|
|
@ -1108,7 +1118,11 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
ARCHS = (
|
||||
x86_64,
|
||||
i386,
|
||||
ppc,
|
||||
);
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
|
|
|
|||
Loading…
Reference in a new issue