// distribution boxbackup-0.11.1 (svn version: 2821_2827)
// Box Backup, http://www.boxbackup.org/
// 
// Copyright (c) 2003-2010, Ben Summers and contributors.
// All rights reserved.
// 
// Note that this project uses mixed licensing. Any file with this license
// attached, or where the code LICENSE-GPL appears on the first line, falls
// under the "Box Backup GPL" license. See the file COPYING.txt for more
// information about this license.
// 
// ---------------------------------------------------------------------
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any software that can be downloaded from
// the OpenSSL website [http://www.openssl.org] under either the
// "OpenSSL License" or the "Original SSLeay License", and to distribute
// the linked executables under the terms of the "Box Backup GPL" license.
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK
// or Microsoft Windows Software Development Kit (SDK), including
// vssapi.lib, that can be downloaded from the Microsoft website
// [*.microsoft.com], and to distribute the linked executables under the
// terms of the "Box Backup GPL" license.
// --------------------------------------------------------------------------
//
// File
//		Name:    BackupStoreRefCountDatabase.cpp
//		Purpose: Backup store object reference count database storage
//		Created: 2009/06/01
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <algorithm>

#include "BackupStoreRefCountDatabase.h"
#include "BackupStoreException.h"
#include "BackupStoreAccountDatabase.h"
#include "BackupStoreAccounts.h"
#include "RaidFileController.h"
#include "RaidFileUtil.h"
#include "RaidFileException.h"
#include "Utils.h"

#include "MemLeakFindOn.h"

#define REFCOUNT_MAGIC_VALUE	0x52656643 // RefC
#define REFCOUNT_FILENAME	"refcount"

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::BackupStoreRefCountDatabase()
//		Purpose: Default constructor
//		Created: 2003/08/28
//
// --------------------------------------------------------------------------
BackupStoreRefCountDatabase::BackupStoreRefCountDatabase(const
	BackupStoreAccountDatabase::Entry& rAccount)
: mAccount(rAccount),
  mFilename(GetFilename(rAccount)),
  mReadOnly(true),
  mIsModified(false)
{
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::~BackupStoreRefCountDatabase
//		Purpose: Destructor
//		Created: 2003/08/28
//
// --------------------------------------------------------------------------
BackupStoreRefCountDatabase::~BackupStoreRefCountDatabase()
{
}

std::string BackupStoreRefCountDatabase::GetFilename(const
	BackupStoreAccountDatabase::Entry& rAccount)
{
	std::string RootDir = BackupStoreAccounts::GetAccountRoot(rAccount);
	ASSERT(RootDir[RootDir.size() - 1] == '/' ||
		RootDir[RootDir.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR);

	std::string fn(RootDir + "refcount.db");
	RaidFileController &rcontroller(RaidFileController::GetController());
	RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(rAccount.GetDiscSet()));
	return RaidFileUtil::MakeWriteFileName(rdiscSet, fn);
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::Create(int32_t,
//			 const std::string &, int, bool)
//		Purpose: Create a new database, overwriting an existing
//			 one only if AllowOverwrite is true.
//		Created: 2003/08/28
//
// --------------------------------------------------------------------------
void BackupStoreRefCountDatabase::Create(const
	BackupStoreAccountDatabase::Entry& rAccount, bool AllowOverwrite)
{
	// Initial header
	refcount_StreamFormat hdr;
	hdr.mMagicValue = htonl(REFCOUNT_MAGIC_VALUE);
	hdr.mAccountID = htonl(rAccount.GetID());
	
	// Generate the filename
	std::string Filename = GetFilename(rAccount);

	// Open the file for writing
	if (FileExists(Filename) && !AllowOverwrite)
	{
		BOX_ERROR("Attempted to overwrite refcount database file: " <<
			Filename);
		THROW_EXCEPTION(RaidFileException, CannotOverwriteExistingFile);
	}

	int flags = O_CREAT | O_BINARY | O_RDWR;
	if (!AllowOverwrite)
	{
		flags |= O_EXCL;
	}

	std::auto_ptr<FileStream> DatabaseFile(new FileStream(Filename, flags));
	
	// Write header
	DatabaseFile->Write(&hdr, sizeof(hdr));
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::Load(int32_t AccountID,
//			 BackupStoreAccountDatabase& rAccountDatabase,
//			 bool ReadOnly);
//		Purpose: Loads the info from disc, given the root
//			 information. Can be marked as read only.
//		Created: 2003/08/28
//
// --------------------------------------------------------------------------
std::auto_ptr<BackupStoreRefCountDatabase> BackupStoreRefCountDatabase::Load(
	const BackupStoreAccountDatabase::Entry& rAccount, bool ReadOnly)
{
	// Generate the filename
	std::string filename = GetFilename(rAccount);
	int flags = ReadOnly ? O_RDONLY : O_RDWR;

	// Open the file for read/write
	std::auto_ptr<FileStream> dbfile(new FileStream(filename,
		flags | O_BINARY));
	
	// Read in a header
	refcount_StreamFormat hdr;
	if(!dbfile->ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */))
	{
		THROW_EXCEPTION(BackupStoreException, CouldNotLoadStoreInfo)
	}
	
	// Check it
	if(ntohl(hdr.mMagicValue) != REFCOUNT_MAGIC_VALUE ||
		(int32_t)ntohl(hdr.mAccountID) != rAccount.GetID())
	{
		THROW_EXCEPTION(BackupStoreException, BadStoreInfoOnLoad)
	}
	
	// Make new object
	std::auto_ptr<BackupStoreRefCountDatabase> refcount(new BackupStoreRefCountDatabase(rAccount));
	
	// Put in basic location info
	refcount->mReadOnly = ReadOnly;
	refcount->mapDatabaseFile = dbfile;
	
	// return it to caller
	return refcount;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::Save()
//		Purpose: Save modified info back to disc
//		Created: 2003/08/28
//
// --------------------------------------------------------------------------
/*
void BackupStoreRefCountDatabase::Save()
{
	// Make sure we're initialised (although should never come to this)
	if(mFilename.empty() || mAccount.GetID() == 0)
	{
		THROW_EXCEPTION(BackupStoreException, Internal)
	}

	// Can we do this?
	if(mReadOnly)
	{
		THROW_EXCEPTION(BackupStoreException, StoreInfoIsReadOnly)
	}
	
	// Then... open a write file
	RaidFileWrite rf(mAccount.GetDiscSet(), mFilename);
	rf.Open(true);		// allow overwriting
	
	// Make header
	info_StreamFormat hdr;
	hdr.mMagicValue 			= htonl(INFO_MAGIC_VALUE);
	hdr.mAccountID 				= htonl(mAccountID);
	hdr.mClientStoreMarker			= box_hton64(mClientStoreMarker);
	hdr.mLastObjectIDUsed			= box_hton64(mLastObjectIDUsed);
	hdr.mBlocksUsed 				= box_hton64(mBlocksUsed);
	hdr.mBlocksInOldFiles 			= box_hton64(mBlocksInOldFiles);
	hdr.mBlocksInDeletedFiles 		= box_hton64(mBlocksInDeletedFiles);
	hdr.mBlocksInDirectories		= box_hton64(mBlocksInDirectories);
	hdr.mBlocksSoftLimit			= box_hton64(mBlocksSoftLimit);
	hdr.mBlocksHardLimit			= box_hton64(mBlocksHardLimit);
	hdr.mCurrentMarkNumber			= 0;
	hdr.mOptionsPresent				= 0;
	hdr.mNumberDeletedDirectories	= box_hton64(mDeletedDirectories.size());
	
	// Write header
	rf.Write(&hdr, sizeof(hdr));
	
	// Write the deleted object list
	if(mDeletedDirectories.size() > 0)
	{
		int64_t objs[NUM_DELETED_DIRS_BLOCK];
		
		int tosave = mDeletedDirectories.size();
		std::vector<int64_t>::iterator i(mDeletedDirectories.begin());
		while(tosave > 0)
		{
			// How many in this one?
			int b = (tosave > NUM_DELETED_DIRS_BLOCK)?NUM_DELETED_DIRS_BLOCK:((int)(tosave));
			
			// Add them
			for(int t = 0; t < b; ++t)
			{
				ASSERT(i != mDeletedDirectories.end());
				objs[t] = box_hton64((*i));
				i++;
			}

			// Write			
			rf.Write(objs, b * sizeof(int64_t));
			
			// Number saved
			tosave -= b;
		}
	}

	// Commit it to disc, converting it to RAID now
	rf.Commit(true);
	
	// Mark is as not modified
	mIsModified = false;
}
*/


// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreRefCountDatabase::GetRefCount(int64_t
//			 ObjectID)
//		Purpose: Get the number of references to the specified object
//			 out of the database
//		Created: 2009/06/01
//
// --------------------------------------------------------------------------
BackupStoreRefCountDatabase::refcount_t
BackupStoreRefCountDatabase::GetRefCount(int64_t ObjectID) const
{
	IOStream::pos_type offset = GetOffset(ObjectID);

	if (GetSize() < offset + GetEntrySize())
	{
		BOX_ERROR("attempted read of unknown refcount for object " <<
			BOX_FORMAT_OBJECTID(ObjectID));
		THROW_EXCEPTION(BackupStoreException,
			UnknownObjectRefCountRequested);
	}

	mapDatabaseFile->Seek(offset, SEEK_SET);

	refcount_t refcount;
	if (mapDatabaseFile->Read(&refcount, sizeof(refcount)) !=
		sizeof(refcount))
	{
		BOX_LOG_SYS_ERROR("short read on refcount database: " <<
			mFilename);
		THROW_EXCEPTION(BackupStoreException, CouldNotLoadStoreInfo);
	}

	return ntohl(refcount);
}

int64_t BackupStoreRefCountDatabase::GetLastObjectIDUsed() const
{
	return (GetSize() - sizeof(refcount_StreamFormat)) /
		sizeof(refcount_t);
}

void BackupStoreRefCountDatabase::AddReference(int64_t ObjectID)
{
	refcount_t refcount;

	if (ObjectID > GetLastObjectIDUsed())
	{
		// new object, assume no previous references
		refcount = 0;
	}
	else
	{
		// read previous value from database
		refcount = GetRefCount(ObjectID);
	}

	refcount++;

	SetRefCount(ObjectID, refcount);
}

void BackupStoreRefCountDatabase::SetRefCount(int64_t ObjectID,
	refcount_t NewRefCount)
{
	IOStream::pos_type offset = GetOffset(ObjectID);
	mapDatabaseFile->Seek(offset, SEEK_SET);
	refcount_t RefCountNetOrder = htonl(NewRefCount);
	mapDatabaseFile->Write(&RefCountNetOrder, sizeof(RefCountNetOrder));
}

bool BackupStoreRefCountDatabase::RemoveReference(int64_t ObjectID)
{
	refcount_t refcount = GetRefCount(ObjectID); // must exist in database
	ASSERT(refcount > 0);
	refcount--;
	SetRefCount(ObjectID, refcount);
	return (refcount > 0);
}

