1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "FSUtils.h"
8
9#include <string.h>
10
11#include <algorithm>
12#include <string>
13
14#include <Directory.h>
15#include <File.h>
16#include <Path.h>
17#include <SymLink.h>
18
19#include <AutoDeleter.h>
20
21#include "DebugSupport.h"
22
23
24static const size_t kCompareDataBufferSize = 64 * 1024;
25const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!";
26
27
28/*static*/ BString
29FSUtils::ShellEscapeString(const BString& string)
30{
31	BString result(string);
32	result.CharacterEscape(kShellEscapeCharacters, '\\');
33	if (result.IsEmpty())
34		throw std::bad_alloc();
35	return result;
36}
37
38
39/*static*/ status_t
40FSUtils::OpenSubDirectory(const BDirectory& baseDirectory,
41	const RelativePath& path, bool create, BDirectory& _directory)
42{
43	// get a string for the path
44	BString pathString = path.ToString();
45	if (pathString.IsEmpty())
46		RETURN_ERROR(B_NO_MEMORY);
47
48	// If creating is not allowed, just try to open it.
49	if (!create)
50		RETURN_ERROR(_directory.SetTo(&baseDirectory, pathString));
51
52	// get an absolute path and create the subdirectory
53	BPath absolutePath;
54	status_t error = absolutePath.SetTo(&baseDirectory, pathString);
55	if (error != B_OK) {
56		ERROR("Volume::OpenSubDirectory(): failed to get absolute path "
57			"for subdirectory \"%s\": %s\n", pathString.String(),
58			strerror(error));
59		RETURN_ERROR(error);
60	}
61
62	error = create_directory(absolutePath.Path(),
63		S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
64	if (error != B_OK) {
65		ERROR("Volume::OpenSubDirectory(): failed to create "
66			"subdirectory \"%s\": %s\n", pathString.String(),
67			strerror(error));
68		RETURN_ERROR(error);
69	}
70
71	RETURN_ERROR(_directory.SetTo(&baseDirectory, pathString));
72}
73
74
75/*static*/ status_t
76FSUtils::CompareFileContent(const Entry& entry1, const Entry& entry2,
77	bool& _equal)
78{
79	BFile file1;
80	status_t error = _OpenFile(entry1, file1);
81	if (error != B_OK)
82		return error;
83
84	BFile file2;
85	error = _OpenFile(entry2, file2);
86	if (error != B_OK)
87		return error;
88
89	return CompareFileContent(file1, file2, _equal);
90}
91
92
93/*static*/ status_t
94FSUtils::CompareFileContent(BPositionIO& content1, BPositionIO& content2,
95	bool& _equal)
96{
97	// get and compare content size
98	off_t size1;
99	status_t error = content1.GetSize(&size1);
100	if (error != B_OK)
101		return error;
102
103	off_t size2;
104	error = content2.GetSize(&size2);
105	if (error != B_OK)
106		return error;
107
108	if (size1 != size2) {
109		_equal = false;
110		return B_OK;
111	}
112
113	if (size1 == 0) {
114		_equal = true;
115		return B_OK;
116	}
117
118	// allocate a data buffer
119	uint8* buffer1 = new(std::nothrow) uint8[2 * kCompareDataBufferSize];
120	if (buffer1 == NULL)
121		return B_NO_MEMORY;
122	ArrayDeleter<uint8> bufferDeleter(buffer1);
123	uint8* buffer2 = buffer1 + kCompareDataBufferSize;
124
125	// compare the data
126	off_t offset = 0;
127	while (offset < size1) {
128		size_t toCompare = std::min(size_t(size1 - offset),
129			kCompareDataBufferSize);
130		ssize_t bytesRead = content1.ReadAt(offset, buffer1, toCompare);
131		if (bytesRead < 0)
132			return bytesRead;
133		if ((size_t)bytesRead != toCompare)
134			return B_ERROR;
135
136		bytesRead = content2.ReadAt(offset, buffer2, toCompare);
137		if (bytesRead < 0)
138			return bytesRead;
139		if ((size_t)bytesRead != toCompare)
140			return B_ERROR;
141
142		if (memcmp(buffer1, buffer2, toCompare) != 0) {
143			_equal = false;
144			return B_OK;
145		}
146
147		offset += bytesRead;
148	}
149
150	_equal = true;
151	return B_OK;
152}
153
154
155/*static*/ status_t
156FSUtils::CompareSymLinks(const Entry& entry1, const Entry& entry2, bool& _equal)
157{
158	BSymLink symLink1;
159	status_t error = _OpenSymLink(entry1, symLink1);
160	if (error != B_OK)
161		return error;
162
163	BSymLink symLink2;
164	error = _OpenSymLink(entry2, symLink2);
165	if (error != B_OK)
166		return error;
167
168	return CompareSymLinks(symLink1, symLink2, _equal);
169}
170
171
172/*static*/ status_t
173FSUtils::CompareSymLinks(BSymLink& symLink1, BSymLink& symLink2, bool& _equal)
174{
175	char buffer1[B_PATH_NAME_LENGTH];
176	ssize_t bytesRead1 = symLink1.ReadLink(buffer1, sizeof(buffer1));
177	if (bytesRead1 < 0)
178		return bytesRead1;
179
180	char buffer2[B_PATH_NAME_LENGTH];
181	ssize_t bytesRead2 = symLink2.ReadLink(buffer2, sizeof(buffer2));
182	if (bytesRead2 < 0)
183		return bytesRead2;
184
185	_equal = bytesRead1 == bytesRead2
186		&& memcmp(buffer1, buffer2, bytesRead1) == 0;
187	return B_OK;
188}
189
190
191/*static*/ status_t
192FSUtils::ExtractPackageContent(const Entry& packageEntry,
193	const char* contentPath, const Entry& targetDirectoryEntry)
194{
195	BPath packagePathBuffer;
196	const char* packagePath;
197	status_t error = packageEntry.GetPath(packagePathBuffer, packagePath);
198	if (error != B_OK)
199		return error;
200
201	BPath targetPathBuffer;
202	const char* targetPath;
203	error = targetDirectoryEntry.GetPath(targetPathBuffer, targetPath);
204	if (error != B_OK)
205		return error;
206
207	return ExtractPackageContent(packagePath, contentPath, targetPath);
208}
209
210
211/*static*/ status_t
212FSUtils::ExtractPackageContent(const char* packagePath, const char* contentPath,
213	const char* targetDirectoryPath)
214{
215	std::string commandLine = std::string("package extract -C ")
216		+ ShellEscapeString(targetDirectoryPath).String()
217		+ " "
218		+ ShellEscapeString(packagePath).String()
219		+ " "
220		+ ShellEscapeString(contentPath).String();
221	if (system(commandLine.c_str()) != 0)
222		return B_ERROR;
223	return B_OK;
224}
225
226
227/*static*/ status_t
228FSUtils::_OpenFile(const Entry& entry, BFile& file)
229{
230	BPath pathBuffer;
231	const char* path;
232	status_t error = entry.GetPath(pathBuffer, path);
233	if (error != B_OK)
234		return error;
235
236	return file.SetTo(path, B_READ_ONLY);
237}
238
239
240/*static*/ status_t
241FSUtils::_OpenSymLink(const Entry& entry, BSymLink& symLink)
242{
243	BPath pathBuffer;
244	const char* path;
245	status_t error = entry.GetPath(pathBuffer, path);
246	if (error != B_OK)
247		return error;
248
249	return symLink.SetTo(path);
250}
251