static void CALLBACK ReadDirectoryChangesNotification(DWORD errCode, DWORD bytesTransfered, LPOVERLAPPED overlapped) { ScopedCritSec cs(&g_threadCritSec); OverlappedEx *over = (OverlappedEx*)overlapped; WatchedDir* wd = (WatchedDir*)over->data; lf(L"ReadDirectoryChangesNotification() dir: %s, numBytes: %d", wd->dirPath, (int)bytesTransfered); CrashIf(wd != wd->overlapped.data); if (errCode == ERROR_OPERATION_ABORTED) { lf(" ERROR_OPERATION_ABORTED"); DeleteWatchedDir(wd); InterlockedDecrement(&gRemovalsPending); return; } // This might mean overflow? Not sure. if (!bytesTransfered) return; FILE_NOTIFY_INFORMATION *notify = (FILE_NOTIFY_INFORMATION*)wd->buf; // collect files that changed, removing duplicates WStrVec changedFiles; for (;;) { ScopedMem<WCHAR> fileName(str::DupN(notify->FileName, notify->FileNameLength / sizeof(WCHAR))); // files can get updated either by writing to them directly or // by writing to a .tmp file first and then moving that file in place // (the latter only yields a RENAMED action with the expected file name) if (notify->Action == FILE_ACTION_MODIFIED || notify->Action == FILE_ACTION_RENAMED_NEW_NAME) { if (!changedFiles.Contains(fileName)) { lf(L"ReadDirectoryChangesNotification() FILE_ACTION_MODIFIED, for '%s'", fileName); changedFiles.Append(fileName.StealData()); } else { lf(L"ReadDirectoryChangesNotification() eliminating duplicate notification for '%s'", fileName); } } else { lf(L"ReadDirectoryChangesNotification() action=%d, for '%s'", (int)notify->Action, fileName); } // step to the next entry if there is one DWORD nextOff = notify->NextEntryOffset; if (!nextOff) break; notify = (FILE_NOTIFY_INFORMATION *)((char*)notify + nextOff); } StartMonitoringDirForChanges(wd); for (const WCHAR *f : changedFiles) { NotifyAboutFile(wd, f); } }
static void CALLBACK ReadDirectoryChangesNotification(DWORD errCode, DWORD bytesTransfered, LPOVERLAPPED overlapped) { ScopedCritSec cs(&g_threadCritSec); OverlappedEx *over = (OverlappedEx*)overlapped; WatchedDir* wd = (WatchedDir*)over->data; lf(L"ReadDirectoryChangesNotification() dir: %s, numBytes: %d", wd->dirPath, (int)bytesTransfered); CrashIf(wd != wd->overlapped.data); if (errCode == ERROR_OPERATION_ABORTED) { lf(" ERROR_OPERATION_ABORTED"); DeleteWatchedDir(wd); return; } // This might mean overflow? Not sure. if (!bytesTransfered) return; FILE_NOTIFY_INFORMATION *notify = (FILE_NOTIFY_INFORMATION*)wd->buf; // collect files that changed, removing duplicates WStrVec changedFiles; for (;;) { WCHAR *fileName = str::DupN(notify->FileName, notify->FileNameLength / sizeof(WCHAR)); if (notify->Action == FILE_ACTION_MODIFIED) { if (!changedFiles.Contains(fileName)) { lf(L"ReadDirectoryChangesNotification() FILE_ACTION_MODIFIED, for '%s'", fileName); changedFiles.Append(fileName); fileName = NULL; } else { lf(L"ReadDirectoryChangesNotification() eliminating duplicate notification for '%s'", fileName); } } else { lf(L"ReadDirectoryChangesNotification() action=%d, for '%s'", (int)notify->Action, fileName); } free(fileName); // step to the next entry if there is one DWORD nextOff = notify->NextEntryOffset; if (!nextOff) break; notify = (FILE_NOTIFY_INFORMATION *)((char*)notify + nextOff); } StartMonitoringDirForChanges(wd); for (WCHAR **f = changedFiles.IterStart(); f; f = changedFiles.IterNext()) { NotifyAboutFile(wd, *f); } }
static void RemoveWatchedDirIfNotReferenced(WatchedDir *wd) { if (IsWatchedDirReferenced(wd)) return; WatchedDir **currPtr = &g_firstDir; WatchedDir *curr; for (;;) { curr = *currPtr; CrashAlwaysIf(!curr); if (curr == wd) break; currPtr = &(curr->next); } WatchedDir *toRemove = curr; *currPtr = toRemove->next; DeleteWatchedDir(toRemove); }
static WatchedDir *NewWatchedDir(const WCHAR *dirPath) { WatchedDir *wd = AllocStruct<WatchedDir>(); wd->dirPath = str::Dup(dirPath); wd->hDir = CreateFile( dirPath, FILE_LIST_DIRECTORY, FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (!wd->hDir) goto Failed; wd->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!wd->overlapped.hEvent) goto Failed; // start watching the directory wd->currBuffer = 0; ReadDirectoryChangesW( wd->hDir, &wd->buffer[wd->currBuffer], /* read results buffer */ sizeof(wd->buffer[wd->currBuffer]), /* length of buffer */ FALSE, /* monitoring option */ FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */ NULL, /* bytes returned */ &wd->overlapped, /* overlapped buffer */ NULL); /* completion routine */ wd->next = g_firstDir; g_firstDir = wd; return wd; Failed: DeleteWatchedDir(wd); return NULL; }