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:
OperationInfoFSTransaction::OperationInfo32 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
FromPathFSTransaction::OperationInfo43 const std::string& FromPath() const
44 {
45 return fFromPath;
46 }
47
ToPathFSTransaction::OperationInfo48 const std::string& ToPath() const
49 {
50 return fToPath;
51 }
52
ModifiedOperationFSTransaction::OperationInfo53 int32 ModifiedOperation() const
54 {
55 return fModifiedOperation;
56 }
57
SetModifiedOperationFSTransaction::OperationInfo58 void SetModifiedOperation(int32 modifiedOperation)
59 {
60 fModifiedOperation = modifiedOperation;
61 }
62
IsEnabledFSTransaction::OperationInfo63 bool IsEnabled() const
64 {
65 return fEnabled;
66 }
67
SetEnabledFSTransaction::OperationInfo68 void SetEnabled(bool enabled)
69 {
70 fEnabled = enabled;
71 }
72
RollBackFSTransaction::OperationInfo73 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
FSTransaction()138 FSTransaction::FSTransaction()
139 {
140 }
141
142
~FSTransaction()143 FSTransaction::~FSTransaction()
144 {
145 }
146
147
148 void
RollBack()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
CreateEntry(const Entry & entry,int32 modifiedOperation)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
RemoveEntry(const Entry & entry,const Entry & backupEntry,int32 modifiedOperation)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
MoveEntry(const Entry & fromEntry,const Entry & toEntry,int32 modifiedOperation)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
RemoveOperationAt(int32 index)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
_GetPath(const Entry & entry)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