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