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