diff options
Diffstat (limited to 'bin/bbackupquery/BackupQueries.cpp')
-rw-r--r-- | bin/bbackupquery/BackupQueries.cpp | 720 |
1 files changed, 537 insertions, 183 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index d254ba9c..ee650b9c 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -1,4 +1,4 @@ -// distribution boxbackup-0.10 (svn version: 494) +// distribution boxbackup-0.11rc1 (svn version: 2023_2024) // // Copyright (c) 2003 - 2006 // Ben Summers and contributors. All rights reserved. @@ -64,6 +64,7 @@ #endif #include <set> +#include <limits> #include "BackupQueries.h" #include "Utils.h" @@ -83,13 +84,19 @@ #include "BackupStoreException.h" #include "ExcludeList.h" #include "BackupClientMakeExcludeList.h" +#include "PathUtils.h" +#include "Logging.h" #include "MemLeakFindOn.h" -#define COMPARE_RETURN_SAME 1 +// min() and max() macros from stdlib.h break numeric_limits<>::min(), etc. +#undef min +#undef max + +#define COMPARE_RETURN_SAME 1 #define COMPARE_RETURN_DIFFERENT 2 #define COMPARE_RETURN_ERROR 3 - +#define COMMAND_RETURN_ERROR 4 // -------------------------------------------------------------------------- // @@ -107,7 +114,11 @@ BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configurat mWarnedAboutOwnerAttributes(false), mReturnCode(0) // default return code { + #ifdef WIN32 + mRunningAsRoot = TRUE; + #else mRunningAsRoot = (::geteuid() == 0); + #endif } // -------------------------------------------------------------------------- @@ -122,15 +133,21 @@ BackupQueries::~BackupQueries() { } +typedef struct +{ + const char* name; + const char* opts; +} QueryCommandSpecification; + // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::DoCommand(const char *) +// Name: BackupQueries::DoCommand(const char *, bool) // Purpose: Perform a command // Created: 2003/10/10 // // -------------------------------------------------------------------------- -void BackupQueries::DoCommand(const char *Command) +void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) { // is the command a shell command? if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') @@ -191,6 +208,25 @@ void BackupQueries::DoCommand(const char *Command) if(!s.empty()) cmdElements.push_back(s); } + #ifdef WIN32 + if (isFromCommandLine) + { + for (std::vector<std::string>::iterator + i = cmdElements.begin(); + i != cmdElements.end(); i++) + { + std::string converted; + if (!ConvertEncoding(*i, CP_ACP, converted, + GetConsoleCP())) + { + BOX_ERROR("Failed to convert encoding"); + return; + } + *i = converted; + } + } + #endif + // Check... if(cmdElements.size() < 1) { @@ -199,8 +235,24 @@ void BackupQueries::DoCommand(const char *Command) } // Data about commands - static const char *commandNames[] = {"quit", "exit", "list", "pwd", "cd", "lcd", "sh", "getobject", "get", "compare", "restore", "help", "usage", "undelete", 0}; - static const char *validOptions[] = {"", "", "rodIFtsh", "", "od", "", "", "", "i", "alcqE", "dri", "", "", "", 0}; + static QueryCommandSpecification commands[] = + { + { "quit", "" }, + { "exit", "" }, + { "list", "rodIFtTsh", }, + { "pwd", "" }, + { "cd", "od" }, + { "lcd", "" }, + { "sh", "" }, + { "getobject", "" }, + { "get", "i" }, + { "compare", "alcqAEQ" }, + { "restore", "dri" }, + { "help", "" }, + { "usage", "" }, + { "undelete", "" }, + { NULL, NULL } + }; #define COMMAND_Quit 0 #define COMMAND_Exit 1 #define COMMAND_List 2 @@ -220,11 +272,11 @@ void BackupQueries::DoCommand(const char *Command) // Work out which command it is... int cmd = 0; - while(commandNames[cmd] != 0 && ::strcmp(cmdElements[0].c_str(), commandNames[cmd]) != 0) + while(commands[cmd].name != 0 && ::strcmp(cmdElements[0].c_str(), commands[cmd].name) != 0) { cmd++; } - if(commandNames[cmd] == 0) + if(commands[cmd].name == 0) { // Check for aliases int a; @@ -241,7 +293,7 @@ void BackupQueries::DoCommand(const char *Command) // No such command if(alias[a] == 0) { - printf("Unrecognised command: %s\n", Command); + BOX_ERROR("Unrecognised command: " << Command); return; } } @@ -259,9 +311,10 @@ void BackupQueries::DoCommand(const char *Command) while(*c != 0) { // Valid option? - if(::strchr(validOptions[cmd], *c) == NULL) + if(::strchr(commands[cmd].opts, *c) == NULL) { - printf("Invalid option '%c' for command %s\n", *c, commandNames[cmd]); + BOX_ERROR("Invalid option '" << *c << "' for " + "command " << commands[cmd].name); return; } opts[(int)*c] = true; @@ -290,9 +343,8 @@ void BackupQueries::DoCommand(const char *Command) case COMMAND_pwd: { // Simple implementation, so do it here - printf("%s (%08llx)\n", - GetCurrentDirectoryName().c_str(), - (long long)GetCurrentDirectoryID()); + BOX_INFO(GetCurrentDirectoryName() << " (" << + BOX_FORMAT_OBJECTID(GetCurrentDirectoryID())); } break; @@ -305,7 +357,7 @@ void BackupQueries::DoCommand(const char *Command) break; case COMMAND_sh: - printf("The command to run must be specified as an argument.\n"); + BOX_ERROR("The command to run must be specified as an argument."); break; case COMMAND_GetObject: @@ -356,8 +408,9 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool #define LIST_OPTION_ALLOWOLD 'o' #define LIST_OPTION_ALLOWDELETED 'd' #define LIST_OPTION_NOOBJECTID 'I' - #define LIST_OPTION_NOFLAGS 'F' - #define LIST_OPTION_TIMES 't' + #define LIST_OPTION_NOFLAGS 'F' + #define LIST_OPTION_TIMES_LOCAL 't' + #define LIST_OPTION_TIMES_UTC 'T' #define LIST_OPTION_SIZEINBLOCKS 's' #define LIST_OPTION_DISPLAY_HASH 'h' @@ -385,8 +438,8 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool if(rootDir == 0) { - printf("Directory '%s' not found on store\n", - args[0].c_str()); + BOX_ERROR("Directory '" << args[0] << "' not found " + "on store."); return; } } @@ -399,7 +452,7 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::CommandList2(int64_t, const std::string &, const bool *) +// Name: BackupQueries::List(int64_t, const std::string &, const bool *, bool) // Purpose: Do the actual listing of directories and files // Created: 2003/10/10 // @@ -474,11 +527,19 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool } } - if(opts[LIST_OPTION_TIMES]) + if(opts[LIST_OPTION_TIMES_UTC]) + { + // Show UTC times... + std::string time = BoxTimeToISO8601String( + en->GetModificationTime(), false); + printf("%s ", time.c_str()); + } + + if(opts[LIST_OPTION_TIMES_LOCAL]) { - // Show times... + // Show local times... std::string time = BoxTimeToISO8601String( - en->GetModificationTime()); + en->GetModificationTime(), true); printf("%s ", time.c_str()); } @@ -723,7 +784,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const { if(args.size() != 1 || args[0].size() == 0) { - printf("Incorrect usage.\ncd [-o] [-d] <directory>\n"); + BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>"); return; } @@ -740,7 +801,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const if(id == 0) { - printf("Directory '%s' not found\n", args[0].c_str()); + BOX_ERROR("Directory '" << args[0] << "' not found."); return; } @@ -761,22 +822,37 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) { if(args.size() != 1 || args[0].size() == 0) { - printf("Incorrect usage.\nlcd <local-directory>\n"); + BOX_ERROR("Incorrect usage. lcd <local-directory>"); + SetReturnCode(COMMAND_RETURN_ERROR); return; } // Try changing directory #ifdef WIN32 std::string dirName; - if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) return; + if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) + { + BOX_ERROR("Failed to convert path from console encoding."); + SetReturnCode(COMMAND_RETURN_ERROR); + return; + } int result = ::chdir(dirName.c_str()); #else int result = ::chdir(args[0].c_str()); #endif if(result != 0) { - printf((errno == ENOENT || errno == ENOTDIR)?"Directory '%s' does not exist\n":"Error changing dir to '%s'\n", - args[0].c_str()); + if(errno == ENOENT || errno == ENOTDIR) + { + BOX_ERROR("Directory '" << args[0] << "' does not exist."); + } + else + { + BOX_ERROR("Error changing to directory '" << + args[0] << ": " << strerror(errno)); + } + + SetReturnCode(COMMAND_RETURN_ERROR); return; } @@ -784,15 +860,22 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args) char wd[PATH_MAX]; if(::getcwd(wd, PATH_MAX) == 0) { - printf("Error getting current directory\n"); + BOX_ERROR("Error getting current directory: " << + strerror(errno)); + SetReturnCode(COMMAND_RETURN_ERROR); return; } #ifdef WIN32 - if(!ConvertUtf8ToConsole(wd, dirName)) return; - printf("Local current directory is now '%s'\n", dirName.c_str()); + if(!ConvertUtf8ToConsole(wd, dirName)) + { + BOX_ERROR("Failed to convert new path from console encoding."); + SetReturnCode(COMMAND_RETURN_ERROR); + return; + } + BOX_INFO("Local current directory is now '" << dirName << "'."); #else - printf("Local current directory is now '%s'\n", wd); + BOX_INFO("Local current directory is now '" << wd << "'."); #endif } @@ -810,14 +893,15 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const // Check args if(args.size() != 2) { - printf("Incorrect usage.\ngetobject <object-id> <local-filename>\n"); + BOX_ERROR("Incorrect usage. getobject <object-id> " + "<local-filename>"); return; } int64_t id = ::strtoll(args[0].c_str(), 0, 16); - if(id == LLONG_MIN || id == LLONG_MAX || id == 0) + if(id == std::numeric_limits<long long>::min() || id == std::numeric_limits<long long>::max() || id == 0) { - printf("Not a valid object ID (specified in hex)\n"); + BOX_ERROR("Not a valid object ID (specified in hex)."); return; } @@ -825,7 +909,7 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const struct stat st; if(::stat(args[1].c_str(), &st) == 0 || errno != ENOENT) { - printf("The local file %s already exists\n", args[1].c_str()); + BOX_ERROR("The local file '" << args[1] << " already exists."); return; } @@ -843,18 +927,20 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream()); objectStream->CopyStreamTo(out); - printf("Object ID %08llx fetched successfully.\n", id); + BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(id) << + " fetched successfully."); } else { - printf("Object does not exist on store.\n"); + BOX_ERROR("Object ID " << BOX_FORMAT_OBJECTID(id) << + " does not exist on store."); ::unlink(args[1].c_str()); } } catch(...) { ::unlink(args[1].c_str()); - printf("Error occured fetching object.\n"); + BOX_ERROR("Error occured fetching object."); } } @@ -868,26 +954,65 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const // Created: 2003/10/12 // // -------------------------------------------------------------------------- -void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool *opts) +void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) { // At least one argument? // Check args if(args.size() < 1 || (opts['i'] && args.size() != 2) || args.size() > 2) { - printf("Incorrect usage.\n" + BOX_ERROR("Incorrect usage.\n" "get <remote-filename> [<local-filename>] or\n" - "get -i <object-id> <local-filename>\n"); + "get -i <object-id> <local-filename>"); return; } // Find object ID somehow - int64_t id; + int64_t fileId; + int64_t dirId = GetCurrentDirectoryID(); std::string localName; + // BLOCK { +#ifdef WIN32 + for (std::vector<std::string>::iterator + i = args.begin(); i != args.end(); i++) + { + std::string out; + if(!ConvertConsoleToUtf8(i->c_str(), out)) + { + BOX_ERROR("Failed to convert encoding."); + return; + } + *i = out; + } +#endif + + std::string fileName(args[0]); + + 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; + } + } + } + + BackupStoreFilenameClear fn(fileName); + // Need to look it up in the current directory mrConnection.QueryListDirectory( - GetCurrentDirectoryID(), + 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 */); @@ -900,17 +1025,24 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool if(opts['i']) { // Specified as ID. - id = ::strtoll(args[0].c_str(), 0, 16); - if(id == LLONG_MIN || id == LLONG_MAX || id == 0) + 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) { - printf("Not a valid object ID (specified in hex)\n"); + BOX_ERROR("Not a valid object ID (specified in hex)."); return; } // Check that the item is actually in the directory - if(dir.FindEntryByID(id) == 0) + if(dir.FindEntryByID(fileId) == 0) { - printf("ID '%08llx' not found in current directory on store.\n(You can only download objects by ID from the current directory.)\n", id); + 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; } @@ -921,26 +1053,23 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool { // Specified by name, find the object in the directory to get the ID BackupStoreDirectory::Iterator i(dir); -#ifdef WIN32 - std::string fileName; - if(!ConvertConsoleToUtf8(args[0].c_str(), fileName)) - return; - BackupStoreFilenameClear fn(fileName); -#else - BackupStoreFilenameClear fn(args[0]); -#endif BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn); if(en == 0) { - printf("Filename '%s' not found in current directory on store.\n(Subdirectories in path not searched.)\n", args[0].c_str()); + BOX_ERROR("Filename '" << args[0] << "' " + "not found in current " + "directory on store.\n" + "(Subdirectories in path not " + "searched.)"); return; } - id = en->GetObjectID(); + fileId = en->GetObjectID(); - // Local name is the last argument, which is either the looked up filename, or - // a filename specified by the user. + // 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]; } } @@ -949,7 +1078,9 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool struct stat st; if(::stat(localName.c_str(), &st) == 0 || errno != ENOENT) { - printf("The local file %s already exists, will not overwrite it.\n", localName.c_str()); + BOX_ERROR("The local file " << localName << " already exists, " + "will not overwrite it."); + SetReturnCode(COMMAND_RETURN_ERROR); return; } @@ -957,7 +1088,7 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool try { // Request object - mrConnection.QueryGetFile(GetCurrentDirectoryID(), id); + mrConnection.QueryGetFile(dirId, fileId); // Stream containing encoded file std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream()); @@ -966,12 +1097,25 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool BackupStoreFile::DecodeFile(*objectStream, localName.c_str(), mrConnection.GetTimeout()); // Done. - printf("Object ID %08llx fetched sucessfully.\n", id); + BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(fileId) << + " fetched successfully."); + } + catch (BoxException &e) + { + BOX_ERROR("Failed to fetch file: " << + e.what()); + ::unlink(localName.c_str()); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to fetch file: " << + e.what()); + ::unlink(localName.c_str()); } catch(...) { + BOX_ERROR("Failed to fetch file: unknown error"); ::unlink(localName.c_str()); - printf("Error occured fetching file.\n"); } } @@ -987,8 +1131,10 @@ void BackupQueries::CommandGet(const std::vector<std::string> &args, const bool BackupQueries::CompareParams::CompareParams() : mQuickCompare(false), mIgnoreExcludes(false), + mIgnoreAttributes(false), mDifferences(0), mDifferencesExplainedByModTime(0), + mUncheckedFiles(0), mExcludedDirs(0), mExcludedFiles(0), mpExcludeFiles(0), @@ -1048,7 +1194,9 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b // Parameters, including count of differences BackupQueries::CompareParams params; params.mQuickCompare = opts['q']; + params.mQuietCompare = opts['Q']; params.mIgnoreExcludes = opts['E']; + params.mIgnoreAttributes = opts['A']; // Try and work out the time before which all files should be on the server { @@ -1064,14 +1212,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b } else { - printf("Warning: couldn't determine the time of the last synchronisation -- checks not performed.\n"); + BOX_WARNING("Failed to determine the time of the last " + "synchronisation -- checks not performed."); } } // Quick compare? if(params.mQuickCompare) { - printf("WARNING: Quick compare used -- file attributes are not checked.\n"); + BOX_WARNING("Quick compare used -- file attributes are not " + "checked."); } if(!opts['l'] && opts['a'] && args.size() == 0) @@ -1096,7 +1246,7 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b // 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) { - printf("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes\n"); + BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes."); return; } else @@ -1107,17 +1257,38 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b } else { - printf("Incorrect usage.\ncompare -a\n or compare -l <location-name>\n or compare <store-dir-name> <local-dir-name>\n"); + BOX_ERROR("Incorrect usage.\ncompare -a\n or compare -l <location-name>\n or compare <store-dir-name> <local-dir-name>"); return; } - - printf("\n[ %d (of %d) differences probably due to file modifications after the last upload ]\nDifferences: %d (%d dirs excluded, %d files excluded)\n", - params.mDifferencesExplainedByModTime, params.mDifferences, params.mDifferences, params.mExcludedDirs, params.mExcludedFiles); + + if (!params.mQuietCompare) + { + BOX_INFO("[ " << + params.mDifferencesExplainedByModTime << " (of " << + params.mDifferences << ") differences probably " + "due to file modifications after the last upload ]"); + } + + BOX_INFO("Differences: " << params.mDifferences << " (" << + params.mExcludedDirs << " dirs excluded, " << + params.mExcludedFiles << " files excluded, " << + params.mUncheckedFiles << " files not checked)"); // Set return code? if(opts['c']) { - SetReturnCode((params.mDifferences == 0)?COMPARE_RETURN_SAME:COMPARE_RETURN_DIFFERENT); + if (params.mUncheckedFiles != 0) + { + SetReturnCode(COMPARE_RETURN_ERROR); + } + else if (params.mDifferences != 0) + { + SetReturnCode(COMPARE_RETURN_DIFFERENT); + } + else + { + SetReturnCode(COMPARE_RETURN_SAME); + } } } @@ -1136,10 +1307,23 @@ void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries: const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations")); if(!locations.SubConfigurationExists(rLocation.c_str())) { - printf("Location %s does not exist.\n", rLocation.c_str()); + BOX_ERROR("Location " << rLocation << " does not exist."); return; } const Configuration &loc(locations.GetSubConfiguration(rLocation.c_str())); + + #ifdef WIN32 + { + std::string path = loc.GetKeyValue("Path"); + if (path.size() > 0 && path[path.size()-1] == + DIRECTORY_SEPARATOR_ASCHAR) + { + BOX_WARNING("Location '" << rLocation << "' path ends " + "with '" DIRECTORY_SEPARATOR "', " + "compare may fail!"); + } + } + #endif try { @@ -1189,9 +1373,9 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo // Found? if(dirID == 0) { - printf("Local directory '%s' exists, but " - "server directory '%s' does not exist\n", - rLocalDir.c_str(), rStoreDir.c_str()); + BOX_WARNING("Local directory '" << rLocalDir << "' exists, " + "but server directory '" << rStoreDir << "' does not " + "exist."); rParams.mDifferences ++; return; } @@ -1222,14 +1406,14 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s #ifdef WIN32 // By this point, rStoreDir and rLocalDir should be in UTF-8 encoding - std::string localName; - std::string storeName; + std::string localDirDisplay; + std::string storeDirDisplay; - if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localName)) return; - if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeName)) return; + if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localDirDisplay)) return; + if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeDirDisplay)) return; #else - const std::string& localName(rLocalDir); - const std::string& storeName(rStoreDir); + const std::string& localDirDisplay(rLocalDir); + const std::string& storeDirDisplay(rStoreDir); #endif // Get info on the local directory @@ -1239,21 +1423,24 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // What kind of error? if(errno == ENOTDIR) { - printf("Local object '%s' is a file, " - "server object '%s' is a directory\n", - localName.c_str(), storeName.c_str()); + BOX_WARNING("Local object '" << localDirDisplay << "' " + "is a file, server object '" << + storeDirDisplay << "' is a directory."); rParams.mDifferences ++; } else if(errno == ENOENT) { - printf("Local directory '%s' does not exist " - "(compared to server directory '%s')\n", - localName.c_str(), storeName.c_str()); + BOX_WARNING("Local directory '" << localDirDisplay << + "' does not exist (compared to server " + "directory '" << storeDirDisplay << "')."); + rParams.mDifferences ++; } else { - printf("ERROR: stat on local dir '%s'\n", - localName.c_str()); + BOX_WARNING("Failed to access local directory '" << + localDirDisplay << ": " << strerror(errno) << + "'."); + rParams.mUncheckedFiles ++; } return; } @@ -1273,8 +1460,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Test out the attributes if(!dir.HasAttributes()) { - printf("Store directory '%s' doesn't have attributes.\n", - storeName.c_str()); + BOX_WARNING("Store directory '" << storeDirDisplay << "' " + "doesn't have attributes."); } else { @@ -1289,9 +1476,9 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s if(!(attr.Compare(localAttr, true, true /* ignore modification times */))) { - printf("Local directory '%s' has different attributes " - "to store directory '%s'.\n", - localName.c_str(), storeName.c_str()); + BOX_WARNING("Local directory '" << localDirDisplay << + "' has different attributes to store " + "directory '" << storeDirDisplay << "'."); rParams.mDifferences ++; } } @@ -1300,7 +1487,9 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s DIR *dirhandle = ::opendir(rLocalDir.c_str()); if(dirhandle == 0) { - printf("ERROR: opendir on local dir '%s'\n", localName.c_str()); + BOX_WARNING("Failed to open local directory '" << + localDirDisplay << "': " << strerror(errno)); + rParams.mUncheckedFiles ++; return; } try @@ -1316,13 +1505,23 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s (localDirEn->d_name[1] == '\0' || (localDirEn->d_name[1] == '.' && localDirEn->d_name[2] == '\0'))) { // ignore, it's . or .. + +#ifdef HAVE_VALID_DIRENT_D_TYPE + if (localDirEn->d_type != DT_DIR) + { + BOX_ERROR("d_type does not really " + "work on your platform. " + "Reconfigure Box!"); + return; + } +#endif + continue; } #ifndef HAVE_VALID_DIRENT_D_TYPE - std::string fn(rLocalDir); - fn += DIRECTORY_SEPARATOR_ASCHAR; - fn += localDirEn->d_name; + std::string fn(MakeFullPath + (rLocalDir, localDirEn->d_name)); struct stat st; if(::lstat(fn.c_str(), &st) != 0) { @@ -1339,7 +1538,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s { // Directory localDirs.insert(std::string(localDirEn->d_name)); - } + } #else // Entry -- file or dir? if(localDirEn->d_type == DT_REG || localDirEn->d_type == DT_LNK) @@ -1357,8 +1556,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Close directory if(::closedir(dirhandle) != 0) { - printf("ERROR: closedir on local dir '%s'\n", - localName.c_str()); + BOX_ERROR("Failed to close local directory '" << + localDirDisplay << "': " << strerror(errno)); } dirhandle = 0; @@ -1395,25 +1594,39 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Now compare files. 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); + // Does the file exist locally? - string_set_iter_t local(localFiles.find(i->first)); + string_set_iter_t local(localFiles.find(fileName)); if(local == localFiles.end()) { // Not found -- report - printf("Local file '%s" DIRECTORY_SEPARATOR - "%s' does not exist, " - "but store file '%s/%s' does.\n", - localName.c_str(), i->first.c_str(), - storeName.c_str(), i->first.c_str()); + BOX_WARNING("Local file '" << + localPathDisplay << "' does not exist, " + "but store file '" << + storePathDisplay << "' does."); rParams.mDifferences ++; } else { try { - // make local name of file for comparison - std::string localName(rLocalDir + DIRECTORY_SEPARATOR + i->first); - // Files the same flag? bool equal = true; @@ -1429,7 +1642,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream()); // Compare - equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(localName.c_str(), *blockIndexStream, mrConnection.GetTimeout()); + equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(localPath.c_str(), *blockIndexStream, mrConnection.GetTimeout()); } else { @@ -1441,7 +1654,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Decode it std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream; - // Got additional attibutes? + // Got additional attributes? if(i->second->HasAttributes()) { // Use these attributes @@ -1464,26 +1677,39 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s // Compare attributes BackupClientFileAttributes localAttr; box_time_t fileModTime = 0; - localAttr.ReadAttributes(localName.c_str(), false /* don't zero mod times */, &fileModTime); + localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime); modifiedAfterLastSync = (fileModTime > rParams.mLatestFileUploadTime); - if(!localAttr.Compare(fileOnServerStream->GetAttributes(), - true /* ignore attr mod time */, + bool ignoreAttrModTime = true; + + #ifdef WIN32 + // attr mod time is really + // creation time, so check it + ignoreAttrModTime = false; + #endif + + if(!rParams.mIgnoreAttributes && + #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE + !fileOnServerStream->IsSymLink() && + #endif + !localAttr.Compare(fileOnServerStream->GetAttributes(), + ignoreAttrModTime, fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */)) { - printf("Local file '%s" - DIRECTORY_SEPARATOR - "%s' has different attributes " - "to store file '%s/%s'.\n", - localName.c_str(), i->first.c_str(), storeName.c_str(), i->first.c_str()); + BOX_WARNING("Local file '" << + localPathDisplay << + "' has different attributes " + "to store file '" << + storePathDisplay << + "'."); rParams.mDifferences ++; if(modifiedAfterLastSync) { rParams.mDifferencesExplainedByModTime ++; - printf("(the file above was modified after the last sync time -- might be reason for difference)\n"); + BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); } else if(i->second->HasAttributes()) { - printf("(the file above has had new attributes applied)\n"); + BOX_INFO("(the file above has had new attributes applied)\n"); } } @@ -1492,7 +1718,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s if(!fileOnServerStream->IsSymLink()) { // Open the local file - FileStream l(localName.c_str()); + FileStream l(localPath.c_str()); // Size IOStream::pos_type fileSizeLocal = l.BytesLeftToRead(); @@ -1522,7 +1748,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s equal = false; } - // Must always read the entire decoded string, if it's not a symlink + // Must always read the entire decoded stream, if it's not a symlink if(fileOnServerStream->StreamDataLeft()) { // Absorb all the data remaining @@ -1532,40 +1758,65 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s 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()); + } + } } } // Report if not equal. if(!equal) { - printf("Local file '%s" - DIRECTORY_SEPARATOR - "%s' has different contents " - "to store file '%s/%s'.\n", - localName.c_str(), i->first.c_str(), storeName.c_str(), i->first.c_str()); + BOX_WARNING("Local file '" << + localPathDisplay << "' " + "has different contents " + "to store file '" << + storePathDisplay << + "'."); rParams.mDifferences ++; if(modifiedAfterLastSync) { rParams.mDifferencesExplainedByModTime ++; - printf("(the file above was modified after the last sync time -- might be reason for difference)\n"); + BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); } else if(i->second->HasAttributes()) { - printf("(the file above has had new attributes applied)\n"); + BOX_INFO("(the file above has had new attributes applied)\n"); } } } catch(BoxException &e) { - printf("ERROR: (%d/%d) during file fetch and comparsion for '%s/%s'\n", - e.GetType(), - e.GetSubType(), - storeName.c_str(), - i->first.c_str()); + BOX_ERROR("Failed to fetch and compare " + "'" << + storePathDisplay.c_str() << + "': error " << e.what() << + " (" << e.GetType() << + "/" << e.GetSubType() << ")"); + rParams.mUncheckedFiles ++; } - catch(...) + catch(std::exception &e) { - printf("ERROR: (unknown) during file fetch and comparsion for '%s/%s'\n", storeName.c_str(), i->first.c_str()); + BOX_ERROR("Failed to fetch and compare " + "'" << + storePathDisplay.c_str() << + "': " << e.what()); + } + catch(...) + { + BOX_ERROR("Failed to fetch and compare " + "'" << + storePathDisplay.c_str() << + "': unknown error"); + rParams.mUncheckedFiles ++; } // Remove from set so that we know it's been compared @@ -1576,28 +1827,43 @@ 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 for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i) { - std::string localFileName(rLocalDir + - DIRECTORY_SEPARATOR + *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); + // Should this be ignored (ie is excluded)? if(rParams.mpExcludeFiles == 0 || - !(rParams.mpExcludeFiles->IsExcluded(localFileName))) + !(rParams.mpExcludeFiles->IsExcluded(localPath))) { - printf("Local file '%s" DIRECTORY_SEPARATOR - "%s' exists, but store file '%s/%s' " - "does not exist.\n", - localName.c_str(), (*i).c_str(), - storeName.c_str(), (*i).c_str()); + BOX_WARNING("Local file '" << + localPathDisplay << + "' exists, but store file '" << + storePathDisplay << + "' does not."); rParams.mDifferences ++; // Check the file modification time { struct stat st; - if(::stat(localFileName.c_str(), &st) == 0) + if(::stat(localPath.c_str(), &st) == 0) { if(FileModificationTime(st) > rParams.mLatestFileUploadTime) { rParams.mDifferencesExplainedByModTime ++; - printf("(the file above was modified after the last sync time -- might be reason for difference)\n"); + BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)"); } } } @@ -1612,26 +1878,58 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s localFiles.clear(); storeFiles.clear(); - // Now do the directories, recusively to check subdirectories + // 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); + // Does the directory exist locally? string_set_iter_t local(localDirs.find(i->first)); - if(local == localDirs.end()) + if(local == localDirs.end() && + rParams.mpExcludeDirs != NULL && + rParams.mpExcludeDirs->IsExcluded(localPath)) + { + // Not found -- report + BOX_WARNING("Local directory '" << + localPathDisplay << "' is excluded, " + "but store directory '" << + storePathDisplay << "' still exists."); + rParams.mDifferences ++; + } + else if(local == localDirs.end()) { // Not found -- report - printf("Local directory '%s" - DIRECTORY_SEPARATOR "%s' " - "does not exist, but store directory " - "'%s/%s' does.\n", - localName.c_str(), i->first.c_str(), - storeName.c_str(), i->first.c_str()); + BOX_WARNING("Local directory '" << + localPathDisplay << "' does not exist, " + "but store directory '" << + storePathDisplay << "' does."); rParams.mDifferences ++; } + else if(rParams.mpExcludeDirs != NULL && + rParams.mpExcludeDirs->IsExcluded(localPath)) + { + // don't recurse into excluded directories + } else { // Compare directory - Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, rLocalDir + DIRECTORY_SEPARATOR + i->first, rParams); + Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, localPath, rParams); // Remove from set so that we know it's been compared localDirs.erase(local); @@ -1641,14 +1939,33 @@ 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 for(std::set<std::string>::const_iterator i = localDirs.begin(); i != localDirs.end(); ++i) { - std::string localName(rLocalDir + DIRECTORY_SEPARATOR + *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); + // Should this be ignored (ie is excluded)? - if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localName))) + if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localPath))) { - printf("Local directory '%s/%s' exists, but " - "store directory '%s/%s' does not exist.\n", - localName.c_str(), (*i).c_str(), - storeName.c_str(), (*i).c_str()); + BOX_WARNING("Local directory '" << + localPathDisplay << "' exists, but " + "store directory '" << + storePathDisplay << "' does not."); rParams.mDifferences ++; } else @@ -1681,7 +1998,7 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b // Check arguments if(args.size() != 2) { - printf("Incorrect usage.\nrestore [-d] [-r] [-i] <directory-name> <local-directory-name>\n"); + BOX_ERROR("Incorrect usage. restore [-d] [-r] [-i] <remote-name> <local-name>"); return; } @@ -1694,9 +2011,9 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b { // Specified as ID. dirID = ::strtoll(args[0].c_str(), 0, 16); - if(dirID == LLONG_MIN || dirID == LLONG_MAX || dirID == 0) + if(dirID == std::numeric_limits<long long>::min() || dirID == std::numeric_limits<long long>::max() || dirID == 0) { - printf("Not a valid object ID (specified in hex)\n"); + BOX_ERROR("Not a valid object ID (specified in hex)"); return; } } @@ -1719,12 +2036,12 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b // Allowable? if(dirID == 0) { - printf("Directory '%s' not found on server\n", args[0].c_str()); + BOX_ERROR("Directory '" << args[0] << "' not found on server"); return; } if(dirID == BackupProtocolClientListDirectory::RootDirectory) { - printf("Cannot restore the root directory -- restore locations individually.\n"); + BOX_ERROR("Cannot restore the root directory -- restore locations individually."); return; } @@ -1736,25 +2053,62 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b #endif // Go and restore... - switch(BackupClientRestore(mrConnection, dirID, localName.c_str(), - true /* print progress dots */, restoreDeleted, - false /* don't undelete after restore! */, - opts['r'] /* resume? */)) + int result; + + try + { + result = BackupClientRestore(mrConnection, dirID, + localName.c_str(), + true /* print progress dots */, restoreDeleted, + false /* don't undelete after restore! */, + opts['r'] /* resume? */); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to restore: " << e.what()); + SetReturnCode(COMMAND_RETURN_ERROR); + return; + } + catch(...) + { + BOX_ERROR("Failed to restore: unknown exception"); + SetReturnCode(COMMAND_RETURN_ERROR); + return; + } + + switch(result) { case Restore_Complete: - printf("Restore complete\n"); + BOX_INFO("Restore complete."); break; case Restore_ResumePossible: - printf("Resume possible -- repeat command with -r flag to resume\n"); + BOX_ERROR("Resume possible -- repeat command with -r flag to resume"); + SetReturnCode(COMMAND_RETURN_ERROR); break; case Restore_TargetExists: - printf("The target directory exists. You cannot restore over an existing directory.\n"); + BOX_ERROR("The target directory exists. You cannot restore over an existing directory."); + SetReturnCode(COMMAND_RETURN_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); + break; + #endif + + case Restore_UnknownError: + BOX_ERROR("Unknown error during restore."); + SetReturnCode(COMMAND_RETURN_ERROR); + break; + default: - printf("ERROR: Unknown restore result.\n"); + BOX_ERROR("Unknown restore result " << result << "."); + SetReturnCode(COMMAND_RETURN_ERROR); break; } } @@ -1874,7 +2228,7 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const // Check arguments if(args.size() != 1) { - printf("Incorrect usage.\nundelete <directory-name>\n"); + BOX_ERROR("Incorrect usage. undelete <directory-name>"); return; } @@ -1892,12 +2246,12 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const // Allowable? if(dirID == 0) { - printf("Directory '%s' not found on server\n", args[0].c_str()); + BOX_ERROR("Directory '" << args[0] << "' not found on server."); return; } if(dirID == BackupProtocolClientListDirectory::RootDirectory) { - printf("Cannot undelete the root directory.\n"); + BOX_ERROR("Cannot undelete the root directory."); return; } |