From 8bee9612b7ec62bb9eddcba0fdc5efcf3554f91c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 13 Sep 2008 15:31:26 +0000 Subject: Allow undelete command to work on files as well as directories. Add delete command that works on files and directories. Document both commands. --- bin/bbackupquery/BackupQueries.cpp | 482 ++++++++++++++++++++++++++----------- bin/bbackupquery/BackupQueries.h | 22 +- bin/bbackupquery/bbackupquery.cpp | 2 +- bin/bbackupquery/documentation.txt | 15 ++ 4 files changed, 373 insertions(+), 148 deletions(-) diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index 92bc5c42..c7a308a7 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -71,8 +71,10 @@ // Created: 2003/10/10 // // -------------------------------------------------------------------------- -BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration) - : mrConnection(rConnection), +BackupQueries::BackupQueries(BackupProtocolClient &rConnection, + const Configuration &rConfiguration, bool readWrite) + : mReadWrite(readWrite), + mrConnection(rConnection), mrConfiguration(rConfiguration), mQuitNow(false), mRunningAsRoot(false), @@ -222,24 +224,32 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) { "help", "" }, { "usage", "" }, { "undelete", "" }, + { "delete", "" }, { NULL, NULL } }; - #define COMMAND_Quit 0 - #define COMMAND_Exit 1 - #define COMMAND_List 2 - #define COMMAND_pwd 3 - #define COMMAND_cd 4 - #define COMMAND_lcd 5 - #define COMMAND_sh 6 - #define COMMAND_GetObject 7 - #define COMMAND_Get 8 - #define COMMAND_Compare 9 - #define COMMAND_Restore 10 - #define COMMAND_Help 11 - #define COMMAND_Usage 12 - #define COMMAND_Undelete 13 - static const char *alias[] = {"ls", 0}; - static const int aliasIs[] = {COMMAND_List, 0}; + + typedef enum + { + Command_Quit = 0, + Command_Exit, + Command_List, + Command_pwd, + Command_cd, + Command_lcd, + Command_sh, + Command_GetObject, + Command_Get, + Command_Compare, + Command_Restore, + Command_Help, + Command_Usage, + Command_Undelete, + Command_Delete, + } + CommandType; + + static const char *alias[] = {"ls", 0}; + static const int aliasIs[] = {Command_List, 0}; // Work out which command it is... int cmd = 0; @@ -293,7 +303,7 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } } - if(cmd != COMMAND_Quit && cmd != COMMAND_Exit) + if(cmd != Command_Quit && cmd != Command_Exit) { // If not a quit command, set the return code to zero SetReturnCode(ReturnCode::Command_OK); @@ -302,16 +312,16 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) // Handle command switch(cmd) { - case COMMAND_Quit: - case COMMAND_Exit: + case Command_Quit: + case Command_Exit: mQuitNow = true; break; - case COMMAND_List: + case Command_List: CommandList(args, opts); break; - case COMMAND_pwd: + case Command_pwd: { // Simple implementation, so do it here BOX_INFO(GetCurrentDirectoryName() << " (" << @@ -319,47 +329,52 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } break; - case COMMAND_cd: + case Command_cd: CommandChangeDir(args, opts); break; - case COMMAND_lcd: + case Command_lcd: CommandChangeLocalDir(args); break; - case COMMAND_sh: + case Command_sh: BOX_ERROR("The command to run must be specified as an argument."); break; - case COMMAND_GetObject: + case Command_GetObject: CommandGetObject(args, opts); break; - case COMMAND_Get: + case Command_Get: CommandGet(args, opts); break; - case COMMAND_Compare: + case Command_Compare: CommandCompare(args, opts); break; - case COMMAND_Restore: + case Command_Restore: CommandRestore(args, opts); break; - case COMMAND_Usage: + case Command_Usage: CommandUsage(); break; - case COMMAND_Help: + case Command_Help: CommandHelp(args); break; - case COMMAND_Undelete: + case Command_Undelete: CommandUndelete(args, opts); break; + case Command_Delete: + CommandDelete(args, opts); + break; + default: + BOX_ERROR("Unknown command: " << Command); break; } } @@ -601,15 +616,18 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool // -------------------------------------------------------------------------- // // Function -// Name: BackupQueries::FindDirectoryObjectID(const std::string &) -// Purpose: Find the object ID of a directory on the store, or return 0 for not found. -// If pStack != 0, the object is set to the stack of directories. -// Will start from the current directory stack. +// Name: BackupQueries::FindDirectoryObjectID(const +// std::string &) +// Purpose: Find the object ID of a directory on the store, +// or return 0 for not found. If pStack != 0, the +// object is set to the stack of directories. +// Will start from the current directory stack. // Created: 2003/10/10 // // -------------------------------------------------------------------------- -int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion, - bool AllowDeletedDirs, std::vector > *pStack) +int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, + bool AllowOldVersion, bool AllowDeletedDirs, + std::vector > *pStack) { // Split up string into elements std::vector dirElements; @@ -935,6 +953,117 @@ void BackupQueries::CommandGetObject(const std::vector &args, const } +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::FindFileID(const std::string& +// rNameOrIdString, const bool *options, +// int64_t *pDirIdOut, std::string* pFileNameOut) +// Purpose: Locate a file on the store (either by name or by +// object ID, depending on opts['i'], where name can +// include a path) and return the file ID, placing the +// directory ID in *pDirIdOut and the filename part +// of the path (if not looking up by ID and not NULL) +// in *pFileNameOut. +// Created: 2008-09-12 +// +// -------------------------------------------------------------------------- +int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString, + const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut, + int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut) +{ + // Find object ID somehow + int64_t fileId; + int64_t dirId = GetCurrentDirectoryID(); + std::string fileName = rNameOrIdString; + + if(!opts['i']) + { + // does this remote filename include a path? + std::string::size_type index = fileName.rfind('/'); + if(index != std::string::npos) + { + std::string dirName(fileName.substr(0, index)); + fileName = fileName.substr(index + 1); + + dirId = FindDirectoryObjectID(dirName); + if(dirId == 0) + { + BOX_ERROR("Directory '" << dirName << + "' not found."); + return 0; + } + } + + if(pFileNameOut) + { + *pFileNameOut = fileName; + } + } + + BackupStoreFilenameClear fn(fileName); + + // Need to look it up in the current directory + mrConnection.QueryListDirectory( + dirId, flagsInclude, flagsExclude, + true /* do want attributes */); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr 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::min() || + fileId == std::numeric_limits::max() || + fileId == 0) + { + BOX_ERROR("Not a valid object ID (specified in hex)."); + return 0; + } + + // Check that the item is actually in the directory + en = dir.FindEntryByID(fileId); + if(en == 0) + { + BOX_ERROR("File ID " << + BOX_FORMAT_OBJECTID(fileId) << + " not found in current directory on store.\n" + "(You can only access files by ID from the " + "current directory.)"); + return 0; + } + } + else + { + // Specified by name, find the object in the directory to get the ID + BackupStoreDirectory::Iterator i(dir); + en = i.FindMatchingClearName(fn); + if(en == 0) + { + BOX_ERROR("Filename '" << rNameOrIdString << "' " + "not found in current directory on store.\n" + "(Subdirectories in path not searched.)"); + return 0; + } + + fileId = en->GetObjectID(); + } + + *pDirIdOut = dirId; + + if(pFlagsOut) + { + *pFlagsOut = en->GetFlags(); + } + + return fileId; +} + // -------------------------------------------------------------------------- // @@ -957,111 +1086,63 @@ void BackupQueries::CommandGet(std::vector args, const bool *opts) } // Find object ID somehow - int64_t fileId; - int64_t dirId = GetCurrentDirectoryID(); + int64_t fileId, dirId; std::string localName; - // BLOCK - { #ifdef WIN32 - for (std::vector::iterator - i = args.begin(); i != args.end(); i++) + for (std::vector::iterator + i = args.begin(); i != args.end(); i++) + { + std::string out; + if(!ConvertConsoleToUtf8(i->c_str(), out)) { - std::string out; - if(!ConvertConsoleToUtf8(i->c_str(), out)) - { - BOX_ERROR("Failed to convert encoding."); - return; - } - *i = out; + BOX_ERROR("Failed to convert encoding."); + return; } + *i = out; + } #endif - std::string fileName(args[0]); + int16_t flagsExclude; - if(!opts['i']) - { - // does this remote filename include a path? - std::string::size_type index = fileName.rfind('/'); - if(index != std::string::npos) - { - std::string dirName(fileName.substr(0, index)); - fileName = fileName.substr(index + 1); - - dirId = FindDirectoryObjectID(dirName); - if(dirId == 0) - { - BOX_ERROR("Directory '" << dirName << - "' not found."); - return; - } - } - } + if(opts['i']) + { + // can retrieve anything by ID + flagsExclude = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING; + } + else + { + // only current versions by name + flagsExclude = + BackupProtocolClientListDirectory::Flags_OldVersion | + BackupProtocolClientListDirectory::Flags_Deleted; + } - BackupStoreFilenameClear fn(fileName); - // Need to look it up in the current directory - mrConnection.QueryListDirectory( - dirId, - BackupProtocolClientListDirectory::Flags_File, // just files - (opts['i'])?(BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING):(BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted), // only current versions - false /* don't want attributes */); + fileId = FindFileID(args[0], opts, &dirId, &localName, + BackupProtocolClientListDirectory::Flags_File, // just files + flagsExclude, NULL /* don't care about flags found */); - // Retrieve the directory from the stream following - BackupStoreDirectory dir; - std::auto_ptr dirstream(mrConnection.ReceiveStream()); - dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); + if (fileId == 0) + { + // error already reported + return; + } - if(opts['i']) + if(opts['i']) + { + // Specified as ID. Must have a local name in the arguments + // (check at beginning of function ensures this) + localName = args[1]; + } + else + { + // Specified by name. Local name already set by FindFileID, + // but may be overridden by user supplying a second argument. + if(args.size() == 2) { - // Specified as ID. - fileId = ::strtoll(args[0].c_str(), 0, 16); - if(fileId == std::numeric_limits::min() || - fileId == std::numeric_limits::max() || - fileId == 0) - { - BOX_ERROR("Not a valid object ID (specified in hex)."); - return; - } - - // Check that the item is actually in the directory - if(dir.FindEntryByID(fileId) == 0) - { - BOX_ERROR("File ID " << - BOX_FORMAT_OBJECTID(fileId) << - " not found in current " - "directory on store.\n" - "(You can only download files by ID " - "from the current directory.)"); - return; - } - - // Must have a local name in the arguments (check at beginning of function ensures this) localName = args[1]; } - else - { - // Specified by name, find the object in the directory to get the ID - BackupStoreDirectory::Iterator i(dir); - BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn); - - if(en == 0) - { - BOX_ERROR("Filename '" << args[0] << "' " - "not found in current " - "directory on store.\n" - "(Subdirectories in path not " - "searched.)"); - return; - } - - fileId = en->GetObjectID(); - - // Local name is the last argument, which is either - // the looked up filename, or a filename specified - // by the user. - localName = args[args.size() - 1]; - } } // Does local file already exist? (don't want to overwrite) @@ -2210,10 +2291,17 @@ void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int // -------------------------------------------------------------------------- void BackupQueries::CommandUndelete(const std::vector &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 "); + BOX_ERROR("Incorrect usage. undelete or undelete -i "); return; } @@ -2223,23 +2311,133 @@ void BackupQueries::CommandUndelete(const std::vector &args, const #else const std::string& storeDirEncoded(args[0]); #endif - - // Get directory ID - int64_t dirID = FindDirectoryObjectID(storeDirEncoded, - false /* no old versions */, true /* find deleted dirs */); - - // Allowable? - if(dirID == 0) + + // Find object ID somehow + int64_t fileId, parentId; + std::string fileName; + int16_t flagsOut; + + fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName, + /* include files and directories */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + /* include old and deleted files */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + &flagsOut); + + if (fileId == 0) { - BOX_ERROR("Directory '" << args[0] << "' not found on server."); + // error already reported return; } - if(dirID == BackupProtocolClientListDirectory::RootDirectory) + + // Undelete it on the store + try + { + // Undelete object + if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + { + mrConnection.QueryUndeleteFile(parentId, fileId); + } + else + { + mrConnection.QueryUndeleteDirectory(fileId); + } + } + catch (BoxException &e) { - BOX_ERROR("Cannot undelete the root directory."); + 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 &, const bool *) +// Purpose: Deletes a file +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +void BackupQueries::CommandDelete(const std::vector &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 "); + 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 */ + BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING, + /* exclude old and deleted files */ + BackupProtocolClientListDirectory::Flags_OldVersion | + BackupProtocolClientListDirectory::Flags_Deleted, + &flagsOut); + + if (fileId == 0) + { + // error already reported return; } - // Undelete - mrConnection.QueryUndeleteDirectory(dirID); + BackupStoreFilenameClear fn(fileName); + + // Delete it on the store + try + { + // Delete object + if(flagsOut & BackupProtocolClientListDirectory::Flags_File) + { + mrConnection.QueryDeleteFile(parentId, fn); + } + else + { + mrConnection.QueryDeleteDirectory(fileId); + } + } + catch (BoxException &e) + { + BOX_ERROR("Failed to delete object: " << + e.what()); + } + catch(std::exception &e) + { + BOX_ERROR("Failed to delete object: " << + e.what()); + } + catch(...) + { + BOX_ERROR("Failed to delete object: unknown error"); + } } diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h index dc2f2126..61124f69 100644 --- a/bin/bbackupquery/BackupQueries.h +++ b/bin/bbackupquery/BackupQueries.h @@ -30,7 +30,9 @@ class ExcludeList; class BackupQueries { public: - BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration); + BackupQueries(BackupProtocolClient &rConnection, + const Configuration &rConfiguration, + bool readWrite); ~BackupQueries(); private: BackupQueries(const BackupQueries &); @@ -54,12 +56,16 @@ private: void CommandCompare(const std::vector &args, const bool *opts); void CommandRestore(const std::vector &args, const bool *opts); void CommandUndelete(const std::vector &args, const bool *opts); + void CommandDelete(const std::vector &args, + const bool *opts); void CommandUsage(); - void CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize); + void CommandUsageDisplayEntry(const char *Name, int64_t Size, + int64_t HardLimit, int32_t BlockSize); void CommandHelp(const std::vector &args); // Implementations - void List(int64_t DirID, const std::string &rListRoot, const bool *opts, bool FirstLevel); + void List(int64_t DirID, const std::string &rListRoot, const bool *opts, + bool FirstLevel); public: class CompareParams @@ -105,13 +111,19 @@ public: private: // Utility functions - int64_t FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion = false, - bool AllowDeletedDirs = false, std::vector > *pStack = 0); + int64_t FindDirectoryObjectID(const std::string &rDirName, + bool AllowOldVersion = false, bool AllowDeletedDirs = false, + std::vector > *pStack = 0); + int64_t FindFileID(const std::string& rNameOrIdString, + const bool *opts, int64_t *pDirIdOut, + std::string* pFileNameOut, int16_t flagsInclude, + int16_t flagsExclude, int16_t* pFlagsOut); int64_t GetCurrentDirectoryID(); std::string GetCurrentDirectoryName(); void SetReturnCode(int code) {mReturnCode = code;} private: + bool mReadWrite; BackupProtocolClient &mrConnection; const Configuration &mrConfiguration; bool mQuitNow; diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp index a0926630..bba3f89f 100644 --- a/bin/bbackupquery/bbackupquery.cpp +++ b/bin/bbackupquery/bbackupquery.cpp @@ -308,7 +308,7 @@ int main(int argc, const char *argv[]) if(!quiet) printf("Login complete.\n\nType \"help\" for a list of commands.\n\n"); // Set up a context for our work - BackupQueries context(connection, conf); + BackupQueries context(connection, conf, readWrite); // Start running commands... first from the command line { diff --git a/bin/bbackupquery/documentation.txt b/bin/bbackupquery/documentation.txt index 84126bee..86e3b7be 100644 --- a/bin/bbackupquery/documentation.txt +++ b/bin/bbackupquery/documentation.txt @@ -159,6 +159,21 @@ compare files is near zero. < +> undelete +undelete -i + + Removes the deleted flag from the specified directory name (in the + current directory) or hex object ID. Be careful not to use this + command where a directory already exists with the same name which is + not marked as deleted. +< + +> delete + + Sets the deleted flag on the specified file name (in the current + directory, or with a relative path). +< + > quit End session and exit. -- cgit v1.2.3