Skip to content

Commit

Permalink
Merge pull request #99 from ianhattendorf/feature/longpaths
Browse files Browse the repository at this point in the history
Support watching long paths on Windows (path length > 259)
  • Loading branch information
implausible authored Jan 16, 2020
2 parents 8e7e951 + 2afdf0c commit 7c860f1
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 6 deletions.
3 changes: 2 additions & 1 deletion includes/win32/Watcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class Watcher
{
public:
Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path);
Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path, bool pathWasNtPrefixed);
~Watcher();

bool isRunning() const { return mRunning; }
Expand Down Expand Up @@ -42,6 +42,7 @@ class Watcher
const std::wstring mPath;
std::shared_ptr<EventQueue> mQueue;
HANDLE mDirectoryHandle;
bool mPathWasNtPrefixed;

std::vector<BYTE> mReadBuffer, mWriteBuffer;
OVERLAPPED mOverlapped;
Expand Down
34 changes: 31 additions & 3 deletions src/win32/Controller.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
#include "../includes/win32/Controller.h"

static bool isNtPath(const std::wstring &path) {
return path.rfind(L"\\\\?\\", 0) == 0 || path.rfind(L"\\??\\", 0) == 0;
}

static std::wstring prefixWithNtPath(const std::wstring &path) {
const ULONG widePathLength = GetFullPathNameW(path.c_str(), 0, nullptr, nullptr);
if (widePathLength == 0) {
return path;
}

std::wstring ntPathString;
ntPathString.resize(widePathLength - 1);
if (GetFullPathNameW(path.c_str(), widePathLength, &(ntPathString[0]), nullptr) != widePathLength - 1) {
return path;
}

return ntPathString.rfind(L"\\\\", 0) == 0
? ntPathString.replace(0, 2, L"\\\\?\\UNC\\")
: ntPathString.replace(0, 0, L"\\\\?\\");
}

static std::wstring convertMultiByteToWideChar(const std::string &multiByte) {
const int wlen = MultiByteToWideChar(CP_UTF8, 0, multiByte.data(), -1, 0, 0);

Expand All @@ -8,11 +29,13 @@ static std::wstring convertMultiByteToWideChar(const std::string &multiByte) {
}

std::wstring wideString;
wideString.resize(wlen-1);
wideString.resize(wlen - 1);

int failureToResolveUTF8 = MultiByteToWideChar(CP_UTF8, 0, multiByte.data(), -1, &(wideString[0]), wlen);
if (failureToResolveUTF8 == 0) {
return std::wstring();
}

return wideString;
}

Expand All @@ -35,13 +58,18 @@ Controller::Controller(std::shared_ptr<EventQueue> queue, const std::string &pat
: mDirectoryHandle(INVALID_HANDLE_VALUE)
{
auto widePath = convertMultiByteToWideChar(path);
const bool isNt = isNtPath(widePath);
if (!isNt) {
// We convert to an NT Path to support paths > MAX_PATH
widePath = prefixWithNtPath(widePath);
}
mDirectoryHandle = openDirectory(widePath);

if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
return;
}

mWatcher.reset(new Watcher(queue, mDirectoryHandle, widePath));
mWatcher.reset(new Watcher(queue, mDirectoryHandle, widePath, isNt));
}

Controller::~Controller() {
Expand All @@ -63,5 +91,5 @@ bool Controller::hasErrored() {
}

bool Controller::isWatching() {
return mWatcher->isRunning();
return !hasErrored() && mWatcher->isRunning();
}
20 changes: 18 additions & 2 deletions src/win32/Watcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

#include <sstream>

static
void stripNTPrefix(std::wstring &path) {
if (path.rfind(L"\\\\?\\UNC\\", 0) != std::wstring::npos) {
path.replace(0, 7, L"\\");
} else if (path.rfind(L"\\\\?\\", 0) != std::wstring::npos) {
path.erase(0, 4);
}
}

static
std::wstring getWStringFileName(LPWSTR cFileName, DWORD length) {
LPWSTR nullTerminatedFileName = new WCHAR[length + 1]();
Expand All @@ -24,6 +33,12 @@ std::string Watcher::getUTF8Directory(std::wstring path) {
}

std::wstring uft16DirectoryString = utf16DirectoryStream.str();
if (!mPathWasNtPrefixed) {
// If we were the ones that prefixed the path, we should strip it
// before returning it to the user
stripNTPrefix(uft16DirectoryString);
}

int utf8length = WideCharToMultiByte(
CP_UTF8,
0,
Expand Down Expand Up @@ -89,11 +104,12 @@ std::string getUTF8FileName(std::wstring path) {
return utf8Directory;
}

Watcher::Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path)
Watcher::Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path, bool pathWasNtPrefixed)
: mRunning(false),
mDirectoryHandle(dirHandle),
mQueue(queue),
mPath(path)
mPath(path),
mPathWasNtPrefixed(pathWasNtPrefixed)
{
ZeroMemory(&mOverlapped, sizeof(OVERLAPPED));
mOverlapped.hEvent = this;
Expand Down

0 comments on commit 7c860f1

Please sign in to comment.