1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/* Copyright 2014-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#include "watchman_system.h"
#include "FileDescriptor.h"
#include "watchman.h"
#include "watchman_scopeguard.h"
using watchman::FileDescriptor;
using watchman::FileInformation;
using watchman::OpenFileHandleOptions;
namespace {
class WinDirHandle : public watchman_dir_handle {
std::wstring dirWPath_;
FileDescriptor h_;
bool win7_{false};
FILE_FULL_DIR_INFO* info_{nullptr};
char __declspec(align(8)) buf_[64 * 1024];
HANDLE hDirFind_{nullptr};
char nameBuf_[WATCHMAN_NAME_MAX];
struct watchman_dir_ent ent_;
public:
~WinDirHandle() {
if (hDirFind_) {
FindClose(hDirFind_);
}
}
explicit WinDirHandle(const char* path, bool strict) {
int err = 0;
dirWPath_ = w_string_piece(path).asWideUNC();
h_ = openFileHandle(path, strict ? OpenFileHandleOptions::strictOpenDir()
: OpenFileHandleOptions::openDir());
// Use Win7 compatibility mode for readDir()
if (getenv("WATCHMAN_WIN7_COMPAT") &&
getenv("WATCHMAN_WIN7_COMPAT")[0] == '1') {
win7_ = true;
}
memset(&ent_, 0, sizeof(ent_));
ent_.d_name = nameBuf_;
ent_.has_stat = true;
if (path[1] == ':') {
ent_.stat.dev = tolower(path[0]) - 'a';
}
}
const watchman_dir_ent* readDir() override {
if (win7_) {
return readDirWin7();
}
try {
return readDirWin8();
} catch (const std::system_error& err) {
if (err.code().value() != ERROR_INVALID_PARAMETER) {
throw;
}
// Fallback on Win7 implementation. FileFullDirectoryInfo
// parameter is not supported before Win8
win7_ = true;
return readDirWin7();
}
}
private:
const watchman_dir_ent* readDirWin8() {
if (!info_) {
if (!GetFileInformationByHandleEx(
(HANDLE)h_.handle(), FileFullDirectoryInfo, buf_, sizeof(buf_))) {
if (GetLastError() == ERROR_NO_MORE_FILES) {
return nullptr;
}
throw std::system_error(
GetLastError(),
std::system_category(),
"GetFileInformationByHandleEx");
}
info_ = (FILE_FULL_DIR_INFO*)buf_;
}
// Decode the item currently pointed at
DWORD len = WideCharToMultiByte(
CP_UTF8,
0,
info_->FileName,
info_->FileNameLength / sizeof(WCHAR),
nameBuf_,
sizeof(nameBuf_) - 1,
nullptr,
nullptr);
if (len <= 0) {
throw std::system_error(
GetLastError(), std::system_category(), "WideCharToMultiByte");
}
nameBuf_[len] = 0;
// Populate stat info to speed up the crawler() routine
ent_.stat = FileInformation(info_->FileAttributes);
FILETIME_LARGE_INTEGER_to_timespec(info_->CreationTime, &ent_.stat.ctime);
FILETIME_LARGE_INTEGER_to_timespec(info_->LastAccessTime, &ent_.stat.atime);
FILETIME_LARGE_INTEGER_to_timespec(info_->LastWriteTime, &ent_.stat.mtime);
ent_.stat.size = info_->EndOfFile.QuadPart;
// Advance the pointer to the next entry ready for the next read
info_ = info_->NextEntryOffset == 0
? nullptr
: (FILE_FULL_DIR_INFO*)(((char*)info_) + info_->NextEntryOffset);
return &ent_;
}
const watchman_dir_ent* readDirWin7() {
// FileFullDirectoryInfo is not supported prior to Windows 8
WIN32_FIND_DATAW findFileData;
bool success;
if (!hDirFind_) {
std::wstring strWPath(dirWPath_);
strWPath += L"\\*";
hDirFind_ = FindFirstFileW(strWPath.c_str(), &findFileData);
success = hDirFind_ != INVALID_HANDLE_VALUE;
} else {
success = FindNextFileW(hDirFind_, &findFileData);
}
if (!success) {
if (GetLastError() == ERROR_NO_MORE_FILES) {
return nullptr;
}
throw std::system_error(
GetLastError(),
std::system_category(),
hDirFind_ ? "FindNextFileW" : "FindFirstFileW");
}
DWORD len = WideCharToMultiByte(
CP_UTF8,
0,
findFileData.cFileName,
-1,
nameBuf_,
sizeof(nameBuf_) - 1,
nullptr,
nullptr);
if (len <= 0) {
throw std::system_error(
GetLastError(), std::system_category(), "WideCharToMultiByte");
}
nameBuf_[len] = 0;
// Populate stat info to speed up the crawler() routine
ent_.stat = FileInformation(findFileData.dwFileAttributes);
FILETIME_to_timespec(&findFileData.ftCreationTime, &ent_.stat.ctime);
FILETIME_to_timespec(&findFileData.ftLastAccessTime, &ent_.stat.atime);
FILETIME_to_timespec(&findFileData.ftLastWriteTime, &ent_.stat.mtime);
LARGE_INTEGER fileSize;
fileSize.HighPart = findFileData.nFileSizeHigh;
fileSize.LowPart = findFileData.nFileSizeLow;
ent_.stat.size = fileSize.QuadPart;
return &ent_;
}
};
}
std::unique_ptr<watchman_dir_handle> w_dir_open(const char* path, bool strict) {
return watchman::make_unique<WinDirHandle>(path, strict);
}
|