xref: /haiku/src/servers/package/FSTransaction.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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