xref: /haiku/src/servers/package/FSUtils.cpp (revision edc6d1558e885df793ab3f4d5bc66e145506da0f)
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 
24 static const size_t kCompareDataBufferSize = 64 * 1024;
25 const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!";
26 
27 
28 /*static*/ BString
ShellEscapeString(const BString & string)29 FSUtils::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
OpenSubDirectory(const BDirectory & baseDirectory,const RelativePath & path,bool create,BDirectory & _directory)40 FSUtils::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
CompareFileContent(const Entry & entry1,const Entry & entry2,bool & _equal)76 FSUtils::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
CompareFileContent(BPositionIO & content1,BPositionIO & content2,bool & _equal)94 FSUtils::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
CompareSymLinks(const Entry & entry1,const Entry & entry2,bool & _equal)156 FSUtils::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
CompareSymLinks(BSymLink & symLink1,BSymLink & symLink2,bool & _equal)173 FSUtils::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
ExtractPackageContent(const Entry & packageEntry,const char * contentPath,const Entry & targetDirectoryEntry)192 FSUtils::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
ExtractPackageContent(const char * packagePath,const char * contentPath,const char * targetDirectoryPath)212 FSUtils::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
_OpenFile(const Entry & entry,BFile & file)228 FSUtils::_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
_OpenSymLink(const Entry & entry,BSymLink & symLink)241 FSUtils::_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