diff options
Diffstat (limited to 'bin/bbackupquery/BackupQueries.cpp')
-rw-r--r-- | bin/bbackupquery/BackupQueries.cpp | 688 |
1 files changed, 319 insertions, 369 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index 60724800..b8b9525b 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -48,9 +48,9 @@ #include "Logging.h" #include "PathUtils.h" #include "SelfFlushingStream.h" -#include "TemporaryDirectory.h" #include "Utils.h" -#include "autogen_BackupProtocolClient.h" +#include "autogen_BackupProtocol.h" +#include "autogen_CipherException.h" #include "MemLeakFindOn.h" @@ -100,12 +100,6 @@ BackupQueries::~BackupQueries() { } -typedef struct -{ - const char* name; - const char* opts; -} QueryCommandSpecification; - // -------------------------------------------------------------------------- // // Function @@ -114,173 +108,46 @@ typedef struct // Created: 2003/10/10 // // -------------------------------------------------------------------------- -void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) +void BackupQueries::DoCommand(ParsedCommand& rCommand) { - // is the command a shell command? - if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') + // Check... + + if(rCommand.mFailed) { - // Yes, run shell command - int result = ::system(Command + 3); - if(result != 0) - { - BOX_WARNING("System command returned error code " << - result); - SetReturnCode(ReturnCode::Command_Error); - } + BOX_ERROR("Parse failed"); return; } - // split command into components - std::vector<std::string> cmdElements; - std::string options; + if(rCommand.mCmdElements.size() < 1) { - const char *c = Command; - bool inQuoted = false; - bool inOptions = false; - - std::string s; - while(*c != 0) - { - // Terminating char? - if(*c == ((inQuoted)?'"':' ')) - { - if(!s.empty()) cmdElements.push_back(s); - s.resize(0); - inQuoted = false; - inOptions = false; - } - else - { - // No. Start of quoted parameter? - if(s.empty() && *c == '"') - { - inQuoted = true; - } - // Start of options? - else if(s.empty() && *c == '-') - { - inOptions = true; - } - else - { - if(inOptions) - { - // Option char - options += *c; - } - else - { - // Normal string char - s += *c; - } - } - } - - ++c; - } - if(!s.empty()) cmdElements.push_back(s); + // blank command + return; } - - #ifdef WIN32 - if (isFromCommandLine) + + if(rCommand.pSpec->type == Command_sh && + rCommand.mCmdElements.size() == 2) { - for (std::vector<std::string>::iterator - i = cmdElements.begin(); - i != cmdElements.end(); i++) + // Yes, run shell command + int result = ::system(rCommand.mCmdElements[1].c_str()); + if(result != 0) { - std::string converted; - if (!ConvertEncoding(*i, CP_ACP, converted, - GetConsoleCP())) - { - BOX_ERROR("Failed to convert encoding"); - return; - } - *i = converted; + BOX_WARNING("System command returned error code " << + result); + SetReturnCode(ReturnCode::Command_Error); } - } - #endif - - // Check... - if(cmdElements.size() < 1) - { - // blank command return; } - - // Data about commands - static QueryCommandSpecification commands[] = - { - { "quit", "" }, - { "exit", "" }, - { "list", "rodIFtTash", }, - { "pwd", "" }, - { "cd", "od" }, - { "lcd", "" }, - { "sh", "" }, - { "getobject", "" }, - { "get", "i" }, - { "compare", "alcqAEQ" }, - { "restore", "drif" }, - { "help", "" }, - { "usage", "m" }, - { "undelete", "i" }, - { "delete", "i" }, - { NULL, NULL } - }; - - typedef enum - { - Command_Quit = 0, - Command_Exit, - Command_List, - Command_pwd, - Command_cd, - Command_lcd, - Command_sh, - Command_GetObject, - Command_Get, - Command_Compare, - Command_Restore, - Command_Help, - Command_Usage, - Command_Undelete, - Command_Delete, - } - CommandType; - - static const char *alias[] = {"ls", 0}; - static const int aliasIs[] = {Command_List, 0}; - - // Work out which command it is... - int cmd = 0; - while(commands[cmd].name != 0 && ::strcmp(cmdElements[0].c_str(), commands[cmd].name) != 0) - { - cmd++; - } - if(commands[cmd].name == 0) + + if(rCommand.pSpec->type == Command_Unknown) { - // Check for aliases - int a; - for(a = 0; alias[a] != 0; ++a) - { - if(::strcmp(cmdElements[0].c_str(), alias[a]) == 0) - { - // Found an alias - cmd = aliasIs[a]; - break; - } - } - // No such command - if(alias[a] == 0) - { - BOX_ERROR("Unrecognised command: " << Command); - return; - } + BOX_ERROR("Unrecognised command: " << rCommand.mCmdElements[0]); + return; } // Arguments - std::vector<std::string> args(cmdElements.begin() + 1, cmdElements.end()); + std::vector<std::string> args(rCommand.mCmdElements.begin() + 1, + rCommand.mCmdElements.end()); // Set up options bool opts[256]; @@ -288,14 +155,14 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) // BLOCK { // options - const char *c = options.c_str(); + const char *c = rCommand.mOptions.c_str(); while(*c != 0) { // Valid option? - if(::strchr(commands[cmd].opts, *c) == NULL) + if(::strchr(rCommand.pSpec->opts, *c) == NULL) { BOX_ERROR("Invalid option '" << *c << "' for " - "command " << commands[cmd].name); + "command " << rCommand.pSpec->name); return; } opts[(int)*c] = true; @@ -303,17 +170,16 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } } - if(cmd != Command_Quit && cmd != Command_Exit) + if(rCommand.pSpec->type != Command_Quit) { // If not a quit command, set the return code to zero SetReturnCode(ReturnCode::Command_OK); } // Handle command - switch(cmd) + switch(rCommand.pSpec->type) { case Command_Quit: - case Command_Exit: mQuitNow = true; break; @@ -375,7 +241,7 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) break; default: - BOX_ERROR("Unknown command: " << Command); + BOX_ERROR("Unknown command: " << rCommand.mCmdElements[0]); break; } } @@ -392,8 +258,6 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) void BackupQueries::CommandList(const std::vector<std::string> &args, const bool *opts) { #define LIST_OPTION_RECURSIVE 'r' - #define LIST_OPTION_ALLOWOLD 'o' - #define LIST_OPTION_ALLOWDELETED 'd' #define LIST_OPTION_NOOBJECTID 'I' #define LIST_OPTION_NOFLAGS 'F' #define LIST_OPTION_TIMES_LOCAL 't' @@ -492,22 +356,28 @@ static std::string GetTimeString(BackupStoreDirectory::Entry& en, // Created: 2003/10/10 // // -------------------------------------------------------------------------- -void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool *opts, bool FirstLevel) +void BackupQueries::List(int64_t DirID, const std::string &rListRoot, + const bool *opts, bool FirstLevel, std::ostream* pOut) { +#ifdef WIN32 + DWORD n_chars; + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + // Generate exclude flags - int16_t excludeFlags = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; - if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; - if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; + int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING; + if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion; + if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted; // Do communication try { mrConnection.QueryListDirectory( - DirID, - BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, - // both files and directories - excludeFlags, - true /* want attributes */); + DirID, + BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING, + // both files and directories + excludeFlags, + true /* want attributes */); } catch (std::exception &e) { @@ -522,7 +392,6 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool return; } - // Retrieve the directory from the stream following BackupStoreDirectory dir; std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream()); @@ -533,6 +402,8 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool BackupStoreDirectory::Entry *en = 0; while((en = i.Next()) != 0) { + std::ostringstream buf; + // Display this entry BackupStoreFilenameClear clear(en->GetName()); @@ -540,11 +411,9 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool if(!opts[LIST_OPTION_NOOBJECTID]) { // add object ID to line -#ifdef _MSC_VER - printf("%08I64x ", (int64_t)en->GetObjectID()); -#else - printf("%08llx ", (long long)en->GetObjectID()); -#endif + buf << std::hex << std::internal << std::setw(8) << + std::setfill('0') << en->GetObjectID() << + std::dec << " "; } // Flags? @@ -571,44 +440,40 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool // terminate *(f++) = ' '; *(f++) = '\0'; - printf(displayflags); + buf << displayflags; if(en_flags != 0) { - printf("[ERROR: Entry has additional flags set] "); + buf << "[ERROR: Entry has additional flags set] "; } } if(opts[LIST_OPTION_TIMES_UTC]) { // Show UTC times... - printf("%s ", GetTimeString(*en, false, - opts[LIST_OPTION_TIMES_ATTRIBS]).c_str()); + buf << GetTimeString(*en, false, + opts[LIST_OPTION_TIMES_ATTRIBS]) << " "; } if(opts[LIST_OPTION_TIMES_LOCAL]) { // Show local times... - printf("%s ", GetTimeString(*en, true, - opts[LIST_OPTION_TIMES_ATTRIBS]).c_str()); + buf << GetTimeString(*en, true, + opts[LIST_OPTION_TIMES_ATTRIBS]) << " "; } if(opts[LIST_OPTION_DISPLAY_HASH]) { -#ifdef _MSC_VER - printf("%016I64x ", (int64_t)en->GetAttributesHash()); -#else - printf("%016llx ", (long long)en->GetAttributesHash()); -#endif + buf << std::hex << std::internal << std::setw(16) << + std::setfill('0') << en->GetAttributesHash() << + std::dec; } if(opts[LIST_OPTION_SIZEINBLOCKS]) { -#ifdef _MSC_VER - printf("%05I64d ", (int64_t)en->GetSizeInBlocks()); -#else - printf("%05lld ", (long long)en->GetSizeInBlocks()); -#endif + buf << std::internal << std::setw(5) << + std::setfill('0') << en->GetSizeInBlocks() << + " "; } // add name @@ -618,30 +483,60 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool std::string listRootDecoded; if(!ConvertUtf8ToConsole(rListRoot.c_str(), listRootDecoded)) return; - printf("%s/", listRootDecoded.c_str()); + listRootDecoded += "/"; + buf << listRootDecoded; + WriteConsole(hOut, listRootDecoded.c_str(), + strlen(listRootDecoded.c_str()), &n_chars, NULL); #else - printf("%s/", rListRoot.c_str()); + buf << rListRoot << "/"; #endif } + std::string fileName; + try + { + fileName = clear.GetClearFilename(); + } + catch(CipherException &e) + { + fileName = "<decrypt failed>"; + } + #ifdef WIN32 + std::string fileNameUtf8 = fileName; + if(!ConvertUtf8ToConsole(fileNameUtf8, fileName)) { - std::string fileName; - if(!ConvertUtf8ToConsole( - clear.GetClearFilename().c_str(), fileName)) - return; - printf("%s", fileName.c_str()); + fileName = fileNameUtf8 + " [convert encoding failed]"; } -#else - printf("%s", clear.GetClearFilename().c_str()); #endif - + + buf << fileName; + if(!en->GetName().IsEncrypted()) { - printf("[FILENAME NOT ENCRYPTED]"); + buf << " [FILENAME NOT ENCRYPTED]"; } - printf("\n"); + buf << std::endl; + + if(pOut) + { + (*pOut) << buf.str(); + } + else + { +#ifdef WIN32 + std::string line = buf.str(); + if (!WriteConsole(hOut, line.c_str(), line.size(), + &n_chars, NULL)) + { + // WriteConsole failed, try standard method + std::cout << buf.str(); + } +#else + std::cout << buf.str(); +#endif + } // Directory? if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0) @@ -652,7 +547,9 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool std::string subroot(rListRoot); if(!FirstLevel) subroot += '/'; subroot += clear.GetClearFilename(); - List(en->GetObjectID(), subroot, opts, false /* not the first level to list */); + List(en->GetObjectID(), subroot, opts, + false /* not the first level to list */, + pOut); } } } @@ -681,7 +578,7 @@ int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, // Start from current stack, or root, whichever is required std::vector<std::pair<std::string, int64_t> > stack; - int64_t dirID = BackupProtocolClientListDirectory::RootDirectory; + int64_t dirID = BackupProtocolListDirectory::RootDirectory; if(rDirName.size() > 0 && rDirName[0] == '/') { // Root, do nothing @@ -697,9 +594,9 @@ int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, } // Generate exclude flags - int16_t excludeFlags = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; - if(!AllowOldVersion) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; - if(!AllowDeletedDirs) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; + int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING; + if(!AllowOldVersion) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion; + if(!AllowDeletedDirs) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted; // Read directories for(unsigned int e = 0; e < dirElements.size(); ++e) @@ -719,20 +616,20 @@ int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, stack.pop_back(); // New dir ID - dirID = (stack.size() > 0)?(stack[stack.size() - 1].second):BackupProtocolClientListDirectory::RootDirectory; + dirID = (stack.size() > 0)?(stack[stack.size() - 1].second):BackupProtocolListDirectory::RootDirectory; } else { // At root anyway - dirID = BackupProtocolClientListDirectory::RootDirectory; + dirID = BackupProtocolListDirectory::RootDirectory; } } else { // Not blank element. Read current directory. - std::auto_ptr<BackupProtocolClientSuccess> dirreply(mrConnection.QueryListDirectory( + std::auto_ptr<BackupProtocolSuccess> dirreply(mrConnection.QueryListDirectory( dirID, - BackupProtocolClientListDirectory::Flags_Dir, // just directories + BackupProtocolListDirectory::Flags_Dir, // just directories excludeFlags, true /* want attributes */)); @@ -783,7 +680,7 @@ int64_t BackupQueries::GetCurrentDirectoryID() // Special case for root if(mDirStack.size() == 0) { - return BackupProtocolClientListDirectory::RootDirectory; + return BackupProtocolListDirectory::RootDirectory; } // Otherwise, get from the last entry on the stack @@ -955,7 +852,8 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const int64_t id = ::strtoll(args[0].c_str(), 0, 16); if(id == std::numeric_limits<long long>::min() || id == std::numeric_limits<long long>::max() || id == 0) { - BOX_ERROR("Not a valid object ID (specified in hex)."); + BOX_ERROR("Not a valid object ID (specified in hex): " << + args[0]); return; } @@ -974,8 +872,8 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const try { // Request object - std::auto_ptr<BackupProtocolClientSuccess> getobj(mrConnection.QueryGetObject(id)); - if(getobj->GetObjectID() != BackupProtocolClientGetObject::NoObject) + std::auto_ptr<BackupProtocolSuccess> getobj(mrConnection.QueryGetObject(id)); + if(getobj->GetObjectID() != BackupProtocolGetObject::NoObject) { // Stream that object out to the file std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream()); @@ -1062,7 +960,8 @@ int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString, fileId == std::numeric_limits<long long>::max() || fileId == 0) { - BOX_ERROR("Not a valid object ID (specified in hex)."); + BOX_ERROR("Not a valid object ID (specified in hex): " + << rNameOrIdString); return 0; } @@ -1154,19 +1053,19 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) if(opts['i']) { // can retrieve anything by ID - flagsExclude = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; + flagsExclude = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING; } else { // only current versions by name flagsExclude = - BackupProtocolClientListDirectory::Flags_OldVersion | - BackupProtocolClientListDirectory::Flags_Deleted; + BackupProtocolListDirectory::Flags_OldVersion | + BackupProtocolListDirectory::Flags_Deleted; } fileId = FindFileID(args[0], opts, &dirId, &localName, - BackupProtocolClientListDirectory::Flags_File, // just files + BackupProtocolListDirectory::Flags_File, // just files flagsExclude, NULL /* don't care about flags found */); if (fileId == 0) @@ -1469,6 +1368,159 @@ void BackupQueries::Compare(const std::string &rStoreDir, Compare(dirID, storeDirEncoded, localDirEncoded, rParams); } +void BackupQueries::CompareOneFile(int64_t DirID, + BackupStoreDirectory::Entry *pEntry, + const std::string& rLocalPath, + const std::string& rStorePath, + BoxBackupCompareParams &rParams) +{ + int64_t fileId = pEntry->GetObjectID(); + int64_t fileSize = 0; + + EMU_STRUCT_STAT st; + if(EMU_STAT(rLocalPath.c_str(), &st) == 0) + { + fileSize = st.st_size; + } + + try + { + // Files the same flag? + bool equal = true; + + // File modified after last sync flag + bool modifiedAfterLastSync = false; + + bool hasDifferentAttribs = false; + + bool alreadyReported = false; + + if(rParams.QuickCompare()) + { + // Compare file -- fetch it + mrConnection.QueryGetBlockIndexByID(fileId); + + // Stream containing block index + std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream()); + + // Compare + equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex( + rLocalPath.c_str(), *blockIndexStream, + mrConnection.GetTimeout()); + } + else + { + // Compare file -- fetch it + mrConnection.QueryGetFile(DirID, pEntry->GetObjectID()); + + // Stream containing encoded file + std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream()); + + // Decode it + std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream; + + // Got additional attributes? + if(pEntry->HasAttributes()) + { + // Use these attributes + const StreamableMemBlock &storeAttr(pEntry->GetAttributes()); + BackupClientFileAttributes attr(storeAttr); + fileOnServerStream.reset( + BackupStoreFile::DecodeFileStream( + *objectStream, + mrConnection.GetTimeout(), + &attr).release()); + } + else + { + // Use attributes stored in file + fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release()); + } + + // Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid. + if(!fileOnServerStream.get()) + { + THROW_EXCEPTION(BackupStoreException, Internal) + } + + // Compare attributes + BackupClientFileAttributes localAttr; + box_time_t fileModTime = 0; + localAttr.ReadAttributes(rLocalPath.c_str(), false /* don't zero mod times */, &fileModTime); + modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime()); + bool ignoreAttrModTime = true; + + #ifdef WIN32 + // attr mod time is really + // creation time, so check it + ignoreAttrModTime = false; + #endif + + if(!rParams.IgnoreAttributes() && + #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE + !fileOnServerStream->IsSymLink() && + #endif + !localAttr.Compare(fileOnServerStream->GetAttributes(), + ignoreAttrModTime, + fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */)) + { + hasDifferentAttribs = true; + } + + // Compare contents, if it's a regular file not a link + // Remember, we MUST read the entire stream from the server. + SelfFlushingStream flushObject(*objectStream); + + if(!fileOnServerStream->IsSymLink()) + { + SelfFlushingStream flushFile(*fileOnServerStream); + // Open the local file + std::auto_ptr<FileStream> apLocalFile; + + try + { + apLocalFile.reset(new FileStream(rLocalPath.c_str())); + } + catch(std::exception &e) + { + rParams.NotifyLocalFileReadFailed(rLocalPath, + rStorePath, fileSize, e); + alreadyReported = true; + } + catch(...) + { + rParams.NotifyLocalFileReadFailed(rLocalPath, + rStorePath, fileSize); + alreadyReported = true; + } + + if(apLocalFile.get()) + { + equal = apLocalFile->CompareWith(*fileOnServerStream, + mrConnection.GetTimeout()); + } + } + } + + rParams.NotifyFileCompared(rLocalPath, rStorePath, fileSize, + hasDifferentAttribs, !equal, modifiedAfterLastSync, + pEntry->HasAttributes()); + } + catch(BoxException &e) + { + rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize, + e); + } + catch(std::exception &e) + { + rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize, + e); + } + catch(...) + { + rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize); + } +} // -------------------------------------------------------------------------- // @@ -1503,10 +1555,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, // Get the directory listing from the store mrConnection.QueryListDirectory( DirID, - BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, + BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING, // get everything - BackupProtocolClientListDirectory::Flags_OldVersion | - BackupProtocolClientListDirectory::Flags_Deleted, + BackupProtocolListDirectory::Flags_OldVersion | + BackupProtocolListDirectory::Flags_Deleted, // except for old versions and deleted files true /* want attributes */); @@ -1700,124 +1752,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, } else { - int64_t fileSize = 0; - - EMU_STRUCT_STAT st; - if(EMU_STAT(localPath.c_str(), &st) == 0) - { - fileSize = st.st_size; - } - - try - { - // Files the same flag? - bool equal = true; - - // File modified after last sync flag - bool modifiedAfterLastSync = false; - - bool hasDifferentAttribs = false; - - if(rParams.QuickCompare()) - { - // Compare file -- fetch it - mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID()); - - // Stream containing block index - std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream()); - - // Compare - equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(localPath.c_str(), *blockIndexStream, mrConnection.GetTimeout()); - } - else - { - // Compare file -- fetch it - mrConnection.QueryGetFile(DirID, i->second->GetObjectID()); - - // Stream containing encoded file - std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream()); - - // Decode it - std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream; - // Got additional attributes? - if(i->second->HasAttributes()) - { - // Use these attributes - const StreamableMemBlock &storeAttr(i->second->GetAttributes()); - BackupClientFileAttributes attr(storeAttr); - fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout(), &attr).release()); - } - else - { - // Use attributes stored in file - fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release()); - } - - // Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid. - if(!fileOnServerStream.get()) - { - THROW_EXCEPTION(BackupStoreException, Internal) - } - - // Compare attributes - BackupClientFileAttributes localAttr; - box_time_t fileModTime = 0; - localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime); - modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime()); - bool ignoreAttrModTime = true; - - #ifdef WIN32 - // attr mod time is really - // creation time, so check it - ignoreAttrModTime = false; - #endif - - if(!rParams.IgnoreAttributes() && - #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE - !fileOnServerStream->IsSymLink() && - #endif - !localAttr.Compare(fileOnServerStream->GetAttributes(), - ignoreAttrModTime, - fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */)) - { - hasDifferentAttribs = true; - } - - // Compare contents, if it's a regular file not a link - // Remember, we MUST read the entire stream from the server. - SelfFlushingStream flushObject(*objectStream); - - if(!fileOnServerStream->IsSymLink()) - { - SelfFlushingStream flushFile(*fileOnServerStream); - // Open the local file - FileStream l(localPath.c_str()); - equal = l.CompareWith(*fileOnServerStream, - mrConnection.GetTimeout()); - } - } - - rParams.NotifyFileCompared(localPath, - storePath, fileSize, - hasDifferentAttribs, !equal, - modifiedAfterLastSync, - i->second->HasAttributes()); - } - catch(BoxException &e) - { - rParams.NotifyDownloadFailed(localPath, - storePath, fileSize, e); - } - catch(std::exception &e) - { - rParams.NotifyDownloadFailed(localPath, - storePath, fileSize, e); - } - catch(...) - { - rParams.NotifyDownloadFailed(localPath, - storePath, fileSize); - } + CompareOneFile(DirID, i->second, localPath, + storePath, rParams); // Remove from set so that we know it's been compared localFiles.erase(local); @@ -1947,9 +1883,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, void BackupQueries::CommandRestore(const std::vector<std::string> &args, const bool *opts) { // Check arguments - if(args.size() != 2) + if(args.size() < 1 || args.size() > 2) { - BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> <local-name>"); + BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> " + "[<local-name>]"); return; } @@ -1966,7 +1903,8 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b dirID = ::strtoll(args[0].c_str(), 0, 16); if(dirID == std::numeric_limits<long long>::min() || dirID == std::numeric_limits<long long>::max() || dirID == 0) { - BOX_ERROR("Not a valid object ID (specified in hex)"); + BOX_ERROR("Not a valid object ID (specified in hex): " + << args[0]); return; } std::ostringstream oss; @@ -1994,18 +1932,30 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b BOX_ERROR("Directory '" << args[0] << "' not found on server"); return; } - if(dirID == BackupProtocolClientListDirectory::RootDirectory) + + if(dirID == BackupProtocolListDirectory::RootDirectory) { BOX_ERROR("Cannot restore the root directory -- restore locations individually."); return; } - -#ifdef WIN32 + std::string localName; - if(!ConvertConsoleToUtf8(args[1].c_str(), localName)) return; -#else - std::string localName(args[1]); -#endif + + if(args.size() == 2) + { + #ifdef WIN32 + if(!ConvertConsoleToUtf8(args[1].c_str(), localName)) + { + return; + } + #else + localName = args[1]; + #endif + } + else + { + localName = args[0]; + } // Go and restore... int result; @@ -2082,8 +2032,8 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b // These are autogenerated by a script. -extern char *help_commands[]; -extern char *help_text[]; +extern const char *help_commands[]; +extern const char *help_text[]; // -------------------------------------------------------------------------- @@ -2140,7 +2090,7 @@ void BackupQueries::CommandUsage(const bool *opts) bool MachineReadable = opts['m']; // Request full details from the server - std::auto_ptr<BackupProtocolClientAccountUsage> usage(mrConnection.QueryGetAccountUsage()); + std::auto_ptr<BackupProtocolAccountUsage> usage(mrConnection.QueryGetAccountUsage()); // Display each entry in turn int64_t hardLimit = usage->GetBlocksHardLimit(); @@ -2216,9 +2166,9 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName, /* include files and directories */ - BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING, /* include old and deleted files */ - BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING, &flagsOut); if (fileId == 0) @@ -2231,7 +2181,7 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const try { // Undelete object - if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + if(flagsOut & BackupProtocolListDirectory::Flags_File) { mrConnection.QueryUndeleteFile(parentId, fileId); } @@ -2296,10 +2246,10 @@ void BackupQueries::CommandDelete(const std::vector<std::string> &args, fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName, /* include files and directories */ - BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING, /* exclude old and deleted files */ - BackupProtocolClientListDirectory::Flags_OldVersion | - BackupProtocolClientListDirectory::Flags_Deleted, + BackupProtocolListDirectory::Flags_OldVersion | + BackupProtocolListDirectory::Flags_Deleted, &flagsOut); if (fileId == 0) @@ -2314,7 +2264,7 @@ void BackupQueries::CommandDelete(const std::vector<std::string> &args, try { // Delete object - if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + if(flagsOut & BackupProtocolListDirectory::Flags_File) { mrConnection.QueryDeleteFile(parentId, fn); } |