added reflog-printing function

This commit is contained in:
Stefan Reitshamer 2011-11-21 09:17:48 -05:00
parent 395a17a05d
commit ec0f06fbea
7 changed files with 224 additions and 13 deletions

View file

@ -44,10 +44,12 @@
#import "ArqSalt.h"
#import "ArqRepo.h"
#import "BackupSet.h"
#import "ReflogPrinter.h"
@interface ArqRestoreCommand (internal)
- (BOOL)printArqFolders:(NSError **)error;
- (BOOL)restorePath:(NSError **)error;
- (BOOL)processPath:(NSError **)error;
- (BOOL)validateS3Keys:(NSError **)error;
@end
@ -112,7 +114,7 @@
if (path == nil) {
ret = [self printArqFolders:error];
} else {
ret = [self restorePath:error];
ret = [self processPath:error];
}
return ret;
}
@ -164,7 +166,7 @@
}
return YES;
}
- (BOOL)restorePath:(NSError **)error {
- (BOOL)processPath:(NSError **)error {
if (![self validateS3Keys:error]) {
return NO;
}
@ -192,19 +194,13 @@
NSString *computerUUID = [path substringWithRange:computerUUIDRange];
NSString *bucketUUID = [path substringWithRange:bucketUUIDRange];
NSString *bucketName = [[plist stringNodeForKey:@"BucketName"] stringValue];
printf("restoring %s from ", [bucketName UTF8String]);
NSError *uacError = nil;
NSData *uacData = [s3 dataAtPath:[NSString stringWithFormat:@"/%@/%@/computerinfo", s3BucketName, computerUUID] 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(" to %s/%s\n", [[[NSFileManager defaultManager] currentDirectoryPath] UTF8String], [bucketName UTF8String]);
NSError *saltError = nil;
ArqSalt *arqSalt = [[[ArqSalt alloc] initWithAccessKeyID:accessKey secretAccessKey:secretKey s3BucketName:s3BucketName computerUUID:computerUUID] autorelease];
@ -222,11 +218,27 @@
return NO;
}
Restorer *restorer = [[[Restorer alloc] initWithRepo:repo bucketName:bucketName commitSHA1:commitSHA1] autorelease];
if (![restorer restore:error]) {
return NO;
if ([commitSHA1 isEqualToString:@"reflog"]) {
printf("printing reflog for %s\n", [bucketName UTF8String]);
ReflogPrinter *printer = [[[ReflogPrinter alloc] initWithS3BucketName:s3BucketName computerUUID:computerUUID bucketUUID:bucketUUID s3:s3 repo:repo] autorelease];
if (![printer printReflog:error]) {
return NO;
}
} else {
printf("restoring %s from ", [bucketName UTF8String]);
if (uac != nil) {
printf("%s (%s)", [[uac computerName] UTF8String], [[uac userName] UTF8String]);
} else {
printf("(unknown computer)");
}
printf(" to %s/%s\n", [[[NSFileManager defaultManager] currentDirectoryPath] UTF8String], [bucketName UTF8String]);
Restorer *restorer = [[[Restorer alloc] initWithRepo:repo bucketName:bucketName commitSHA1:commitSHA1] autorelease];
if (![restorer restore:error]) {
return NO;
}
printf("restored files are in %s\n", [bucketName fileSystemRepresentation]);
}
printf("restored files are in %s\n", [bucketName fileSystemRepresentation]);
return YES;
}
- (BOOL)validateS3Keys:(NSError **)error {

20
ReflogEntry.h Normal file
View file

@ -0,0 +1,20 @@
//
// ReflogEntry.h
// arq_restore
//
// Created by Stefan Reitshamer on 11/20/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class BlobKey;
@interface ReflogEntry : NSObject {
BlobKey *oldHeadBlobKey;
BlobKey *newHeadBlobKey;
}
- (id)initWithData:(NSData *)theData error:(NSError **)error;
- (BlobKey *)oldHeadBlobKey;
- (BlobKey *)newHeadBlobKey;
@end

51
ReflogEntry.m Normal file
View file

@ -0,0 +1,51 @@
//
// ReflogEntry.m
// arq_restore
//
// Created by Stefan Reitshamer on 11/20/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "ReflogEntry.h"
#import "BlobKey.h"
#import "DictNode.h"
#import "SetNSError.h"
@implementation ReflogEntry
- (id)initWithData:(NSData *)theData error:(NSError **)error {
if (self = [super init]) {
DictNode *dictNode = [DictNode dictNodeWithXMLData:theData error:error];
if (dictNode == nil) {
[self release];
return nil;
}
if (![dictNode containsKey:@"oldHeadSHA1"]
|| ![dictNode containsKey:@"oldHeadStretchKey"]
|| ![dictNode containsKey:@"newHeadSHA1"]
|| ![dictNode containsKey:@"newHeadStretchKey"]) {
SETNSERROR(@"ReflogEntryErrorDomain", -1, @"missing values in reflog entry");
[self release];
return nil;
}
oldHeadBlobKey = [[BlobKey alloc] initWithSHA1:[[dictNode stringNodeForKey:@"oldHeadSHA1"] stringValue]
stretchEncryptionKey:[[dictNode booleanNodeForKey:@"oldHeadStretchKey"] booleanValue]];
newHeadBlobKey = [[BlobKey alloc] initWithSHA1:[[dictNode stringNodeForKey:@"newHeadSHA1"] stringValue]
stretchEncryptionKey:[[dictNode booleanNodeForKey:@"newHeadStretchKey"] booleanValue]];
}
return self;
}
- (void)dealloc {
[oldHeadBlobKey release];
[newHeadBlobKey release];
[super dealloc];
}
- (BlobKey *)oldHeadBlobKey {
return oldHeadBlobKey;
}
- (BlobKey *)newHeadBlobKey {
return newHeadBlobKey;
}
@end

22
ReflogPrinter.h Normal file
View file

@ -0,0 +1,22 @@
//
// ReflogPrinter.h
// arq_restore
//
// Created by Stefan Reitshamer on 11/21/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class S3Service;
@class ArqRepo;
@interface ReflogPrinter : NSObject {
NSString *s3BucketName;
NSString *computerUUID;
NSString *bucketUUID;
S3Service *s3;
ArqRepo *repo;
}
- (id)initWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID s3:(S3Service *)theS3 repo:(ArqRepo *)theRepo;
- (BOOL)printReflog:(NSError **)error;
@end

99
ReflogPrinter.m Normal file
View file

@ -0,0 +1,99 @@
//
// ReflogPrinter.m
// arq_restore
//
// Created by Stefan Reitshamer on 11/21/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "ReflogPrinter.h"
#import "S3Service.h"
#import "ArqRepo.h"
#import "ReflogEntry.h"
#import "Commit.h"
#import "BlobKey.h"
@interface ReflogPrinter (internal)
- (BOOL)printEntry:(NSString *)path error:(NSError **)error;
@end
@implementation ReflogPrinter
- (id)initWithS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID bucketUUID:(NSString *)theBucketUUID s3:(S3Service *)theS3 repo:(ArqRepo *)theRepo {
if (self = [super init]) {
s3BucketName = [theS3BucketName retain];
computerUUID = [theComputerUUID retain];
bucketUUID = [theBucketUUID retain];
s3 = [theS3 retain];
repo = [theRepo retain];
}
return self;
}
- (void)dealloc {
[s3BucketName release];
[computerUUID release];
[bucketUUID release];
[s3 release];
[repo release];
[super dealloc];
}
- (BOOL)printReflog:(NSError **)error {
NSString *prefix = [NSString stringWithFormat:@"/%@/%@/bucketdata/%@/refs/logs/master/", s3BucketName, computerUUID, bucketUUID];
NSArray *paths = [s3 pathsWithPrefix:prefix error:error];
if (paths == nil) {
return NO;
}
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"description" ascending:NO] autorelease];
NSArray *sortedPaths = [paths sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
BOOL ret = YES;
NSAutoreleasePool *pool = nil;
for (NSString *path in sortedPaths) {
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
if (![self printEntry:path error:error]) {
ret = NO;
break;
}
}
if (!ret && error != NULL) {
[*error retain];
}
[pool drain];
if (!ret && error != NULL) {
[*error autorelease];
}
return ret;
}
@end
@implementation ReflogPrinter (internal)
- (BOOL)printEntry:(NSString *)path error:(NSError **)error {
printf("reflog %s\n", [path UTF8String]);
NSData *data = [s3 dataAtPath:path error:error];
if (data == nil) {
return NO;
}
NSError *myError = nil;
ReflogEntry *entry = [[[ReflogEntry alloc] initWithData:data error:&myError] autorelease];
if (entry == nil) {
printf("\terror reading reflog entry: %s\n", [[myError description] UTF8String]);
} else {
Commit *commit = [repo commitForBlobKey:[entry newHeadBlobKey] error:&myError];
if (commit == nil) {
printf("\tcommit %s: %s\n", [[[entry newHeadBlobKey] description] UTF8String], [[myError localizedDescription] UTF8String]);
} else {
printf("\tblobkey: %s\n", [[[entry newHeadBlobKey] description] UTF8String]);
printf("\tauthor: %s\n", [[commit author] UTF8String]);
printf("\tdate: %s\n", [[[commit creationDate] description] UTF8String]);
printf("\tlocation: %s\n", [[commit location] UTF8String]);
printf("\trestore command: arq_restore /%s/%s/buckets/%s %s\n", [s3BucketName UTF8String], [computerUUID UTF8String], [bucketUUID UTF8String],
[[[entry newHeadBlobKey] sha1] UTF8String]);
}
}
return YES;
}
@end

View file

@ -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 reflog\n", exeName);
fprintf(stderr, "\t%s [-l log_level] /s3bucket/computerUUID/folderUUID <commitSHA1>\n", exeName);
}
int main (int argc, const char **argv) {

View file

@ -52,6 +52,7 @@
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 */; };
F8373E5A147A8DEC005AFBE6 /* ReflogPrinter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8373E59147A8DEC005AFBE6 /* ReflogPrinter.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 */; };
@ -342,6 +343,8 @@
F81426D614541A6C00D7E50A /* BackupSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackupSet.m; sourceTree = "<group>"; };
F8373E0D14794D01005AFBE6 /* ReflogEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflogEntry.h; sourceTree = "<group>"; };
F8373E0E14794D01005AFBE6 /* ReflogEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReflogEntry.m; sourceTree = "<group>"; };
F8373E58147A8DEC005AFBE6 /* ReflogPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflogPrinter.h; sourceTree = "<group>"; };
F8373E59147A8DEC005AFBE6 /* ReflogPrinter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReflogPrinter.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>"; };
@ -552,6 +555,8 @@
F89A1F3D13FAC6820071D321 /* UserLibrary_Arq.m */,
F81426D514541A6C00D7E50A /* BackupSet.h */,
F81426D614541A6C00D7E50A /* BackupSet.m */,
F8373E58147A8DEC005AFBE6 /* ReflogPrinter.h */,
F8373E59147A8DEC005AFBE6 /* ReflogPrinter.m */,
F8373E0D14794D01005AFBE6 /* ReflogEntry.h */,
F8373E0E14794D01005AFBE6 /* ReflogEntry.m */,
);
@ -986,6 +991,7 @@
F89A205A13FAE3010071D321 /* DataOutputStream.m in Sources */,
F81426D714541A6C00D7E50A /* BackupSet.m in Sources */,
F8373E0F14794D01005AFBE6 /* ReflogEntry.m in Sources */,
F8373E5A147A8DEC005AFBE6 /* ReflogPrinter.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};