1 //===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // IO functions implementation for Windows.
10 //===----------------------------------------------------------------------===//
11 #include "FuzzerDefs.h"
12 #if LIBFUZZER_WINDOWS
13 
14 #include "FuzzerExtFunctions.h"
15 #include "FuzzerIO.h"
16 #include <cstdarg>
17 #include <cstdio>
18 #include <fstream>
19 #include <io.h>
20 #include <iterator>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <windows.h>
24 
25 namespace fuzzer {
26 
IsFile(const std::string & Path,const DWORD & FileAttributes)27 static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
28 
29   if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
30     return true;
31 
32   if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
33     return false;
34 
35   HANDLE FileHandle(
36       CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
37                   FILE_FLAG_BACKUP_SEMANTICS, 0));
38 
39   if (FileHandle == INVALID_HANDLE_VALUE) {
40     Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
41         GetLastError());
42     return false;
43   }
44 
45   DWORD FileType = GetFileType(FileHandle);
46 
47   if (FileType == FILE_TYPE_UNKNOWN) {
48     Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
49         GetLastError());
50     CloseHandle(FileHandle);
51     return false;
52   }
53 
54   if (FileType != FILE_TYPE_DISK) {
55     CloseHandle(FileHandle);
56     return false;
57   }
58 
59   CloseHandle(FileHandle);
60   return true;
61 }
62 
IsFile(const std::string & Path)63 bool IsFile(const std::string &Path) {
64   DWORD Att = GetFileAttributesA(Path.c_str());
65 
66   if (Att == INVALID_FILE_ATTRIBUTES) {
67     Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
68         Path.c_str(), GetLastError());
69     return false;
70   }
71 
72   return IsFile(Path, Att);
73 }
74 
ListFilesInDirRecursive(const std::string & Dir,long * Epoch,std::vector<std::string> * V,bool TopDir)75 void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
76                              std::vector<std::string> *V, bool TopDir) {
77   auto E = GetEpoch(Dir);
78   if (Epoch)
79     if (E && *Epoch >= E) return;
80 
81   std::string Path(Dir);
82   assert(!Path.empty());
83   if (Path.back() != '\\')
84       Path.push_back('\\');
85   Path.push_back('*');
86 
87   // Get the first directory entry.
88   WIN32_FIND_DATAA FindInfo;
89   HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
90   if (FindHandle == INVALID_HANDLE_VALUE)
91   {
92     Printf("No file found in: %s.\n", Dir.c_str());
93     return;
94   }
95 
96   do {
97     std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
98 
99     if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
100       size_t FilenameLen = strlen(FindInfo.cFileName);
101       if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
102           (FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
103                                FindInfo.cFileName[1] == '.'))
104         continue;
105 
106       ListFilesInDirRecursive(FileName, Epoch, V, false);
107     }
108     else if (IsFile(FileName, FindInfo.dwFileAttributes))
109       V->push_back(FileName);
110   } while (FindNextFileA(FindHandle, &FindInfo));
111 
112   DWORD LastError = GetLastError();
113   if (LastError != ERROR_NO_MORE_FILES)
114     Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
115 
116   FindClose(FindHandle);
117 
118   if (Epoch && TopDir)
119     *Epoch = E;
120 }
121 
GetSeparator()122 char GetSeparator() {
123   return '\\';
124 }
125 
OpenFile(int Fd,const char * Mode)126 FILE* OpenFile(int Fd, const char* Mode) {
127   return _fdopen(Fd, Mode);
128 }
129 
CloseFile(int Fd)130 int CloseFile(int Fd) {
131   return _close(Fd);
132 }
133 
DuplicateFile(int Fd)134 int DuplicateFile(int Fd) {
135   return _dup(Fd);
136 }
137 
RemoveFile(const std::string & Path)138 void RemoveFile(const std::string &Path) {
139   _unlink(Path.c_str());
140 }
141 
IsSeparator(char C)142 static bool IsSeparator(char C) {
143   return C == '\\' || C == '/';
144 }
145 
146 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
147 // Returns number of characters considered if successful.
ParseDrive(const std::string & FileName,const size_t Offset,bool Relative=true)148 static size_t ParseDrive(const std::string &FileName, const size_t Offset,
149                          bool Relative = true) {
150   if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
151     return 0;
152   if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
153     if (!Relative) // Accept relative path?
154       return 0;
155     else
156       return 2;
157   }
158   return 3;
159 }
160 
161 // Parse a file name, like: SomeFile.txt
162 // Returns number of characters considered if successful.
ParseFileName(const std::string & FileName,const size_t Offset)163 static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
164   size_t Pos = Offset;
165   const size_t End = FileName.size();
166   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
167     ;
168   return Pos - Offset;
169 }
170 
171 // Parse a directory ending in separator, like: SomeDir\
172 // Returns number of characters considered if successful.
ParseDir(const std::string & FileName,const size_t Offset)173 static size_t ParseDir(const std::string &FileName, const size_t Offset) {
174   size_t Pos = Offset;
175   const size_t End = FileName.size();
176   if (Pos >= End || IsSeparator(FileName[Pos]))
177     return 0;
178   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
179     ;
180   if (Pos >= End)
181     return 0;
182   ++Pos; // Include separator.
183   return Pos - Offset;
184 }
185 
186 // Parse a servername and share, like: SomeServer\SomeShare\
187 // Returns number of characters considered if successful.
ParseServerAndShare(const std::string & FileName,const size_t Offset)188 static size_t ParseServerAndShare(const std::string &FileName,
189                                   const size_t Offset) {
190   size_t Pos = Offset, Res;
191   if (!(Res = ParseDir(FileName, Pos)))
192     return 0;
193   Pos += Res;
194   if (!(Res = ParseDir(FileName, Pos)))
195     return 0;
196   Pos += Res;
197   return Pos - Offset;
198 }
199 
200 // Parse the given Ref string from the position Offset, to exactly match the given
201 // string Patt.
202 // Returns number of characters considered if successful.
ParseCustomString(const std::string & Ref,size_t Offset,const char * Patt)203 static size_t ParseCustomString(const std::string &Ref, size_t Offset,
204                                 const char *Patt) {
205   size_t Len = strlen(Patt);
206   if (Offset + Len > Ref.size())
207     return 0;
208   return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
209 }
210 
211 // Parse a location, like:
212 // \\?\UNC\Server\Share\  \\?\C:\  \\Server\Share\  \  C:\  C:
213 // Returns number of characters considered if successful.
ParseLocation(const std::string & FileName)214 static size_t ParseLocation(const std::string &FileName) {
215   size_t Pos = 0, Res;
216 
217   if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
218     Pos += Res;
219     if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
220       Pos += Res;
221       if ((Res = ParseServerAndShare(FileName, Pos)))
222         return Pos + Res;
223       return 0;
224     }
225     if ((Res = ParseDrive(FileName, Pos, false)))
226       return Pos + Res;
227     return 0;
228   }
229 
230   if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
231     ++Pos;
232     if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
233       ++Pos;
234       if ((Res = ParseServerAndShare(FileName, Pos)))
235         return Pos + Res;
236       return 0;
237     }
238     return Pos;
239   }
240 
241   if ((Res = ParseDrive(FileName, Pos)))
242     return Pos + Res;
243 
244   return Pos;
245 }
246 
DirName(const std::string & FileName)247 std::string DirName(const std::string &FileName) {
248   size_t LocationLen = ParseLocation(FileName);
249   size_t DirLen = 0, Res;
250   while ((Res = ParseDir(FileName, LocationLen + DirLen)))
251     DirLen += Res;
252   size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
253 
254   if (LocationLen + DirLen + FileLen != FileName.size()) {
255     Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
256     exit(1);
257   }
258 
259   if (DirLen) {
260     --DirLen; // Remove trailing separator.
261     if (!FileLen) { // Path ended in separator.
262       assert(DirLen);
263       // Remove file name from Dir.
264       while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
265         --DirLen;
266       if (DirLen) // Remove trailing separator.
267         --DirLen;
268     }
269   }
270 
271   if (!LocationLen) { // Relative path.
272     if (!DirLen)
273       return ".";
274     return std::string(".\\").append(FileName, 0, DirLen);
275   }
276 
277   return FileName.substr(0, LocationLen + DirLen);
278 }
279 
280 }  // namespace fuzzer
281 
282 #endif // LIBFUZZER_WINDOWS
283