diff options
Diffstat (limited to 'bin/bbackupquery/BackupQueries.cpp')
-rw-r--r-- | bin/bbackupquery/BackupQueries.cpp | 1240 |
1 files changed, 643 insertions, 597 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index b6984641..687dcb05 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -13,7 +13,6 @@ #include <unistd.h> #endif -#include <string.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> @@ -25,29 +24,33 @@ #include <dirent.h> #endif -#include <set> +#include <cstring> #include <limits> +#include <iostream> +#include <ostream> +#include <set> +#include "BackupClientFileAttributes.h" +#include "BackupClientMakeExcludeList.h" +#include "BackupClientRestore.h" #include "BackupQueries.h" -#include "Utils.h" -#include "Configuration.h" -#include "autogen_BackupProtocolClient.h" -#include "BackupStoreFilenameClear.h" #include "BackupStoreDirectory.h" -#include "IOStream.h" -#include "BoxTimeToText.h" -#include "FileStream.h" +#include "BackupStoreException.h" #include "BackupStoreFile.h" -#include "TemporaryDirectory.h" -#include "FileModificationTime.h" -#include "BackupClientFileAttributes.h" +#include "BackupStoreFilenameClear.h" +#include "BoxTimeToText.h" #include "CommonException.h" -#include "BackupClientRestore.h" -#include "BackupStoreException.h" +#include "Configuration.h" #include "ExcludeList.h" -#include "BackupClientMakeExcludeList.h" -#include "PathUtils.h" +#include "FileModificationTime.h" +#include "FileStream.h" +#include "IOStream.h" #include "Logging.h" +#include "PathUtils.h" +#include "SelfFlushingStream.h" +#include "TemporaryDirectory.h" +#include "Utils.h" +#include "autogen_BackupProtocolClient.h" #include "MemLeakFindOn.h" @@ -68,8 +71,10 @@ // Created: 2003/10/10 // // -------------------------------------------------------------------------- -BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration) - : mrConnection(rConnection), +BackupQueries::BackupQueries(BackupProtocolClient &rConnection, + const Configuration &rConfiguration, bool readWrite) + : mReadWrite(readWrite), + mrConnection(rConnection), mrConfiguration(rConfiguration), mQuitNow(false), mRunningAsRoot(false), @@ -115,7 +120,13 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') { // Yes, run shell command - ::system(Command + 3); + int result = ::system(Command + 3); + if(result != 0) + { + BOX_WARNING("System command returned error code " << + result); + SetReturnCode(ReturnCode::Command_Error); + } return; } @@ -209,28 +220,36 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) { "getobject", "" }, { "get", "i" }, { "compare", "alcqAEQ" }, - { "restore", "dri" }, + { "restore", "drif" }, { "help", "" }, - { "usage", "" }, + { "usage", "m" }, { "undelete", "" }, + { "delete", "" }, { NULL, NULL } }; - #define COMMAND_Quit 0 - #define COMMAND_Exit 1 - #define COMMAND_List 2 - #define COMMAND_pwd 3 - #define COMMAND_cd 4 - #define COMMAND_lcd 5 - #define COMMAND_sh 6 - #define COMMAND_GetObject 7 - #define COMMAND_Get 8 - #define COMMAND_Compare 9 - #define COMMAND_Restore 10 - #define COMMAND_Help 11 - #define COMMAND_Usage 12 - #define COMMAND_Undelete 13 - static const char *alias[] = {"ls", 0}; - static const int aliasIs[] = {COMMAND_List, 0}; + + 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; @@ -284,25 +303,25 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } } - if(cmd != COMMAND_Quit && cmd != COMMAND_Exit) + if(cmd != Command_Quit && cmd != Command_Exit) { // If not a quit command, set the return code to zero - SetReturnCode(0); + SetReturnCode(ReturnCode::Command_OK); } // Handle command switch(cmd) { - case COMMAND_Quit: - case COMMAND_Exit: + case Command_Quit: + case Command_Exit: mQuitNow = true; break; - case COMMAND_List: + case Command_List: CommandList(args, opts); break; - case COMMAND_pwd: + case Command_pwd: { // Simple implementation, so do it here BOX_INFO(GetCurrentDirectoryName() << " (" << @@ -310,47 +329,52 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } break; - case COMMAND_cd: + case Command_cd: CommandChangeDir(args, opts); break; - case COMMAND_lcd: + case Command_lcd: CommandChangeLocalDir(args); break; - case COMMAND_sh: + case Command_sh: BOX_ERROR("The command to run must be specified as an argument."); break; - case COMMAND_GetObject: + case Command_GetObject: CommandGetObject(args, opts); break; - case COMMAND_Get: + case Command_Get: CommandGet(args, opts); break; - case COMMAND_Compare: + case Command_Compare: CommandCompare(args, opts); break; - case COMMAND_Restore: + case Command_Restore: CommandRestore(args, opts); break; - case COMMAND_Usage: - CommandUsage(); + case Command_Usage: + CommandUsage(opts); break; - case COMMAND_Help: + case Command_Help: CommandHelp(args); break; - case COMMAND_Undelete: + case Command_Undelete: CommandUndelete(args, opts); break; + case Command_Delete: + CommandDelete(args, opts); + break; + default: + BOX_ERROR("Unknown command: " << Command); break; } } @@ -402,6 +426,7 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool { BOX_ERROR("Directory '" << args[0] << "' not found " "on store."); + SetReturnCode(ReturnCode::Command_Error); return; } } @@ -427,11 +452,28 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; // Do communication - mrConnection.QueryListDirectory( - DirID, - BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // both files and directories - excludeFlags, - true /* want attributes */); + try + { + mrConnection.QueryListDirectory( + DirID, + BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, + // both files and directories + excludeFlags, + true /* want attributes */); + } + catch (std::exception &e) + { + BOX_ERROR("Failed to list directory: " << e.what()); + SetReturnCode(ReturnCode::Command_Error); + return; + } + catch (...) + { + BOX_ERROR("Failed to list directory: unknown error"); + SetReturnCode(ReturnCode::Command_Error); + return; + } + // Retrieve the directory from the stream following BackupStoreDirectory dir; @@ -574,15 +616,18 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::FindDirectoryObjectID(const std::string &) -// Purpose: Find the object ID of a directory on the store, or return 0 for not found. -// If pStack != 0, the object is set to the stack of directories. -// Will start from the current directory stack. +// Name: BackupQueries::FindDirectoryObjectID(const +// std::string &) +// Purpose: Find the object ID of a directory on the store, +// or return 0 for not found. If pStack != 0, the +// object is set to the stack of directories. +// Will start from the current directory stack. // Created: 2003/10/10 // // -------------------------------------------------------------------------- -int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion, - bool AllowDeletedDirs, std::vector<std::pair<std::string, int64_t> > *pStack) +int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, + bool AllowOldVersion, bool AllowDeletedDirs, + std::vector<std::pair<std::string, int64_t> > *pStack) { // Split up string into elements std::vector<std::string> dirElements; @@ -747,6 +792,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const if(args.size() != 1 || args[0].size() == 0) { BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>"); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -764,6 +810,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const if(id == 0) { BOX_ERROR("Directory '" << args[0] << "' not found."); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -785,7 +832,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) if(args.size() != 1 || args[0].size() == 0) { BOX_ERROR("Incorrect usage. lcd <local-directory>"); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -795,7 +842,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) { BOX_ERROR("Failed to convert path from console encoding."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } int result = ::chdir(dirName.c_str()); @@ -810,11 +857,11 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) } else { - BOX_ERROR("Error changing to directory '" << - args[0] << ": " << strerror(errno)); + BOX_LOG_SYS_ERROR("Failed to change to directory " + "'" << args[0] << "'"); } - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -822,9 +869,8 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) char wd[PATH_MAX]; if(::getcwd(wd, PATH_MAX) == 0) { - BOX_ERROR("Error getting current directory: " << - strerror(errno)); - SetReturnCode(COMMAND_RETURN_ERROR); + BOX_LOG_SYS_ERROR("Error getting current directory"); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -832,7 +878,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) if(!ConvertUtf8ToConsole(wd, dirName)) { BOX_ERROR("Failed to convert new path from console encoding."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } BOX_INFO("Local current directory is now '" << dirName << "'."); @@ -868,8 +914,8 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const } // Does file exist? - struct stat st; - if(::stat(args[1].c_str(), &st) == 0 || errno != ENOENT) + EMU_STRUCT_STAT st; + if(EMU_STAT(args[1].c_str(), &st) == 0 || errno != ENOENT) { BOX_ERROR("The local file '" << args[1] << " already exists."); return; @@ -907,6 +953,117 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const } +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::FindFileID(const std::string& +// rNameOrIdString, const bool *options, +// int64_t *pDirIdOut, std::string* pFileNameOut) +// Purpose: Locate a file on the store (either by name or by +// object ID, depending on opts['i'], where name can +// include a path) and return the file ID, placing the +// directory ID in *pDirIdOut and the filename part +// of the path (if not looking up by ID and not NULL) +// in *pFileNameOut. +// Created: 2008-09-12 +// +// -------------------------------------------------------------------------- +int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString, + const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut, + int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut) +{ + // Find object ID somehow + int64_t fileId; + int64_t dirId = GetCurrentDirectoryID(); + std::string fileName = rNameOrIdString; + + if(!opts['i']) + { + // does this remote filename include a path? + std::string::size_type index = fileName.rfind('/'); + if(index != std::string::npos) + { + std::string dirName(fileName.substr(0, index)); + fileName = fileName.substr(index + 1); + + dirId = FindDirectoryObjectID(dirName); + if(dirId == 0) + { + BOX_ERROR("Directory '" << dirName << + "' not found."); + return 0; + } + } + + if(pFileNameOut) + { + *pFileNameOut = fileName; + } + } + + BackupStoreFilenameClear fn(fileName); + + // Need to look it up in the current directory + mrConnection.QueryListDirectory( + dirId, flagsInclude, flagsExclude, + true /* do want attributes */); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream()); + dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + BackupStoreDirectory::Entry *en; + + if(opts['i']) + { + // Specified as ID. + fileId = ::strtoll(rNameOrIdString.c_str(), 0, 16); + if(fileId == std::numeric_limits<long long>::min() || + fileId == std::numeric_limits<long long>::max() || + fileId == 0) + { + BOX_ERROR("Not a valid object ID (specified in hex)."); + return 0; + } + + // Check that the item is actually in the directory + en = dir.FindEntryByID(fileId); + if(en == 0) + { + BOX_ERROR("File ID " << + BOX_FORMAT_OBJECTID(fileId) << + " not found in current directory on store.\n" + "(You can only access files by ID from the " + "current directory.)"); + return 0; + } + } + else + { + // Specified by name, find the object in the directory to get the ID + BackupStoreDirectory::Iterator i(dir); + en = i.FindMatchingClearName(fn); + if(en == 0) + { + BOX_ERROR("Filename '" << rNameOrIdString << "' " + "not found in current directory on store.\n" + "(Subdirectories in path not searched.)"); + return 0; + } + + fileId = en->GetObjectID(); + } + + *pDirIdOut = dirId; + + if(pFlagsOut) + { + *pFlagsOut = en->GetFlags(); + } + + return fileId; +} + // -------------------------------------------------------------------------- // @@ -929,120 +1086,72 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) } // Find object ID somehow - int64_t fileId; - int64_t dirId = GetCurrentDirectoryID(); + int64_t fileId, dirId; std::string localName; - // BLOCK - { #ifdef WIN32 - for (std::vector<std::string>::iterator - i = args.begin(); i != args.end(); i++) + for (std::vector<std::string>::iterator + i = args.begin(); i != args.end(); i++) + { + std::string out; + if(!ConvertConsoleToUtf8(i->c_str(), out)) { - std::string out; - if(!ConvertConsoleToUtf8(i->c_str(), out)) - { - BOX_ERROR("Failed to convert encoding."); - return; - } - *i = out; + BOX_ERROR("Failed to convert encoding."); + return; } + *i = out; + } #endif - std::string fileName(args[0]); + int16_t flagsExclude; - if(!opts['i']) - { - // does this remote filename include a path? - std::string::size_type index = fileName.rfind('/'); - if(index != std::string::npos) - { - std::string dirName(fileName.substr(0, index)); - fileName = fileName.substr(index + 1); - - dirId = FindDirectoryObjectID(dirName); - if(dirId == 0) - { - BOX_ERROR("Directory '" << dirName << - "' not found."); - return; - } - } - } + if(opts['i']) + { + // can retrieve anything by ID + flagsExclude = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; + } + else + { + // only current versions by name + flagsExclude = + BackupProtocolClientListDirectory::Flags_OldVersion | + BackupProtocolClientListDirectory::Flags_Deleted; + } - BackupStoreFilenameClear fn(fileName); - // Need to look it up in the current directory - mrConnection.QueryListDirectory( - dirId, - BackupProtocolClientListDirectory::Flags_File, // just files - (opts['i'])?(BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING):(BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted), // only current versions - false /* don't want attributes */); + fileId = FindFileID(args[0], opts, &dirId, &localName, + BackupProtocolClientListDirectory::Flags_File, // just files + flagsExclude, NULL /* don't care about flags found */); - // Retrieve the directory from the stream following - BackupStoreDirectory dir; - std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream()); - dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + if (fileId == 0) + { + // error already reported + return; + } - if(opts['i']) + if(opts['i']) + { + // Specified as ID. Must have a local name in the arguments + // (check at beginning of function ensures this) + localName = args[1]; + } + else + { + // Specified by name. Local name already set by FindFileID, + // but may be overridden by user supplying a second argument. + if(args.size() == 2) { - // Specified as ID. - fileId = ::strtoll(args[0].c_str(), 0, 16); - if(fileId == std::numeric_limits<long long>::min() || - fileId == std::numeric_limits<long long>::max() || - fileId == 0) - { - BOX_ERROR("Not a valid object ID (specified in hex)."); - return; - } - - // Check that the item is actually in the directory - if(dir.FindEntryByID(fileId) == 0) - { - BOX_ERROR("File ID " << - BOX_FORMAT_OBJECTID(fileId) << - " not found in current " - "directory on store.\n" - "(You can only download files by ID " - "from the current directory.)"); - return; - } - - // Must have a local name in the arguments (check at beginning of function ensures this) localName = args[1]; } - else - { - // Specified by name, find the object in the directory to get the ID - BackupStoreDirectory::Iterator i(dir); - BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn); - - if(en == 0) - { - BOX_ERROR("Filename '" << args[0] << "' " - "not found in current " - "directory on store.\n" - "(Subdirectories in path not " - "searched.)"); - return; - } - - fileId = en->GetObjectID(); - - // Local name is the last argument, which is either - // the looked up filename, or a filename specified - // by the user. - localName = args[args.size() - 1]; - } } // Does local file already exist? (don't want to overwrite) - struct stat st; - if(::stat(localName.c_str(), &st) == 0 || errno != ENOENT) + EMU_STRUCT_STAT st; + if(EMU_STAT(localName.c_str(), &st) == 0 || errno != ENOENT) { BOX_ERROR("The local file " << localName << " already exists, " "will not overwrite it."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -1081,7 +1190,6 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) } } - // -------------------------------------------------------------------------- // // Function @@ -1090,58 +1198,17 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) // Created: 29/1/04 // // -------------------------------------------------------------------------- -BackupQueries::CompareParams::CompareParams() - : mQuickCompare(false), - mIgnoreExcludes(false), - mIgnoreAttributes(false), - mDifferences(0), - mDifferencesExplainedByModTime(0), - mUncheckedFiles(0), - mExcludedDirs(0), - mExcludedFiles(0), - mpExcludeFiles(0), - mpExcludeDirs(0), - mLatestFileUploadTime(0) -{ -} - - -// -------------------------------------------------------------------------- -// -// Function -// Name: BackupQueries::CompareParams::~CompareParams() -// Purpose: Destructor -// Created: 29/1/04 -// -// -------------------------------------------------------------------------- -BackupQueries::CompareParams::~CompareParams() -{ - DeleteExcludeLists(); -} - - -// -------------------------------------------------------------------------- -// -// Function -// Name: BackupQueries::CompareParams::DeleteExcludeLists() -// Purpose: Delete the include lists contained -// Created: 29/1/04 -// -// -------------------------------------------------------------------------- -void BackupQueries::CompareParams::DeleteExcludeLists() -{ - if(mpExcludeFiles != 0) - { - delete mpExcludeFiles; - mpExcludeFiles = 0; - } - if(mpExcludeDirs != 0) - { - delete mpExcludeDirs; - mpExcludeDirs = 0; - } -} - +BackupQueries::CompareParams::CompareParams(bool QuickCompare, + bool IgnoreExcludes, bool IgnoreAttributes, + box_time_t LatestFileUploadTime) +: BoxBackupCompareParams(QuickCompare, IgnoreExcludes, IgnoreAttributes, + LatestFileUploadTime), + mDifferences(0), + mDifferencesExplainedByModTime(0), + mUncheckedFiles(0), + mExcludedDirs(0), + mExcludedFiles(0) +{ } // -------------------------------------------------------------------------- // @@ -1153,24 +1220,19 @@ void BackupQueries::CompareParams::DeleteExcludeLists() // -------------------------------------------------------------------------- void BackupQueries::CommandCompare(const std::vector<std::string> &args, const bool *opts) { - // Parameters, including count of differences - BackupQueries::CompareParams params; - params.mQuickCompare = opts['q']; - params.mQuietCompare = opts['Q']; - params.mIgnoreExcludes = opts['E']; - params.mIgnoreAttributes = opts['A']; + box_time_t LatestFileUploadTime = GetCurrentBoxTime(); // Try and work out the time before which all files should be on the server { std::string syncTimeFilename(mrConfiguration.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR); syncTimeFilename += "last_sync_start"; // Stat it to get file time - struct stat st; - if(::stat(syncTimeFilename.c_str(), &st) == 0) + EMU_STRUCT_STAT st; + if(EMU_STAT(syncTimeFilename.c_str(), &st) == 0) { // Files modified after this time shouldn't be on the server, so report errors slightly differently - params.mLatestFileUploadTime = FileModificationTime(st) - - SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge")); + LatestFileUploadTime = FileModificationTime(st) - + SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge")); } else { @@ -1179,8 +1241,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b } } + // Parameters, including count of differences + BackupQueries::CompareParams params(opts['q'], // quick compare? + opts['E'], // ignore excludes + opts['A'], // ignore attributes + LatestFileUploadTime); + + params.mQuietCompare = opts['Q']; + // Quick compare? - if(params.mQuickCompare) + if(params.QuickCompare()) { BOX_WARNING("Quick compare used -- file attributes are not " "checked."); @@ -1189,11 +1259,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b if(!opts['l'] && opts['a'] && args.size() == 0) { // Compare all locations - const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations")); - for(std::list<std::pair<std::string, Configuration> >::const_iterator i = locations.mSubConfigurations.begin(); - i != locations.mSubConfigurations.end(); ++i) + const Configuration &rLocations( + mrConfiguration.GetSubConfiguration("BackupLocations")); + std::vector<std::string> locNames = + rLocations.GetSubConfigurationNames(); + for(std::vector<std::string>::iterator + pLocName = locNames.begin(); + pLocName != locNames.end(); + pLocName++) { - CompareLocation(i->first, params); + CompareLocation(*pLocName, params); } } else if(opts['l'] && !opts['a'] && args.size() == 1) @@ -1206,7 +1281,7 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b // Compare directory to directory // Can't be bothered to do all the hard work to work out which location it's on, and hence which exclude list - if(!params.mIgnoreExcludes) + if(!params.IgnoreExcludes()) { BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes."); return; @@ -1241,15 +1316,15 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b { if (params.mUncheckedFiles != 0) { - SetReturnCode(COMPARE_RETURN_ERROR); + SetReturnCode(ReturnCode::Compare_Error); } else if (params.mDifferences != 0) { - SetReturnCode(COMPARE_RETURN_DIFFERENT); + SetReturnCode(ReturnCode::Compare_Different); } else { - SetReturnCode(COMPARE_RETURN_SAME); + SetReturnCode(ReturnCode::Compare_Same); } } } @@ -1263,7 +1338,8 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b // Created: 2003/10/13 // // -------------------------------------------------------------------------- -void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries::CompareParams &rParams) +void BackupQueries::CompareLocation(const std::string &rLocation, + BoxBackupCompareParams &rParams) { // Find the location's sub configuration const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations")); @@ -1287,45 +1363,36 @@ void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries: } #endif - try - { - // Generate the exclude lists - if(!rParams.mIgnoreExcludes) - { - rParams.mpExcludeFiles = BackupClientMakeExcludeList_Files(loc); - rParams.mpExcludeDirs = BackupClientMakeExcludeList_Dirs(loc); - } - - // Then get it compared - Compare(std::string("/") + rLocation, - loc.GetKeyValue("Path"), rParams); - } - catch(...) + // Generate the exclude lists + if(!rParams.IgnoreExcludes()) { - // Clean up - rParams.DeleteExcludeLists(); - throw; + rParams.LoadExcludeLists(loc); } - - // Delete exclude lists - rParams.DeleteExcludeLists(); + + // Then get it compared + Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams); } // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::Compare(const std::string &, const std::string &, BackupQueries::CompareParams &) +// Name: BackupQueries::Compare(const std::string &, +// const std::string &, BackupQueries::CompareParams &) // Purpose: Compare a store directory against a local directory // Created: 2003/10/13 // // -------------------------------------------------------------------------- -void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams) +void BackupQueries::Compare(const std::string &rStoreDir, + const std::string &rLocalDir, BoxBackupCompareParams &rParams) { #ifdef WIN32 + std::string localDirEncoded; std::string storeDirEncoded; + if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return; if(!ConvertConsoleToUtf8(rStoreDir.c_str(), storeDirEncoded)) return; #else + const std::string& localDirEncoded(rLocalDir); const std::string& storeDirEncoded(rStoreDir); #endif @@ -1335,19 +1402,22 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo // Found? if(dirID == 0) { - BOX_WARNING("Local directory '" << rLocalDir << "' exists, " - "but server directory '" << rStoreDir << "' does not " - "exist."); - rParams.mDifferences ++; + bool modifiedAfterLastSync = false; + + EMU_STRUCT_STAT st; + if(EMU_STAT(rLocalDir.c_str(), &st) == 0) + { + if(FileAttrModificationTime(st) > + rParams.LatestFileUploadTime()) + { + modifiedAfterLastSync = true; + } + } + + rParams.NotifyRemoteFileMissing(localDirEncoded, + storeDirEncoded, modifiedAfterLastSync); return; } - -#ifdef WIN32 - std::string localDirEncoded; - if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return; -#else - std::string localDirEncoded(rLocalDir); -#endif // Go! Compare(dirID, storeDirEncoded, localDirEncoded, rParams); @@ -1363,56 +1433,36 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo // Created: 2003/10/13 // // -------------------------------------------------------------------------- -void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams) +void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, + const std::string &rLocalDir, BoxBackupCompareParams &rParams) { -#ifdef WIN32 - // By this point, rStoreDir and rLocalDir should be in UTF-8 encoding - - std::string localDirDisplay; - std::string storeDirDisplay; - - if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localDirDisplay)) return; - if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeDirDisplay)) return; -#else - const std::string& localDirDisplay(rLocalDir); - const std::string& storeDirDisplay(rStoreDir); -#endif + rParams.NotifyDirComparing(rLocalDir, rStoreDir); // Get info on the local directory - struct stat st; - if(::lstat(rLocalDir.c_str(), &st) != 0) + EMU_STRUCT_STAT st; + if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0) { // What kind of error? - if(errno == ENOTDIR) - { - BOX_WARNING("Local object '" << localDirDisplay << "' " - "is a file, server object '" << - storeDirDisplay << "' is a directory."); - rParams.mDifferences ++; - } - else if(errno == ENOENT) + if(errno == ENOTDIR || errno == ENOENT) { - BOX_WARNING("Local directory '" << localDirDisplay << - "' does not exist (compared to server " - "directory '" << storeDirDisplay << "')."); - rParams.mDifferences ++; + rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir); } else { - BOX_WARNING("Failed to access local directory '" << - localDirDisplay << ": " << strerror(errno) << - "'."); - rParams.mUncheckedFiles ++; + rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir); } return; } // Get the directory listing from the store mrConnection.QueryListDirectory( - DirID, - BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // get everything - BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted, // except for old versions and deleted files - true /* want attributes */); + DirID, + BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, + // get everything + BackupProtocolClientListDirectory::Flags_OldVersion | + BackupProtocolClientListDirectory::Flags_Deleted, + // except for old versions and deleted files + true /* want attributes */); // Retrieve the directory from the stream following BackupStoreDirectory dir; @@ -1422,8 +1472,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Test out the attributes if(!dir.HasAttributes()) { - BOX_WARNING("Store directory '" << storeDirDisplay << "' " - "doesn't have attributes."); + rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir); } else { @@ -1436,12 +1485,27 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s localAttr.ReadAttributes(rLocalDir.c_str(), true /* directories have zero mod times */); - if(!(attr.Compare(localAttr, true, true /* ignore modification times */))) + if(attr.Compare(localAttr, true, true /* ignore modification times */)) { - BOX_WARNING("Local directory '" << localDirDisplay << - "' has different attributes to store " - "directory '" << storeDirDisplay << "'."); - rParams.mDifferences ++; + rParams.NotifyDirCompared(rLocalDir, rStoreDir, + false, false /* actually we didn't check :) */); + } + else + { + bool modifiedAfterLastSync = false; + + EMU_STRUCT_STAT st; + if(EMU_STAT(rLocalDir.c_str(), &st) == 0) + { + if(FileAttrModificationTime(st) > + rParams.LatestFileUploadTime()) + { + modifiedAfterLastSync = true; + } + } + + rParams.NotifyDirCompared(rLocalDir, rStoreDir, + true, modifiedAfterLastSync); } } @@ -1449,11 +1513,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s DIR *dirhandle = ::opendir(rLocalDir.c_str()); if(dirhandle == 0) { - BOX_WARNING("Failed to open local directory '" << - localDirDisplay << "': " << strerror(errno)); - rParams.mUncheckedFiles ++; + rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir); return; } + try { // Read the files and directories into sets @@ -1484,8 +1547,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s #ifndef HAVE_VALID_DIRENT_D_TYPE std::string fn(MakeFullPath (rLocalDir, localDirEn->d_name)); - struct stat st; - if(::lstat(fn.c_str(), &st) != 0) + EMU_STRUCT_STAT st; + if(EMU_LSTAT(fn.c_str(), &st) != 0) { THROW_EXCEPTION(CommonException, OSFileError) } @@ -1518,8 +1581,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Close directory if(::closedir(dirhandle) != 0) { - BOX_ERROR("Failed to close local directory '" << - localDirDisplay << "': " << strerror(errno)); + BOX_LOG_SYS_ERROR("Failed to close local directory " + "'" << rLocalDir << "'"); } dirhandle = 0; @@ -1557,36 +1620,30 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeFiles.begin(); i != storeFiles.end(); ++i) { const std::string& fileName(i->first); -#ifdef WIN32 - // File name is also in UTF-8 encoding, - // need to convert to console - std::string fileNameDisplay; - if(!ConvertUtf8ToConsole(i->first.c_str(), - fileNameDisplay)) return; -#else - const std::string& fileNameDisplay(i->first); -#endif - std::string localPath(MakeFullPath - (rLocalDir, fileName)); - std::string localPathDisplay(MakeFullPath - (localDirDisplay, fileNameDisplay)); - std::string storePathDisplay - (storeDirDisplay + "/" + fileNameDisplay); + std::string localPath(MakeFullPath(rLocalDir, fileName)); + std::string storePath(rStoreDir + "/" + fileName); + rParams.NotifyFileComparing(localPath, storePath); + // Does the file exist locally? string_set_iter_t local(localFiles.find(fileName)); if(local == localFiles.end()) { // Not found -- report - BOX_WARNING("Local file '" << - localPathDisplay << "' does not exist, " - "but store file '" << - storePathDisplay << "' does."); - rParams.mDifferences ++; + rParams.NotifyLocalFileMissing(localPath, + storePath); } 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? @@ -1594,8 +1651,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // File modified after last sync flag bool modifiedAfterLastSync = false; + + bool hasDifferentAttribs = false; - if(rParams.mQuickCompare) + if(rParams.QuickCompare()) { // Compare file -- fetch it mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID()); @@ -1640,7 +1699,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s BackupClientFileAttributes localAttr; box_time_t fileModTime = 0; localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime); - modifiedAfterLastSync = (fileModTime > rParams.mLatestFileUploadTime); + modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime()); bool ignoreAttrModTime = true; #ifdef WIN32 @@ -1649,7 +1708,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s ignoreAttrModTime = false; #endif - if(!rParams.mIgnoreAttributes && + if(!rParams.IgnoreAttributes() && #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE !fileOnServerStream->IsSymLink() && #endif @@ -1657,128 +1716,43 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s ignoreAttrModTime, fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */)) { - BOX_WARNING("Local file '" << - localPathDisplay << - "' has different attributes " - "to store file '" << - storePathDisplay << - "'."); - rParams.mDifferences ++; - if(modifiedAfterLastSync) - { - rParams.mDifferencesExplainedByModTime ++; - BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); - } - else if(i->second->HasAttributes()) - { - BOX_INFO("(the file above has had new attributes applied)\n"); - } + 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()); - - // Size - IOStream::pos_type fileSizeLocal = l.BytesLeftToRead(); - IOStream::pos_type fileSizeServer = 0; - - // Test the contents - char buf1[2048]; - char buf2[2048]; - while(fileOnServerStream->StreamDataLeft() && l.StreamDataLeft()) - { - int size = fileOnServerStream->Read(buf1, sizeof(buf1), mrConnection.GetTimeout()); - fileSizeServer += size; - - if(l.Read(buf2, size) != size - || ::memcmp(buf1, buf2, size) != 0) - { - equal = false; - break; - } - } - - // Check read all the data from the server and file -- can't be equal if local and remote aren't the same length - // Can't use StreamDataLeft() test on file, because if it's the same size, it won't know - // it's EOF yet. - if(fileOnServerStream->StreamDataLeft() || fileSizeServer != fileSizeLocal) - { - equal = false; - } - - // Must always read the entire decoded stream, if it's not a symlink - if(fileOnServerStream->StreamDataLeft()) - { - // Absorb all the data remaining - char buffer[2048]; - while(fileOnServerStream->StreamDataLeft()) - { - fileOnServerStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout()); - } - } - - // Must always read the entire encoded stream - if(objectStream->StreamDataLeft()) - { - // Absorb all the data remaining - char buffer[2048]; - while(objectStream->StreamDataLeft()) - { - objectStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout()); - } - } + equal = l.CompareWith(*fileOnServerStream, + mrConnection.GetTimeout()); } } - // Report if not equal. - if(!equal) - { - BOX_WARNING("Local file '" << - localPathDisplay << "' " - "has different contents " - "to store file '" << - storePathDisplay << - "'."); - rParams.mDifferences ++; - if(modifiedAfterLastSync) - { - rParams.mDifferencesExplainedByModTime ++; - BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); - } - else if(i->second->HasAttributes()) - { - BOX_INFO("(the file above has had new attributes applied)\n"); - } - } + rParams.NotifyFileCompared(localPath, + storePath, fileSize, + hasDifferentAttribs, !equal, + modifiedAfterLastSync, + i->second->HasAttributes()); } catch(BoxException &e) { - BOX_ERROR("Failed to fetch and compare " - "'" << - storePathDisplay.c_str() << - "': error " << e.what() << - " (" << e.GetType() << - "/" << e.GetSubType() << ")"); - rParams.mUncheckedFiles ++; + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize, e); } catch(std::exception &e) { - BOX_ERROR("Failed to fetch and compare " - "'" << - storePathDisplay.c_str() << - "': " << e.what()); + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize, e); } catch(...) { - BOX_ERROR("Failed to fetch and compare " - "'" << - storePathDisplay.c_str() << - "': unknown error"); - rParams.mUncheckedFiles ++; + rParams.NotifyDownloadFailed(localPath, + storePath, fileSize); } // Remove from set so that we know it's been compared @@ -1786,53 +1760,34 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s } } - // Report any files which exist on the locally, but not on the store + // Report any files which exist locally, but not on the store for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i) { -#ifdef WIN32 - // File name is also in UTF-8 encoding, - // need to convert to console - std::string fileNameDisplay; - if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay)) - return; -#else - const std::string& fileNameDisplay(*i); -#endif - - std::string localPath(MakeFullPath - (rLocalDir, *i)); - std::string localPathDisplay(MakeFullPath - (localDirDisplay, fileNameDisplay)); - std::string storePathDisplay - (storeDirDisplay + "/" + fileNameDisplay); + std::string localPath(MakeFullPath(rLocalDir, *i)); + std::string storePath(rStoreDir + "/" + *i); // Should this be ignored (ie is excluded)? - if(rParams.mpExcludeFiles == 0 || - !(rParams.mpExcludeFiles->IsExcluded(localPath))) + if(!rParams.IsExcludedFile(localPath)) { - BOX_WARNING("Local file '" << - localPathDisplay << - "' exists, but store file '" << - storePathDisplay << - "' does not."); - rParams.mDifferences ++; + bool modifiedAfterLastSync = false; - // Check the file modification time + EMU_STRUCT_STAT st; + if(EMU_STAT(localPath.c_str(), &st) == 0) { - struct stat st; - if(::stat(localPath.c_str(), &st) == 0) + if(FileModificationTime(st) > + rParams.LatestFileUploadTime()) { - if(FileModificationTime(st) > rParams.mLatestFileUploadTime) - { - rParams.mDifferencesExplainedByModTime ++; - BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); - } + modifiedAfterLastSync = true; } } + + rParams.NotifyRemoteFileMissing(localPath, + storePath, modifiedAfterLastSync); } else { - rParams.mExcludedFiles ++; + rParams.NotifyExcludedFile(localPath, + storePath); } } @@ -1843,99 +1798,69 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Now do the directories, recursively to check subdirectories for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i) { -#ifdef WIN32 - // Directory name is also in UTF-8 encoding, - // need to convert to console - std::string subdirNameDisplay; - if(!ConvertUtf8ToConsole(i->first.c_str(), - subdirNameDisplay)) - return; -#else - const std::string& subdirNameDisplay(i->first); -#endif - - std::string localPath(MakeFullPath - (rLocalDir, i->first)); - std::string localPathDisplay(MakeFullPath - (localDirDisplay, subdirNameDisplay)); - std::string storePathDisplay - (storeDirDisplay + "/" + subdirNameDisplay); + std::string localPath(MakeFullPath(rLocalDir, i->first)); + std::string storePath(rLocalDir + "/" + i->first); // Does the directory exist locally? string_set_iter_t local(localDirs.find(i->first)); if(local == localDirs.end() && - rParams.mpExcludeDirs != NULL && - rParams.mpExcludeDirs->IsExcluded(localPath)) + rParams.IsExcludedDir(localPath)) { - // Not found -- report - BOX_WARNING("Local directory '" << - localPathDisplay << "' is excluded, " - "but store directory '" << - storePathDisplay << "' still exists."); - rParams.mDifferences ++; + rParams.NotifyExcludedFileNotDeleted(localPath, + storePath); } else if(local == localDirs.end()) { // Not found -- report - BOX_WARNING("Local directory '" << - localPathDisplay << "' does not exist, " - "but store directory '" << - storePathDisplay << "' does."); - rParams.mDifferences ++; + rParams.NotifyRemoteFileMissing(localPath, + storePath, false); } - else if(rParams.mpExcludeDirs != NULL && - rParams.mpExcludeDirs->IsExcluded(localPath)) + else if(rParams.IsExcludedDir(localPath)) { // don't recurse into excluded directories } else { // Compare directory - Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, localPath, rParams); + Compare(i->second->GetObjectID(), + rStoreDir + "/" + i->first, + localPath, rParams); // Remove from set so that we know it's been compared localDirs.erase(local); } } - // Report any files which exist on the locally, but not on the store - for(std::set<std::string>::const_iterator i = localDirs.begin(); i != localDirs.end(); ++i) + // Report any directories which exist locally, but not on the store + for(std::set<std::string>::const_iterator + i = localDirs.begin(); + i != localDirs.end(); ++i) { -#ifdef WIN32 - // File name is also in UTF-8 encoding, - // need to convert to console - std::string fileNameDisplay; - if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay)) - return; -#else - const std::string& fileNameDisplay(*i); -#endif - - std::string localPath(MakeFullPath - (rLocalDir, *i)); - std::string localPathDisplay(MakeFullPath - (localDirDisplay, fileNameDisplay)); - - std::string storePath - (rStoreDir + "/" + *i); - std::string storePathDisplay - (storeDirDisplay + "/" + fileNameDisplay); + std::string localPath(MakeFullPath(rLocalDir, *i)); + std::string storePath(rStoreDir + "/" + *i); // Should this be ignored (ie is excluded)? - if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localPath))) + if(!rParams.IsExcludedDir(localPath)) { - BOX_WARNING("Local directory '" << - localPathDisplay << "' exists, but " - "store directory '" << - storePathDisplay << "' does not."); - rParams.mDifferences ++; + bool modifiedAfterLastSync = false; + + // Check the dir modification time + EMU_STRUCT_STAT st; + if(EMU_STAT(localPath.c_str(), &st) == 0 && + FileModificationTime(st) > + rParams.LatestFileUploadTime()) + { + modifiedAfterLastSync = true; + } + + rParams.NotifyRemoteFileMissing(localPath, + storePath, modifiedAfterLastSync); } else { - rParams.mExcludedDirs ++; + rParams.NotifyExcludedDir(localPath, storePath); } } - } catch(...) { @@ -1943,6 +1868,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s { ::closedir(dirhandle); } + throw; } } @@ -1960,7 +1886,7 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b // Check arguments if(args.size() != 2) { - BOX_ERROR("Incorrect usage. restore [-d] [-r] [-i] <remote-name> <local-name>"); + BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> <local-name>"); return; } @@ -2023,18 +1949,19 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b localName.c_str(), true /* print progress dots */, restoreDeleted, false /* don't undelete after restore! */, - opts['r'] /* resume? */); + opts['r'] /* resume? */, + opts['f'] /* force continue after errors */); } catch(std::exception &e) { BOX_ERROR("Failed to restore: " << e.what()); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } catch(...) { BOX_ERROR("Failed to restore: unknown exception"); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); return; } @@ -2044,33 +1971,38 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b BOX_INFO("Restore complete."); break; + case Restore_CompleteWithErrors: + BOX_WARNING("Restore complete, but some files could not be " + "restored."); + break; + case Restore_ResumePossible: - BOX_ERROR("Resume possible -- repeat command with -r flag to resume"); - SetReturnCode(COMMAND_RETURN_ERROR); + BOX_ERROR("Resume possible -- repeat command with -r flag " + "to resume."); + SetReturnCode(ReturnCode::Command_Error); break; case Restore_TargetExists: - BOX_ERROR("The target directory exists. You cannot restore over an existing directory."); - SetReturnCode(COMMAND_RETURN_ERROR); + BOX_ERROR("The target directory exists. You cannot restore " + "over an existing directory."); + SetReturnCode(ReturnCode::Command_Error); break; - #ifdef WIN32 case Restore_TargetPathNotFound: BOX_ERROR("The target directory path does not exist.\n" "To restore to a directory whose parent " "does not exist, create the parent first."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); break; - #endif case Restore_UnknownError: BOX_ERROR("Unknown error during restore."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); break; default: BOX_ERROR("Unknown restore result " << result << "."); - SetReturnCode(COMMAND_RETURN_ERROR); + SetReturnCode(ReturnCode::Command_Error); break; } } @@ -2131,49 +2063,46 @@ void BackupQueries::CommandHelp(const std::vector<std::string> &args) // Created: 19/4/04 // // -------------------------------------------------------------------------- -void BackupQueries::CommandUsage() +void BackupQueries::CommandUsage(const bool *opts) { + bool MachineReadable = opts['m']; + // Request full details from the server std::auto_ptr<BackupProtocolClientAccountUsage> usage(mrConnection.QueryGetAccountUsage()); // Display each entry in turn int64_t hardLimit = usage->GetBlocksHardLimit(); int32_t blockSize = usage->GetBlockSize(); - CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit, blockSize); - CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(), hardLimit, blockSize); - CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(), hardLimit, blockSize); - CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(), hardLimit, blockSize); - CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(), hardLimit, blockSize); - CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize); + CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit, + blockSize, MachineReadable); + CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(), + hardLimit, blockSize, MachineReadable); + CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(), + hardLimit, blockSize, MachineReadable); + CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(), + hardLimit, blockSize, MachineReadable); + CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(), + hardLimit, blockSize, MachineReadable); + CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize, + MachineReadable); } // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::CommandUsageDisplayEntry(const char *, int64_t, int64_t, int32_t) +// Name: BackupQueries::CommandUsageDisplayEntry(const char *, +// int64_t, int64_t, int32_t, bool) // Purpose: Display an entry in the usage table // Created: 19/4/04 // // -------------------------------------------------------------------------- -void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize) +void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, +int64_t HardLimit, int32_t BlockSize, bool MachineReadable) { - // Calculate size in Mb - double mb = (((double)Size) * ((double)BlockSize)) / ((double)(1024*1024)); - int64_t percent = (Size * 100) / HardLimit; - - // Bar graph - char bar[41]; - unsigned int b = (int)((Size * (sizeof(bar)-1)) / HardLimit); - if(b > sizeof(bar)-1) {b = sizeof(bar)-1;} - for(unsigned int l = 0; l < b; l++) - { - bar[l] = '*'; - } - bar[b] = '\0'; - - // Print the entryj - ::printf("%14s %10.1fMb %3d%% %s\n", Name, mb, (int32_t)percent, bar); + std::cout << FormatUsageLineStart(Name, MachineReadable) << + FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize, + MachineReadable) << std::endl; } @@ -2187,10 +2116,17 @@ void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int // -------------------------------------------------------------------------- void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const bool *opts) { + if (!mReadWrite) + { + BOX_ERROR("This command requires a read-write connection. " + "Please reconnect with the -w option."); + return; + } + // Check arguments if(args.size() != 1) { - BOX_ERROR("Incorrect usage. undelete <directory-name>"); + BOX_ERROR("Incorrect usage. undelete <name> or undelete -i <object-id>"); return; } @@ -2200,23 +2136,133 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const #else const std::string& storeDirEncoded(args[0]); #endif - - // Get directory ID - int64_t dirID = FindDirectoryObjectID(storeDirEncoded, - false /* no old versions */, true /* find deleted dirs */); - - // Allowable? - if(dirID == 0) + + // Find object ID somehow + int64_t fileId, parentId; + std::string fileName; + int16_t flagsOut; + + fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName, + /* include files and directories */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + /* include old and deleted files */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + &flagsOut); + + if (fileId == 0) { - BOX_ERROR("Directory '" << args[0] << "' not found on server."); + // error already reported return; } - if(dirID == BackupProtocolClientListDirectory::RootDirectory) + + // Undelete it on the store + try + { + // Undelete object + if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + { + mrConnection.QueryUndeleteFile(parentId, fileId); + } + else + { + mrConnection.QueryUndeleteDirectory(fileId); + } + } + catch (BoxException &e) + { + BOX_ERROR("Failed to undelete object: " << + e.what()); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to undelete object: " << + e.what()); + } + catch(...) + { + BOX_ERROR("Failed to undelete object: unknown error"); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::CommandDelete(const +// std::vector<std::string> &, const bool *) +// Purpose: Deletes a file +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandDelete(const std::vector<std::string> &args, + const bool *opts) +{ + if (!mReadWrite) + { + BOX_ERROR("This command requires a read-write connection. " + "Please reconnect with the -w option."); + return; + } + + // Check arguments + if(args.size() != 1) { - BOX_ERROR("Cannot undelete the root directory."); + BOX_ERROR("Incorrect usage. delete <name>"); return; } - // Undelete - mrConnection.QueryUndeleteDirectory(dirID); +#ifdef WIN32 + std::string storeDirEncoded; + if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return; +#else + const std::string& storeDirEncoded(args[0]); +#endif + + // Find object ID somehow + int64_t fileId, parentId; + std::string fileName; + int16_t flagsOut; + + fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName, + /* include files and directories */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + /* exclude old and deleted files */ + BackupProtocolClientListDirectory::Flags_OldVersion | + BackupProtocolClientListDirectory::Flags_Deleted, + &flagsOut); + + if (fileId == 0) + { + // error already reported + return; + } + + BackupStoreFilenameClear fn(fileName); + + // Delete it on the store + try + { + // Delete object + if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + { + mrConnection.QueryDeleteFile(parentId, fn); + } + else + { + mrConnection.QueryDeleteDirectory(fileId); + } + } + catch (BoxException &e) + { + BOX_ERROR("Failed to delete object: " << + e.what()); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to delete object: " << + e.what()); + } + catch(...) + { + BOX_ERROR("Failed to delete object: unknown error"); + } } |