summaryrefslogtreecommitdiff
path: root/bin/bbackupquery/BackupQueries.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bin/bbackupquery/BackupQueries.cpp')
-rw-r--r--bin/bbackupquery/BackupQueries.cpp2410
1 files changed, 0 insertions, 2410 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp
deleted file mode 100644
index bcb1827e..00000000
--- a/bin/bbackupquery/BackupQueries.cpp
+++ /dev/null
@@ -1,2410 +0,0 @@
-// --------------------------------------------------------------------------
-//
-// File
-// Name: BackupQueries.cpp
-// Purpose: Perform various queries on the backup store server.
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-
-#include "Box.h"
-
-#ifdef HAVE_UNISTD_H
- #include <unistd.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_DIRENT_H
- #include <dirent.h>
-#endif
-
-#include <algorithm>
-#include <cstring>
-#include <limits>
-#include <iostream>
-#include <ostream>
-#include <set>
-
-#include "BackupClientFileAttributes.h"
-#include "BackupClientMakeExcludeList.h"
-#include "BackupClientRestore.h"
-#include "BackupQueries.h"
-#include "BackupStoreDirectory.h"
-#include "BackupStoreException.h"
-#include "BackupStoreFile.h"
-#include "BackupStoreFilenameClear.h"
-#include "BoxTimeToText.h"
-#include "CommonException.h"
-#include "Configuration.h"
-#include "ExcludeList.h"
-#include "FileModificationTime.h"
-#include "FileStream.h"
-#include "IOStream.h"
-#include "Logging.h"
-#include "PathUtils.h"
-#include "SelfFlushingStream.h"
-#include "Utils.h"
-#include "autogen_BackupProtocol.h"
-#include "autogen_CipherException.h"
-
-#include "MemLeakFindOn.h"
-
-// 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
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::BackupQueries()
-// Purpose: Constructor
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-BackupQueries::BackupQueries(BackupProtocolCallable &rConnection,
- const Configuration &rConfiguration, bool readWrite)
- : mReadWrite(readWrite),
- mrConnection(rConnection),
- mrConfiguration(rConfiguration),
- mQuitNow(false),
- mRunningAsRoot(false),
- mWarnedAboutOwnerAttributes(false),
- mReturnCode(0) // default return code
-{
- #ifdef WIN32
- mRunningAsRoot = TRUE;
- #else
- mRunningAsRoot = (::geteuid() == 0);
- #endif
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::~BackupQueries()
-// Purpose: Destructor
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-BackupQueries::~BackupQueries()
-{
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::DoCommand(const char *, bool)
-// Purpose: Perform a command
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-void BackupQueries::DoCommand(ParsedCommand& rCommand)
-{
- // Check...
-
- if(rCommand.mFailed)
- {
- BOX_ERROR("Parse failed: unknown command '" <<
- rCommand.mCmdElements[0] << "' or failed to convert "
- "encoding of arguments");
- return;
- }
-
- if(rCommand.mCmdElements.size() < 1)
- {
- // blank command
- return;
- }
-
- if(rCommand.pSpec->type == Command_sh &&
- rCommand.mCmdElements.size() == 2)
- {
- // Yes, run shell command
- int result = ::system(rCommand.mCmdElements[1].c_str());
- if(result != 0)
- {
- BOX_WARNING("System command returned error code " <<
- result);
- SetReturnCode(ReturnCode::Command_Error);
- }
- return;
- }
-
- if(rCommand.pSpec->type == Command_Unknown)
- {
- // No such command
- BOX_ERROR("Unrecognised command: " << rCommand.mCmdElements[0]);
- return;
- }
-
- // Arguments
- std::vector<std::string> args(rCommand.mCmdElements.begin() + 1,
- rCommand.mCmdElements.end());
-
- // Set up options
- bool opts[256];
- for(int o = 0; o < 256; ++o) opts[o] = false;
- // BLOCK
- {
- // options
- const char *c = rCommand.mOptions.c_str();
- while(*c != 0)
- {
- // Valid option?
- if(::strchr(rCommand.pSpec->opts, *c) == NULL)
- {
- BOX_ERROR("Invalid option '" << *c << "' for "
- "command " << rCommand.pSpec->name);
- return;
- }
- opts[(int)*c] = true;
- ++c;
- }
- }
-
- if(rCommand.pSpec->type != Command_Quit)
- {
- // If not a quit command, set the return code to zero
- SetReturnCode(ReturnCode::Command_OK);
- }
-
- // Handle command
- switch(rCommand.pSpec->type)
- {
- case Command_Quit:
- mQuitNow = true;
- break;
-
- case Command_List:
- CommandList(args, opts);
- break;
-
- case Command_pwd:
- {
- // Simple implementation, so do it here
- BOX_NOTICE(GetCurrentDirectoryName() << " (" <<
- BOX_FORMAT_OBJECTID(GetCurrentDirectoryID()) <<
- ")");
- }
- break;
-
- case Command_cd:
- CommandChangeDir(args, opts);
- break;
-
- case Command_lcd:
- CommandChangeLocalDir(args);
- break;
-
- case Command_sh:
- BOX_ERROR("The command to run must be specified as an argument.");
- break;
-
- case Command_GetObject:
- CommandGetObject(args, opts);
- break;
-
- case Command_Get:
- CommandGet(args, opts);
- break;
-
- case Command_Compare:
- CommandCompare(args, opts);
- break;
-
- case Command_Restore:
- CommandRestore(args, opts);
- break;
-
- case Command_Usage:
- CommandUsage(opts);
- break;
-
- case Command_Help:
- CommandHelp(args);
- break;
-
- case Command_Undelete:
- CommandUndelete(args, opts);
- break;
-
- case Command_Delete:
- CommandDelete(args, opts);
- break;
-
- default:
- BOX_ERROR("Unknown command: " << rCommand.mCmdElements[0]);
- break;
- }
-}
-
-#define LIST_OPTION_TIMES_ATTRIBS 'a'
-#define LIST_OPTION_SORT_NO_DIRS_FIRST 'D'
-#define LIST_OPTION_NOFLAGS 'F'
-#define LIST_OPTION_DISPLAY_HASH 'h'
-#define LIST_OPTION_SORT_ID 'i'
-#define LIST_OPTION_NOOBJECTID 'I'
-#define LIST_OPTION_SORT_REVERSE 'r'
-#define LIST_OPTION_RECURSIVE 'R'
-#define LIST_OPTION_SIZEINBLOCKS 's'
-#define LIST_OPTION_SORT_SIZE 'S'
-#define LIST_OPTION_TIMES_LOCAL 't'
-#define LIST_OPTION_TIMES_UTC 'T'
-#define LIST_OPTION_SORT_NONE 'U'
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandList(const std::vector<std::string> &, const bool *)
-// Purpose: List directories (optionally recursive)
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandList(const std::vector<std::string> &args, const bool *opts)
-{
- // default to using the current directory
- int64_t rootDir = GetCurrentDirectoryID();
-
- // name of base directory
- std::string listRoot; // blank
-
- // Got a directory in the arguments?
- if(args.size() > 0)
- {
-#ifdef WIN32
- std::string storeDirEncoded;
- if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded))
- return;
-#else
- const std::string& storeDirEncoded(args[0]);
-#endif
-
- // Attempt to find the directory
- rootDir = FindDirectoryObjectID(storeDirEncoded,
- opts[LIST_OPTION_ALLOWOLD],
- opts[LIST_OPTION_ALLOWDELETED]);
-
- if(rootDir == 0)
- {
- BOX_ERROR("Directory '" << args[0] << "' not found "
- "on store.");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
- }
-
- // List it
- List(rootDir, listRoot, opts, true /* first level to list */);
-}
-
-static std::string GetTimeString(BackupStoreDirectory::Entry& en,
- bool useLocalTime, bool showAttrModificationTimes)
-{
- std::ostringstream out;
- box_time_t originalTime, newAttributesTime;
-
- // there is no attribute modification time in the directory
- // entry, unfortunately, so we can't display it.
- originalTime = en.GetModificationTime();
- out << BoxTimeToISO8601String(originalTime, useLocalTime);
-
- if(en.HasAttributes())
- {
- const StreamableMemBlock &storeAttr(en.GetAttributes());
- BackupClientFileAttributes attr(storeAttr);
-
- box_time_t NewModificationTime, NewAttrModificationTime;
- attr.GetModificationTimes(&NewModificationTime,
- &NewAttrModificationTime);
-
- if (showAttrModificationTimes)
- {
- newAttributesTime = NewAttrModificationTime;
- }
- else
- {
- newAttributesTime = NewModificationTime;
- }
-
- if (newAttributesTime == originalTime)
- {
- out << "*";
- }
- else
- {
- out << "~" << BoxTimeToISO8601String(newAttributesTime,
- useLocalTime);
- }
- }
- else
- {
- out << " ";
- }
-
- return out.str();
-}
-
-/* We need a way to pass options to sort functions for sorting. The algorithm
- * doesn't seem to provide a way to do this, so I'm using a global variable.
- * Which is not thread safe, but we don't currently use threads so that should
- * be OK. Do not use threads without checking!
- */
-const bool *gThreadUnsafeOptions;
-
-int DirsFirst(BackupStoreDirectory::Entry* a,
- BackupStoreDirectory::Entry* b)
-{
- if (a->IsDir() && !b->IsDir())
- {
- return -1; // a < b
- }
- else if (!a->IsDir() && b->IsDir())
- {
- return 1; // b > a
- }
- else
- {
- return 0; // continue comparison
- }
-}
-
-#define MAYBE_DIRS_FIRST(a, b) \
- if (!gThreadUnsafeOptions[LIST_OPTION_SORT_NO_DIRS_FIRST]) \
- { \
- int result = DirsFirst(a, b); \
- if (result < 0) return true; /* a < b */ \
- else if (result > 0) return false; /* a > b */ \
- /* else: fall through */ \
- }
-
-#define MAYBE_REVERSE(result) \
- (result != gThreadUnsafeOptions[LIST_OPTION_SORT_REVERSE])
-// result is false, opts[reverse] is false => return false
-// result is false, opts[reverse] is true => return true
-// result is true, opts[reverse] is false => return true
-// result is true, opts[reverse] is true => return false
-// this is logical XOR, for which the boolean operator is !=.
-
-bool SortById(BackupStoreDirectory::Entry* a,
- BackupStoreDirectory::Entry* b)
-{
- MAYBE_DIRS_FIRST(a, b);
- bool result = (a->GetObjectID() < b->GetObjectID());
- return MAYBE_REVERSE(result);
-}
-
-bool SortBySize(BackupStoreDirectory::Entry* a,
- BackupStoreDirectory::Entry* b)
-{
- MAYBE_DIRS_FIRST(a, b);
- bool result = (a->GetSizeInBlocks() < b->GetSizeInBlocks());
- return MAYBE_REVERSE(result);
-}
-
-bool SortByName(BackupStoreDirectory::Entry* a,
- BackupStoreDirectory::Entry* b)
-{
- MAYBE_DIRS_FIRST(a, b);
- BackupStoreFilenameClear afc(a->GetName());
- BackupStoreFilenameClear bfc(b->GetName());
- std::string an = afc.GetClearFilename();
- std::string bn = bfc.GetClearFilename();
- bool result = (an < bn);
- return MAYBE_REVERSE(result);
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::List(int64_t, const std::string &, const bool *, bool)
-// Purpose: Do the actual listing of directories and files
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-void BackupQueries::List(int64_t DirID, const std::string &rListRoot,
- const bool *opts, bool FirstLevel, std::ostream* pOut)
-{
-#ifdef WIN32
- DWORD n_chars;
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
-#endif
-
- // Generate exclude flags
- int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
- if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion;
- if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted;
-
- // Do communication
- try
- {
- mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolListDirectory::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;
- std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
- dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
-
- // Store entry pointers in a std::vector for sorting
- BackupStoreDirectory::Iterator i(dir);
- BackupStoreDirectory::Entry *en = 0;
- std::vector<BackupStoreDirectory::Entry*> sorted_entries;
- while((en = i.Next()) != 0)
- {
- sorted_entries.push_back(en);
- }
-
- // Typedef to avoid mind-bending while dealing with pointers to functions.
- typedef bool (EntryComparator_t)(BackupStoreDirectory::Entry* a,
- BackupStoreDirectory::Entry* b);
- // Default is no comparator, i.e. no sorting.
- EntryComparator_t* pComparator = NULL;
-
- if (opts[LIST_OPTION_SORT_ID])
- {
- pComparator = &SortById;
- }
- else if (opts[LIST_OPTION_SORT_SIZE])
- {
- pComparator = &SortBySize;
- }
- else if (opts[LIST_OPTION_SORT_NONE])
- {
- // do nothing
- }
- else // sort by name
- {
- pComparator = &SortByName;
- }
-
- if (pComparator != NULL)
- {
- gThreadUnsafeOptions = opts;
- sort(sorted_entries.begin(), sorted_entries.end(),
- pComparator);
- gThreadUnsafeOptions = NULL;
- }
-
- for (std::vector<BackupStoreDirectory::Entry*>::const_iterator
- i = sorted_entries.begin();
- i != sorted_entries.end(); i++)
- {
- en = *i;
- std::ostringstream buf;
-
- // Display this entry
- BackupStoreFilenameClear clear(en->GetName());
-
- // Object ID?
- if(!opts[LIST_OPTION_NOOBJECTID])
- {
- // add object ID to line
- buf << std::hex << std::internal << std::setw(8) <<
- std::setfill('0') << en->GetObjectID() <<
- std::dec << " ";
- }
-
- // Flags?
- if(!opts[LIST_OPTION_NOFLAGS])
- {
- static const char *flags = BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES;
- char displayflags[16];
- // make sure f is big enough
- ASSERT(sizeof(displayflags) >= sizeof(BACKUPSTOREDIRECTORY_ENTRY_FLAGS_DISPLAY_NAMES) + 3);
- // Insert flags
- char *f = displayflags;
- const char *t = flags;
- int16_t en_flags = en->GetFlags();
- while(*t != 0)
- {
- *f = ((en_flags&1) == 0)?'-':*t;
- en_flags >>= 1;
- f++;
- t++;
- }
- // attributes flags
- *(f++) = (en->HasAttributes())?'a':'-';
-
- // terminate
- *(f++) = ' ';
- *(f++) = '\0';
- buf << displayflags;
-
- if(en_flags != 0)
- {
- buf << "[ERROR: Entry has additional flags set] ";
- }
- }
-
- if(opts[LIST_OPTION_TIMES_UTC])
- {
- // Show UTC times...
- buf << GetTimeString(*en, false,
- opts[LIST_OPTION_TIMES_ATTRIBS]) << " ";
- }
-
- if(opts[LIST_OPTION_TIMES_LOCAL])
- {
- // Show local times...
- buf << GetTimeString(*en, true,
- opts[LIST_OPTION_TIMES_ATTRIBS]) << " ";
- }
-
- if(opts[LIST_OPTION_DISPLAY_HASH])
- {
- buf << std::hex << std::internal << std::setw(16) <<
- std::setfill('0') << en->GetAttributesHash() <<
- std::dec;
- }
-
- if(opts[LIST_OPTION_SIZEINBLOCKS])
- {
- buf << std::internal << std::setw(5) <<
- std::setfill('0') << en->GetSizeInBlocks() <<
- " ";
- }
-
- // add name
- if(!FirstLevel)
- {
-#ifdef WIN32
- std::string listRootDecoded;
- if(!ConvertUtf8ToConsole(rListRoot.c_str(),
- listRootDecoded)) return;
- listRootDecoded += "/";
- buf << listRootDecoded;
- WriteConsole(hOut, listRootDecoded.c_str(),
- strlen(listRootDecoded.c_str()), &n_chars, NULL);
-#else
- buf << rListRoot << "/";
-#endif
- }
-
- std::string fileName;
- try
- {
- fileName = clear.GetClearFilename();
- }
- catch(CipherException &e)
- {
- fileName = "<decrypt failed>";
- }
-
-#ifdef WIN32
- std::string fileNameUtf8 = fileName;
- if(!ConvertUtf8ToConsole(fileNameUtf8, fileName))
- {
- fileName = fileNameUtf8 + " [convert encoding failed]";
- }
-#endif
-
- buf << fileName;
-
- if(!en->GetName().IsEncrypted())
- {
- buf << " [FILENAME NOT ENCRYPTED]";
- }
-
- buf << std::endl;
-
- if(pOut)
- {
- (*pOut) << buf.str();
- }
- else
- {
-#ifdef WIN32
- std::string line = buf.str();
- if (!WriteConsole(hOut, line.c_str(), line.size(),
- &n_chars, NULL))
- {
- // WriteConsole failed, try standard method
- std::cout << buf.str();
- }
-#else
- std::cout << buf.str();
-#endif
- }
-
- // Directory?
- if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)
- {
- // Recurse?
- if(opts[LIST_OPTION_RECURSIVE])
- {
- std::string subroot(rListRoot);
- if(!FirstLevel) subroot += '/';
- subroot += clear.GetClearFilename();
- List(en->GetObjectID(), subroot, opts,
- false /* not the first level to list */,
- pOut);
- }
- }
- }
-}
-
-
-// --------------------------------------------------------------------------
-//
-// 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.
-// 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)
-{
- // Split up string into elements
- std::vector<std::string> dirElements;
- SplitString(rDirName, '/', dirElements);
-
- // Start from current stack, or root, whichever is required
- std::vector<std::pair<std::string, int64_t> > stack;
- int64_t dirID = BackupProtocolListDirectory::RootDirectory;
- if(rDirName.size() > 0 && rDirName[0] == '/')
- {
- // Root, do nothing
- }
- else
- {
- // Copy existing stack
- stack = mDirStack;
- if(stack.size() > 0)
- {
- dirID = stack[stack.size() - 1].second;
- }
- }
-
- // Generate exclude flags
- int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
- if(!AllowOldVersion) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion;
- if(!AllowDeletedDirs) excludeFlags |= BackupProtocolListDirectory::Flags_Deleted;
-
- // Read directories
- for(unsigned int e = 0; e < dirElements.size(); ++e)
- {
- if(dirElements[e].size() > 0)
- {
- if(dirElements[e] == ".")
- {
- // Ignore.
- }
- else if(dirElements[e] == "..")
- {
- // Up one!
- if(stack.size() > 0)
- {
- // Remove top element
- stack.pop_back();
-
- // New dir ID
- dirID = (stack.size() > 0)?(stack[stack.size() - 1].second):BackupProtocolListDirectory::RootDirectory;
- }
- else
- {
- // At root anyway
- dirID = BackupProtocolListDirectory::RootDirectory;
- }
- }
- else
- {
- // Not blank element. Read current directory.
- std::auto_ptr<BackupProtocolSuccess> dirreply(mrConnection.QueryListDirectory(
- dirID,
- BackupProtocolListDirectory::Flags_Dir, // just directories
- excludeFlags,
- true /* want attributes */));
-
- // Retrieve the directory from the stream following
- BackupStoreDirectory dir;
- std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
- dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
-
- // Then... find the directory within it
- BackupStoreDirectory::Iterator i(dir);
- BackupStoreFilenameClear dirname(dirElements[e]);
- BackupStoreDirectory::Entry *en = i.FindMatchingClearName(dirname);
- if(en == 0)
- {
- // Not found
- return 0;
- }
-
- // Object ID for next round of searching
- dirID = en->GetObjectID();
-
- // Push onto stack
- stack.push_back(std::pair<std::string, int64_t>(dirElements[e], dirID));
- }
- }
- }
-
- // If required, copy the new stack to the caller
- if(pStack)
- {
- *pStack = stack;
- }
-
- return dirID;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::GetCurrentDirectoryID()
-// Purpose: Returns the ID of the current directory
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-int64_t BackupQueries::GetCurrentDirectoryID()
-{
- // Special case for root
- if(mDirStack.size() == 0)
- {
- return BackupProtocolListDirectory::RootDirectory;
- }
-
- // Otherwise, get from the last entry on the stack
- return mDirStack[mDirStack.size() - 1].second;
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::GetCurrentDirectoryName()
-// Purpose: Gets the name of the current directory
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-std::string BackupQueries::GetCurrentDirectoryName()
-{
- // Special case for root
- if(mDirStack.size() == 0)
- {
- return std::string("/");
- }
-
- // Build path
- std::string r;
- for(unsigned int l = 0; l < mDirStack.size(); ++l)
- {
- r += "/";
-#ifdef WIN32
- std::string dirName;
- if(!ConvertUtf8ToConsole(mDirStack[l].first.c_str(), dirName))
- return "error";
- r += dirName;
-#else
- r += mDirStack[l].first;
-#endif
- }
-
- return r;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandChangeDir(const std::vector<std::string> &)
-// Purpose: Change directory command
-// Created: 2003/10/10
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const bool *opts)
-{
- if(args.size() != 1 || args[0].size() == 0)
- {
- BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
-
-#ifdef WIN32
- std::string dirName;
- if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) return;
-#else
- const std::string& dirName(args[0]);
-#endif
-
- std::vector<std::pair<std::string, int64_t> > newStack;
- int64_t id = FindDirectoryObjectID(dirName, opts['o'], opts['d'],
- &newStack);
-
- if(id == 0)
- {
- BOX_ERROR("Directory '" << args[0] << "' not found.");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
-
- // Store new stack
- mDirStack = newStack;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &)
-// Purpose: Change local directory command
-// Created: 2003/10/11
-//
-// --------------------------------------------------------------------------
-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(ReturnCode::Command_Error);
- return;
- }
-
- // Try changing directory
-#ifdef WIN32
- std::string dirName;
- if(!ConvertConsoleToUtf8(args[0].c_str(), dirName))
- {
- BOX_ERROR("Failed to convert path from console encoding.");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
- int result = ::chdir(dirName.c_str());
-#else
- int result = ::chdir(args[0].c_str());
-#endif
- if(result != 0)
- {
- if(errno == ENOENT || errno == ENOTDIR)
- {
- BOX_ERROR("Directory '" << args[0] << "' does not exist.");
- }
- else
- {
- BOX_LOG_SYS_ERROR("Failed to change to directory "
- "'" << args[0] << "'");
- }
-
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
-
- // Report current dir
- char wd[PATH_MAX];
- if(::getcwd(wd, PATH_MAX) == 0)
- {
- BOX_LOG_SYS_ERROR("Error getting current directory");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
-
-#ifdef WIN32
- if(!ConvertUtf8ToConsole(wd, dirName))
- {
- BOX_ERROR("Failed to convert new path from console encoding.");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
- BOX_INFO("Local current directory is now '" << dirName << "'.");
-#else
- BOX_INFO("Local current directory is now '" << wd << "'.");
-#endif
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandGetObject(const std::vector<std::string> &, const bool *)
-// Purpose: Gets an object without any translation.
-// Created: 2003/10/11
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const bool *opts)
-{
- // Check args
- if(args.size() != 2)
- {
- BOX_ERROR("Incorrect usage. getobject <object-id> "
- "<local-filename>");
- return;
- }
-
- int64_t id = ::strtoll(args[0].c_str(), 0, 16);
- if(id == std::numeric_limits<long long>::min() || id == std::numeric_limits<long long>::max() || id == 0)
- {
- BOX_ERROR("Not a valid object ID (specified in hex): " <<
- args[0]);
- return;
- }
-
- // Does file exist?
- 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;
- }
-
- // Open file
- FileStream out(args[1].c_str(), O_WRONLY | O_CREAT | O_EXCL);
-
- // Request that object
- try
- {
- // Request object
- std::auto_ptr<BackupProtocolSuccess> getobj(mrConnection.QueryGetObject(id));
-
- // Stream that object out to the file
- std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
- objectStream->CopyStreamTo(out);
-
- BOX_INFO("Object ID " << BOX_FORMAT_OBJECTID(id) <<
- " fetched successfully.");
- }
- catch(ConnectionException &e)
- {
- if(mrConnection.GetLastErrorType() == BackupProtocolError::Err_DoesNotExist)
- {
- BOX_ERROR("Object ID " << BOX_FORMAT_OBJECTID(id) <<
- " does not exist on store.");
- ::unlink(args[1].c_str());
- }
- else
- {
- BOX_ERROR("Error occured fetching object.");
- ::unlink(args[1].c_str());
- }
- }
- catch(...)
- {
- ::unlink(args[1].c_str());
- BOX_ERROR("Error occured fetching object.");
- }
-}
-
-
-// --------------------------------------------------------------------------
-//
-// 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 in *pFileNameOut (if not NULL).
-// 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;
- }
- }
- }
-
- 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): "
- << rNameOrIdString);
- 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();
- }
-
- if(pFileNameOut)
- {
- BackupStoreFilenameClear entryName(en->GetName());
- *pFileNameOut = entryName.GetClearFilename();
- }
-
- return fileId;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandGet(const std::vector<std::string> &, const bool *)
-// Purpose: Command to get a file from the store
-// Created: 2003/10/12
-//
-// --------------------------------------------------------------------------
-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)
- {
- BOX_ERROR("Incorrect usage.\n"
- "get <remote-filename> [<local-filename>] or\n"
- "get -i <object-id> <local-filename>");
- return;
- }
-
- // Find object ID somehow
- int64_t fileId, dirId;
- std::string localName;
-
-#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
-
- int16_t flagsExclude;
-
- if(opts['i'])
- {
- // can retrieve anything by ID
- flagsExclude = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
- }
- else
- {
- // only current versions by name
- flagsExclude =
- BackupProtocolListDirectory::Flags_OldVersion |
- BackupProtocolListDirectory::Flags_Deleted;
- }
-
-
- fileId = FindFileID(args[0], opts, &dirId, &localName,
- BackupProtocolListDirectory::Flags_File, // just files
- flagsExclude, NULL /* don't care about flags found */);
-
- if (fileId == 0)
- {
- // error already reported
- return;
- }
-
- 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)
- {
- localName = args[1];
- }
- }
-
- // Does local file already exist? (don't want to overwrite)
- 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(ReturnCode::Command_Error);
- return;
- }
-
- // Request it from the store
- try
- {
- // Request object
- mrConnection.QueryGetFile(dirId, fileId);
-
- // Stream containing encoded file
- std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
-
- // Decode it
- BackupStoreFile::DecodeFile(*objectStream, localName.c_str(), mrConnection.GetTimeout());
-
- // Done.
- 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());
- }
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareParams::CompareParams()
-// Purpose: Constructor
-// Created: 29/1/04
-//
-// --------------------------------------------------------------------------
-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)
-{ }
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandCompare(const std::vector<std::string> &, const bool *)
-// Purpose: Command to compare data on the store with local data
-// Created: 2003/10/12
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandCompare(const std::vector<std::string> &args, const bool *opts)
-{
- 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
- 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
- LatestFileUploadTime = FileModificationTime(st) -
- SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
- }
- else
- {
- BOX_WARNING("Failed to determine the time of the last "
- "synchronisation -- checks not performed.");
- }
- }
-
- // 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.QuickCompare())
- {
- BOX_WARNING("Quick compare used -- file attributes are not "
- "checked.");
- }
-
- if(!opts['l'] && opts['a'] && args.size() == 0)
- {
- // Compare all locations
- 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(*pLocName, params);
- }
- }
- else if(opts['l'] && !opts['a'] && args.size() == 1)
- {
- // Compare one location
- CompareLocation(args[0], params);
- }
- else if(!opts['l'] && !opts['a'] && args.size() == 2)
- {
- // 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.IgnoreExcludes())
- {
- BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes.");
- return;
- }
- else
- {
- // Do compare
- Compare(args[0], args[1], params);
- }
- }
- else
- {
- BOX_ERROR("Incorrect usage.\ncompare -a\n or compare -l <location-name>\n or compare <store-dir-name> <local-dir-name>");
- return;
- }
-
- 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'])
- {
- if (params.mUncheckedFiles != 0)
- {
- SetReturnCode(ReturnCode::Compare_Error);
- }
- else if (params.mDifferences != 0)
- {
- SetReturnCode(ReturnCode::Compare_Different);
- }
- else
- {
- SetReturnCode(ReturnCode::Compare_Same);
- }
- }
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareLocation(const std::string &, BackupQueries::CompareParams &)
-// Purpose: Compare a location
-// Created: 2003/10/13
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CompareLocation(const std::string &rLocation,
- BoxBackupCompareParams &rParams)
-{
- // Find the location's sub configuration
- const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
- if(!locations.SubConfigurationExists(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
-
- // Generate the exclude lists
- if(!rParams.IgnoreExcludes())
- {
- rParams.LoadExcludeLists(loc);
- }
-
- // Then get it compared
- Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams);
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// 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, 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
-
- // Get the directory ID of the directory -- only use current data
- int64_t dirID = FindDirectoryObjectID(storeDirEncoded);
-
- // Found?
- if(dirID == 0)
- {
- 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;
- }
-
- // Go!
- Compare(dirID, storeDirEncoded, localDirEncoded, rParams);
-}
-
-void BackupQueries::CompareOneFile(int64_t DirID,
- BackupStoreDirectory::Entry *pEntry,
- const std::string& rLocalPath,
- const std::string& rStorePath,
- BoxBackupCompareParams &rParams)
-{
- int64_t fileId = pEntry->GetObjectID();
- int64_t fileSize = 0;
-
- EMU_STRUCT_STAT st;
- if(EMU_STAT(rLocalPath.c_str(), &st) == 0)
- {
- fileSize = st.st_size;
- }
-
- try
- {
- // Files the same flag?
- bool equal = true;
-
- // File modified after last sync flag
- bool modifiedAfterLastSync = false;
-
- bool hasDifferentAttribs = false;
-
- bool alreadyReported = false;
-
- if(rParams.QuickCompare())
- {
- // Compare file -- fetch it
- mrConnection.QueryGetBlockIndexByID(fileId);
-
- // Stream containing block index
- std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream());
-
- // Compare
- equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(
- rLocalPath.c_str(), *blockIndexStream,
- mrConnection.GetTimeout());
- }
- else
- {
- // Compare file -- fetch it
- mrConnection.QueryGetFile(DirID, pEntry->GetObjectID());
-
- // Stream containing encoded file
- std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
-
- // Decode it
- std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream;
-
- // Got additional attributes?
- if(pEntry->HasAttributes())
- {
- // Use these attributes
- const StreamableMemBlock &storeAttr(pEntry->GetAttributes());
- BackupClientFileAttributes attr(storeAttr);
- fileOnServerStream.reset(
- BackupStoreFile::DecodeFileStream(
- *objectStream,
- mrConnection.GetTimeout(),
- &attr).release());
- }
- else
- {
- // Use attributes stored in file
- fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release());
- }
-
- // Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid.
- if(!fileOnServerStream.get())
- {
- THROW_EXCEPTION(BackupStoreException, Internal)
- }
-
- // Compare attributes
- BackupClientFileAttributes localAttr;
- box_time_t fileModTime = 0;
- localAttr.ReadAttributes(rLocalPath.c_str(), false /* don't zero mod times */, &fileModTime);
- modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime());
- bool ignoreAttrModTime = true;
-
- #ifdef WIN32
- // attr mod time is really
- // creation time, so check it
- ignoreAttrModTime = false;
- #endif
-
- if(!rParams.IgnoreAttributes() &&
- #ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE
- !fileOnServerStream->IsSymLink() &&
- #endif
- !localAttr.Compare(fileOnServerStream->GetAttributes(),
- ignoreAttrModTime,
- fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */))
- {
- hasDifferentAttribs = true;
- }
-
- // Compare contents, if it's a regular file not a link
- // Remember, we MUST read the entire stream from the server.
- SelfFlushingStream flushObject(*objectStream);
-
- if(!fileOnServerStream->IsSymLink())
- {
- SelfFlushingStream flushFile(*fileOnServerStream);
- // Open the local file
- std::auto_ptr<FileStream> apLocalFile;
-
- try
- {
- apLocalFile.reset(new FileStream(rLocalPath.c_str()));
- }
- catch(std::exception &e)
- {
- rParams.NotifyLocalFileReadFailed(rLocalPath,
- rStorePath, fileSize, e);
- alreadyReported = true;
- }
- catch(...)
- {
- rParams.NotifyLocalFileReadFailed(rLocalPath,
- rStorePath, fileSize);
- alreadyReported = true;
- }
-
- if(apLocalFile.get())
- {
- equal = apLocalFile->CompareWith(*fileOnServerStream,
- mrConnection.GetTimeout());
- }
- }
- }
-
- rParams.NotifyFileCompared(rLocalPath, rStorePath, fileSize,
- hasDifferentAttribs, !equal, modifiedAfterLastSync,
- pEntry->HasAttributes());
- }
- catch(BoxException &e)
- {
- rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize,
- e);
- }
- catch(std::exception &e)
- {
- rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize,
- e);
- }
- catch(...)
- {
- rParams.NotifyDownloadFailed(rLocalPath, rStorePath, fileSize);
- }
-}
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::Compare(int64_t, const std::string &,
-// const std::string &, BackupQueries::CompareParams &)
-// Purpose: Compare a store directory against a local directory
-// Created: 2003/10/13
-//
-// --------------------------------------------------------------------------
-void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir,
- const std::string &rLocalDir, BoxBackupCompareParams &rParams)
-{
- rParams.NotifyDirComparing(rLocalDir, rStoreDir);
-
- // Get info on the local directory
- EMU_STRUCT_STAT st;
- if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0)
- {
- // What kind of error?
- if(errno == ENOTDIR || errno == ENOENT)
- {
- rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir);
- }
- else
- {
- rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
- }
- return;
- }
-
- // Get the directory listing from the store
- mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING,
- // get everything
- BackupProtocolListDirectory::Flags_OldVersion |
- BackupProtocolListDirectory::Flags_Deleted,
- // except for old versions and deleted files
- true /* want attributes */);
-
- // Retrieve the directory from the stream following
- BackupStoreDirectory dir;
- std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
- dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
-
- // Test out the attributes
- if(!dir.HasAttributes())
- {
- rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir);
- }
- else
- {
- // Fetch the attributes
- const StreamableMemBlock &storeAttr(dir.GetAttributes());
- BackupClientFileAttributes attr(storeAttr);
-
- // Get attributes of local directory
- BackupClientFileAttributes localAttr;
- localAttr.ReadAttributes(rLocalDir.c_str(),
- true /* directories have zero mod times */);
-
- if(attr.Compare(localAttr, true, true /* ignore modification times */))
- {
- 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);
- }
- }
-
- // Open the local directory
- DIR *dirhandle = ::opendir(rLocalDir.c_str());
- if(dirhandle == 0)
- {
- rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
- return;
- }
-
- try
- {
- // Read the files and directories into sets
- std::set<std::string> localFiles;
- std::set<std::string> localDirs;
- struct dirent *localDirEn = 0;
- while((localDirEn = readdir(dirhandle)) != 0)
- {
- // Not . and ..!
- if(localDirEn->d_name[0] == '.' &&
- (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;
- }
-
- std::string localDirPath(MakeFullPath(rLocalDir,
- localDirEn->d_name));
- std::string storeDirPath(rStoreDir + "/" +
- localDirEn->d_name);
-
-#ifndef HAVE_VALID_DIRENT_D_TYPE
- EMU_STRUCT_STAT st;
- if(EMU_LSTAT(localDirPath.c_str(), &st) != 0)
- {
- // Check whether dir is excluded before trying
- // to stat it, to fix problems with .gvfs
- // directories that are not readable by root
- // causing compare to crash:
- // http://lists.boxbackup.org/pipermail/boxbackup/2010-January/000013.html
- if(rParams.IsExcludedDir(localDirPath))
- {
- rParams.NotifyExcludedDir(localDirPath,
- storeDirPath);
- continue;
- }
- else
- {
- THROW_EXCEPTION_MESSAGE(CommonException,
- OSFileError, localDirPath);
- }
- }
-
- // Entry -- file or dir?
- if(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
- {
- // File or symbolic link
- localFiles.insert(std::string(localDirEn->d_name));
- }
- else if(S_ISDIR(st.st_mode))
- {
- // Directory
- localDirs.insert(std::string(localDirEn->d_name));
- }
-#else
- // Entry -- file or dir?
- if(localDirEn->d_type == DT_REG || localDirEn->d_type == DT_LNK)
- {
- // File or symbolic link
- localFiles.insert(std::string(localDirEn->d_name));
- }
- else if(localDirEn->d_type == DT_DIR)
- {
- // Directory
- localDirs.insert(std::string(localDirEn->d_name));
- }
-#endif
- }
- // Close directory
- if(::closedir(dirhandle) != 0)
- {
- BOX_LOG_SYS_ERROR("Failed to close local directory "
- "'" << rLocalDir << "'");
- }
- dirhandle = 0;
-
- // Do the same for the store directories
- std::set<std::pair<std::string, BackupStoreDirectory::Entry *> > storeFiles;
- std::set<std::pair<std::string, BackupStoreDirectory::Entry *> > storeDirs;
-
- BackupStoreDirectory::Iterator i(dir);
- BackupStoreDirectory::Entry *storeDirEn = 0;
- while((storeDirEn = i.Next()) != 0)
- {
- // Decrypt filename
- BackupStoreFilenameClear name(storeDirEn->GetName());
-
- // What is it?
- if((storeDirEn->GetFlags() & BackupStoreDirectory::Entry::Flags_File) == BackupStoreDirectory::Entry::Flags_File)
- {
- // File
- storeFiles.insert(std::pair<std::string, BackupStoreDirectory::Entry *>(name.GetClearFilename(), storeDirEn));
- }
- else
- {
- // Dir
- storeDirs.insert(std::pair<std::string, BackupStoreDirectory::Entry *>(name.GetClearFilename(), storeDirEn));
- }
- }
-
-#ifdef _MSC_VER
- typedef std::set<std::string>::iterator string_set_iter_t;
-#else
- typedef std::set<std::string>::const_iterator string_set_iter_t;
-#endif
-
- // 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);
-
- 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
- rParams.NotifyLocalFileMissing(localPath,
- storePath);
- }
- else
- {
- CompareOneFile(DirID, i->second, localPath,
- storePath, rParams);
-
- // Remove from set so that we know it's been compared
- localFiles.erase(local);
- }
- }
-
- // Report any files which exist locally, but not on the store
- for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i)
- {
- std::string localPath(MakeFullPath(rLocalDir, *i));
- std::string storePath(rStoreDir + "/" + *i);
-
- // Should this be ignored (ie is excluded)?
- if(!rParams.IsExcludedFile(localPath))
- {
- bool modifiedAfterLastSync = false;
-
- EMU_STRUCT_STAT st;
- if(EMU_STAT(localPath.c_str(), &st) == 0)
- {
- if(FileModificationTime(st) >
- rParams.LatestFileUploadTime())
- {
- modifiedAfterLastSync = true;
- }
- }
-
- rParams.NotifyRemoteFileMissing(localPath,
- storePath, modifiedAfterLastSync);
- }
- else
- {
- rParams.NotifyExcludedFile(localPath,
- storePath);
- }
- }
-
- // Finished with the files, clear the sets to reduce memory usage slightly
- localFiles.clear();
- storeFiles.clear();
-
- // 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)
- {
- std::string localPath(MakeFullPath(rLocalDir, i->first));
- std::string storePath(rStoreDir + "/" + i->first);
-
- // Does the directory exist locally?
- string_set_iter_t local(localDirs.find(i->first));
- if(local == localDirs.end() &&
- rParams.IsExcludedDir(localPath))
- {
- rParams.NotifyExcludedFileNotDeleted(localPath,
- storePath);
- }
- else if(local == localDirs.end())
- {
- // Not found -- report
- rParams.NotifyLocalFileMissing(localPath,
- storePath);
- }
- else if(rParams.IsExcludedDir(localPath))
- {
- // don't recurse into excluded directories
- }
- else
- {
- // Compare directory
- Compare(i->second->GetObjectID(),
- storePath, localPath, rParams);
-
- // Remove from set so that we know it's been compared
- localDirs.erase(local);
- }
- }
-
- // 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)
- {
- std::string localPath(MakeFullPath(rLocalDir, *i));
- std::string storePath(rStoreDir + "/" + *i);
-
- // Should this be ignored (ie is excluded)?
- if(!rParams.IsExcludedDir(localPath))
- {
- 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.NotifyExcludedDir(localPath, storePath);
- }
- }
- }
- catch(...)
- {
- if(dirhandle != 0)
- {
- ::closedir(dirhandle);
- }
- throw;
- }
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandRestore(const std::vector<std::string> &, const bool *)
-// Purpose: Restore a directory
-// Created: 23/11/03
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandRestore(const std::vector<std::string> &args, const bool *opts)
-{
- // Check arguments
- if(args.size() < 1 || args.size() > 2)
- {
- BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> "
- "[<local-name>]");
- return;
- }
-
- // Restoring deleted things?
- bool restoreDeleted = opts['d'];
-
- std::string storeDirEncoded;
-
- // Get directory ID
- int64_t dirID = 0;
- if(opts['i'])
- {
- // Specified as ID.
- dirID = ::strtoll(args[0].c_str(), 0, 16);
- if(dirID == std::numeric_limits<long long>::min() || dirID == std::numeric_limits<long long>::max() || dirID == 0)
- {
- BOX_ERROR("Not a valid object ID (specified in hex): "
- << args[0]);
- return;
- }
- std::ostringstream oss;
- oss << BOX_FORMAT_OBJECTID(args[0]);
- storeDirEncoded = oss.str();
- }
- else
- {
-#ifdef WIN32
- if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded))
- return;
-#else
- storeDirEncoded = args[0];
-#endif
-
- // Look up directory ID
- dirID = FindDirectoryObjectID(storeDirEncoded,
- false /* no old versions */,
- restoreDeleted /* find deleted dirs */);
- }
-
- // Allowable?
- if(dirID == 0)
- {
- BOX_ERROR("Directory '" << args[0] << "' not found on server");
- return;
- }
-
- if(dirID == BackupProtocolListDirectory::RootDirectory)
- {
- BOX_ERROR("Cannot restore the root directory -- restore locations individually.");
- return;
- }
-
- std::string localName;
-
- if(args.size() == 2)
- {
- #ifdef WIN32
- if(!ConvertConsoleToUtf8(args[1].c_str(), localName))
- {
- return;
- }
- #else
- localName = args[1];
- #endif
- }
- else
- {
- localName = args[0];
- }
-
- // Go and restore...
- int result;
-
- try
- {
- // At TRACE level, we print a line for each file and
- // directory, so we don't need dots.
-
- result = BackupClientRestore(mrConnection, dirID,
- storeDirEncoded.c_str(), localName.c_str(),
- true /* print progress dots */, restoreDeleted,
- false /* don't undelete after restore! */,
- opts['r'] /* resume? */,
- opts['f'] /* force continue after errors */);
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Failed to restore: " << e.what());
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
- catch(...)
- {
- BOX_ERROR("Failed to restore: unknown exception");
- SetReturnCode(ReturnCode::Command_Error);
- return;
- }
-
- switch(result)
- {
- case Restore_Complete:
- 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(ReturnCode::Command_Error);
- break;
-
- case Restore_TargetExists:
- BOX_ERROR("The target directory exists. You cannot restore "
- "over an existing directory.");
- SetReturnCode(ReturnCode::Command_Error);
- break;
-
- 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(ReturnCode::Command_Error);
- break;
-
- case Restore_UnknownError:
- BOX_ERROR("Unknown error during restore.");
- SetReturnCode(ReturnCode::Command_Error);
- break;
-
- default:
- BOX_ERROR("Unknown restore result " << result << ".");
- SetReturnCode(ReturnCode::Command_Error);
- break;
- }
-}
-
-
-
-// These are autogenerated by a script.
-extern const char *help_commands[];
-extern const char *help_text[];
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandHelp(const std::vector<std::string> &args)
-// Purpose: Display help on commands
-// Created: 15/2/04
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandHelp(const std::vector<std::string> &args)
-{
- if(args.size() == 0)
- {
- // Display a list of all commands
- printf("Available commands are:\n");
- for(int c = 0; help_commands[c] != 0; ++c)
- {
- printf(" %s\n", help_commands[c]);
- }
- printf("Type \"help <command>\" for more information on a command.\n\n");
- }
- else
- {
- // Display help on a particular command
- int c;
- for(c = 0; help_commands[c] != 0; ++c)
- {
- if(::strcmp(help_commands[c], args[0].c_str()) == 0)
- {
- // Found the command, print help
- printf("\n%s\n", help_text[c]);
- break;
- }
- }
- if(help_commands[c] == 0)
- {
- printf("No help found for command '%s'\n", args[0].c_str());
- }
- }
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandUsage()
-// Purpose: Display storage space used on server
-// Created: 19/4/04
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CommandUsage(const bool *opts)
-{
- bool MachineReadable = opts['m'];
-
- // Request full details from the server
- std::auto_ptr<BackupProtocolAccountUsage> usage(mrConnection.QueryGetAccountUsage());
-
- // Display each entry in turn
- int64_t hardLimit = usage->GetBlocksHardLimit();
- int32_t blockSize = usage->GetBlockSize();
- 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, 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, bool MachineReadable)
-{
- std::cout << FormatUsageLineStart(Name, MachineReadable) <<
- FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize,
- MachineReadable) << std::endl;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CommandUndelete(const std::vector<std::string> &, const bool *)
-// Purpose: Undelete a directory
-// Created: 23/11/03
-//
-// --------------------------------------------------------------------------
-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 <name> or undelete -i <object-id>");
- return;
- }
-
-#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 */
- BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
- /* include old and deleted files */
- BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
- &flagsOut);
-
- if (fileId == 0)
- {
- // error already reported
- return;
- }
-
- // Undelete it on the store
- try
- {
- // Undelete object
- if(flagsOut & BackupProtocolListDirectory::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("Incorrect usage. delete <name>");
- return;
- }
-
-#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 */
- BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
- /* exclude old and deleted files */
- BackupProtocolListDirectory::Flags_OldVersion |
- BackupProtocolListDirectory::Flags_Deleted,
- &flagsOut);
-
- if (fileId == 0)
- {
- // error already reported
- return;
- }
-
- BackupStoreFilenameClear fn(fileName);
-
- // Delete it on the store
- try
- {
- // Delete object
- if(flagsOut & BackupProtocolListDirectory::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");
- }
-}