xref: /haiku/src/apps/haikudepot/util/StorageUtils.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 <stdio.h>
9 #include <errno.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 				if (Logger::IsDebugEnabled()) {
90 					fprintf(stdout, "did delete [%s]\n",
91 						directoryEntryPath.Path());
92 				}
93 			} else {
94 				fprintf(stderr, "unable to delete [%s]\n",
95 					directoryEntryPath.Path());
96 				result = B_ERROR;
97 			}
98 		}
99 
100 	}
101 
102 	return result;
103 }
104 
105 
106 /* This method checks to see if a file object exists at the path specified.  If
107  * something does exist then the value of the 'exists' pointer is set to true.
108  * If the object is a directory then this value is also set to true.
109  */
110 
111 status_t
112 StorageUtils::ExistsObject(const BPath& path,
113 	bool* exists,
114 	bool* isDirectory,
115 	off_t* size)
116 {
117 	struct stat s;
118 
119 	if (exists != NULL)
120 		*exists = false;
121 
122 	if (isDirectory != NULL)
123 		*isDirectory = false;
124 
125 	if (size != NULL)
126 		*size = 0;
127 
128 	if (-1 == stat(path.Path(), &s)) {
129 		if (ENOENT != errno)
130 			 return B_ERROR;
131 	} else {
132 		if (exists != NULL)
133 			*exists = true;
134 
135 		if (isDirectory != NULL)
136 			*isDirectory = S_ISDIR(s.st_mode);
137 
138 		if (size != NULL)
139 			*size = s.st_size;
140 	}
141 
142 	return B_OK;
143 }
144 
145 
146 /*! This method will check that it is possible to write to the specified file.
147     This may create the file, write some data to it and then read that data
148     back again to be sure.  This can be used as an effective safety measure as
149     the application starts up in order to ensure that the storage systems are
150     in place for the application to startup.
151 
152     It is assumed here that the directory containing the test file exists.
153 */
154 
155 /*static*/ status_t
156 StorageUtils::CheckCanWriteTo(const BPath& path)
157 {
158 	status_t result = B_OK;
159 	bool exists = false;
160 	uint8 buffer[16];
161 
162 	// create some random latin letters into the buffer to write.
163 	for (int i = 0; i < 16; i++)
164 		buffer[i] = 65 + (abs(rand()) % 26);
165 
166 	if (result == B_OK)
167 		result = ExistsObject(path, &exists, NULL, NULL);
168 
169 	if (result == B_OK && exists) {
170 		if (Logger::IsTraceEnabled()) {
171 			printf("an object exists at the candidate path "
172 				"[%s] - it will be deleted\n", path.Path());
173 		}
174 
175 		if (remove(path.Path()) == 0) {
176 			if (Logger::IsTraceEnabled()) {
177 				printf("did delete the candidate file [%s]\n", path.Path());
178 			}
179 		} else {
180 			printf("unable to delete the candidate file [%s]\n", path.Path());
181 			result = B_ERROR;
182 		}
183 	}
184 
185 	if (result == B_OK) {
186 		BFile file(path.Path(), O_WRONLY | O_CREAT);
187 		if (file.Write(buffer, 16) != 16) {
188 			printf("unable to write test data to candidate file [%s]\n",
189 				path.Path());
190 			result = B_ERROR;
191 		}
192 	}
193 
194 	if (result == B_OK) {
195 		BFile file(path.Path(), O_RDONLY);
196 		uint8 readBuffer[16];
197 		if (file.Read(readBuffer, 16) != 16) {
198 			printf("unable to read test data from candidate file [%s]\n",
199 				path.Path());
200 			result = B_ERROR;
201 		}
202 
203 		for (int i = 0; result == B_OK && i < 16; i++) {
204 			if (readBuffer[i] != buffer[i]) {
205 				printf("mismatched read..write check on candidate file [%s]\n",
206 					path.Path());
207 				result = B_ERROR;
208 			}
209 		}
210 	}
211 
212 	return result;
213 }
214 
215 
216 /*! As the application runs it will need to store some files into the local
217     disk system.  This method, given a leafname, will write into the supplied
218     path variable, a final path where this leafname should be stored.
219 */
220 
221 /*static*/ status_t
222 StorageUtils::LocalWorkingFilesPath(const BString leaf, BPath& path,
223 	bool failOnCreateDirectory)
224 {
225 	BPath resultPath;
226 	status_t result = B_OK;
227 
228 	if (result == B_OK)
229 		result = find_directory(B_USER_CACHE_DIRECTORY, &resultPath);
230 
231 	if (result == B_OK)
232 		result = resultPath.Append(CACHE_DIRECTORY_APP);
233 
234 	if (result == B_OK) {
235 		if (failOnCreateDirectory)
236 			result = create_directory(resultPath.Path(), 0777);
237 		else
238 			create_directory(resultPath.Path(), 0777);
239 	}
240 
241 	if (result == B_OK)
242 		result = resultPath.Append(leaf);
243 
244 	if (result == B_OK)
245 		path.SetTo(resultPath.Path());
246 	else {
247 		path.Unset();
248 		fprintf(stdout, "unable to find the user cache file for "
249 			"[%s] data; %s\n", leaf.String(), strerror(result));
250 	}
251 
252 	return result;
253 }
254 
255 
256 /*static*/ status_t
257 StorageUtils::LocalWorkingDirectoryPath(const BString leaf, BPath& path,
258 	bool failOnCreateDirectory)
259 {
260 	BPath resultPath;
261 	status_t result = B_OK;
262 
263 	if (result == B_OK)
264 		result = find_directory(B_USER_CACHE_DIRECTORY, &resultPath);
265 
266 	if (result == B_OK)
267 		result = resultPath.Append(CACHE_DIRECTORY_APP);
268 
269 	if (result == B_OK)
270 		result = resultPath.Append(leaf);
271 
272 	if (result == B_OK) {
273 		if (failOnCreateDirectory)
274 			result = create_directory(resultPath.Path(), 0777);
275 		else
276 			create_directory(resultPath.Path(), 0777);
277 	}
278 
279 	if (result == B_OK)
280 		path.SetTo(resultPath.Path());
281 	else {
282 		path.Unset();
283 		fprintf(stdout, "unable to find the user cache directory for "
284 			"[%s] data; %s\n", leaf.String(), strerror(result));
285 	}
286 
287 	return result;
288 }
289