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