@@ -104,22 +104,31 @@ std::string getUTF8FileName(std::wstring path) {
104104 return utf8Directory;
105105}
106106
107- Watcher::Watcher (std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path, bool pathWasNtPrefixed)
107+ Watcher::Watcher (std::shared_ptr<EventQueue> queue, const std::wstring &path, bool pathWasNtPrefixed)
108108 : mRunning(false ),
109- mDirectoryHandle(dirHandle),
110109 mQueue(queue),
111110 mPath(path),
112111 mPathWasNtPrefixed(pathWasNtPrefixed)
113112{
114- ZeroMemory (&mOverlapped , sizeof (OVERLAPPED));
115- mOverlapped .hEvent = this ;
116- resizeBuffers (1024 * 1024 );
117- mWatchedPath = getWatchedPath ();
118- start ();
113+ mDirectoryHandle = openDirectory (mPath );
114+ if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
115+ setError (" Failed to open directory" );
116+ } else {
117+ ZeroMemory (&mOverlapped , sizeof (OVERLAPPED));
118+ mOverlapped .hEvent = this ;
119+ resizeBuffers (1024 * 1024 );
120+ mWatchedPath = getWatchedPathFromHandle ();
121+ start ();
122+ }
119123}
120124
121125Watcher::~Watcher () {
122126 stop ();
127+ std::lock_guard<std::mutex> lock (mHandleMutex );
128+ if (mDirectoryHandle != INVALID_HANDLE_VALUE) {
129+ CancelIo (mDirectoryHandle );
130+ CloseHandle (mDirectoryHandle );
131+ }
123132}
124133
125134void Watcher::resizeBuffers (std::size_t size) {
@@ -140,6 +149,7 @@ bool Watcher::pollDirectoryChanges() {
140149 return false ;
141150 }
142151
152+ std::lock_guard<std::mutex> lock (mHandleMutex );
143153 if (!ReadDirectoryChangesW (
144154 mDirectoryHandle ,
145155 mWriteBuffer .data (),
@@ -167,6 +177,39 @@ bool Watcher::pollDirectoryChanges() {
167177 return true ;
168178}
169179
180+ HANDLE Watcher::openDirectory (const std::wstring &path) {
181+ return CreateFileW (
182+ path.data (),
183+ FILE_LIST_DIRECTORY,
184+ FILE_SHARE_READ
185+ | FILE_SHARE_WRITE
186+ | FILE_SHARE_DELETE,
187+ NULL ,
188+ OPEN_EXISTING,
189+ FILE_FLAG_BACKUP_SEMANTICS
190+ | FILE_FLAG_OVERLAPPED,
191+ NULL
192+ );
193+ }
194+
195+ void Watcher::reopenWathedFolder () {
196+ std::lock_guard<std::mutex> lock (mHandleMutex );
197+ {
198+ CancelIo (mDirectoryHandle );
199+ CloseHandle (mDirectoryHandle );
200+ mDirectoryHandle = openDirectory (mPath );
201+ if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
202+ if (GetFileAttributesW (mWatchedPath .data ()) == INVALID_FILE_ATTRIBUTES && GetLastError () == ERROR_PATH_NOT_FOUND) {
203+ setError (" Service shutdown: root path changed (renamed or deleted)" );
204+ } else {
205+ setError (" Service shutdown unexpectedly" );
206+ }
207+ return ;
208+ }
209+ }
210+ pollDirectoryChanges ();
211+ }
212+
170213void Watcher::eventCallback (DWORD errorCode) {
171214 if (errorCode != ERROR_SUCCESS) {
172215 if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
@@ -179,7 +222,10 @@ void Watcher::eventCallback(DWORD errorCode) {
179222 setError (" failed resizing buffers for network traffic" );
180223 }
181224 } else if (errorCode == ERROR_ACCESS_DENIED) {
182- pollDirectoryChanges ();
225+ // Access denied can happen when we try to delete the watched folder
226+ // In Windows Server 2019 and older the folder is not removed until folder handler is released
227+ // So the code will reopen the watched folder to release the handler and it will try to open it again
228+ reopenWathedFolder ();
183229 } else {
184230 setError (" Service shutdown unexpectedly" );
185231 }
@@ -268,11 +314,13 @@ void Watcher::start() {
268314}
269315
270316void Watcher::stop () {
271- mRunning = false ;
272- // schedule a NOOP APC to force the running loop in `Watcher::run()` to wake
273- // up, notice the changed `mRunning` and properly terminate the running loop
274- QueueUserAPC ([](__in ULONG_PTR) {}, mRunner .native_handle (), (ULONG_PTR)this );
275- mRunner .join ();
317+ if (isRunning ()) {
318+ mRunning = false ;
319+ // schedule a NOOP APC to force the running loop in `Watcher::run()` to wake
320+ // up, notice the changed `mRunning` and properly terminate the running loop
321+ QueueUserAPC ([](__in ULONG_PTR) {}, mRunner .native_handle (), (ULONG_PTR)this );
322+ mRunner .join ();
323+ }
276324}
277325
278326void Watcher::setError (const std::string &error) {
@@ -298,15 +346,18 @@ std::string Watcher::getError() {
298346 return mError ;
299347}
300348
301- std::wstring Watcher::getWatchedPath () {
349+ std::wstring Watcher::getWatchedPathFromHandle () {
350+ std::lock_guard<std::mutex> lock (mHandleMutex );
351+ if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
352+ return NULL ;
353+ }
302354 DWORD pathLen = GetFinalPathNameByHandleW (mDirectoryHandle , NULL , 0 , VOLUME_NAME_NT);
303355 if (pathLen == 0 ) {
304356 setError (" Service shutdown: root path changed (renamed or deleted)" );
305357 return NULL ;
306358 }
307359
308360 WCHAR* path = new WCHAR[pathLen];
309-
310361 if (GetFinalPathNameByHandleW (mDirectoryHandle , path, pathLen, VOLUME_NAME_NT) != pathLen - 1 ) {
311362 setError (" Service shutdown: root path changed (renamed or deleted)" );
312363 delete[] path;
@@ -319,7 +370,7 @@ std::wstring Watcher::getWatchedPath() {
319370}
320371
321372void Watcher::checkWatchedPath () {
322- std::wstring path = getWatchedPath ();
373+ std::wstring path = getWatchedPathFromHandle ();
323374 if (!path.empty () && path.compare (mWatchedPath ) != 0 ) {
324375 setError (" Service shutdown: root path changed (renamed or deleted)" );
325376 }
0 commit comments