1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "FSTransaction.h" 8 9 #include <Entry.h> 10 #include <package/CommitTransactionResult.h> 11 #include <Path.h> 12 13 #include <CopyEngine.h> 14 #include <RemoveEngine.h> 15 16 #include "DebugSupport.h" 17 #include "Exception.h" 18 19 20 // #pragma mark - OperationInfo 21 22 23 struct FSTransaction::OperationInfo { 24 public: 25 enum Type { 26 TYPE_CREATE, 27 TYPE_REMOVE, 28 TYPE_MOVE, 29 }; 30 31 public: 32 OperationInfo(Type type, const std::string& fromPath, 33 const std::string& toPath, int32 modifiedOperation) 34 : 35 fType(type), 36 fFromPath(fromPath), 37 fToPath(toPath), 38 fModifiedOperation(modifiedOperation), 39 fEnabled(true) 40 { 41 } 42 43 const std::string& FromPath() const 44 { 45 return fFromPath; 46 } 47 48 const std::string& ToPath() const 49 { 50 return fToPath; 51 } 52 53 int32 ModifiedOperation() const 54 { 55 return fModifiedOperation; 56 } 57 58 void SetModifiedOperation(int32 modifiedOperation) 59 { 60 fModifiedOperation = modifiedOperation; 61 } 62 63 bool IsEnabled() const 64 { 65 return fEnabled; 66 } 67 68 void SetEnabled(bool enabled) 69 { 70 fEnabled = enabled; 71 } 72 73 status_t RollBack() const 74 { 75 switch (fType) { 76 case TYPE_CREATE: 77 { 78 status_t error = BRemoveEngine().RemoveEntry( 79 Entry(fFromPath.c_str())); 80 if (error != B_OK) { 81 ERROR("Failed to remove \"%s\": %s\n", fFromPath.c_str(), 82 strerror(error)); 83 } 84 return error; 85 } 86 87 case TYPE_REMOVE: 88 { 89 if (fToPath.empty()) 90 return B_NOT_SUPPORTED; 91 92 status_t error = BCopyEngine( 93 BCopyEngine::COPY_RECURSIVELY 94 | BCopyEngine::UNLINK_DESTINATION) 95 .CopyEntry(fToPath.c_str(), fFromPath.c_str()); 96 if (error != B_OK) { 97 ERROR("Failed to copy \"%s\" to \"%s\": %s\n", 98 fToPath.c_str(), fFromPath.c_str(), strerror(error)); 99 } 100 return error; 101 } 102 103 case TYPE_MOVE: 104 { 105 BEntry entry; 106 status_t error = entry.SetTo(fToPath.c_str()); 107 if (error != B_OK) { 108 ERROR("Failed to init entry for \"%s\": %s\n", 109 fToPath.c_str(), strerror(error)); 110 return error; 111 } 112 113 error = entry.Rename(fFromPath.c_str(), true); 114 if (error != B_OK) { 115 ERROR("Failed to move \"%s\" to \"%s\": %s\n", 116 fToPath.c_str(), fFromPath.c_str(), strerror(error)); 117 return error; 118 } 119 return error; 120 } 121 } 122 123 return B_ERROR; 124 } 125 126 private: 127 Type fType; 128 std::string fFromPath; 129 std::string fToPath; 130 int32 fModifiedOperation; 131 bool fEnabled; 132 }; 133 134 135 // #pragma mark - FSTransaction 136 137 138 FSTransaction::FSTransaction() 139 { 140 } 141 142 143 FSTransaction::~FSTransaction() 144 { 145 } 146 147 148 void 149 FSTransaction::RollBack() 150 { 151 int32 count = (int32)fOperations.size(); 152 for (int32 i = count - 1; i >= 0; i--) { 153 const OperationInfo& operation = fOperations[i]; 154 bool rolledBack = false; 155 if (operation.IsEnabled()) 156 rolledBack = operation.RollBack() == B_OK; 157 158 if (!rolledBack && operation.ModifiedOperation() >= 0) 159 fOperations[operation.ModifiedOperation()].SetEnabled(false); 160 } 161 } 162 163 164 int32 165 FSTransaction::CreateEntry(const Entry& entry, int32 modifiedOperation) 166 { 167 fOperations.push_back( 168 OperationInfo(OperationInfo::TYPE_CREATE, _GetPath(entry), 169 std::string(), modifiedOperation)); 170 return (int32)fOperations.size() - 1; 171 } 172 173 174 int32 175 FSTransaction::RemoveEntry(const Entry& entry, const Entry& backupEntry, 176 int32 modifiedOperation) 177 { 178 fOperations.push_back( 179 OperationInfo(OperationInfo::TYPE_REMOVE, _GetPath(entry), 180 _GetPath(backupEntry), modifiedOperation)); 181 return (int32)fOperations.size() - 1; 182 } 183 184 185 int32 186 FSTransaction::MoveEntry(const Entry& fromEntry, const Entry& toEntry, 187 int32 modifiedOperation) 188 { 189 fOperations.push_back( 190 OperationInfo(OperationInfo::TYPE_MOVE, _GetPath(fromEntry), 191 _GetPath(toEntry), modifiedOperation)); 192 return (int32)fOperations.size() - 1; 193 } 194 195 196 void 197 FSTransaction::RemoveOperationAt(int32 index) 198 { 199 int32 count = fOperations.size(); 200 if (index < 0 || index >= count) { 201 ERROR("FSTransaction::RemoveOperationAt(): invalid " 202 "operation index %" B_PRId32 "/%" B_PRId32, index, count); 203 throw Exception(BPackageKit::B_TRANSACTION_INTERNAL_ERROR); 204 } 205 206 fOperations.erase(fOperations.begin() + index); 207 208 for (int32 i = index; i < count; i++) { 209 int32 modifiedOperation = fOperations[i].ModifiedOperation(); 210 if (modifiedOperation == index) 211 fOperations[i].SetModifiedOperation(-1); 212 else if (modifiedOperation > index) 213 fOperations[i].SetModifiedOperation(modifiedOperation - 1); 214 } 215 } 216 217 218 /*static*/ std::string 219 FSTransaction::_GetPath(const Entry& entry) 220 { 221 BPath pathBuffer; 222 const char* path; 223 status_t error = entry.GetPath(pathBuffer, path); 224 if (error == B_OK && path[0] != '/') { 225 // make absolute 226 error = pathBuffer.SetTo(path); 227 } 228 229 if (error != B_OK) { 230 if (error == B_NO_MEMORY) 231 throw Exception(BPackageKit::B_TRANSACTION_NO_MEMORY); 232 233 throw Exception(BPackageKit::B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) 234 .SetPath1(entry.PathOrName()) 235 .SetSystemError(error); 236 } 237 238 return path; 239 } 240