// 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: BackupCommands.cpp
// Purpose: Implement commands for the Backup store protocol
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
#include "Box.h"
#include <syslog.h>
#include "autogen_BackupProtocolServer.h"
#include "BackupConstants.h"
#include "BackupContext.h"
#include "CollectInBufferStream.h"
#include "BackupStoreDirectory.h"
#include "BackupStoreException.h"
#include "BackupStoreFile.h"
#include "StreamableMemBlock.h"
#include "BackupStoreConstants.h"
#include "RaidFileController.h"
#include "BackupStoreInfo.h"
#include "RaidFileController.h"
#include "FileStream.h"
#include "MemLeakFindOn.h"
#define CHECK_PHASE(phase) \
if(rContext.GetPhase() != BackupContext::phase) \
{ \
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError( \
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_NotInRightProtocolPhase)); \
}
#define CHECK_WRITEABLE_SESSION \
if(rContext.SessionIsReadOnly()) \
{ \
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError( \
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_SessionReadOnly)); \
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerVersion::DoCommand(Protocol &, BackupContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Version)
// Correct version?
if(mVersion != BACKUP_STORE_SERVER_VERSION)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_WrongVersion));
}
// Mark the next phase
rContext.SetPhase(BackupContext::Phase_Login);
// Return our version
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerVersion(BACKUP_STORE_SERVER_VERSION));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerLogin::DoCommand(Protocol &, BackupContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Login)
// Check given client ID against the ID in the certificate certificate
// and that the client actually has an account on this machine
if(mClientID != rContext.GetClientID() || !rContext.GetClientHasAccount())
{
::syslog(LOG_INFO, "Failed login: Client ID presented was %08X", mClientID);
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_BadLogin));
}
// If we need to write, check that nothing else has got a write lock
if((mFlags & Flags_ReadOnly) != Flags_ReadOnly)
{
// See if the context will get the lock
if(!rContext.AttemptToGetWriteLock())
{
::syslog(LOG_INFO, "Failed to get write lock (for Client ID %08X)", mClientID);
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_CannotLockStoreForWriting));
}
// Debug: check we got the lock
ASSERT(!rContext.SessionIsReadOnly());
}
// Load the store info
rContext.LoadStoreInfo();
// Get the last client store marker
int64_t clientStoreMarker = rContext.GetClientStoreMarker();
// Mark the next phase
rContext.SetPhase(BackupContext::Phase_Commands);
// Log login
::syslog(LOG_INFO, "Login: Client ID %08X, %s", mClientID, ((mFlags & Flags_ReadOnly) != Flags_ReadOnly)?"Read/Write":"Read-only");
// Get the usage info for reporting to the client
int64_t blocksUsed = 0, blocksSoftLimit = 0, blocksHardLimit = 0;
rContext.GetStoreDiscUsageInfo(blocksUsed, blocksSoftLimit, blocksHardLimit);
// Return success
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerLoginConfirmed(clientStoreMarker, blocksUsed, blocksSoftLimit, blocksHardLimit));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupContext &)
// Purpose: Marks end of conversation (Protocol framework handles this)
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
::syslog(LOG_INFO, "Session finished");
// Let the context know about it
rContext.ReceivedFinishCommand();
// can be called in any phase
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerFinished);
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupContext &)
// Purpose: Command to list a directory
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Ask the context for a directory
const BackupStoreDirectory &rdir(rContext.GetDirectory(mObjectID));
// Store the listing to a stream
std::auto_ptr<CollectInBufferStream> stream(new CollectInBufferStream);
rdir.WriteToStream(*stream, mFlagsMustBeSet, mFlagsNotToBeSet, mSendAttributes,
false /* never send dependency info to the client */);
stream->SetForReading();
// Get the protocol to send the stream
rProtocol.SendStreamAfterCommand(stream.release());
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupContext &)
// Purpose: Command to store a file on the server
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Check that the diff from file actually exists, if it's specified
if(mDiffFromFileID != 0)
{
if(!rContext.ObjectExists(mDiffFromFileID, BackupContext::ObjectExists_File))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DiffFromFileDoesNotExist));
}
}
// A stream follows, which contains the file
std::auto_ptr<IOStream> dirstream(rProtocol.ReceiveStream());
// Ask the context to store it
int64_t id = 0;
try
{
id = rContext.AddFile(*dirstream, mDirectoryObjectID, mModificationTime, mAttributesHash, mDiffFromFileID,
mFilename, true /* mark files with same name as old versions */);
}
catch(BackupStoreException &e)
{
if(e.GetSubType() == BackupStoreException::AddedFileDoesNotVerify)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_FileDoesNotVerify));
}
else if(e.GetSubType() == BackupStoreException::AddedFileExceedsStorageLimit)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_StorageLimitExceeded));
}
else
{
throw;
}
}
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(id));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupContext &)
// Purpose: Command to get an arbitary object from the server
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Check the object exists
if(!rContext.ObjectExists(mObjectID))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(NoObject));
}
// Open the object
std::auto_ptr<IOStream> object(rContext.OpenObject(mObjectID));
// Stream it to the peer
rProtocol.SendStreamAfterCommand(object.release());
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetFile::DoCommand(Protocol &, BackupContext &)
// Purpose: Command to get an file object from the server -- may have to do a bit of
// work to get the object.
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Check the objects exist
if(!rContext.ObjectExists(mObjectID)
|| !rContext.ObjectExists(mInDirectory))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist));
}
// Get the directory it's in
const BackupStoreDirectory &rdir(rContext.GetDirectory(mInDirectory));
// Find the object within the directory
BackupStoreDirectory::Entry *pfileEntry = rdir.FindEntryByID(mObjectID);
if(pfileEntry == 0)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExistInDirectory));
}
// The result
std::auto_ptr<IOStream> stream;
// Does this depend on anything?
if(pfileEntry->GetDependsNewer() != 0)
{
// File exists, but is a patch from a new version. Generate the older version.
std::vector<int64_t> patchChain;
int64_t id = mObjectID;
BackupStoreDirectory::Entry *en = 0;
do
{
patchChain.push_back(id);
en = rdir.FindEntryByID(id);
if(en == 0)
{
::syslog(LOG_ERR, "Object %llx in dir %llx for account %x references object %llx which does not exist in dir",
mObjectID, mInDirectory, rContext.GetClientID(), id);
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_PatchConsistencyError));
}
id = en->GetDependsNewer();
} while(en != 0 && id != 0);
// OK! The last entry in the chain is the full file, the others are patches back from it.
// Open the last one, which is the current from file
std::auto_ptr<IOStream> from(rContext.OpenObject(patchChain[patchChain.size() - 1]));
// Then, for each patch in the chain, do a combine
for(int p = ((int)patchChain.size()) - 2; p >= 0; --p)
{
// ID of patch
int64_t patchID = patchChain[p];
// Open it a couple of times
std::auto_ptr<IOStream> diff(rContext.OpenObject(patchID));
std::auto_ptr<IOStream> diff2(rContext.OpenObject(patchID));
// Choose a temporary filename for the result of the combination
std::string tempFn(RaidFileController::DiscSetPathToFileSystemPath(rContext.GetStoreDiscSet(), rContext.GetStoreRoot() + ".recombinetemp",
p + 16 /* rotate which disc it's on */));
// Open the temporary file
std::auto_ptr<IOStream> combined;
try
{
{
// Write nastily to allow this to work with gcc 2.x
std::auto_ptr<IOStream> t(new FileStream(tempFn.c_str(), O_RDWR | O_CREAT | O_EXCL));
combined = t;
}
// Unlink immediately as it's a temporary file
if(::unlink(tempFn.c_str()) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError);
}
}
catch(...)
{
// Make sure it goes
::unlink(tempFn.c_str());
throw;
}
// Do the combining
BackupStoreFile::CombineFile(*diff, *diff2, *from, *combined);
// Move to the beginning of the combined file
combined->Seek(0, IOStream::SeekType_Absolute);
// Then shuffle round for the next go
from = combined;
}
// Now, from contains a nice file to send to the client. Reorder it
{
// Write nastily to allow this to work with gcc 2.x
std::auto_ptr<IOStream> t(BackupStoreFile::ReorderFileToStreamOrder(from.get(), true /* take ownership */));
stream = t;
}
// Release from file to avoid double deletion
from.release();
}
else
{
// Simple case: file already exists on disc ready to go
// Open the object
std::auto_ptr<IOStream> object(rContext.OpenObject(mObjectID));
// Verify it
if(!BackupStoreFile::VerifyEncodedFileFormat(*object))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_FileDoesNotVerify));
}
// Reset stream -- seek to beginning
object->Seek(0, IOStream::SeekType_Absolute);
// Reorder the stream/file into stream order
{
// Write nastily to allow this to work with gcc 2.x
std::auto_ptr<IOStream> t(BackupStoreFile::ReorderFileToStreamOrder(object.get(), true /* take ownership */));
stream = t;
}
// Object will be deleted when the stream is deleted, so can release the object auto_ptr here to
// avoid premature deletiong
object.release();
}
// Stream the reordered stream to the peer
rProtocol.SendStreamAfterCommand(stream.get());
// Don't delete the stream here
stream.release();
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupContext &)
// Purpose: Create directory command
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Get the stream containing the attributes
std::auto_ptr<IOStream> attrstream(rProtocol.ReceiveStream());
// Collect the attributes -- do this now so no matter what the outcome,
// the data has been absorbed.
StreamableMemBlock attr;
attr.Set(*attrstream, rProtocol.GetTimeout());
// Check to see if the hard limit has been exceeded
if(rContext.HardLimitExceeded())
{
// Won't allow creation if the limit has been exceeded
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_StorageLimitExceeded));
}
bool alreadyExists = false;
int64_t id = rContext.AddDirectory(mContainingDirectoryID, mDirectoryName, attr, mAttributesModTime, alreadyExists);
if(alreadyExists)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DirectoryAlreadyExists));
}
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(id));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Get the stream containing the attributes
std::auto_ptr<IOStream> attrstream(rProtocol.ReceiveStream());
// Collect the attributes -- do this now so no matter what the outcome,
// the data has been absorbed.
StreamableMemBlock attr;
attr.Set(*attrstream, rProtocol.GetTimeout());
// Get the context to do it's magic
rContext.ChangeDirAttributes(mObjectID, attr, mAttributesModTime);
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Get the stream containing the attributes
std::auto_ptr<IOStream> attrstream(rProtocol.ReceiveStream());
// Collect the attributes -- do this now so no matter what the outcome,
// the data has been absorbed.
StreamableMemBlock attr;
attr.Set(*attrstream, rProtocol.GetTimeout());
// Get the context to do it's magic
int64_t objectID = 0;
if(!rContext.ChangeFileAttributes(mFilename, mInDirectory, attr, mAttributesHash, objectID))
{
// Didn't exist
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist));
}
// Tell the caller what the file was
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(objectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Delete a file
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Context handles this
int64_t objectID = 0;
rContext.DeleteFile(mFilename, mInDirectory, objectID);
// return the object ID or zero for not found
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(objectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Delete a directory
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Check it's not asking for the root directory to be deleted
if(mObjectID == BACKUPSTORE_ROOT_DIRECTORY_ID)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_CannotDeleteRoot));
}
// Context handles this
rContext.DeleteDirectory(mObjectID);
// return the object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Undelete a directory
// Created: 23/11/03
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Check it's not asking for the root directory to be deleted
if(mObjectID == BACKUPSTORE_ROOT_DIRECTORY_ID)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_CannotDeleteRoot));
}
// Context handles this
rContext.DeleteDirectory(mObjectID, true /* undelete */);
// return the object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Command to set the client's store marker
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Set the marker
rContext.SetClientStoreMarker(mClientStoreMarker);
// return store marker set
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mClientStoreMarker));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Command to move an object from one directory to another
// Created: 2003/11/12
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
// Let context do this, but modify error reporting on exceptions...
try
{
rContext.MoveObject(mObjectID, mMoveFromDirectory, mMoveToDirectory,
mNewFilename, (mFlags & Flags_MoveAllWithSameName) == Flags_MoveAllWithSameName,
(mFlags & Flags_AllowMoveOverDeletedObject) == Flags_AllowMoveOverDeletedObject);
}
catch(BackupStoreException &e)
{
if(e.GetSubType() == BackupStoreException::CouldNotFindEntryInDirectory)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DoesNotExist));
}
else if(e.GetSubType() == BackupStoreException::NameAlreadyExistsInDirectory)
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_TargetNameExists));
}
else
{
throw;
}
}
// Return the object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Command to find the name of an object
// Created: 12/11/03
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Create a stream for the list of filenames
std::auto_ptr<CollectInBufferStream> stream(new CollectInBufferStream);
// Object and directory IDs
int64_t objectID = mObjectID;
int64_t dirID = mContainingDirectoryID;
// Data to return in the reply
int32_t numNameElements = 0;
int16_t objectFlags = 0;
int64_t modTime = 0;
uint64_t attrModHash = 0;
bool haveModTimes = false;
do
{
// Check the directory really exists
if(!rContext.ObjectExists(dirID, BackupContext::ObjectExists_Directory))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0));
}
// Load up the directory
const BackupStoreDirectory &rdir(rContext.GetDirectory(dirID));
// Find the element in this directory and store it's name
if(objectID != ObjectID_DirectoryOnly)
{
const BackupStoreDirectory::Entry *en = rdir.FindEntryByID(objectID);
// If this can't be found, then there is a problem... tell the caller it can't be found
if(en == 0)
{
// Abort!
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0));
}
// Store flags?
if(objectFlags == 0)
{
objectFlags = en->GetFlags();
}
// Store modification times?
if(!haveModTimes)
{
modTime = en->GetModificationTime();
attrModHash = en->GetAttributesHash();
haveModTimes = true;
}
// Store the name in the stream
en->GetName().WriteToStream(*stream);
// Count of name elements
++numNameElements;
}
// Setup for next time round
objectID = dirID;
dirID = rdir.GetContainerID();
} while(objectID != 0 && objectID != BACKUPSTORE_ROOT_DIRECTORY_ID);
// Stream to send?
if(numNameElements > 0)
{
// Get the stream ready to go
stream->SetForReading();
// Tell the protocol to send the stream
rProtocol.SendStreamAfterCommand(stream.release());
}
// Make reply
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerObjectName(numNameElements, modTime, attrModHash, objectFlags));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Get the block index from a file, by ID
// Created: 19/1/04
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Open the file
std::auto_ptr<IOStream> stream(rContext.OpenObject(mObjectID));
// Move the file pointer to the block index
BackupStoreFile::MoveStreamPositionToBlockIndex(*stream);
// Return the stream to the client
rProtocol.SendStreamAfterCommand(stream.release());
// Return the object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Get the block index from a file, by name within a directory
// Created: 19/1/04
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Get the directory
const BackupStoreDirectory &dir(rContext.GetDirectory(mInDirectory));
// Find the latest object ID within it which has the same name
int64_t objectID = 0;
BackupStoreDirectory::Iterator i(dir);
BackupStoreDirectory::Entry *en = 0;
while((en = i.Next(BackupStoreDirectory::Entry::Flags_File)) != 0)
{
if(en->GetName() == mFilename)
{
// Store the ID, if it's a newer ID than the last one
if(en->GetObjectID() > objectID)
{
objectID = en->GetObjectID();
}
}
}
// Found anything?
if(objectID == 0)
{
// No... return a zero object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(0));
}
// Open the file
std::auto_ptr<IOStream> stream(rContext.OpenObject(objectID));
// Move the file pointer to the block index
BackupStoreFile::MoveStreamPositionToBlockIndex(*stream);
// Return the stream to the client
rProtocol.SendStreamAfterCommand(stream.release());
// Return the object ID
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(objectID));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
// Get store info from context
const BackupStoreInfo &rinfo(rContext.GetBackupStoreInfo());
// Find block size
RaidFileController &rcontroller(RaidFileController::GetController());
RaidFileDiscSet &rdiscSet(rcontroller.GetDiscSet(rinfo.GetDiscSetNumber()));
// Return info
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerAccountUsage(
rinfo.GetBlocksUsed(),
rinfo.GetBlocksInOldFiles(),
rinfo.GetBlocksInDeletedFiles(),
rinfo.GetBlocksInDirectories(),
rinfo.GetBlocksSoftLimit(),
rinfo.GetBlocksHardLimit(),
rdiscSet.GetBlockSize()
));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
{
CHECK_PHASE(Phase_Commands)
//
// NOOP
//
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerIsAlive());
}
syntax highlighted by Code2HTML, v. 0.9.1