1 //===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // IO functions implementation for Windows. 9 //===----------------------------------------------------------------------===// 10 #include "FuzzerDefs.h" 11 #if LIBFUZZER_WINDOWS 12 13 #include "FuzzerExtFunctions.h" 14 #include "FuzzerIO.h" 15 #include <cstdarg> 16 #include <cstdio> 17 #include <fstream> 18 #include <io.h> 19 #include <iterator> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <windows.h> 23 24 namespace fuzzer { 25 26 static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { 27 28 if (FileAttributes & FILE_ATTRIBUTE_NORMAL) 29 return true; 30 31 if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) 32 return false; 33 34 HANDLE FileHandle( 35 CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 36 FILE_FLAG_BACKUP_SEMANTICS, 0)); 37 38 if (FileHandle == INVALID_HANDLE_VALUE) { 39 Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), 40 GetLastError()); 41 return false; 42 } 43 44 DWORD FileType = GetFileType(FileHandle); 45 46 if (FileType == FILE_TYPE_UNKNOWN) { 47 Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), 48 GetLastError()); 49 CloseHandle(FileHandle); 50 return false; 51 } 52 53 if (FileType != FILE_TYPE_DISK) { 54 CloseHandle(FileHandle); 55 return false; 56 } 57 58 CloseHandle(FileHandle); 59 return true; 60 } 61 62 bool IsFile(const std::string &Path) { 63 DWORD Att = GetFileAttributesA(Path.c_str()); 64 65 if (Att == INVALID_FILE_ATTRIBUTES) { 66 Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", 67 Path.c_str(), GetLastError()); 68 return false; 69 } 70 71 return IsFile(Path, Att); 72 } 73 74 std::string Basename(const std::string &Path) { 75 size_t Pos = Path.find_last_of("/\\"); 76 if (Pos == std::string::npos) return Path; 77 assert(Pos < Path.size()); 78 return Path.substr(Pos + 1); 79 } 80 81 size_t FileSize(const std::string &Path) { 82 WIN32_FILE_ATTRIBUTE_DATA attr; 83 if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { 84 Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", 85 Path.c_str(), GetLastError()); 86 return 0; 87 } 88 ULARGE_INTEGER size; 89 size.HighPart = attr.nFileSizeHigh; 90 size.LowPart = attr.nFileSizeLow; 91 return size.QuadPart; 92 } 93 94 void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 95 Vector<std::string> *V, bool TopDir) { 96 auto E = GetEpoch(Dir); 97 if (Epoch) 98 if (E && *Epoch >= E) return; 99 100 std::string Path(Dir); 101 assert(!Path.empty()); 102 if (Path.back() != '\\') 103 Path.push_back('\\'); 104 Path.push_back('*'); 105 106 // Get the first directory entry. 107 WIN32_FIND_DATAA FindInfo; 108 HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); 109 if (FindHandle == INVALID_HANDLE_VALUE) 110 { 111 if (GetLastError() == ERROR_FILE_NOT_FOUND) 112 return; 113 Printf("No such file or directory: %s; exiting\n", Dir.c_str()); 114 exit(1); 115 } 116 117 do { 118 std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); 119 120 if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 121 size_t FilenameLen = strlen(FindInfo.cFileName); 122 if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || 123 (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && 124 FindInfo.cFileName[1] == '.')) 125 continue; 126 127 ListFilesInDirRecursive(FileName, Epoch, V, false); 128 } 129 else if (IsFile(FileName, FindInfo.dwFileAttributes)) 130 V->push_back(FileName); 131 } while (FindNextFileA(FindHandle, &FindInfo)); 132 133 DWORD LastError = GetLastError(); 134 if (LastError != ERROR_NO_MORE_FILES) 135 Printf("FindNextFileA failed (Error code: %lu).\n", LastError); 136 137 FindClose(FindHandle); 138 139 if (Epoch && TopDir) 140 *Epoch = E; 141 } 142 143 144 void IterateDirRecursive(const std::string &Dir, 145 void (*DirPreCallback)(const std::string &Dir), 146 void (*DirPostCallback)(const std::string &Dir), 147 void (*FileCallback)(const std::string &Dir)) { 148 // Unimplemented. 149 // TODO: implement, and then implement ListFilesInDirRecursive via this one. 150 } 151 152 char GetSeparator() { 153 return '\\'; 154 } 155 156 FILE* OpenFile(int Fd, const char* Mode) { 157 return _fdopen(Fd, Mode); 158 } 159 160 int CloseFile(int Fd) { 161 return _close(Fd); 162 } 163 164 int DuplicateFile(int Fd) { 165 return _dup(Fd); 166 } 167 168 void RemoveFile(const std::string &Path) { 169 _unlink(Path.c_str()); 170 } 171 172 void DiscardOutput(int Fd) { 173 FILE* Temp = fopen("nul", "w"); 174 if (!Temp) 175 return; 176 _dup2(_fileno(Temp), Fd); 177 fclose(Temp); 178 } 179 180 intptr_t GetHandleFromFd(int fd) { 181 return _get_osfhandle(fd); 182 } 183 184 static bool IsSeparator(char C) { 185 return C == '\\' || C == '/'; 186 } 187 188 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". 189 // Returns number of characters considered if successful. 190 static size_t ParseDrive(const std::string &FileName, const size_t Offset, 191 bool Relative = true) { 192 if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') 193 return 0; 194 if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { 195 if (!Relative) // Accept relative path? 196 return 0; 197 else 198 return 2; 199 } 200 return 3; 201 } 202 203 // Parse a file name, like: SomeFile.txt 204 // Returns number of characters considered if successful. 205 static size_t ParseFileName(const std::string &FileName, const size_t Offset) { 206 size_t Pos = Offset; 207 const size_t End = FileName.size(); 208 for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) 209 ; 210 return Pos - Offset; 211 } 212 213 // Parse a directory ending in separator, like: `SomeDir\` 214 // Returns number of characters considered if successful. 215 static size_t ParseDir(const std::string &FileName, const size_t Offset) { 216 size_t Pos = Offset; 217 const size_t End = FileName.size(); 218 if (Pos >= End || IsSeparator(FileName[Pos])) 219 return 0; 220 for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) 221 ; 222 if (Pos >= End) 223 return 0; 224 ++Pos; // Include separator. 225 return Pos - Offset; 226 } 227 228 // Parse a servername and share, like: `SomeServer\SomeShare\` 229 // Returns number of characters considered if successful. 230 static size_t ParseServerAndShare(const std::string &FileName, 231 const size_t Offset) { 232 size_t Pos = Offset, Res; 233 if (!(Res = ParseDir(FileName, Pos))) 234 return 0; 235 Pos += Res; 236 if (!(Res = ParseDir(FileName, Pos))) 237 return 0; 238 Pos += Res; 239 return Pos - Offset; 240 } 241 242 // Parse the given Ref string from the position Offset, to exactly match the given 243 // string Patt. 244 // Returns number of characters considered if successful. 245 static size_t ParseCustomString(const std::string &Ref, size_t Offset, 246 const char *Patt) { 247 size_t Len = strlen(Patt); 248 if (Offset + Len > Ref.size()) 249 return 0; 250 return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; 251 } 252 253 // Parse a location, like: 254 // \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: 255 // Returns number of characters considered if successful. 256 static size_t ParseLocation(const std::string &FileName) { 257 size_t Pos = 0, Res; 258 259 if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { 260 Pos += Res; 261 if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { 262 Pos += Res; 263 if ((Res = ParseServerAndShare(FileName, Pos))) 264 return Pos + Res; 265 return 0; 266 } 267 if ((Res = ParseDrive(FileName, Pos, false))) 268 return Pos + Res; 269 return 0; 270 } 271 272 if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { 273 ++Pos; 274 if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { 275 ++Pos; 276 if ((Res = ParseServerAndShare(FileName, Pos))) 277 return Pos + Res; 278 return 0; 279 } 280 return Pos; 281 } 282 283 if ((Res = ParseDrive(FileName, Pos))) 284 return Pos + Res; 285 286 return Pos; 287 } 288 289 std::string DirName(const std::string &FileName) { 290 size_t LocationLen = ParseLocation(FileName); 291 size_t DirLen = 0, Res; 292 while ((Res = ParseDir(FileName, LocationLen + DirLen))) 293 DirLen += Res; 294 size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); 295 296 if (LocationLen + DirLen + FileLen != FileName.size()) { 297 Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); 298 exit(1); 299 } 300 301 if (DirLen) { 302 --DirLen; // Remove trailing separator. 303 if (!FileLen) { // Path ended in separator. 304 assert(DirLen); 305 // Remove file name from Dir. 306 while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) 307 --DirLen; 308 if (DirLen) // Remove trailing separator. 309 --DirLen; 310 } 311 } 312 313 if (!LocationLen) { // Relative path. 314 if (!DirLen) 315 return "."; 316 return std::string(".\\").append(FileName, 0, DirLen); 317 } 318 319 return FileName.substr(0, LocationLen + DirLen); 320 } 321 322 std::string TmpDir() { 323 std::string Tmp; 324 Tmp.resize(MAX_PATH + 1); 325 DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]); 326 if (Size == 0) { 327 Printf("Couldn't get Tmp path.\n"); 328 exit(1); 329 } 330 Tmp.resize(Size); 331 return Tmp; 332 } 333 334 bool IsInterestingCoverageFile(const std::string &FileName) { 335 if (FileName.find("Program Files") != std::string::npos) 336 return false; 337 if (FileName.find("compiler-rt\\lib\\") != std::string::npos) 338 return false; // sanitizer internal. 339 if (FileName == "<null>") 340 return false; 341 return true; 342 } 343 344 void RawPrint(const char *Str) { 345 _write(2, Str, strlen(Str)); 346 } 347 348 void MkDir(const std::string &Path) { 349 Printf("MkDir: unimplemented\n"); 350 } 351 352 void RmDir(const std::string &Path) { 353 Printf("RmDir: unimplemented\n"); 354 } 355 356 } // namespace fuzzer 357 358 #endif // LIBFUZZER_WINDOWS 359