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 std::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
);
162 void IterateDirRecursive(const std::string
&Dir
,
163 void (*DirPreCallback
)(const std::string
&Dir
),
164 void (*DirPostCallback
)(const std::string
&Dir
),
165 void (*FileCallback
)(const std::string
&Dir
)) {
166 // TODO(metzman): Implement ListFilesInDirRecursive via this function.
169 DWORD DirAttrs
= GetFileAttributesA(Dir
.c_str());
170 if (!IsDir(DirAttrs
)) return;
172 std::string
TargetDir(Dir
);
173 assert(!TargetDir
.empty());
174 if (TargetDir
.back() != '\\') TargetDir
.push_back('\\');
175 TargetDir
.push_back('*');
177 WIN32_FIND_DATAA FindInfo
;
178 // Find the directory's first file.
179 HANDLE FindHandle
= FindFirstFileA(TargetDir
.c_str(), &FindInfo
);
180 if (FindHandle
== INVALID_HANDLE_VALUE
) {
181 DWORD LastError
= GetLastError();
182 if (LastError
!= ERROR_FILE_NOT_FOUND
) {
183 // If the directory isn't empty, then something abnormal is going on.
184 Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir
.c_str(),
191 std::string Path
= DirPlusFile(Dir
, FindInfo
.cFileName
);
192 DWORD PathAttrs
= FindInfo
.dwFileAttributes
;
193 if (IsDir(PathAttrs
)) {
194 // Is Path the current directory (".") or the parent ("..")?
195 if (strcmp(FindInfo
.cFileName
, ".") == 0 ||
196 strcmp(FindInfo
.cFileName
, "..") == 0)
198 IterateDirRecursive(Path
, DirPreCallback
, DirPostCallback
, FileCallback
);
199 } else if (PathAttrs
!= INVALID_FILE_ATTRIBUTES
) {
202 } while (FindNextFileA(FindHandle
, &FindInfo
));
204 DWORD LastError
= GetLastError();
205 if (LastError
!= ERROR_NO_MORE_FILES
)
206 Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir
.c_str(),
209 FindClose(FindHandle
);
210 DirPostCallback(Dir
);
213 char GetSeparator() {
217 FILE* OpenFile(int Fd
, const char* Mode
) {
218 return _fdopen(Fd
, Mode
);
221 int CloseFile(int Fd
) {
225 int DuplicateFile(int Fd
) {
229 void RemoveFile(const std::string
&Path
) {
230 _unlink(Path
.c_str());
233 void RenameFile(const std::string
&OldPath
, const std::string
&NewPath
) {
234 rename(OldPath
.c_str(), NewPath
.c_str());
237 intptr_t GetHandleFromFd(int fd
) {
238 return _get_osfhandle(fd
);
241 bool IsSeparator(char C
) {
242 return C
== '\\' || C
== '/';
245 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
246 // Returns number of characters considered if successful.
247 static size_t ParseDrive(const std::string
&FileName
, const size_t Offset
,
248 bool Relative
= true) {
249 if (Offset
+ 1 >= FileName
.size() || FileName
[Offset
+ 1] != ':')
251 if (Offset
+ 2 >= FileName
.size() || !IsSeparator(FileName
[Offset
+ 2])) {
252 if (!Relative
) // Accept relative path?
260 // Parse a file name, like: SomeFile.txt
261 // Returns number of characters considered if successful.
262 static size_t ParseFileName(const std::string
&FileName
, const size_t Offset
) {
264 const size_t End
= FileName
.size();
265 for(; Pos
< End
&& !IsSeparator(FileName
[Pos
]); ++Pos
)
270 // Parse a directory ending in separator, like: `SomeDir\`
271 // Returns number of characters considered if successful.
272 static size_t ParseDir(const std::string
&FileName
, const size_t Offset
) {
274 const size_t End
= FileName
.size();
275 if (Pos
>= End
|| IsSeparator(FileName
[Pos
]))
277 for(; Pos
< End
&& !IsSeparator(FileName
[Pos
]); ++Pos
)
281 ++Pos
; // Include separator.
285 // Parse a servername and share, like: `SomeServer\SomeShare\`
286 // Returns number of characters considered if successful.
287 static size_t ParseServerAndShare(const std::string
&FileName
,
288 const size_t Offset
) {
289 size_t Pos
= Offset
, Res
;
290 if (!(Res
= ParseDir(FileName
, Pos
)))
293 if (!(Res
= ParseDir(FileName
, Pos
)))
299 // Parse the given Ref string from the position Offset, to exactly match the
300 // given string Patt. Returns number of characters considered if successful.
301 static size_t ParseCustomString(const std::string
&Ref
, size_t Offset
,
303 size_t Len
= strlen(Patt
);
304 if (Offset
+ Len
> Ref
.size())
306 return Ref
.compare(Offset
, Len
, Patt
) == 0 ? Len
: 0;
309 // Parse a location, like:
310 // \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
311 // Returns number of characters considered if successful.
312 static size_t ParseLocation(const std::string
&FileName
) {
315 if ((Res
= ParseCustomString(FileName
, Pos
, R
"(\\?\)"))) {
317 if ((Res
= ParseCustomString(FileName
, Pos
, R
"(UNC\)"))) {
319 if ((Res
= ParseServerAndShare(FileName
, Pos
)))
323 if ((Res
= ParseDrive(FileName
, Pos
, false)))
328 if (Pos
< FileName
.size() && IsSeparator(FileName
[Pos
])) {
330 if (Pos
< FileName
.size() && IsSeparator(FileName
[Pos
])) {
332 if ((Res
= ParseServerAndShare(FileName
, Pos
)))
339 if ((Res
= ParseDrive(FileName
, Pos
)))
345 std::string
DirName(const std::string
&FileName
) {
346 size_t LocationLen
= ParseLocation(FileName
);
347 size_t DirLen
= 0, Res
;
348 while ((Res
= ParseDir(FileName
, LocationLen
+ DirLen
)))
350 size_t FileLen
= ParseFileName(FileName
, LocationLen
+ DirLen
);
352 if (LocationLen
+ DirLen
+ FileLen
!= FileName
.size()) {
353 Printf("DirName() failed for \"%s\", invalid path.\n", FileName
.c_str());
358 --DirLen
; // Remove trailing separator.
359 if (!FileLen
) { // Path ended in separator.
361 // Remove file name from Dir.
362 while (DirLen
&& !IsSeparator(FileName
[LocationLen
+ DirLen
- 1]))
364 if (DirLen
) // Remove trailing separator.
369 if (!LocationLen
) { // Relative path.
372 return std::string(".\\").append(FileName
, 0, DirLen
);
375 return FileName
.substr(0, LocationLen
+ DirLen
);
378 std::string
TmpDir() {
380 Tmp
.resize(MAX_PATH
+ 1);
381 DWORD Size
= GetTempPathA(Tmp
.size(), &Tmp
[0]);
383 Printf("Couldn't get Tmp path.\n");
390 bool IsInterestingCoverageFile(const std::string
&FileName
) {
391 if (FileName
.find("Program Files") != std::string::npos
)
393 if (FileName
.find("compiler-rt\\lib\\") != std::string::npos
)
394 return false; // sanitizer internal.
395 if (FileName
== "<null>")
400 void RawPrint(const char *Str
) {
401 _write(2, Str
, strlen(Str
));
404 void MkDir(const std::string
&Path
) {
405 if (CreateDirectoryA(Path
.c_str(), nullptr)) return;
406 Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path
.c_str(),
410 void RmDir(const std::string
&Path
) {
411 if (RemoveDirectoryA(Path
.c_str())) return;
412 Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path
.c_str(),
416 const std::string
&getDevNull() {
417 static const std::string devNull
= "NUL";
421 } // namespace fuzzer
423 #endif // LIBFUZZER_WINDOWS