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 }