xref: /haiku/src/apps/haikudepot/util/StorageUtils.cpp (revision 06ed32b8c4ed857f589da5eb5a5a4d668f271645)
1 /*
2  * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "StorageUtils.h"
7 
8 #include <errno.h>
9 #include <stdlib.h>
10 
11 #include <Directory.h>
12 #include <File.h>
13 #include <FindDirectory.h>
14 #include <Entry.h>
15 #include <String.h>
16 
17 #include "HaikuDepotConstants.h"
18 #include "Logger.h"
19 
20 #define FILE_TO_STRING_BUFFER_LEN 64
21 
22 
23 static bool sAreWorkingFilesAvailable = true;
24 
25 
26 /*static*/ bool
27 StorageUtils::AreWorkingFilesAvailable()
28 {
29 	return sAreWorkingFilesAvailable;
30 }
31 
32 
33 /*static*/ void
34 StorageUtils::SetWorkingFilesUnavailable()
35 {
36 	sAreWorkingFilesAvailable = false;
37 }
38 
39 
40 /* This method will append the contents of the file at the supplied path to the
41  * string provided.
42  */
43 
44 status_t
45 StorageUtils::AppendToString(BPath& path, BString& result)
46 {
47 	BFile file(path.Path(), O_RDONLY);
48 	uint8_t buffer[FILE_TO_STRING_BUFFER_LEN];
49 	size_t buffer_read;
50 
51 	while((buffer_read = file.Read(buffer, FILE_TO_STRING_BUFFER_LEN)) > 0)
52 		result.Append((char *) buffer, buffer_read);
53 
54 	return (status_t) buffer_read;
55 }
56 
57 
58 /* This method will traverse the directory structure and will remove all of the
59  * files that are present in the directories as well as the directories
60  * themselves.
61  */
62 
63 status_t
64 StorageUtils::RemoveDirectoryContents(BPath& path)
65 {
66 	BDirectory directory(path.Path());
67 	BEntry directoryEntry;
68 	status_t result = B_OK;
69 
70 	while (result == B_OK &&
71 		directory.GetNextEntry(&directoryEntry) != B_ENTRY_NOT_FOUND) {
72 
73 		bool exists = false;
74 		bool isDirectory = false;
75 		BPath directoryEntryPath;
76 
77 		result = directoryEntry.GetPath(&directoryEntryPath);
78 
79 		if (result == B_OK) {
80 			result = ExistsObject(directoryEntryPath, &exists, &isDirectory,
81 				NULL);
82 		}
83 
84 		if (result == B_OK) {
85 			if (isDirectory)
86 				RemoveDirectoryContents(directoryEntryPath);
87 
88 			if (remove(directoryEntryPath.Path()) == 0)
89 				HDDEBUG("did delete [%s]", directoryEntryPath.Path());
90 			else {
91 				HDERROR("unable to delete [%s]", directoryEntryPath.Path());
92 				result = B_ERROR;
93 			}
94 		}
95 
96 	}
97 
98 	return result;
99 }
100 
101 
102 /* This method checks to see if a file object exists at the path specified.  If
103  * something does exist then the value of the 'exists' pointer is set to true.
104  * If the object is a directory then this value is also set to true.
105  */
106 
107 status_t
108 StorageUtils::ExistsObject(const BPath& path,
109 	bool* exists,
110 	bool* isDirectory,
111 	off_t* size)
112 {
113 	struct stat s;
114 
115 	if (exists != NULL)
116 		*exists = false;
117 
118 	if (isDirectory != NULL)
119 		*isDirectory = false;
120 
121 	if (size != NULL)
122 		*size = 0;
123 
124 	if (-1 == stat(path.Path(), &s)) {
125 		if (ENOENT != errno)
126 			 return B_ERROR;
127 	} else {
128 		if (exists != NULL)
129 			*exists = true;
130 
131 		if (isDirectory != NULL)
132 			*isDirectory = S_ISDIR(s.st_mode);
133 
134 		if (size != NULL)
135 			*size = s.st_size;
136 	}
137 
138 	return B_OK;
139 }
140 
141 
142 /*! This method will check that it is possible to write to the specified file.
143     This may create the file, write some data to it and then read that data
144     back again to be sure.  This can be used as an effective safety measure as
145     the application starts up in order to ensure that the storage systems are
146     in place for the application to startup.
147 
148     It is assumed here that the directory containing the test file exists.
149 */
150 
151 /*static*/ status_t
152 StorageUtils::CheckCanWriteTo(const BPath& path)
153 {
154 	status_t result = B_OK;
155 	bool exists = false;
156 	uint8 buffer[16];
157 
158 	// create some random latin letters into the buffer to write.
159 	for (int i = 0; i < 16; i++)
160 		buffer[i] = 65 + (abs(rand()) % 26);
161 
162 	if (result == B_OK)
163 		result = ExistsObject(path, &exists, NULL, NULL);
164 
165 	if (result == B_OK && exists) {
166 		HDTRACE("an object exists at the candidate path "
167 			"[%s] - it will be deleted", path.Path());
168 
169 		if (remove(path.Path()) == 0) {
170 			HDTRACE("did delete the candidate file [%s]", path.Path());
171 		} else {
172 			HDERROR("unable to delete the candidate file [%s]", path.Path());
173 			result = B_ERROR;
174 		}
175 	}
176 
177 	if (result == B_OK) {
178 		BFile file(path.Path(), O_WRONLY | O_CREAT);
179 		if (file.Write(buffer, 16) != 16) {
180 			HDERROR("unable to write test data to candidate file [%s]",
181 				path.Path());
182 			result = B_ERROR;
183 		}
184 	}
185 
186 	if (result == B_OK) {
187 		BFile file(path.Path(), O_RDONLY);
188 		uint8 readBuffer[16];
189 		if (file.Read(readBuffer, 16) != 16) {
190 			HDERROR("unable to read test data from candidate file [%s]",
191 				path.Path());
192 			result = B_ERROR;
193 		}
194 
195 		for (int i = 0; result == B_OK && i < 16; i++) {
196 			if (readBuffer[i] != buffer[i]) {
197 				HDERROR("mismatched read..write check on candidate file [%s]",
198 					path.Path());
199 				result = B_ERROR;
200 			}
201 		}
202 	}
203 
204 	return result;
205 }
206 
207 
208 /*! As the application runs it will need to store some files into the local
209     disk system.  This method, given a leafname, will write into the supplied
210     path variable, a final path where this leafname should be stored.
211 */
212 
213 /*static*/ status_t
214 StorageUtils::LocalWorkingFilesPath(const BString leaf, BPath& path,
215 	bool failOnCreateDirectory)
216 {
217 	BPath resultPath;
218 	status_t result = B_OK;
219 
220 	if (result == B_OK)
221 		result = find_directory(B_USER_CACHE_DIRECTORY, &resultPath);
222 
223 	if (result == B_OK)
224 		result = resultPath.Append(CACHE_DIRECTORY_APP);
225 
226 	if (result == B_OK) {
227 		if (failOnCreateDirectory)
228 			result = create_directory(resultPath.Path(), 0777);
229 		else
230 			create_directory(resultPath.Path(), 0777);
231 	}
232 
233 	if (result == B_OK)
234 		result = resultPath.Append(leaf);
235 
236 	if (result == B_OK)
237 		path.SetTo(resultPath.Path());
238 	else {
239 		path.Unset();
240 		HDERROR("unable to find the user cache file for "
241 			"[%s] data; %s", leaf.String(), strerror(result));
242 	}
243 
244 	return result;
245 }
246 
247 
248 /*static*/ status_t
249 StorageUtils::LocalWorkingDirectoryPath(const BString leaf, BPath& path,
250 	bool failOnCreateDirectory)
251 {
252 	BPath resultPath;
253 	status_t result = B_OK;
254 
255 	if (result == B_OK)
256 		result = find_directory(B_USER_CACHE_DIRECTORY, &resultPath);
257 
258 	if (result == B_OK)
259 		result = resultPath.Append(CACHE_DIRECTORY_APP);
260 
261 	if (result == B_OK)
262 		result = resultPath.Append(leaf);
263 
264 	if (result == B_OK) {
265 		if (failOnCreateDirectory)
266 			result = create_directory(resultPath.Path(), 0777);
267 		else
268 			create_directory(resultPath.Path(), 0777);
269 	}
270 
271 	if (result == B_OK)
272 		path.SetTo(resultPath.Path());
273 	else {
274 		path.Unset();
275 		HDERROR("unable to find the user cache directory for "
276 			"[%s] data; %s", leaf.String(), strerror(result));
277 	}
278 
279 	return result;
280 }
281 
282 
283 /*static*/ status_t
284 StorageUtils::SwapExtensionOnPath(BPath& path, const char* extension)
285 {
286 	BPath parent;
287 	status_t result = path.GetParent(&parent);
288 	if (result == B_OK) {
289 		path.SetTo(parent.Path(),
290 			SwapExtensionOnPathComponent(path.Leaf(), extension).String());
291 	}
292 	return result;
293 }
294 
295 
296 /*static*/ BString
297 StorageUtils::SwapExtensionOnPathComponent(const char* pathComponent,
298 	const char* extension)
299 {
300 	BString result(pathComponent);
301 	int32 lastDot = result.FindLast(".");
302 	if (lastDot != B_ERROR) {
303 		result.Truncate(lastDot);
304 	}
305 	result.Append(".");
306 	result.Append(extension);
307 	return result;
308 }