//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// IO functions implementation for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerPlatform.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <io.h>
#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
namespace fuzzer {
static bool IsFile(
const std::string &Path,
const DWORD &FileAttributes) {
if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
return true;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return false;
HANDLE FileHandle(
CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0));
if (FileHandle == INVALID_HANDLE_VALUE) {
Printf(
"CreateFileA() failed for \"%s\
" (Error code: %lu).\n", Path.c_str(),
GetLastError());
return false;
}
DWORD FileType = GetFileType(FileHandle);
if (FileType == FILE_TYPE_UNKNOWN) {
Printf(
"GetFileType() failed for \"%s\
" (Error code: %lu).\n", Path.c_str(),
GetLastError());
CloseHandle(FileHandle);
return false;
}
if (FileType != FILE_TYPE_DISK) {
CloseHandle(FileHandle);
return false;
}
CloseHandle(FileHandle);
return true;
}
bool IsFile(
const std::string &Path) {
DWORD Att = GetFileAttributesA(Path.c_str());
if (Att == INVALID_FILE_ATTRIBUTES) {
Printf(
"GetFileAttributesA() failed for \"%s\
" (Error code: %lu).\n",
Path.c_str(), GetLastError());
return false;
}
return IsFile(Path, Att);
}
static bool IsDir(DWORD FileAttrs) {
if (FileAttrs == INVALID_FILE_ATTRIBUTES)
return false;
return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
}
std::string Basename(
const std::string &Path) {
size_t Pos = Path.find_last_of(
"/\\");
if (Pos == std::string::npos)
return Path;
assert(Pos < Path.size());
return Path.substr(Pos + 1);
}
size_t FileSize(
const std::string &Path) {
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
DWORD LastError = GetLastError();
if (LastError != ERROR_FILE_NOT_FOUND)
Printf(
"GetFileAttributesExA() failed for \"%s\
" (Error code: %lu).\n",
Path.c_str(), LastError);
return 0;
}
ULARGE_INTEGER size;
size.HighPart = attr.nFileSizeHigh;
size.LowPart = attr.nFileSizeLow;
return size.QuadPart;
}
int ListFilesInDirRecursive(
const std::string &Dir,
long *Epoch,
Vector<std::string> *V,
bool TopDir) {
int Res;
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E)
return 0;
std::string Path(Dir);
assert(!Path.empty());
if (Path.back() !=
'\\')
Path.push_back(
'\\');
Path.push_back(
'*');
// Get the first directory entry.
WIN32_FIND_DATAA FindInfo;
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
if (FindHandle == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return 0;
Printf(
"No such file or directory: %s; exiting\n", Dir.c_str());
return 1;
}
do {
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
size_t FilenameLen = strlen(FindInfo.cFileName);
if ((FilenameLen == 1 && FindInfo.cFileName[0] ==
'.') ||
(FilenameLen == 2 && FindInfo.cFileName[0] ==
'.' &&
FindInfo.cFileName[1] ==
'.'))
continue;
int Res = ListFilesInDirRecursive(FileName, Epoch, V,
false);
if (Res != 0)
return Res;
}
else if (IsFile(FileName, FindInfo.dwFileAttributes))
V->push_back(FileName);
}
while (FindNextFileA(FindHandle, &FindInfo));
DWORD LastError = GetLastError();
if (LastError != ERROR_NO_MORE_FILES)
Printf(
"FindNextFileA failed (Error code: %lu).\n", LastError);
FindClose(FindHandle);
if (Epoch && TopDir)
*Epoch = E;
return 0;
}
void IterateDirRecursive(
const std::string &Dir,
void (*DirPreCallback)(
const std::string &Dir),
void (*DirPostCallback)(
const std::string &Dir),
void (*FileCallback)(
const std::string &Dir)) {
// TODO(metzman): Implement ListFilesInDirRecursive via this function.
DirPreCallback(Dir);
DWORD DirAttrs = GetFileAttributesA(Dir.c_str());
if (!IsDir(DirAttrs))
return;
std::string TargetDir(Dir);
assert(!TargetDir.empty());
if (TargetDir.back() !=
'\\') TargetDir.push_back(
'\\');
TargetDir.push_back(
'*');
WIN32_FIND_DATAA FindInfo;
// Find the directory's first file.
HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo);
if (FindHandle == INVALID_HANDLE_VALUE) {
DWORD LastError = GetLastError();
if (LastError != ERROR_FILE_NOT_FOUND) {
// If the directory isn't empty, then something abnormal is going on.
Printf(
"FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
LastError);
}
return;
}
do {
std::string Path = DirPlusFile(Dir, FindInfo.cFileName);
DWORD PathAttrs = FindInfo.dwFileAttributes;
if (IsDir(PathAttrs)) {
// Is Path the current directory (".") or the parent ("..")?
if (strcmp(FindInfo.cFileName,
".") == 0 ||
strcmp(FindInfo.cFileName,
"..") == 0)
continue;
IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
}
else if (PathAttrs != INVALID_FILE_ATTRIBUTES) {
FileCallback(Path);
}
}
while (FindNextFileA(FindHandle, &FindInfo));
DWORD LastError = GetLastError();
if (LastError != ERROR_NO_MORE_FILES)
Printf(
"FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
LastError);
FindClose(FindHandle);
DirPostCallback(Dir);
}
char GetSeparator() {
return '\\';
}
FILE* OpenFile(
int Fd,
const char* Mode) {
return _fdopen(Fd, Mode);
}
int CloseFile(
int Fd) {
return _close(Fd);
}
int DuplicateFile(
int Fd) {
return _dup(Fd);
}
void RemoveFile(
const std::string &Path) {
_unlink(Path.c_str());
}
void RenameFile(
const std::string &OldPath,
const std::string &NewPath) {
rename(OldPath.c_str(), NewPath.c_str());
}
intptr_t GetHandleFromFd(
int fd) {
return _get_osfhandle(fd);
}
static bool IsSeparator(
char C) {
return C ==
'\\' || C ==
'/';
}
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
// Returns number of characters considered if successful.
static size_t ParseDrive(
const std::string &FileName,
const size_t Offset,
bool Relative =
true) {
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] !=
':')
return 0;
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
if (!Relative)
// Accept relative path?
return 0;
else
return 2;
}
return 3;
}
// Parse a file name, like: SomeFile.txt
// Returns number of characters considered if successful.
static size_t ParseFileName(
const std::string &FileName,
const size_t Offset) {
size_t Pos = Offset;
const size_t End = FileName.size();
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
return Pos - Offset;
}
// Parse a directory ending in separator, like: `SomeDir\`
// Returns number of characters considered if successful.
static size_t ParseDir(
const std::string &FileName,
const size_t Offset) {
size_t Pos = Offset;
const size_t End = FileName.size();
if (Pos >= End || IsSeparator(FileName[Pos]))
return 0;
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
if (Pos >= End)
return 0;
++Pos;
// Include separator.
return Pos - Offset;
}
// Parse a servername and share, like: `SomeServer\SomeShare\`
// Returns number of characters considered if successful.
static size_t ParseServerAndShare(
const std::string &FileName,
const size_t Offset) {
size_t Pos = Offset, Res;
if (!(Res = ParseDir(FileName, Pos)))
return 0;
Pos += Res;
if (!(Res = ParseDir(FileName, Pos)))
return 0;
Pos += Res;
return Pos - Offset;
}
// Parse the given Ref string from the position Offset, to exactly match the given
// string Patt.
// Returns number of characters considered if successful.
static size_t ParseCustomString(
const std::string &Ref, size_t Offset,
const char *Patt) {
size_t Len = strlen(Patt);
if (Offset + Len > Ref.size())
return 0;
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
}
// Parse a location, like:
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
// Returns number of characters considered if successful.
static size_t ParseLocation(
const std::string &FileName) {
size_t Pos = 0, Res;
if ((Res = ParseCustomString(FileName, Pos, R
"(\\?\)"))) {
Pos += Res;
if ((Res = ParseCustomString(FileName, Pos, R
"(UNC\)"))) {
Pos += Res;
if ((Res = ParseServerAndShare(FileName, Pos)))
return Pos + Res;
return 0;
}
if ((Res = ParseDrive(FileName, Pos,
false)))
return Pos + Res;
return 0;
}
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if ((Res = ParseServerAndShare(FileName, Pos)))
return Pos + Res;
return 0;
}
return Pos;
}
if ((Res = ParseDrive(FileName, Pos)))
return Pos + Res;
return Pos;
}
std::string DirName(
const std::string &FileName) {
size_t LocationLen = ParseLocation(FileName);
size_t DirLen = 0, Res;
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
DirLen += Res;
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
if (LocationLen + DirLen + FileLen != FileName.size()) {
Printf(
"DirName() failed for \"%s\
", invalid path.\n", FileName.c_str());
exit(1);
}
if (DirLen) {
--DirLen;
// Remove trailing separator.
if (!FileLen) {
// Path ended in separator.
assert(DirLen);
// Remove file name from Dir.
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
--DirLen;
if (DirLen)
// Remove trailing separator.
--DirLen;
}
}
if (!LocationLen) {
// Relative path.
if (!DirLen)
return ".";
return std::string(
".\\").append(FileName, 0, DirLen);
}
return FileName.substr(0, LocationLen + DirLen);
}
std::string TmpDir() {
std::string Tmp;
Tmp.resize(MAX_PATH + 1);
DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
if (Size == 0) {
Printf(
"Couldn't get Tmp path.\n");
exit(1);
}
Tmp.resize(Size);
return Tmp;
}
bool IsInterestingCoverageFile(
const std::string &FileName) {
if (FileName.find(
"Program Files") != std::string::npos)
return false;
if (FileName.find(
"compiler-rt\\lib\\") != std::string::npos)
return false;
// sanitizer internal.
if (FileName ==
"")
return false;
return true;
}
void RawPrint(
const char *Str) {
_write(2, Str, strlen(Str));
}
void MkDir(
const std::string &Path) {
if (CreateDirectoryA(Path.c_str(), nullptr))
return;
Printf(
"CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
GetLastError());
}
void RmDir(
const std::string &Path) {
if (RemoveDirectoryA(Path.c_str()))
return;
Printf(
"RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
GetLastError());
}
const std::string &getDevNull() {
static const std::string devNull =
"NUL";
return devNull;
}
}
// namespace fuzzer
#endif // LIBFUZZER_WINDOWS