// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// 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 <string.h>
#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 <set>
#include "BackupQueries.h"
#include "Utils.h"
#include "Configuration.h"
#include "autogen_BackupProtocolClient.h"
#include "BackupStoreFilenameClear.h"
#include "BackupStoreDirectory.h"
#include "IOStream.h"
#include "BoxTimeToText.h"
#include "FileStream.h"
#include "BackupStoreFile.h"
#include "TemporaryDirectory.h"
#include "FileModificationTime.h"
#include "BackupClientFileAttributes.h"
#include "CommonException.h"
#include "BackupClientRestore.h"
#include "BackupStoreException.h"
#include "ExcludeList.h"
#include "BackupClientMakeExcludeList.h"
#include "MemLeakFindOn.h"
#define COMPARE_RETURN_SAME 1
#define COMPARE_RETURN_DIFFERENT 2
#define COMPARE_RETURN_ERROR 3
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::BackupQueries()
// Purpose: Constructor
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration)
: mrConnection(rConnection),
mrConfiguration(rConfiguration),
mQuitNow(false),
mRunningAsRoot(false),
mWarnedAboutOwnerAttributes(false),
mReturnCode(0) // default return code
{
mRunningAsRoot = (::geteuid() == 0);
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::~BackupQueries()
// Purpose: Destructor
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
BackupQueries::~BackupQueries()
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::DoCommand(const char *)
// Purpose: Perform a command
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
void BackupQueries::DoCommand(const char *Command)
{
// is the command a shell command?
if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0')
{
// Yes, run shell command
::system(Command + 3);
return;
}
// split command into components
std::vector<std::string> cmdElements;
std::string options;
{
const char *c = Command;
bool inQuoted = false;
bool inOptions = false;
std::string s;
while(*c != 0)
{
// Terminating char?
if(*c == ((inQuoted)?'"':' '))
{
if(!s.empty()) cmdElements.push_back(s);
s.resize(0);
inQuoted = false;
inOptions = false;
}
else
{
// No. Start of quoted parameter?
if(s.empty() && *c == '"')
{
inQuoted = true;
}
// Start of options?
else if(s.empty() && *c == '-')
{
inOptions = true;
}
else
{
if(inOptions)
{
// Option char
options += *c;
}
else
{
// Normal string char
s += *c;
}
}
}
++c;
}
if(!s.empty()) cmdElements.push_back(s);
}
// Check...
if(cmdElements.size() < 1)
{
// blank command
return;
}
// 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};
#define COMMAND_Quit 0
#define COMMAND_Exit 1
#define COMMAND_List 2
#define COMMAND_pwd 3
#define COMMAND_cd 4
#define COMMAND_lcd 5
#define COMMAND_sh 6
#define COMMAND_GetObject 7
#define COMMAND_Get 8
#define COMMAND_Compare 9
#define COMMAND_Restore 10
#define COMMAND_Help 11
#define COMMAND_Usage 12
#define COMMAND_Undelete 13
static const char *alias[] = {"ls", 0};
static const int aliasIs[] = {COMMAND_List, 0};
// Work out which command it is...
int cmd = 0;
while(commandNames[cmd] != 0 && ::strcmp(cmdElements[0].c_str(), commandNames[cmd]) != 0)
{
cmd++;
}
if(commandNames[cmd] == 0)
{
// Check for aliases
int a;
for(a = 0; alias[a] != 0; ++a)
{
if(::strcmp(cmdElements[0].c_str(), alias[a]) == 0)
{
// Found an alias
cmd = aliasIs[a];
break;
}
}
// No such command
if(alias[a] == 0)
{
printf("Unrecognised command: %s\n", Command);
return;
}
}
// Arguments
std::vector<std::string> args(cmdElements.begin() + 1, cmdElements.end());
// Set up options
bool opts[256];
for(int o = 0; o < 256; ++o) opts[o] = false;
// BLOCK
{
// options
const char *c = options.c_str();
while(*c != 0)
{
// Valid option?
if(::strchr(validOptions[cmd], *c) == NULL)
{
printf("Invalid option '%c' for command %s\n", *c, commandNames[cmd]);
return;
}
opts[(int)*c] = true;
++c;
}
}
if(cmd != COMMAND_Quit && cmd != COMMAND_Exit)
{
// If not a quit command, set the return code to zero
SetReturnCode(0);
}
// Handle command
switch(cmd)
{
case COMMAND_Quit:
case COMMAND_Exit:
mQuitNow = true;
break;
case COMMAND_List:
CommandList(args, opts);
break;
case COMMAND_pwd:
{
// Simple implementation, so do it here
printf("%s (%08llx)\n",
GetCurrentDirectoryName().c_str(),
(long long)GetCurrentDirectoryID());
}
break;
case COMMAND_cd:
CommandChangeDir(args, opts);
break;
case COMMAND_lcd:
CommandChangeLocalDir(args);
break;
case COMMAND_sh:
printf("The command to run must be specified as an argument.\n");
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();
break;
case COMMAND_Help:
CommandHelp(args);
break;
case COMMAND_Undelete:
CommandUndelete(args, opts);
break;
default:
break;
}
}
// --------------------------------------------------------------------------
//
// 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)
{
#define LIST_OPTION_RECURSIVE 'r'
#define LIST_OPTION_ALLOWOLD 'o'
#define LIST_OPTION_ALLOWDELETED 'd'
#define LIST_OPTION_NOOBJECTID 'I'
#define LIST_OPTION_NOFLAGS 'F'
#define LIST_OPTION_TIMES 't'
#define LIST_OPTION_SIZEINBLOCKS 's'
#define LIST_OPTION_DISPLAY_HASH 'h'
// 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)
{
printf("Directory '%s' not found on store\n",
args[0].c_str());
return;
}
}
// List it
List(rootDir, listRoot, opts, true /* first level to list */);
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CommandList2(int64_t, const std::string &, const 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)
{
// Generate exclude flags
int16_t excludeFlags = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING;
if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion;
if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted;
// Do communication
mrConnection.QueryListDirectory(
DirID,
BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // both files and 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... display everything
BackupStoreDirectory::Iterator i(dir);
BackupStoreDirectory::Entry *en = 0;
while((en = i.Next()) != 0)
{
// Display this entry
BackupStoreFilenameClear clear(en->GetName());
// Object ID?
if(!opts[LIST_OPTION_NOOBJECTID])
{
// add object ID to line
#ifdef _MSC_VER
printf("%08I64x ", (int64_t)en->GetObjectID());
#else
printf("%08llx ", (long long)en->GetObjectID());
#endif
}
// 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';
printf(displayflags);
if(en_flags != 0)
{
printf("[ERROR: Entry has additional flags set] ");
}
}
if(opts[LIST_OPTION_TIMES])
{
// Show times...
std::string time = BoxTimeToISO8601String(
en->GetModificationTime());
printf("%s ", time.c_str());
}
if(opts[LIST_OPTION_DISPLAY_HASH])
{
#ifdef _MSC_VER
printf("%016I64x ", (int64_t)en->GetAttributesHash());
#else
printf("%016llx ", (long long)en->GetAttributesHash());
#endif
}
if(opts[LIST_OPTION_SIZEINBLOCKS])
{
#ifdef _MSC_VER
printf("%05I64d ", (int64_t)en->GetSizeInBlocks());
#else
printf("%05lld ", (long long)en->GetSizeInBlocks());
#endif
}
// add name
if(!FirstLevel)
{
#ifdef WIN32
std::string listRootDecoded;
if(!ConvertUtf8ToConsole(rListRoot.c_str(),
listRootDecoded)) return;
printf("%s/", listRootDecoded.c_str());
#else
printf("%s/", rListRoot.c_str());
#endif
}
#ifdef WIN32
{
std::string fileName;
if(!ConvertUtf8ToConsole(
clear.GetClearFilename().c_str(), fileName))
return;
printf("%s", fileName.c_str());
}
#else
printf("%s", clear.GetClearFilename().c_str());
#endif
if(!en->GetName().IsEncrypted())
{
printf("[FILENAME NOT ENCRYPTED]");
}
printf("\n");
// 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 */);
}
}
}
}
// --------------------------------------------------------------------------
//
// 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 = BackupProtocolClientListDirectory::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 = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING;
if(!AllowOldVersion) excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion;
if(!AllowDeletedDirs) excludeFlags |= BackupProtocolClientListDirectory::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):BackupProtocolClientListDirectory::RootDirectory;
}
else
{
// At root anyway
dirID = BackupProtocolClientListDirectory::RootDirectory;
}
}
else
{
// Not blank element. Read current directory.
std::auto_ptr<BackupProtocolClientSuccess> dirreply(mrConnection.QueryListDirectory(
dirID,
BackupProtocolClientListDirectory::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 BackupProtocolClientListDirectory::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)
{
printf("Incorrect usage.\ncd [-o] [-d] <directory>\n");
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)
{
printf("Directory '%s' not found\n", args[0].c_str());
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)
{
printf("Incorrect usage.\nlcd <local-directory>\n");
return;
}
// Try changing directory
#ifdef WIN32
std::string dirName;
if(!ConvertConsoleToUtf8(args[0].c_str(), dirName)) 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());
return;
}
// Report current dir
char wd[PATH_MAX];
if(::getcwd(wd, PATH_MAX) == 0)
{
printf("Error getting current directory\n");
return;
}
#ifdef WIN32
if(!ConvertUtf8ToConsole(wd, dirName)) return;
printf("Local current directory is now '%s'\n", dirName.c_str());
#else
printf("Local current directory is now '%s'\n", 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)
{
printf("Incorrect usage.\ngetobject <object-id> <local-filename>\n");
return;
}
int64_t id = ::strtoll(args[0].c_str(), 0, 16);
if(id == LLONG_MIN || id == LLONG_MAX || id == 0)
{
printf("Not a valid object ID (specified in hex)\n");
return;
}
// Does file exist?
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());
return;
}
// Open file
FileStream out(args[1].c_str(), O_WRONLY | O_CREAT | O_EXCL);
// Request that object
try
{
// Request object
std::auto_ptr<BackupProtocolClientSuccess> getobj(mrConnection.QueryGetObject(id));
if(getobj->GetObjectID() != BackupProtocolClientGetObject::NoObject)
{
// Stream that object out to the file
std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
objectStream->CopyStreamTo(out);
printf("Object ID %08llx fetched successfully.\n", id);
}
else
{
printf("Object does not exist on store.\n");
::unlink(args[1].c_str());
}
}
catch(...)
{
::unlink(args[1].c_str());
printf("Error occured fetching object.\n");
}
}
// --------------------------------------------------------------------------
//
// 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(const 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"
"get <remote-filename> [<local-filename>] or\n"
"get -i <object-id> <local-filename>\n");
return;
}
// Find object ID somehow
int64_t id;
std::string localName;
// BLOCK
{
// Need to look it up in the current directory
mrConnection.QueryListDirectory(
GetCurrentDirectoryID(),
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 */);
// Retrieve the directory from the stream following
BackupStoreDirectory dir;
std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
if(opts['i'])
{
// Specified as ID.
id = ::strtoll(args[0].c_str(), 0, 16);
if(id == LLONG_MIN || id == LLONG_MAX || id == 0)
{
printf("Not a valid object ID (specified in hex)\n");
return;
}
// Check that the item is actually in the directory
if(dir.FindEntryByID(id) == 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);
return;
}
// Must have a local name in the arguments (check at beginning of function ensures this)
localName = args[1];
}
else
{
// Specified by name, find the object in the directory to get the ID
BackupStoreDirectory::Iterator i(dir);
#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());
return;
}
id = en->GetObjectID();
// Local name is the last argument, which is either the looked up filename, or
// a filename specified by the user.
localName = args[args.size() - 1];
}
}
// Does local file already exist? (don't want to overwrite)
struct stat st;
if(::stat(localName.c_str(), &st) == 0 || errno != ENOENT)
{
printf("The local file %s already exists, will not overwrite it.\n", localName.c_str());
return;
}
// Request it from the store
try
{
// Request object
mrConnection.QueryGetFile(GetCurrentDirectoryID(), id);
// Stream containing encoded file
std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
// Decode it
BackupStoreFile::DecodeFile(*objectStream, localName.c_str(), mrConnection.GetTimeout());
// Done.
printf("Object ID %08llx fetched sucessfully.\n", id);
}
catch(...)
{
::unlink(localName.c_str());
printf("Error occured fetching file.\n");
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CompareParams::CompareParams()
// Purpose: Constructor
// Created: 29/1/04
//
// --------------------------------------------------------------------------
BackupQueries::CompareParams::CompareParams()
: mQuickCompare(false),
mIgnoreExcludes(false),
mDifferences(0),
mDifferencesExplainedByModTime(0),
mExcludedDirs(0),
mExcludedFiles(0),
mpExcludeFiles(0),
mpExcludeDirs(0),
mLatestFileUploadTime(0)
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CompareParams::~CompareParams()
// Purpose: Destructor
// Created: 29/1/04
//
// --------------------------------------------------------------------------
BackupQueries::CompareParams::~CompareParams()
{
DeleteExcludeLists();
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CompareParams::DeleteExcludeLists()
// Purpose: Delete the include lists contained
// Created: 29/1/04
//
// --------------------------------------------------------------------------
void BackupQueries::CompareParams::DeleteExcludeLists()
{
if(mpExcludeFiles != 0)
{
delete mpExcludeFiles;
mpExcludeFiles = 0;
}
if(mpExcludeDirs != 0)
{
delete mpExcludeDirs;
mpExcludeDirs = 0;
}
}
// --------------------------------------------------------------------------
//
// 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)
{
// Parameters, including count of differences
BackupQueries::CompareParams params;
params.mQuickCompare = opts['q'];
params.mIgnoreExcludes = opts['E'];
// Try and work out the time before which all files should be on the server
{
std::string syncTimeFilename(mrConfiguration.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR);
syncTimeFilename += "last_sync_start";
// Stat it to get file time
struct stat st;
if(::stat(syncTimeFilename.c_str(), &st) == 0)
{
// Files modified after this time shouldn't be on the server, so report errors slightly differently
params.mLatestFileUploadTime = FileModificationTime(st)
- SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
}
else
{
printf("Warning: couldn't determine the time of the last synchronisation -- checks not performed.\n");
}
}
// Quick compare?
if(params.mQuickCompare)
{
printf("WARNING: Quick compare used -- file attributes are not checked.\n");
}
if(!opts['l'] && opts['a'] && args.size() == 0)
{
// Compare all locations
const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
for(std::list<std::pair<std::string, Configuration> >::const_iterator i = locations.mSubConfigurations.begin();
i != locations.mSubConfigurations.end(); ++i)
{
CompareLocation(i->first, 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.mIgnoreExcludes)
{
printf("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes\n");
return;
}
else
{
// Do compare
Compare(args[0], args[1], params);
}
}
else
{
printf("Incorrect usage.\ncompare -a\n or compare -l <location-name>\n or compare <store-dir-name> <local-dir-name>\n");
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);
// Set return code?
if(opts['c'])
{
SetReturnCode((params.mDifferences == 0)?COMPARE_RETURN_SAME:COMPARE_RETURN_DIFFERENT);
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CompareLocation(const std::string &, BackupQueries::CompareParams &)
// Purpose: Compare a location
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries::CompareParams &rParams)
{
// Find the location's sub configuration
const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
if(!locations.SubConfigurationExists(rLocation.c_str()))
{
printf("Location %s does not exist.\n", rLocation.c_str());
return;
}
const Configuration &loc(locations.GetSubConfiguration(rLocation.c_str()));
try
{
// Generate the exclude lists
if(!rParams.mIgnoreExcludes)
{
rParams.mpExcludeFiles = BackupClientMakeExcludeList_Files(loc);
rParams.mpExcludeDirs = BackupClientMakeExcludeList_Dirs(loc);
}
// Then get it compared
Compare(std::string("/") + rLocation,
loc.GetKeyValue("Path"), rParams);
}
catch(...)
{
// Clean up
rParams.DeleteExcludeLists();
throw;
}
// Delete exclude lists
rParams.DeleteExcludeLists();
}
// --------------------------------------------------------------------------
//
// 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, BackupQueries::CompareParams &rParams)
{
#ifdef WIN32
std::string storeDirEncoded;
if(!ConvertConsoleToUtf8(rStoreDir.c_str(), storeDirEncoded)) return;
#else
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)
{
printf("Local directory '%s' exists, but "
"server directory '%s' does not exist\n",
rLocalDir.c_str(), rStoreDir.c_str());
rParams.mDifferences ++;
return;
}
#ifdef WIN32
std::string localDirEncoded;
if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
#else
std::string localDirEncoded(rLocalDir);
#endif
// Go!
Compare(dirID, storeDirEncoded, localDirEncoded, rParams);
}
// --------------------------------------------------------------------------
//
// 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, BackupQueries::CompareParams &rParams)
{
#ifdef WIN32
// By this point, rStoreDir and rLocalDir should be in UTF-8 encoding
std::string localName;
std::string storeName;
if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localName)) return;
if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeName)) return;
#else
const std::string& localName(rLocalDir);
const std::string& storeName(rStoreDir);
#endif
// Get info on the local directory
struct stat st;
if(::lstat(rLocalDir.c_str(), &st) != 0)
{
// 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());
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());
}
else
{
printf("ERROR: stat on local dir '%s'\n",
localName.c_str());
}
return;
}
// Get the directory listing from the store
mrConnection.QueryListDirectory(
DirID,
BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // get everything
BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted, // except for old versions and deleted files
true /* want attributes */);
// 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())
{
printf("Store directory '%s' doesn't have attributes.\n",
storeName.c_str());
}
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 */)))
{
printf("Local directory '%s' has different attributes "
"to store directory '%s'.\n",
localName.c_str(), storeName.c_str());
rParams.mDifferences ++;
}
}
// Open the local directory
DIR *dirhandle = ::opendir(rLocalDir.c_str());
if(dirhandle == 0)
{
printf("ERROR: opendir on local dir '%s'\n", localName.c_str());
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 ..
continue;
}
#ifndef HAVE_VALID_DIRENT_D_TYPE
std::string fn(rLocalDir);
fn += DIRECTORY_SEPARATOR_ASCHAR;
fn += localDirEn->d_name;
struct stat st;
if(::lstat(fn.c_str(), &st) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
// 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)
{
printf("ERROR: closedir on local dir '%s'\n",
localName.c_str());
}
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)
{
// Does the file exist locally?
string_set_iter_t local(localFiles.find(i->first));
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());
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;
// File modified after last sync flag
bool modifiedAfterLastSync = false;
if(rParams.mQuickCompare)
{
// Compare file -- fetch it
mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID());
// Stream containing block index
std::auto_ptr<IOStream> blockIndexStream(mrConnection.ReceiveStream());
// Compare
equal = BackupStoreFile::CompareFileContentsAgainstBlockIndex(localName.c_str(), *blockIndexStream, mrConnection.GetTimeout());
}
else
{
// Compare file -- fetch it
mrConnection.QueryGetFile(DirID, i->second->GetObjectID());
// Stream containing encoded file
std::auto_ptr<IOStream> objectStream(mrConnection.ReceiveStream());
// Decode it
std::auto_ptr<BackupStoreFile::DecodedStream> fileOnServerStream;
// Got additional attibutes?
if(i->second->HasAttributes())
{
// Use these attributes
const StreamableMemBlock &storeAttr(i->second->GetAttributes());
BackupClientFileAttributes attr(storeAttr);
fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout(), &attr).release());
}
else
{
// Use attributes stored in file
fileOnServerStream.reset(BackupStoreFile::DecodeFileStream(*objectStream, mrConnection.GetTimeout()).release());
}
// Should always be something in the auto_ptr, it's how the interface is defined. But be paranoid.
if(!fileOnServerStream.get())
{
THROW_EXCEPTION(BackupStoreException, Internal)
}
// Compare attributes
BackupClientFileAttributes localAttr;
box_time_t fileModTime = 0;
localAttr.ReadAttributes(localName.c_str(), false /* don't zero mod times */, &fileModTime);
modifiedAfterLastSync = (fileModTime > rParams.mLatestFileUploadTime);
if(!localAttr.Compare(fileOnServerStream->GetAttributes(),
true /* ignore attr mod time */,
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());
rParams.mDifferences ++;
if(modifiedAfterLastSync)
{
rParams.mDifferencesExplainedByModTime ++;
printf("(the file above was modified after the last sync time -- might be reason for difference)\n");
}
else if(i->second->HasAttributes())
{
printf("(the file above has had new attributes applied)\n");
}
}
// Compare contents, if it's a regular file not a link
// Remember, we MUST read the entire stream from the server.
if(!fileOnServerStream->IsSymLink())
{
// Open the local file
FileStream l(localName.c_str());
// Size
IOStream::pos_type fileSizeLocal = l.BytesLeftToRead();
IOStream::pos_type fileSizeServer = 0;
// Test the contents
char buf1[2048];
char buf2[2048];
while(fileOnServerStream->StreamDataLeft() && l.StreamDataLeft())
{
int size = fileOnServerStream->Read(buf1, sizeof(buf1), mrConnection.GetTimeout());
fileSizeServer += size;
if(l.Read(buf2, size) != size
|| ::memcmp(buf1, buf2, size) != 0)
{
equal = false;
break;
}
}
// Check read all the data from the server and file -- can't be equal if local and remote aren't the same length
// Can't use StreamDataLeft() test on file, because if it's the same size, it won't know
// it's EOF yet.
if(fileOnServerStream->StreamDataLeft() || fileSizeServer != fileSizeLocal)
{
equal = false;
}
// Must always read the entire decoded string, if it's not a symlink
if(fileOnServerStream->StreamDataLeft())
{
// Absorb all the data remaining
char buffer[2048];
while(fileOnServerStream->StreamDataLeft())
{
fileOnServerStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout());
}
}
}
}
// 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());
rParams.mDifferences ++;
if(modifiedAfterLastSync)
{
rParams.mDifferencesExplainedByModTime ++;
printf("(the file above was modified after the last sync time -- might be reason for difference)\n");
}
else if(i->second->HasAttributes())
{
printf("(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());
}
catch(...)
{
printf("ERROR: (unknown) during file fetch and comparsion for '%s/%s'\n", storeName.c_str(), i->first.c_str());
}
// Remove from set so that we know it's been compared
localFiles.erase(local);
}
}
// 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);
// Should this be ignored (ie is excluded)?
if(rParams.mpExcludeFiles == 0 ||
!(rParams.mpExcludeFiles->IsExcluded(localFileName)))
{
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());
rParams.mDifferences ++;
// Check the file modification time
{
struct stat st;
if(::stat(localFileName.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");
}
}
}
}
else
{
rParams.mExcludedFiles ++;
}
}
// Finished with the files, clear the sets to reduce memory usage slightly
localFiles.clear();
storeFiles.clear();
// Now do the directories, recusively to check subdirectories
for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i)
{
// Does the directory exist locally?
string_set_iter_t local(localDirs.find(i->first));
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());
rParams.mDifferences ++;
}
else
{
// Compare directory
Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, rLocalDir + DIRECTORY_SEPARATOR + i->first, rParams);
// Remove from set so that we know it's been compared
localDirs.erase(local);
}
}
// Report any files which exist on the locally, but not on the store
for(std::set<std::string>::const_iterator i = localDirs.begin(); i != localDirs.end(); ++i)
{
std::string localName(rLocalDir + DIRECTORY_SEPARATOR + *i);
// Should this be ignored (ie is excluded)?
if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localName)))
{
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());
rParams.mDifferences ++;
}
else
{
rParams.mExcludedDirs ++;
}
}
}
catch(...)
{
if(dirhandle != 0)
{
::closedir(dirhandle);
}
}
}
// --------------------------------------------------------------------------
//
// 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() != 2)
{
printf("Incorrect usage.\nrestore [-d] [-r] [-i] <directory-name> <local-directory-name>\n");
return;
}
// Restoring deleted things?
bool restoreDeleted = opts['d'];
// Get directory ID
int64_t dirID = 0;
if(opts['i'])
{
// Specified as ID.
dirID = ::strtoll(args[0].c_str(), 0, 16);
if(dirID == LLONG_MIN || dirID == LLONG_MAX || dirID == 0)
{
printf("Not a valid object ID (specified in hex)\n");
return;
}
}
else
{
#ifdef WIN32
std::string storeDirEncoded;
if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded))
return;
#else
const std::string& storeDirEncoded(args[0]);
#endif
// Look up directory ID
dirID = FindDirectoryObjectID(storeDirEncoded,
false /* no old versions */,
restoreDeleted /* find deleted dirs */);
}
// Allowable?
if(dirID == 0)
{
printf("Directory '%s' not found on server\n", args[0].c_str());
return;
}
if(dirID == BackupProtocolClientListDirectory::RootDirectory)
{
printf("Cannot restore the root directory -- restore locations individually.\n");
return;
}
#ifdef WIN32
std::string localName;
if(!ConvertConsoleToUtf8(args[1].c_str(), localName)) return;
#else
std::string localName(args[1]);
#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? */))
{
case Restore_Complete:
printf("Restore complete\n");
break;
case Restore_ResumePossible:
printf("Resume possible -- repeat command with -r flag to resume\n");
break;
case Restore_TargetExists:
printf("The target directory exists. You cannot restore over an existing directory.\n");
break;
default:
printf("ERROR: Unknown restore result.\n");
break;
}
}
// These are autogenerated by a script.
extern char *help_commands[];
extern 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()
{
// Request full details from the server
std::auto_ptr<BackupProtocolClientAccountUsage> usage(mrConnection.QueryGetAccountUsage());
// Display each entry in turn
int64_t hardLimit = usage->GetBlocksHardLimit();
int32_t blockSize = usage->GetBlockSize();
CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit, blockSize);
CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(), hardLimit, blockSize);
CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(), hardLimit, blockSize);
CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(), hardLimit, blockSize);
CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(), hardLimit, blockSize);
CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize);
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupQueries::CommandUsageDisplayEntry(const char *, int64_t, int64_t, int32_t)
// 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)
{
// Calculate size in Mb
double mb = (((double)Size) * ((double)BlockSize)) / ((double)(1024*1024));
int64_t percent = (Size * 100) / HardLimit;
// Bar graph
char bar[41];
unsigned int b = (int)((Size * (sizeof(bar)-1)) / HardLimit);
if(b > sizeof(bar)-1) {b = sizeof(bar)-1;}
for(unsigned int l = 0; l < b; l++)
{
bar[l] = '*';
}
bar[b] = '\0';
// Print the entryj
::printf("%14s %10.1fMb %3d%% %s\n", Name, mb, (int32_t)percent, bar);
}
// --------------------------------------------------------------------------
//
// 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)
{
// Check arguments
if(args.size() != 1)
{
printf("Incorrect usage.\nundelete <directory-name>\n");
return;
}
#ifdef WIN32
std::string storeDirEncoded;
if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return;
#else
const std::string& storeDirEncoded(args[0]);
#endif
// Get directory ID
int64_t dirID = FindDirectoryObjectID(storeDirEncoded,
false /* no old versions */, true /* find deleted dirs */);
// Allowable?
if(dirID == 0)
{
printf("Directory '%s' not found on server\n", args[0].c_str());
return;
}
if(dirID == BackupProtocolClientListDirectory::RootDirectory)
{
printf("Cannot undelete the root directory.\n");
return;
}
// Undelete
mrConnection.QueryUndeleteDirectory(dirID);
}
syntax highlighted by Code2HTML, v. 0.9.1