[Box Backup-commit] COMMIT r2283 - box/trunk/bin/bbackupquery

boxbackup-dev at fluffy.co.uk boxbackup-dev at fluffy.co.uk
Sat Sep 13 16:31:27 BST 2008


Author: chris
Date: 2008-09-13 16:31:26 +0100 (Sat, 13 Sep 2008)
New Revision: 2283

Modified:
   box/trunk/bin/bbackupquery/BackupQueries.cpp
   box/trunk/bin/bbackupquery/BackupQueries.h
   box/trunk/bin/bbackupquery/bbackupquery.cpp
   box/trunk/bin/bbackupquery/documentation.txt
Log:
Allow undelete command to work on files as well as directories.

Add delete command that works on files and directories.

Document both commands.


Modified: box/trunk/bin/bbackupquery/BackupQueries.cpp
===================================================================
--- box/trunk/bin/bbackupquery/BackupQueries.cpp	2008-09-13 15:29:26 UTC (rev 2282)
+++ box/trunk/bin/bbackupquery/BackupQueries.cpp	2008-09-13 15:31:26 UTC (rev 2283)
@@ -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 @@
 		{ "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 @@
 		}
 	}
 
-	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 @@
 	// 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 @@
 		}
 		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 @@
 // --------------------------------------------------------------------------
 //
 // Function
-//		Name:    BackupQueries::FindDirectoryObjectID(const std::string &)
-//		Purpose: Find the object ID of a directory on the store, or return 0 for not found.
-//				 If pStack != 0, the object is set to the stack of directories.
-//				 Will start from the current directory stack.
+//		Name:    BackupQueries::FindDirectoryObjectID(const
+//			 std::string &)
+//		Purpose: Find the object ID of a directory on the store,
+//			 or return 0 for not found. If pStack != 0, the
+//			 object is set to the stack of directories.
+//			 Will start from the current directory stack.
 //		Created: 2003/10/10
 //
 // --------------------------------------------------------------------------
-int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion,
-			bool AllowDeletedDirs, std::vector<std::pair<std::string, int64_t> > *pStack)
+int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName,
+	bool AllowOldVersion, bool AllowDeletedDirs,
+	std::vector<std::pair<std::string, int64_t> > *pStack)
 {
 	// Split up string into elements
 	std::vector<std::string> dirElements;
@@ -935,7 +953,118 @@
 }
 
 
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupQueries::FindFileID(const std::string&
+//			 rNameOrIdString, const bool *options,
+//			 int64_t *pDirIdOut, std::string* pFileNameOut)
+//		Purpose: Locate a file on the store (either by name or by
+//			 object ID, depending on opts['i'], where name can
+//			 include a path) and return the file ID, placing the
+//			 directory ID in *pDirIdOut and the filename part
+//			 of the path (if not looking up by ID and not NULL)
+//			 in *pFileNameOut.
+//		Created: 2008-09-12
+//
+// --------------------------------------------------------------------------
+int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString,
+	const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut,
+	int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut)
+{
+	// Find object ID somehow
+	int64_t fileId;
+	int64_t dirId = GetCurrentDirectoryID();
+	std::string fileName = rNameOrIdString;
 
+	if(!opts['i'])
+	{
+		// does this remote filename include a path?
+		std::string::size_type index = fileName.rfind('/');
+		if(index != std::string::npos)
+		{
+			std::string dirName(fileName.substr(0, index));
+			fileName = fileName.substr(index + 1);
+
+			dirId = FindDirectoryObjectID(dirName);
+			if(dirId == 0)
+			{
+				BOX_ERROR("Directory '" << dirName <<
+					"' not found.");
+				return 0;
+			}
+		}
+
+		if(pFileNameOut)
+		{
+			*pFileNameOut = fileName;
+		}
+	}
+
+	BackupStoreFilenameClear fn(fileName);
+
+	// Need to look it up in the current directory
+	mrConnection.QueryListDirectory(
+		dirId, flagsInclude, flagsExclude,
+		true /* do want attributes */);
+
+	// Retrieve the directory from the stream following
+	BackupStoreDirectory dir;
+	std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
+	dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
+	BackupStoreDirectory::Entry *en;
+
+	if(opts['i'])
+	{
+		// Specified as ID. 
+		fileId = ::strtoll(rNameOrIdString.c_str(), 0, 16);
+		if(fileId == std::numeric_limits<long long>::min() || 
+			fileId == std::numeric_limits<long long>::max() || 
+			fileId == 0)
+		{
+			BOX_ERROR("Not a valid object ID (specified in hex).");
+			return 0;
+		}
+		
+		// Check that the item is actually in the directory
+		en = dir.FindEntryByID(fileId);
+		if(en == 0)
+		{
+			BOX_ERROR("File ID " << 
+				BOX_FORMAT_OBJECTID(fileId) <<
+				" not found in current directory on store.\n"
+				"(You can only access files by ID from the "
+				"current directory.)");
+			return 0;
+		}
+	}
+	else
+	{				
+		// Specified by name, find the object in the directory to get the ID
+		BackupStoreDirectory::Iterator i(dir);
+		en = i.FindMatchingClearName(fn);
+		if(en == 0)
+		{
+			BOX_ERROR("Filename '" << rNameOrIdString << "' "
+				"not found in current directory on store.\n"
+				"(Subdirectories in path not searched.)");
+			return 0;
+		}
+		
+		fileId = en->GetObjectID();
+	}
+
+	*pDirIdOut = dirId;
+
+	if(pFlagsOut)
+	{
+		*pFlagsOut = en->GetFlags();
+	}
+
+	return fileId;
+}
+
+
 // --------------------------------------------------------------------------
 //
 // Function
@@ -957,111 +1086,63 @@
 	}
 
 	// Find object ID somehow
-	int64_t fileId;
-	int64_t dirId = GetCurrentDirectoryID();
+	int64_t fileId, dirId;
 	std::string localName;
 
-	// BLOCK
+#ifdef WIN32
+	for (std::vector<std::string>::iterator 
+		i = args.begin(); i != args.end(); i++)
 	{
-#ifdef WIN32
-		for (std::vector<std::string>::iterator 
-			i = args.begin(); i != args.end(); i++)
+		std::string out;
+		if(!ConvertConsoleToUtf8(i->c_str(), out))
 		{
-			std::string out;
-			if(!ConvertConsoleToUtf8(i->c_str(), out))
-			{
-				BOX_ERROR("Failed to convert encoding.");
-				return;
-			}
-			*i = out;
+			BOX_ERROR("Failed to convert encoding.");
+			return;
 		}
+		*i = out;
+	}
 #endif
 
-		std::string fileName(args[0]);
+	int16_t flagsExclude;
 
-		if(!opts['i'])
-		{
-			// does this remote filename include a path?
-			std::string::size_type index = fileName.rfind('/');
-			if(index != std::string::npos)
-			{
-				std::string dirName(fileName.substr(0, index));
-				fileName = fileName.substr(index + 1);
+	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;
+	}
 
-				dirId = FindDirectoryObjectID(dirName);
-				if(dirId == 0)
-				{
-					BOX_ERROR("Directory '" << dirName <<
-						"' not found.");
-					return;
-				}
-			}
-		}
 
-		BackupStoreFilenameClear fn(fileName);
+	fileId = FindFileID(args[0], opts, &dirId, &localName,
+		BackupProtocolClientListDirectory::Flags_File, // just files
+		flagsExclude, NULL /* don't care about flags found */);
 
-		// 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 */);
+	if (fileId == 0)
+	{
+		// error already reported
+		return;
+	}
 
-		// Retrieve the directory from the stream following
-		BackupStoreDirectory dir;
-		std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
-		dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
-
-		if(opts['i'])
+	if(opts['i'])
+	{
+		// Specified as ID.  Must have a local name in the arguments
+		// (check at beginning of function ensures this)
+		localName = args[1];
+	}
+	else
+	{				
+		// Specified by name. Local name already set by FindFileID,
+		// but may be overridden by user supplying a second argument.
+		if(args.size() == 2)
 		{
-			// Specified as ID. 
-			fileId = ::strtoll(args[0].c_str(), 0, 16);
-			if(fileId == std::numeric_limits<long long>::min() || 
-				fileId == std::numeric_limits<long long>::max() || 
-				fileId == 0)
-			{
-				BOX_ERROR("Not a valid object ID (specified in hex).");
-				return;
-			}
-			
-			// Check that the item is actually in the directory
-			if(dir.FindEntryByID(fileId) == 0)
-			{
-				BOX_ERROR("File ID " << 
-					BOX_FORMAT_OBJECTID(fileId) <<
-					" not found in current "
-					"directory on store.\n"
-					"(You can only download files by ID "
-					"from the current directory.)");
-				return;
-			}
-			
-			// Must have a local name in the arguments (check at beginning of function ensures this)
 			localName = args[1];
 		}
-		else
-		{				
-			// Specified by name, find the object in the directory to get the ID
-			BackupStoreDirectory::Iterator i(dir);
-			BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn);
-			
-			if(en == 0)
-			{
-				BOX_ERROR("Filename '" << args[0] << "' "
-					"not found in current "
-					"directory on store.\n"
-					"(Subdirectories in path not "
-					"searched.)");
-				return;
-			}
-			
-			fileId = en->GetObjectID();
-			
-			// Local name is the last argument, which is either 
-			// the looked up filename, or a filename specified 
-			// by the user.
-			localName = args[args.size() - 1];
-		}
 	}
 	
 	// Does local file already exist? (don't want to overwrite)
@@ -2210,10 +2291,17 @@
 // --------------------------------------------------------------------------
 void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const bool *opts)
 {
+	if (!mReadWrite)
+	{
+		BOX_ERROR("This command requires a read-write connection. "
+			"Please reconnect with the -w option.");
+		return;
+	}
+
 	// Check arguments
 	if(args.size() != 1)
 	{
-		BOX_ERROR("Incorrect usage. undelete <directory-name>");
+		BOX_ERROR("Incorrect usage. undelete <name> or undelete -i <object-id>");
 		return;
 	}
 
@@ -2223,23 +2311,133 @@
 #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
 	{
-		BOX_ERROR("Cannot undelete the root directory.");
+		// Undelete object
+		if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+		{
+			mrConnection.QueryUndeleteFile(parentId, fileId);
+		}
+		else
+		{
+			mrConnection.QueryUndeleteDirectory(fileId);
+		}
+	}
+	catch (BoxException &e)
+	{
+		BOX_ERROR("Failed to undelete object: " << 
+			e.what());
+	}
+	catch(std::exception &e)
+	{
+		BOX_ERROR("Failed to undelete object: " <<
+			e.what());
+	}
+	catch(...)
+	{
+		BOX_ERROR("Failed to undelete object: unknown error");
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupQueries::CommandDelete(const
+//			 std::vector<std::string> &, const bool *)
+//		Purpose: Deletes a file
+//		Created: 23/11/03
+//
+// --------------------------------------------------------------------------
+void BackupQueries::CommandDelete(const std::vector<std::string> &args,
+	const bool *opts)
+{
+	if (!mReadWrite)
+	{
+		BOX_ERROR("This command requires a read-write connection. "
+			"Please reconnect with the -w option.");
 		return;
 	}
 
-	// Undelete
-	mrConnection.QueryUndeleteDirectory(dirID);
+	// 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 */
+		BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+		/* exclude old and deleted files */
+		BackupProtocolClientListDirectory::Flags_OldVersion |
+		BackupProtocolClientListDirectory::Flags_Deleted,
+		&flagsOut);
+
+	if (fileId == 0)
+	{
+		// error already reported
+		return;
+	}
+
+	BackupStoreFilenameClear fn(fileName);
+
+	// Delete it on the store
+	try
+	{
+		// Delete object
+		if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+		{
+			mrConnection.QueryDeleteFile(parentId, fn);
+		}
+		else
+		{
+			mrConnection.QueryDeleteDirectory(fileId);
+		}
+	}
+	catch (BoxException &e)
+	{
+		BOX_ERROR("Failed to delete object: " << 
+			e.what());
+	}
+	catch(std::exception &e)
+	{
+		BOX_ERROR("Failed to delete object: " <<
+			e.what());
+	}
+	catch(...)
+	{
+		BOX_ERROR("Failed to delete object: unknown error");
+	}
 }

Modified: box/trunk/bin/bbackupquery/BackupQueries.h
===================================================================
--- box/trunk/bin/bbackupquery/BackupQueries.h	2008-09-13 15:29:26 UTC (rev 2282)
+++ box/trunk/bin/bbackupquery/BackupQueries.h	2008-09-13 15:31:26 UTC (rev 2283)
@@ -30,7 +30,9 @@
 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 @@
 	void CommandCompare(const std::vector<std::string> &args, const bool *opts);
 	void CommandRestore(const std::vector<std::string> &args, const bool *opts);
 	void CommandUndelete(const std::vector<std::string> &args, const bool *opts);
+	void CommandDelete(const std::vector<std::string> &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<std::string> &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 @@
 private:
 
 	// Utility functions
-	int64_t FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion = false,
-		bool AllowDeletedDirs = false, std::vector<std::pair<std::string, int64_t> > *pStack = 0);
+	int64_t FindDirectoryObjectID(const std::string &rDirName,
+		bool AllowOldVersion = false, bool AllowDeletedDirs = false,
+		std::vector<std::pair<std::string, int64_t> > *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;

Modified: box/trunk/bin/bbackupquery/bbackupquery.cpp
===================================================================
--- box/trunk/bin/bbackupquery/bbackupquery.cpp	2008-09-13 15:29:26 UTC (rev 2282)
+++ box/trunk/bin/bbackupquery/bbackupquery.cpp	2008-09-13 15:31:26 UTC (rev 2283)
@@ -308,7 +308,7 @@
 	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
 	{

Modified: box/trunk/bin/bbackupquery/documentation.txt
===================================================================
--- box/trunk/bin/bbackupquery/documentation.txt	2008-09-13 15:29:26 UTC (rev 2282)
+++ box/trunk/bin/bbackupquery/documentation.txt	2008-09-13 15:31:26 UTC (rev 2283)
@@ -159,6 +159,21 @@
 	files is near zero.
 <
 
+> undelete <directory-name>
+undelete -i <object-id>
+
+	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 <file-name>
+
+	Sets the deleted flag on the specified file name (in the current
+	directory, or with a relative path).
+<
+
 > quit
 
 	End session and exit.




More information about the Boxbackup-commit mailing list