/* * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "FSTransaction.h" #include #include #include #include #include #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; }