CDirectoryWatcher::~CDirectoryWatcher(void) { Stop(); AutoLocker lock(m_critSec); ClearInfoMap(); CleanupWatchInfo(); }
CPathWatcher::~CPathWatcher(void) { InterlockedExchange(&m_bRunning, FALSE); m_hThread.CloseHandle(); AutoLocker lock(m_critSec); ClearInfoMap(); }
CDirectoryWatcher::~CDirectoryWatcher(void) { InterlockedExchange(&m_bRunning, FALSE); if (m_hThread != INVALID_HANDLE_VALUE) { CloseHandle(m_hThread); m_hThread = INVALID_HANDLE_VALUE; } AutoLocker lock(m_critSec); ClearInfoMap(); }
void CDirectoryWatcher::WorkerThread() { DWORD numBytes; CDirWatchInfo * pdi = NULL; LPOVERLAPPED lpOverlapped; WCHAR buf[READ_DIR_CHANGE_BUFFER_SIZE] = {0}; WCHAR * pFound = NULL; while (m_bRunning) { CleanupWatchInfo(); if (watchedPaths.GetCount()) { // Any incoming notifications? pdi = NULL; numBytes = 0; InterlockedExchange(&m_bCleaned, FALSE); if ((!m_hCompPort) || !GetQueuedCompletionStatus(m_hCompPort, &numBytes, (PULONG_PTR) &pdi, &lpOverlapped, 600000 /*10 minutes*/)) { // No. Still trying? if (!m_bRunning) return; ATLTRACE(_T(": restarting watcher\n")); m_hCompPort.CloseHandle(); // We must sync the whole section because other threads may // receive "AddPath" calls that will delete the completion // port *while* we are adding references to it . AutoLocker lock(m_critSec); // Clear the list of watched objects and recreate that list. // This will also delete the old completion port ClearInfoMap(); CleanupWatchInfo(); for (int i=0; i<watchedPaths.GetCount(); ++i) { CTGitPath watchedPath = watchedPaths[i]; CAutoFile hDir = CreateFile(watchedPath.GetWinPath(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, //security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | //required privileges: SE_BACKUP_NAME and SE_RESTORE_NAME. FILE_FLAG_OVERLAPPED, NULL); if (!hDir) { // this could happen if a watched folder has been removed/renamed ATLTRACE(_T("CDirectoryWatcher: CreateFile failed. Can't watch directory %s\n"), watchedPaths[i].GetWinPath()); watchedPaths.RemovePath(watchedPath); break; } DEV_BROADCAST_HANDLE NotificationFilter; SecureZeroMemory(&NotificationFilter, sizeof(NotificationFilter)); NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE); NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE; NotificationFilter.dbch_handle = hDir; // RegisterDeviceNotification sends a message to the UI thread: // make sure we *can* send it and that the UI thread isn't waiting on a lock int numPaths = watchedPaths.GetCount(); size_t numWatch = watchInfoMap.size(); lock.Unlock(); NotificationFilter.dbch_hdevnotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); lock.Lock(); // since we released the lock to prevent a deadlock with the UI thread, // it could happen that new paths were added to watch, or another thread // could have cleared our info map. // if that happened, we have to restart watching all paths again. if ((numPaths != watchedPaths.GetCount()) || (numWatch != watchInfoMap.size())) { ClearInfoMap(); CleanupWatchInfo(); Sleep(200); break; } CDirWatchInfo * pDirInfo = new CDirWatchInfo(hDir, watchedPath); hDir.Detach(); // the new CDirWatchInfo object owns the handle now pDirInfo->m_hDevNotify = NotificationFilter.dbch_hdevnotify; HANDLE port = CreateIoCompletionPort(pDirInfo->m_hDir, m_hCompPort, (ULONG_PTR)pDirInfo, 0); if (port == NULL) { ATLTRACE(_T("CDirectoryWatcher: CreateIoCompletionPort failed. Can't watch directory %s\n"), watchedPath.GetWinPath()); // we must close the directory handle to allow ClearInfoMap() // to close the completion port properly pDirInfo->CloseDirectoryHandle(); ClearInfoMap(); CleanupWatchInfo(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPath); break; } m_hCompPort = port; if (!ReadDirectoryChangesW(pDirInfo->m_hDir, pDirInfo->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pDirInfo->m_Overlapped, NULL)) //no completion routine! { ATLTRACE(_T("CDirectoryWatcher: ReadDirectoryChangesW failed. Can't watch directory %s\n"), watchedPath.GetWinPath()); // we must close the directory handle to allow ClearInfoMap() // to close the completion port properly pDirInfo->CloseDirectoryHandle(); ClearInfoMap(); CleanupWatchInfo(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPath); break; } ATLTRACE(_T("watching path %s\n"), pDirInfo->m_DirName.GetWinPath()); watchInfoMap[pDirInfo->m_hDir] = pDirInfo; } } else { if (!m_bRunning) return; if (watchInfoMap.empty()) continue; // NOTE: the longer this code takes to execute until ReadDirectoryChangesW // is called again, the higher the chance that we miss some // changes in the file system! if (pdi) { BOOL bRet = false; std::list<CTGitPath> notifyPaths; { AutoLocker lock(m_critSec); // in case the CDirectoryWatcher objects have been cleaned, // the m_bCleaned variable will be set to true here. If the // objects haven't been cleared, we can access them here. if (InterlockedExchange(&m_bCleaned, FALSE)) continue; if ( (!pdi->m_hDir) || watchInfoMap.empty() || (watchInfoMap.find(pdi->m_hDir) == watchInfoMap.end())) { continue; } PFILE_NOTIFY_INFORMATION pnotify = (PFILE_NOTIFY_INFORMATION)pdi->m_Buffer; DWORD nOffset = 0; do { pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset); if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; nOffset = pnotify->NextEntryOffset; if (pnotify->FileNameLength >= (READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR))) continue; SecureZeroMemory(buf, READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR)); _tcsncpy_s(buf, pdi->m_DirPath, _countof(buf) - 1); errno_t err = _tcsncat_s(buf + pdi->m_DirPath.GetLength(), READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), pnotify->FileName, min(READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), pnotify->FileNameLength/sizeof(TCHAR))); if (err == STRUNCATE) { continue; } buf[(pnotify->FileNameLength/sizeof(TCHAR))+pdi->m_DirPath.GetLength()] = 0; if (m_FolderCrawler) { if ((pFound = wcsstr(buf, L"\\tmp")) != NULL) { pFound += 4; if (((*pFound)=='\\')||((*pFound)=='\0')) { continue; } } if ((pFound = wcsstr(buf, L":\\RECYCLER\\")) != NULL) { if ((pFound-buf) < 5) { // a notification for the recycle bin - ignore it continue; } } if ((pFound = wcsstr(buf, L":\\$Recycle.Bin\\")) != NULL) { if ((pFound-buf) < 5) { // a notification for the recycle bin - ignore it continue; } } if (wcsstr(buf, L".tmp") != NULL) { // assume files with a .tmp extension are not versioned and interesting, // so ignore them. continue; } CTGitPath path; bool isIndex = false; if ((pFound = wcsstr(buf, L".git")) != NULL) { // omit repository data change except .git/index.lock- or .git/HEAD.lock-files if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; path = g_AdminDirMap.GetWorkingCopy(CTGitPath(buf).GetContainingDirectory().GetWinPathString()); if ((wcsstr(pFound, L"index.lock") != NULL || wcsstr(pFound, L"HEAD.lock") != NULL) && pnotify->Action == FILE_ACTION_ADDED) { CGitStatusCache::Instance().BlockPath(path); continue; } else if (((wcsstr(pFound, L"index.lock") != NULL || wcsstr(pFound, L"HEAD.lock") != NULL) && pnotify->Action == FILE_ACTION_REMOVED) || (((wcsstr(pFound, L"index") != NULL && wcsstr(pFound, L"index.lock") == NULL) || (wcsstr(pFound, L"HEAD") != NULL && wcsstr(pFound, L"HEAD.lock") != NULL)) && pnotify->Action == FILE_ACTION_MODIFIED) || ((wcsstr(pFound, L"index.lock") == NULL || wcsstr(pFound, L"HEAD.lock") != NULL) && pnotify->Action == FILE_ACTION_RENAMED_NEW_NAME)) { isIndex = true; CGitStatusCache::Instance().BlockPath(path, 1); } else { continue; } } else path.SetFromUnknown(buf); if(!path.HasAdminDir() && !isIndex) continue; ATLTRACE(_T("change notification: %s\n"), buf); notifyPaths.push_back(path); } } while ((nOffset > 0)&&(nOffset < READ_DIR_CHANGE_BUFFER_SIZE)); // setup next notification cycle SecureZeroMemory (pdi->m_Buffer, sizeof(pdi->m_Buffer)); SecureZeroMemory (&pdi->m_Overlapped, sizeof(OVERLAPPED)); bRet = ReadDirectoryChangesW(pdi->m_hDir, pdi->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pdi->m_Overlapped, NULL); //no completion routine! } if (!notifyPaths.empty()) { for (std::list<CTGitPath>::const_iterator nit = notifyPaths.begin(); nit != notifyPaths.end(); ++nit) { m_FolderCrawler->AddPathForUpdate(*nit); } } // any clean-up to do? CleanupWatchInfo(); if (!bRet) { // Since the call to ReadDirectoryChangesW failed, just // wait a while. We don't want to have this thread // running using 100% CPU if something goes completely // wrong. Sleep(200); } } } }// if (watchedPaths.GetCount()) else Sleep(200); }// while (m_bRunning) }
bool CDirectoryWatcher::AddPath(const CTGitPath& path, bool bCloseInfoMap) { if (!CGitStatusCache::Instance().IsPathAllowed(path)) return false; if ((!blockedPath.IsEmpty())&&(blockedPath.IsAncestorOf(path))) { if (GetTickCount() < blockTickCount) { ATLTRACE(_T("Path %s prevented from being watched\n"), path.GetWinPath()); return false; } } if (path.GetWinPathString().Find(L":\\RECYCLER\\") >= 0) return false; if (path.GetWinPathString().Find(L":\\$Recycle.Bin\\") >= 0) return false; AutoLocker lock(m_critSec); for (int i=0; i<watchedPaths.GetCount(); ++i) { if (watchedPaths[i].IsAncestorOf(path)) return false; // already watched (recursively) } // now check if with the new path we might have a new root CTGitPath newroot; for (int i=0; i<watchedPaths.GetCount(); ++i) { const CString& watched = watchedPaths[i].GetWinPathString(); const CString& sPath = path.GetWinPathString(); int minlen = min(sPath.GetLength(), watched.GetLength()); int len = 0; for (len = 0; len < minlen; ++len) { if (watched.GetAt(len) != sPath.GetAt(len)) { if ((len > 1)&&(len < minlen)) { if (sPath.GetAt(len)=='\\') { newroot = CTGitPath(sPath.Left(len)); } else if (watched.GetAt(len)=='\\') { newroot = CTGitPath(watched.Left(len)); } } break; } } if (len == minlen) { if (sPath.GetLength() == minlen) { if (watched.GetLength() > minlen) { if (watched.GetAt(len)=='\\') { newroot = path; } else if (sPath.GetLength() == 3 && sPath[1] == ':') { newroot = path; } } } else { if (sPath.GetLength() > minlen) { if (sPath.GetAt(len)=='\\') { newroot = CTGitPath(watched); } else if (watched.GetLength() == 3 && watched[1] == ':') { newroot = CTGitPath(watched); } } } } } if (!newroot.IsEmpty()) { ATLTRACE(_T("add path to watch %s\n"), newroot.GetWinPath()); watchedPaths.AddPath(newroot); watchedPaths.RemoveChildren(); if (bCloseInfoMap) ClearInfoMap(); return true; } ATLTRACE(_T("add path to watch %s\n"), path.GetWinPath()); watchedPaths.AddPath(path); if (bCloseInfoMap) ClearInfoMap(); return true; }
void CDirectoryWatcher::WorkerThread() { DWORD numBytes; CDirWatchInfo * pdi = NULL; LPOVERLAPPED lpOverlapped; WCHAR buf[READ_DIR_CHANGE_BUFFER_SIZE] = {0}; WCHAR * pFound = NULL; CTGitPath path; while (m_bRunning) { if (watchedPaths.GetCount()) { if (!GetQueuedCompletionStatus(m_hCompPort, &numBytes, (PULONG_PTR) &pdi, &lpOverlapped, INFINITE)) { // Error retrieving changes // Clear the list of watched objects and recreate that list if (!m_bRunning) return; { AutoLocker lock(m_critSec); ClearInfoMap(); } DWORD lasterr = GetLastError(); if ((m_hCompPort != INVALID_HANDLE_VALUE)&&(lasterr!=ERROR_SUCCESS)&&(lasterr!=ERROR_INVALID_HANDLE)) { CloseHandle(m_hCompPort); m_hCompPort = INVALID_HANDLE_VALUE; } // Since we pass m_hCompPort to CreateIoCompletionPort, we // have to set this to NULL to have that API create a new // handle. m_hCompPort = NULL; for (int i=0; i<watchedPaths.GetCount(); ++i) { CTGitPath watchedPath = watchedPaths[i]; HANDLE hDir = CreateFile(watchedPath.GetWinPath(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, //security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | //required privileges: SE_BACKUP_NAME and SE_RESTORE_NAME. FILE_FLAG_OVERLAPPED, NULL); if (hDir == INVALID_HANDLE_VALUE) { // this could happen if a watched folder has been removed/renamed ATLTRACE(_T("CDirectoryWatcher: CreateFile failed. Can't watch directory %s\n"), watchedPaths[i].GetWinPath()); CloseHandle(m_hCompPort); m_hCompPort = INVALID_HANDLE_VALUE; AutoLocker lock(m_critSec); watchedPaths.RemovePath(watchedPath); i--; if (i<0) i=0; break; } DEV_BROADCAST_HANDLE NotificationFilter; SecureZeroMemory(&NotificationFilter, sizeof(NotificationFilter)); NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE); NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE; NotificationFilter.dbch_handle = hDir; NotificationFilter.dbch_hdevnotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); CDirWatchInfo * pDirInfo = new CDirWatchInfo(hDir, watchedPath); pDirInfo->m_hDevNotify = NotificationFilter.dbch_hdevnotify; m_hCompPort = CreateIoCompletionPort(hDir, m_hCompPort, (ULONG_PTR)pDirInfo, 0); if (m_hCompPort == NULL) { ATLTRACE(_T("CDirectoryWatcher: CreateIoCompletionPort failed. Can't watch directory %s\n"), watchedPath.GetWinPath()); AutoLocker lock(m_critSec); ClearInfoMap(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPath); i--; if (i<0) i=0; break; } if (!ReadDirectoryChangesW(pDirInfo->m_hDir, pDirInfo->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pDirInfo->m_Overlapped, NULL)) //no completion routine! { ATLTRACE(_T("CDirectoryWatcher: ReadDirectoryChangesW failed. Can't watch directory %s\n"), watchedPath.GetWinPath()); AutoLocker lock(m_critSec); ClearInfoMap(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPath); i--; if (i<0) i=0; break; } AutoLocker lock(m_critSec); watchInfoMap[pDirInfo->m_hDir] = pDirInfo; ATLTRACE(_T("watching path %s\n"), pDirInfo->m_DirName.GetWinPath()); } } else { if (!m_bRunning) return; // NOTE: the longer this code takes to execute until ReadDirectoryChangesW // is called again, the higher the chance that we miss some // changes in the file system! if (pdi) { if (numBytes == 0) { goto continuewatching; } PFILE_NOTIFY_INFORMATION pnotify = (PFILE_NOTIFY_INFORMATION)pdi->m_Buffer; if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) goto continuewatching; DWORD nOffset = pnotify->NextEntryOffset; do { nOffset = pnotify->NextEntryOffset; if (pnotify->FileNameLength >= (READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR))) continue; SecureZeroMemory(buf, READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR)); _tcsncpy_s(buf, READ_DIR_CHANGE_BUFFER_SIZE, pdi->m_DirPath, READ_DIR_CHANGE_BUFFER_SIZE); errno_t err = _tcsncat_s(buf+pdi->m_DirPath.GetLength(), READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), pnotify->FileName, _TRUNCATE); if (err == STRUNCATE) { pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset); continue; } buf[(pnotify->FileNameLength/sizeof(TCHAR))+pdi->m_DirPath.GetLength()] = 0; pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset); if (m_FolderCrawler) { if ((pFound = wcsstr(buf, L"\\tmp"))!=NULL) { pFound += 4; if (((*pFound)=='\\')||((*pFound)=='\0')) { if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; continue; } } if ((pFound = wcsstr(buf, L":\\RECYCLER\\"))!=NULL) { if ((pFound-buf) < 5) { // a notification for the recycle bin - ignore it if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; continue; } } if ((pFound = wcsstr(buf, L":\\$Recycle.Bin\\"))!=NULL) { if ((pFound-buf) < 5) { // a notification for the recycle bin - ignore it if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; continue; } } if ((pFound = wcsstr(buf, L".tmp"))!=NULL) { // assume files with a .tmp extension are not versioned and interesting, // so ignore them. if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; continue; } bool isIndex = false; if ((pFound = wcsstr(buf, L".git"))!=NULL) { // omit repository data change except .git/index.lock- or .git/HEAD.lock-files if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; if ((wcsstr(pFound, L"index.lock") != NULL && wcsstr(pFound, L"HEAD.lock") != NULL) && pnotify->Action == FILE_ACTION_ADDED) { m_FolderCrawler->BlockPath(CTGitPath(buf).GetContainingDirectory().GetContainingDirectory()); // optimize here, and use general BlockPath with priorities continue; } else if ((wcsstr(pFound, L"index.lock") != NULL && wcsstr(pFound, L"HEAD.lock") != NULL) && pnotify->Action == FILE_ACTION_REMOVED) { isIndex = true; m_FolderCrawler->BlockPath(CTGitPath(buf).GetContainingDirectory().GetContainingDirectory(), 1); } else { continue; } } path.SetFromWin(buf); if(!path.HasAdminDir() && !isIndex) continue; ATLTRACE(_T("change notification: %s\n"), buf); m_FolderCrawler->AddPathForUpdate(CTGitPath(buf)); } if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; } while (nOffset); continuewatching: SecureZeroMemory(pdi->m_Buffer, sizeof(pdi->m_Buffer)); SecureZeroMemory(&pdi->m_Overlapped, sizeof(OVERLAPPED)); if (!ReadDirectoryChangesW(pdi->m_hDir, pdi->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pdi->m_Overlapped, NULL)) //no completion routine! { // Since the call to ReadDirectoryChangesW failed, just // wait a while. We don't want to have this thread // running using 100% CPU if something goes completely // wrong. Sleep(200); } } } } // if (watchedPaths.GetCount()) else Sleep(200); } // while (m_bRunning) }
void CPathWatcher::WorkerThread() { DWORD numBytes; CDirWatchInfo * pdi = NULL; LPOVERLAPPED lpOverlapped; WCHAR buf[MAX_PATH*4] = {0}; while (m_bRunning) { if (watchedPaths.GetCount()) { if (!GetQueuedCompletionStatus(m_hCompPort, &numBytes, (PULONG_PTR) &pdi, &lpOverlapped, INFINITE)) { // Error retrieving changes // Clear the list of watched objects and recreate that list if (!m_bRunning) return; { AutoLocker lock(m_critSec); ClearInfoMap(); } DWORD lasterr = GetLastError(); if ((m_hCompPort != INVALID_HANDLE_VALUE)&&(lasterr!=ERROR_SUCCESS)&&(lasterr!=ERROR_INVALID_HANDLE)) { CloseHandle(m_hCompPort); m_hCompPort = INVALID_HANDLE_VALUE; } // Since we pass m_hCompPort to CreateIoCompletionPort, we // have to set this to NULL to have that API create a new // handle. m_hCompPort = NULL; for (int i=0; i<watchedPaths.GetCount(); ++i) { HANDLE hDir = CreateFile(watchedPaths[i].GetWinPath(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, //security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | //required privileges: SE_BACKUP_NAME and SE_RESTORE_NAME. FILE_FLAG_OVERLAPPED, NULL); if (hDir == INVALID_HANDLE_VALUE) { // this could happen if a watched folder has been removed/renamed CloseHandle(m_hCompPort); m_hCompPort = INVALID_HANDLE_VALUE; AutoLocker lock(m_critSec); watchedPaths.RemovePath(watchedPaths[i]); i--; if (i<0) i=0; break; } CDirWatchInfo * pDirInfo = new CDirWatchInfo(hDir, watchedPaths[i]); m_hCompPort = CreateIoCompletionPort(hDir, m_hCompPort, (ULONG_PTR)pDirInfo, 0); if (m_hCompPort == NULL) { AutoLocker lock(m_critSec); ClearInfoMap(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPaths[i]); i--; if (i<0) i=0; break; } if (!ReadDirectoryChangesW(pDirInfo->m_hDir, pDirInfo->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pDirInfo->m_Overlapped, NULL)) //no completion routine! { AutoLocker lock(m_critSec); ClearInfoMap(); delete pDirInfo; pDirInfo = NULL; watchedPaths.RemovePath(watchedPaths[i]); i--; if (i<0) i=0; break; } AutoLocker lock(m_critSec); watchInfoMap[pDirInfo->m_hDir] = pDirInfo; ATLTRACE(_T("watching path %s\n"), pDirInfo->m_DirName.GetWinPath()); } } else { if (!m_bRunning) return; // NOTE: the longer this code takes to execute until ReadDirectoryChangesW // is called again, the higher the chance that we miss some // changes in the file system! if (pdi) { if (numBytes == 0) { goto continuewatching; } PFILE_NOTIFY_INFORMATION pnotify = (PFILE_NOTIFY_INFORMATION)pdi->m_Buffer; if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) goto continuewatching; DWORD nOffset = pnotify->NextEntryOffset; do { nOffset = pnotify->NextEntryOffset; SecureZeroMemory(buf, MAX_PATH*4*sizeof(TCHAR)); _tcsncpy_s(buf, MAX_PATH*4, pdi->m_DirPath, MAX_PATH*4); errno_t err = _tcsncat_s(buf+pdi->m_DirPath.GetLength(), (MAX_PATH*4)-pdi->m_DirPath.GetLength(), pnotify->FileName, _TRUNCATE); if (err == STRUNCATE) { pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset); continue; } buf[min(MAX_PATH*4-1, pdi->m_DirPath.GetLength()+(pnotify->FileNameLength/sizeof(WCHAR)))] = 0; pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset); ATLTRACE(_T("change notification: %s\n"), buf); m_changedPaths.AddPath(CTGitPath(buf)); if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE) break; } while (nOffset); continuewatching: SecureZeroMemory(pdi->m_Buffer, sizeof(pdi->m_Buffer)); SecureZeroMemory(&pdi->m_Overlapped, sizeof(OVERLAPPED)); if (!ReadDirectoryChangesW(pdi->m_hDir, pdi->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &numBytes,// not used &pdi->m_Overlapped, NULL)) //no completion routine! { // Since the call to ReadDirectoryChangesW failed, just // wait a while. We don't want to have this thread // running using 100% CPU if something goes completely // wrong. Sleep(200); } } } }// if (watchedPaths.GetCount()) else Sleep(200); }// while (m_bRunning) }
bool CDirectoryWatcher::AddPath(const CTSVNPath& path, bool bCloseInfoMap) { if (!CSVNStatusCache::Instance().IsPathAllowed(path)) return false; if ((!blockedPath.IsEmpty())&&(blockedPath.IsAncestorOf(path))) { if (GetTickCount64() < blockTickCount) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Path %s prevented from being watched\n", path.GetWinPath()); return false; } } // ignore the recycle bin PTSTR pFound = StrStrI(path.GetWinPath(), L":\\RECYCLER"); if (pFound != NULL) { if ((*(pFound + 10) == '\0') || (*(pFound + 10) == '\\')) return false; } pFound = StrStrI(path.GetWinPath(), L":\\$Recycle.Bin"); if (pFound != NULL) { if ((*(pFound + 14) == '\0') || (*(pFound + 14) == '\\')) return false; } AutoLocker lock(m_critSec); for (int i=0; i<watchedPaths.GetCount(); ++i) { if (watchedPaths[i].IsAncestorOf(path)) return false; // already watched (recursively) } // now check if with the new path we might have a new root CTSVNPath newroot; for (int i=0; i<watchedPaths.GetCount(); ++i) { const CString& watched = watchedPaths[i].GetWinPathString(); const CString& sPath = path.GetWinPathString(); int minlen = min(sPath.GetLength(), watched.GetLength()); int len = 0; for (len = 0; len < minlen; ++len) { if (watched.GetAt(len) != sPath.GetAt(len)) { if ((len > 1)&&(len < minlen)) { if (sPath.GetAt(len)=='\\') { newroot = CTSVNPath(sPath.Left(len)); } else if (watched.GetAt(len)=='\\') { newroot = CTSVNPath(watched.Left(len)); } } break; } } if (len == minlen) { if (sPath.GetLength() == minlen) { if (watched.GetLength() > minlen) { if (watched.GetAt(len)=='\\') { newroot = path; } else if (sPath.GetLength() == 3 && sPath[1] == ':') { newroot = path; } } } else { if (sPath.GetLength() > minlen) { if (sPath.GetAt(len)=='\\') { newroot = CTSVNPath(watched); } else if (watched.GetLength() == 3 && watched[1] == ':') { newroot = CTSVNPath(watched); } } } } } if (!newroot.IsEmpty() && SVNHelper::IsVersioned(newroot, false)) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": AddPath for %s\n", newroot.GetWinPath()); watchedPaths.AddPath(newroot); watchedPaths.RemoveChildren(); if (bCloseInfoMap) ClearInfoMap(); return true; } if (!SVNHelper::IsVersioned(path, false)) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Path %s prevented from being watched: not versioned\n", path.GetWinPath()); return false; } CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": AddPath for %s\n", path.GetWinPath()); watchedPaths.AddPath(path); if (bCloseInfoMap) ClearInfoMap(); return true; }