1 //===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
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
7 //===----------------------------------------------------------------------===//
8 // IO functions implementation for Windows.
9 //===----------------------------------------------------------------------===//
10 #include "FuzzerPlatform.h"
13 #include "FuzzerExtFunctions.h"
21 #include <sys/types.h>
26 static bool IsFile(const std::string
&Path
, const DWORD
&FileAttributes
) {
28 if (FileAttributes
& FILE_ATTRIBUTE_NORMAL
)
31 if (FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
35 CreateFileA(Path
.c_str(), 0, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
,
36 FILE_FLAG_BACKUP_SEMANTICS
, 0));
38 if (FileHandle
== INVALID_HANDLE_VALUE
) {
39 Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path
.c_str(),
44 DWORD FileType
= GetFileType(FileHandle
);
46 if (FileType
== FILE_TYPE_UNKNOWN
) {
47 Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path
.c_str(),
49 CloseHandle(FileHandle
);
53 if (FileType
!= FILE_TYPE_DISK
) {
54 CloseHandle(FileHandle
);
58 CloseHandle(FileHandle
);
62 bool IsFile(const std::string
&Path
) {
63 DWORD Att
= GetFileAttributesA(Path
.c_str());
65 if (Att
== INVALID_FILE_ATTRIBUTES
) {
66 Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
67 Path
.c_str(), GetLastError());
71 return IsFile(Path
, Att
);
74 static bool IsDir(DWORD FileAttrs
) {
75 if (FileAttrs
== INVALID_FILE_ATTRIBUTES
) return false;
76 return FileAttrs
& FILE_ATTRIBUTE_DIRECTORY
;
79 bool IsDirectory(const std::string
&Path
) {
80 DWORD Att
= GetFileAttributesA(Path
.c_str());
82 if (Att
== INVALID_FILE_ATTRIBUTES
) {
83 Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
84 Path
.c_str(), GetLastError());
91 std::string
Basename(const std::string
&Path
) {
92 size_t Pos
= Path
.find_last_of("/\\");
93 if (Pos
== std::string::npos
) return Path
;
94 assert(Pos
< Path
.size());
95 return Path
.substr(Pos
+ 1);
98 size_t FileSize(const std::string
&Path
) {
99 WIN32_FILE_ATTRIBUTE_DATA attr
;
100 if (!GetFileAttributesExA(Path
.c_str(), GetFileExInfoStandard
, &attr
)) {
101 DWORD LastError
= GetLastError();
102 if (LastError
!= ERROR_FILE_NOT_FOUND
)
103 Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
104 Path
.c_str(), LastError
);
108 size
.HighPart
= attr
.nFileSizeHigh
;
109 size
.LowPart
= attr
.nFileSizeLow
;
110 return size
.QuadPart
;
113 void ListFilesInDirRecursive(const std::string
&Dir
, long *Epoch
,
114 Vector
<std::string
> *V
, bool TopDir
) {
115 auto E
= GetEpoch(Dir
);
117 if (E
&& *Epoch
>= E
) return;
119 std::string
Path(Dir
);
120 assert(!Path
.empty());
121 if (Path
.back() != '\\')
122 Path
.push_back('\\');
125 // Get the first directory entry.
126 WIN32_FIND_DATAA FindInfo
;
127 HANDLE
FindHandle(FindFirstFileA(Path
.c_str(), &FindInfo
));
128 if (FindHandle
== INVALID_HANDLE_VALUE
)
130 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
132 Printf("No such file or directory: %s; exiting\n", Dir
.c_str());
137 std::string FileName
= DirPlusFile(Dir
, FindInfo
.cFileName
);
139 if (FindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
140 size_t FilenameLen
= strlen(FindInfo
.cFileName
);
141 if ((FilenameLen
== 1 && FindInfo
.cFileName
[0] == '.') ||
142 (FilenameLen
== 2 && FindInfo
.cFileName
[0] == '.' &&
143 FindInfo
.cFileName
[1] == '.'))
146 ListFilesInDirRecursive(FileName
, Epoch
, V
, false);
148 else if (IsFile(FileName
, FindInfo
.dwFileAttributes
))
149 V
->push_back(FileName
);
150 } while (FindNextFileA(FindHandle
, &FindInfo
));
152 DWORD LastError
= GetLastError();
153 if (LastError
!= ERROR_NO_MORE_FILES
)
154 Printf("FindNextFileA failed (Error code: %lu).\n", LastError
);
156 FindClose(FindHandle
);
163 void IterateDirRecursive(const std::string
&Dir
,
164 void (*DirPreCallback
)(const std::string
&Dir
),
165 void (*DirPostCallback
)(const std::string
&Dir
),
166 void (*FileCallback
)(const std::string
&Dir
)) {
167 // TODO(metzman): Implement ListFilesInDirRecursive via this function.
170 DWORD DirAttrs
= GetFileAttributesA(Dir
.c_str());
171 if (!IsDir(DirAttrs
)) return;
173 std::string
TargetDir(Dir
);
174 assert(!TargetDir
.empty());
175 if (TargetDir
.back() != '\\') TargetDir
.push_back('\\');
176 TargetDir
.push_back('*');
178 WIN32_FIND_DATAA FindInfo
;
179 // Find the directory's first file.
180 HANDLE FindHandle
= FindFirstFileA(TargetDir
.c_str(), &FindInfo
);
181 if (FindHandle
== INVALID_HANDLE_VALUE
) {
182 DWORD LastError
= GetLastError();
183 if (LastError
!= ERROR_FILE_NOT_FOUND
) {
184 // If the directory isn't empty, then something abnormal is going on.
185 Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir
.c_str(),
192 std::string Path
= DirPlusFile(Dir
, FindInfo
.cFileName
);
193 DWORD PathAttrs
= FindInfo
.dwFileAttributes
;
194 if (IsDir(PathAttrs
)) {
195 // Is Path the current directory (".") or the parent ("..")?
196 if (strcmp(FindInfo
.cFileName
, ".") == 0 ||
197 strcmp(FindInfo
.cFileName
, "..") == 0)
199 IterateDirRecursive(Path
, DirPreCallback
, DirPostCallback
, FileCallback
);
200 } else if (PathAttrs
!= INVALID_FILE_ATTRIBUTES
) {
203 } while (FindNextFileA(FindHandle
, &FindInfo
));
205 DWORD LastError
= GetLastError();
206 if (LastError
!= ERROR_NO_MORE_FILES
)
207 Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir
.c_str(),
210 FindClose(FindHandle
);
211 DirPostCallback(Dir
);
214 char GetSeparator() {
218 FILE* OpenFile(int Fd
, const char* Mode
) {
219 return _fdopen(Fd
, Mode
);
222 int CloseFile(int Fd
) {
226 int DuplicateFile(int Fd
) {
230 void RemoveFile(const std::string
&Path
) {
231 _unlink(Path
.c_str());
234 void RenameFile(const std::string
&OldPath
, const std::string
&NewPath
) {
235 rename(OldPath
.c_str(), NewPath
.c_str());
238 intptr_t GetHandleFromFd(int fd
) {
239 return _get_osfhandle(fd
);
242 bool IsSeparator(char C
) {
243 return C
== '\\' || C
== '/';
246 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
247 // Returns number of characters considered if successful.
248 static size_t ParseDrive(const std::string
&FileName
, const size_t Offset
,
249 bool Relative
= true) {
250 if (Offset
+ 1 >= FileName
.size() || FileName
[Offset
+ 1] != ':')
252 if (Offset
+ 2 >= FileName
.size() || !IsSeparator(FileName
[Offset
+ 2])) {
253 if (!Relative
) // Accept relative path?
261 // Parse a file name, like: SomeFile.txt
262 // Returns number of characters considered if successful.
263 static size_t ParseFileName(const std::string
&FileName
, const size_t Offset
) {
265 const size_t End
= FileName
.size();
266 for(; Pos
< End
&& !IsSeparator(FileName
[Pos
]); ++Pos
)
271 // Parse a directory ending in separator, like: `SomeDir\`
272 // Returns number of characters considered if successful.
273 static size_t ParseDir(const std::string
&FileName
, const size_t Offset
) {
275 const size_t End
= FileName
.size();
276 if (Pos
>= End
|| IsSeparator(FileName
[Pos
]))
278 for(; Pos
< End
&& !IsSeparator(FileName
[Pos
]); ++Pos
)
282 ++Pos
; // Include separator.
286 // Parse a servername and share, like: `SomeServer\SomeShare\`
287 // Returns number of characters considered if successful.
288 static size_t ParseServerAndShare(const std::string
&FileName
,
289 const size_t Offset
) {
290 size_t Pos
= Offset
, Res
;
291 if (!(Res
= ParseDir(FileName
, Pos
)))
294 if (!(Res
= ParseDir(FileName
, Pos
)))
300 // Parse the given Ref string from the position Offset, to exactly match the given
302 // Returns number of characters considered if successful.
303 static size_t ParseCustomString(const std::string
&Ref
, size_t Offset
,
305 size_t Len
= strlen(Patt
);
306 if (Offset
+ Len
> Ref
.size())
308 return Ref
.compare(Offset
, Len
, Patt
) == 0 ? Len
: 0;
311 // Parse a location, like:
312 // \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
313 // Returns number of characters considered if successful.
314 static size_t ParseLocation(const std::string
&FileName
) {
317 if ((Res
= ParseCustomString(FileName
, Pos
, R
"(\\?\)"))) {
319 if ((Res
= ParseCustomString(FileName
, Pos
, R
"(UNC\)"))) {
321 if ((Res
= ParseServerAndShare(FileName
, Pos
)))
325 if ((Res
= ParseDrive(FileName
, Pos
, false)))
330 if (Pos
< FileName
.size() && IsSeparator(FileName
[Pos
])) {
332 if (Pos
< FileName
.size() && IsSeparator(FileName
[Pos
])) {
334 if ((Res
= ParseServerAndShare(FileName
, Pos
)))
341 if ((Res
= ParseDrive(FileName
, Pos
)))
347 std::string
DirName(const std::string
&FileName
) {
348 size_t LocationLen
= ParseLocation(FileName
);
349 size_t DirLen
= 0, Res
;
350 while ((Res
= ParseDir(FileName
, LocationLen
+ DirLen
)))
352 size_t FileLen
= ParseFileName(FileName
, LocationLen
+ DirLen
);
354 if (LocationLen
+ DirLen
+ FileLen
!= FileName
.size()) {
355 Printf("DirName() failed for \"%s\", invalid path.\n", FileName
.c_str());
360 --DirLen
; // Remove trailing separator.
361 if (!FileLen
) { // Path ended in separator.
363 // Remove file name from Dir.
364 while (DirLen
&& !IsSeparator(FileName
[LocationLen
+ DirLen
- 1]))
366 if (DirLen
) // Remove trailing separator.
371 if (!LocationLen
) { // Relative path.
374 return std::string(".\\").append(FileName
, 0, DirLen
);
377 return FileName
.substr(0, LocationLen
+ DirLen
);
380 std::string
TmpDir() {
382 Tmp
.resize(MAX_PATH
+ 1);
383 DWORD Size
= GetTempPathA(Tmp
.size(), &Tmp
[0]);
385 Printf("Couldn't get Tmp path.\n");
392 bool IsInterestingCoverageFile(const std::string
&FileName
) {
393 if (FileName
.find("Program Files") != std::string::npos
)
395 if (FileName
.find("compiler-rt\\lib\\") != std::string::npos
)
396 return false; // sanitizer internal.
397 if (FileName
== "<null>")
402 void RawPrint(const char *Str
) {
403 _write(2, Str
, strlen(Str
));
406 void MkDir(const std::string
&Path
) {
407 if (CreateDirectoryA(Path
.c_str(), nullptr)) return;
408 Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path
.c_str(),
412 void RmDir(const std::string
&Path
) {
413 if (RemoveDirectoryA(Path
.c_str())) return;
414 Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path
.c_str(),
418 const std::string
&getDevNull() {
419 static const std::string devNull
= "NUL";
423 } // namespace fuzzer
425 #endif // LIBFUZZER_WINDOWS