From 395a17a05d08b7e58ed6ae627388ff821572e082 Mon Sep 17 00:00:00 2001 From: Stefan Reitshamer Date: Mon, 21 Nov 2011 08:35:36 -0500 Subject: [PATCH] Added an option to explicitly give a SHA1 for the commit you want to restore. --- ArqRestoreCommand.h | 1 + ArqRestoreCommand.m | 7 ++- Restorer.h | 7 ++- Restorer.m | 62 ++++++++++++++++++--------- arq_restore.m | 1 + arq_restore.xcodeproj/project.pbxproj | 6 +++ 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/ArqRestoreCommand.h b/ArqRestoreCommand.h index 1bed128..80ebccc 100644 --- a/ArqRestoreCommand.h +++ b/ArqRestoreCommand.h @@ -39,6 +39,7 @@ NSString *encryptionPassword; S3Service *s3; NSString *path; + NSString *commitSHA1; } - (BOOL)readArgc:(int)argc argv:(const char **)argv; - (BOOL)execute:(NSError **)error; diff --git a/ArqRestoreCommand.m b/ArqRestoreCommand.m index 6c731dd..f2b726d 100644 --- a/ArqRestoreCommand.m +++ b/ArqRestoreCommand.m @@ -80,6 +80,7 @@ [encryptionPassword release]; [s3 release]; [path release]; + [commitSHA1 release]; [super dealloc]; } - (BOOL)readArgc:(int)argc argv:(const char **)argv { @@ -98,6 +99,8 @@ setHSLogLevel(hsLogLevelForName(level)); } else if (path == nil) { path = [[NSString alloc] initWithUTF8String:argv[i]]; + } else if (commitSHA1 == nil) { + commitSHA1 = [[NSString alloc] initWithUTF8String:argv[i]]; } else { fprintf(stderr, "warning: ignoring argument '%s'\n", argv[i]); } @@ -110,7 +113,7 @@ ret = [self printArqFolders:error]; } else { ret = [self restorePath:error]; - } + } return ret; } @end @@ -219,7 +222,7 @@ return NO; } - Restorer *restorer = [[[Restorer alloc] initWithRepo:repo bucketName:bucketName] autorelease]; + Restorer *restorer = [[[Restorer alloc] initWithRepo:repo bucketName:bucketName commitSHA1:commitSHA1] autorelease]; if (![restorer restore:error]) { return NO; } diff --git a/Restorer.h b/Restorer.h index e5ec601..ec1ea52 100644 --- a/Restorer.h +++ b/Restorer.h @@ -40,6 +40,7 @@ @interface Restorer : NSObject { ArqRepo *repo; NSString *bucketName; + NSString *commitSHA1; NSString *rootPath; NSUInteger superUserNodeCount; NSMutableArray *restoreNodes; @@ -51,10 +52,8 @@ unsigned long long transferred; unsigned long long total; - BlobKey *headBlobKey; - Commit *head; - Tree *headTree; + Tree *rootTree; } -- (id)initWithRepo:(ArqRepo *)theRepo bucketName:(NSString *)theBucketName; +- (id)initWithRepo:(ArqRepo *)theRepo bucketName:(NSString *)theBucketName commitSHA1:(NSString *)theCommitSHA1; - (BOOL)restore:(NSError **)error; @end diff --git a/Restorer.m b/Restorer.m index cc60da6..5163cbf 100644 --- a/Restorer.m +++ b/Restorer.m @@ -50,6 +50,8 @@ #import "NSData-Gzip.h" #import "GunzipInputStream.h" #import "FileACL.h" +#import "BlobKey.h" + #define MAX_RETRIES (10) #define MY_BUF_SIZE (8192) @@ -61,7 +63,6 @@ - (BOOL)restoreNode:(Node *)theNode ofTree:(Tree *)theTree toPath:(NSString *)thePath error:(NSError **)error; - (BOOL)needSuperUserForTree:(Tree *)theTree; - (BOOL)needSuperUserForTree:(Tree *)theTree node:(Node *)theNode; -- (BOOL)performSuperUserOps:(NSError **)error; - (BOOL)chownNode:(Node *)theNode ofTree:(Tree *)theTree atPath:(NSString *)thePath error:(NSError **)error; - (BOOL)chownTree:(Tree *)theTree atPath:(NSString *)thePath error:(NSError **)error; - (BOOL)applyUID:(int)theUID gid:(int)theGID mode:(int)theMode rdev:(int)theRdev toPath:(NSString *)thePath error:(NSError **)error; @@ -77,10 +78,11 @@ @end @implementation Restorer -- (id)initWithRepo:(ArqRepo *)theArqRepo bucketName:(NSString *)theBucketName { +- (id)initWithRepo:(ArqRepo *)theArqRepo bucketName:(NSString *)theBucketName commitSHA1:(NSString *)theCommitSHA1 { if (self = [super init]) { repo = [theArqRepo retain]; bucketName = [theBucketName copy]; + commitSHA1 = [theCommitSHA1 copy]; rootPath = [[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:theBucketName] copy]; restoreNodes = [[NSMutableArray alloc] init]; hardlinks = [[NSMutableDictionary alloc] init]; @@ -93,13 +95,12 @@ - (void)dealloc { [repo release]; [bucketName release]; + [commitSHA1 release]; [rootPath release]; [restoreNodes release]; [hardlinks release]; [errorsByPath release]; - [headBlobKey release]; - [head release]; - [headTree release]; + [rootTree release]; [super dealloc]; } - (BOOL)restore:(NSError **)error { @@ -111,20 +112,44 @@ HSLogError(@"failed to create directory %@", rootPath); return NO; } - headBlobKey = [[repo headBlobKey:error] retain]; - if (headBlobKey == nil) { - SETNSERROR([Restorer errorDomain], -1, @"no backup found"); + BlobKey *commitBlobKey = nil; + Commit *commit = nil; + NSError *myError = nil; + if (commitSHA1 != nil) { + commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 stretchEncryptionKey:YES] autorelease]; + commit = [repo commitForBlobKey:commitBlobKey error:&myError]; + if (commit == nil) { + HSLogError(@"error attempting to read commit for %@", commitBlobKey); + + // Try without stretched encryption key. + commitBlobKey = [[[BlobKey alloc] initWithSHA1:commitSHA1 stretchEncryptionKey:NO] autorelease]; + commit = [repo commitForBlobKey:commitBlobKey error:&myError]; + if (commit == nil) { + HSLogError(@"error attempting to read commit for %@", commitBlobKey); + if (error != NULL) { + *error = myError; + } + return NO; + } + } + } else { + commitBlobKey = [[repo headBlobKey:error] retain]; + if (commitBlobKey == nil) { + SETNSERROR([Restorer errorDomain], -1, @"no backup found"); + return NO; + } + commit = [repo commitForBlobKey:commitBlobKey error:error]; + if (commit == nil) { + return NO; + } + } + printf("restoring %scommit %s\n", (commitSHA1 == nil ? "head " : ""), [[commitBlobKey description] UTF8String]); + + rootTree = [[repo treeForBlobKey:[commit treeBlobKey] error:error] retain]; + if (rootTree == nil) { return NO; } - head = [[repo commitForBlobKey:headBlobKey error:error] retain]; - if (head == nil) { - return NO; - } - headTree = [[repo treeForBlobKey:[head treeBlobKey] error:error] retain]; - if (headTree == nil) { - return NO; - } - if (![self restoreTree:headTree toPath:rootPath error:error]) { + if (![self restoreTree:rootTree toPath:rootPath error:error]) { return NO; } return YES; @@ -296,9 +321,6 @@ } return NO; } -- (BOOL)performSuperUserOps:(NSError **)error { - return [self chownTree:headTree atPath:rootPath error:error]; -} - (BOOL)chownNode:(Node *)theNode ofTree:(Tree *)theTree atPath:(NSString *)thePath error:(NSError **)error { if ([[errorsByPath allKeys] containsObject:thePath]) { HSLogDebug(@"error restoring %@; skipping chownNode", thePath); diff --git a/arq_restore.m b/arq_restore.m index bbab2e0..aa2fc7a 100644 --- a/arq_restore.m +++ b/arq_restore.m @@ -39,6 +39,7 @@ static void printUsage(const char *exeName) { fprintf(stderr, "Usage:\n"); fprintf(stderr, "\t%s [-l log_level]\n", exeName); fprintf(stderr, "\t%s [-l log_level] /s3bucket/computerUUID/folderUUID\n", exeName); + fprintf(stderr, "\t%s [-l log_level] /s3bucket/computerUUID/folderUUID \n", exeName); } int main (int argc, const char **argv) { setHSLogLevel(HSLOG_LEVEL_WARN); diff --git a/arq_restore.xcodeproj/project.pbxproj b/arq_restore.xcodeproj/project.pbxproj index 9f1b88e..383ac95 100644 --- a/arq_restore.xcodeproj/project.pbxproj +++ b/arq_restore.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ F805B8C21160EC41007EC01E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8C11160EC41007EC01E /* Security.framework */; }; F805B8CE1160ECD7007EC01E /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F805B8CD1160ECD7007EC01E /* CoreServices.framework */; }; F81426D714541A6C00D7E50A /* BackupSet.m in Sources */ = {isa = PBXBuildFile; fileRef = F81426D614541A6C00D7E50A /* BackupSet.m */; }; + F8373E0F14794D01005AFBE6 /* ReflogEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = F8373E0E14794D01005AFBE6 /* ReflogEntry.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 */; }; @@ -339,6 +340,8 @@ F805B8CD1160ECD7007EC01E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; F81426D514541A6C00D7E50A /* BackupSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackupSet.h; sourceTree = ""; }; F81426D614541A6C00D7E50A /* BackupSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackupSet.m; sourceTree = ""; }; + F8373E0D14794D01005AFBE6 /* ReflogEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflogEntry.h; sourceTree = ""; }; + F8373E0E14794D01005AFBE6 /* ReflogEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReflogEntry.m; sourceTree = ""; }; F83C1A5F11CA7A6B0001958F /* arq_verify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = arq_verify.m; sourceTree = ""; }; F83C1A6111CA7BD20001958F /* ArqVerifyCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArqVerifyCommand.h; sourceTree = ""; }; F83C1A6211CA7BD20001958F /* ArqVerifyCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArqVerifyCommand.m; sourceTree = ""; }; @@ -549,6 +552,8 @@ F89A1F3D13FAC6820071D321 /* UserLibrary_Arq.m */, F81426D514541A6C00D7E50A /* BackupSet.h */, F81426D614541A6C00D7E50A /* BackupSet.m */, + F8373E0D14794D01005AFBE6 /* ReflogEntry.h */, + F8373E0E14794D01005AFBE6 /* ReflogEntry.m */, ); name = Source; sourceTree = ""; @@ -980,6 +985,7 @@ F89A205413FAE2DA0071D321 /* LocalS3Signer.m in Sources */, F89A205A13FAE3010071D321 /* DataOutputStream.m in Sources */, F81426D714541A6C00D7E50A /* BackupSet.m in Sources */, + F8373E0F14794D01005AFBE6 /* ReflogEntry.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };