⛏️ index : haiku.git

/*
 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "FSTransaction.h"

#include <Entry.h>
#include <package/CommitTransactionResult.h>
#include <Path.h>

#include <CopyEngine.h>
#include <RemoveEngine.h>

#include "DebugSupport.h"
#include "Exception.h"


// #pragma mark - OperationInfo


struct FSTransaction::OperationInfo {
public:
	enum Type {
		TYPE_CREATE,
		TYPE_REMOVE,
		TYPE_MOVE,
	};

public:
	OperationInfo(Type type, const std::string& fromPath,
		const std::string& toPath, int32 modifiedOperation)
		:
		fType(type),
		fFromPath(fromPath),
		fToPath(toPath),
		fModifiedOperation(modifiedOperation),
		fEnabled(true)
	{
	}

	const std::string& FromPath() const
	{
		return fFromPath;
	}

	const std::string& ToPath() const
	{
		return fToPath;
	}

	int32 ModifiedOperation() const
	{
		return fModifiedOperation;
	}

	void SetModifiedOperation(int32 modifiedOperation)
	{
		fModifiedOperation = modifiedOperation;
	}

	bool IsEnabled() const
	{
		return fEnabled;
	}

	void SetEnabled(bool enabled)
	{
		fEnabled = enabled;
	}

	status_t RollBack() const
	{
		switch (fType) {
			case TYPE_CREATE:
			{
				status_t error = BRemoveEngine().RemoveEntry(
					Entry(fFromPath.c_str()));
				if (error != B_OK) {
					ERROR("Failed to remove \"%s\": %s\n", fFromPath.c_str(),
						strerror(error));
				}
				return error;
			}

			case TYPE_REMOVE:
			{
				if (fToPath.empty())
					return B_NOT_SUPPORTED;

				status_t error = BCopyEngine(
						BCopyEngine::COPY_RECURSIVELY
							| BCopyEngine::UNLINK_DESTINATION)
					.CopyEntry(fToPath.c_str(), fFromPath.c_str());
				if (error != B_OK) {
					ERROR("Failed to copy \"%s\" to \"%s\": %s\n",
						fToPath.c_str(), fFromPath.c_str(), strerror(error));
				}
				return error;
			}

			case TYPE_MOVE:
			{
				BEntry entry;
				status_t error = entry.SetTo(fToPath.c_str());
				if (error != B_OK) {
					ERROR("Failed to init entry for \"%s\": %s\n",
						fToPath.c_str(), strerror(error));
					return error;
				}

				error = entry.Rename(fFromPath.c_str(), true);
				if (error != B_OK) {
					ERROR("Failed to move \"%s\" to \"%s\": %s\n",
						fToPath.c_str(), fFromPath.c_str(), strerror(error));
					return error;
				}
				return error;
			}
		}

		return B_ERROR;
	}

private:
	Type		fType;
	std::string	fFromPath;
	std::string	fToPath;
	int32		fModifiedOperation;
	bool		fEnabled;
};


// #pragma mark - FSTransaction


FSTransaction::FSTransaction()
{
}


FSTransaction::~FSTransaction()
{
}


void
FSTransaction::RollBack()
{
	int32 count = (int32)fOperations.size();
	for (int32 i = count - 1; i >= 0; i--) {
		const OperationInfo& operation = fOperations[i];
		bool rolledBack = false;
		if (operation.IsEnabled())
			rolledBack = operation.RollBack() == B_OK;

		if (!rolledBack && operation.ModifiedOperation() >= 0)
			fOperations[operation.ModifiedOperation()].SetEnabled(false);
	}
}


int32
FSTransaction::CreateEntry(const Entry& entry, int32 modifiedOperation)
{
	fOperations.push_back(
		OperationInfo(OperationInfo::TYPE_CREATE, _GetPath(entry),
			std::string(), modifiedOperation));
	return (int32)fOperations.size() - 1;
}


int32
FSTransaction::RemoveEntry(const Entry& entry, const Entry& backupEntry,
	int32 modifiedOperation)
{
	fOperations.push_back(
		OperationInfo(OperationInfo::TYPE_REMOVE, _GetPath(entry),
			_GetPath(backupEntry), modifiedOperation));
	return (int32)fOperations.size() - 1;
}


int32
FSTransaction::MoveEntry(const Entry& fromEntry, const Entry& toEntry,
	int32 modifiedOperation)
{
	fOperations.push_back(
		OperationInfo(OperationInfo::TYPE_MOVE, _GetPath(fromEntry),
			_GetPath(toEntry), modifiedOperation));
	return (int32)fOperations.size() - 1;
}


void
FSTransaction::RemoveOperationAt(int32 index)
{
	int32 count = fOperations.size();
	if (index < 0 || index >= count) {
		ERROR("FSTransaction::RemoveOperationAt(): invalid "
			"operation index %" B_PRId32 "/%" B_PRId32, index, count);
		throw Exception(BPackageKit::B_TRANSACTION_INTERNAL_ERROR);
	}

	fOperations.erase(fOperations.begin() + index);

	for (int32 i = index; i < count; i++) {
		int32 modifiedOperation = fOperations[i].ModifiedOperation();
		if (modifiedOperation == index)
			fOperations[i].SetModifiedOperation(-1);
		else if (modifiedOperation > index)
			fOperations[i].SetModifiedOperation(modifiedOperation - 1);
	}
}


/*static*/ std::string
FSTransaction::_GetPath(const Entry& entry)
{
	BPath pathBuffer;
	const char* path;
	status_t error = entry.GetPath(pathBuffer, path);
	if (error == B_OK && path[0] != '/') {
		// make absolute
		error = pathBuffer.SetTo(path);
	}

	if (error != B_OK) {
		if (error == B_NO_MEMORY)
			throw Exception(BPackageKit::B_TRANSACTION_NO_MEMORY);

		throw Exception(BPackageKit::B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH)
			.SetPath1(entry.PathOrName())
			.SetSystemError(error);
	}

	return path;
}