1 // fileWatcher3.cpp : Defines the entry point for the console application.
15 const int ROOT_COUNT = 26;
17 WatchRootInfo watchRootInfos[ROOT_COUNT];
19 CRITICAL_SECTION csOutput;
21 bool IsNetworkDrive(const char *name)
23 const int BUF_SIZE = 1024;
24 char buffer[BUF_SIZE];
25 UNIVERSAL_NAME_INFO* uni = (UNIVERSAL_NAME_INFO*) buffer;
26 DWORD size = BUF_SIZE;
28 DWORD result = WNetGetUniversalNameA(
29 name, // path for network resource
30 UNIVERSAL_NAME_INFO_LEVEL, // level of information
31 buffer, // name buffer
32 &size // size of buffer
35 return result == NO_ERROR;
38 bool IsSubstedDrive(const char* name)
40 char deviceName[3] = {name[0], name[1], 0};
41 const int BUF_SIZE = 1024;
42 char targetPath[BUF_SIZE];
44 DWORD result = QueryDosDeviceA(deviceName, targetPath, BUF_SIZE);
49 bool result = (targetPath[0] == '\\' && targetPath[1] == '?' && targetPath[2] == '?');
54 bool IsUnwatchableFS(const char *path)
56 char volumeName[MAX_PATH];
57 char fsName[MAX_PATH];
59 DWORD maxComponentLength;
60 SetErrorMode(SEM_FAILCRITICALERRORS);
61 if (!GetVolumeInformationA(path, volumeName, MAX_PATH-1, NULL, &maxComponentLength, &fsFlags, fsName, MAX_PATH-1))
63 if (strcmp(fsName, "NTFS") && strcmp(fsName, "FAT") && strcmp(fsName, "FAT32"))
66 if (!strcmp(fsName, "NTFS") && maxComponentLength != 255 && !(fsFlags & FILE_SUPPORTS_REPARSE_POINTS))
68 // SAMBA reports itself as NTFS
75 bool IsWatchable(const char *path)
77 if (IsNetworkDrive(path))
79 if (IsSubstedDrive(path))
81 if (IsUnwatchableFS(path))
86 const int BUFSIZE = 1024;
88 void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
90 HANDLE hPt; // handle for mount point scan
91 char Path[BUFSIZE]; // string buffer for mount points
92 DWORD dwSysFlags; // flags that describe the file system
93 char FileSysNameBuf[BUFSIZE];
95 // Is this volume NTFS?
96 GetVolumeInformationA(Buf, NULL, 0, NULL, NULL, &dwSysFlags, FileSysNameBuf, BUFSIZE);
98 // Detect support for reparse points, and therefore for volume
99 // mount points, which are implemented using reparse points.
101 if (! (dwSysFlags & FILE_SUPPORTS_REPARSE_POINTS)) {
105 // Start processing mount points on this volume.
106 hPt = FindFirstVolumeMountPointA(
107 Buf, // root path of volume to be scanned
108 Path, // pointer to output string
109 BUFSIZE // size of output buffer
112 // Shall we error out?
113 if (hPt == INVALID_HANDLE_VALUE) {
117 // Process the volume mount point.
119 printf("%s%s\n", volumePath, Path);
120 } while (FindNextVolumeMountPointA(hPt, Path, BUFSIZE));
122 FindVolumeMountPointClose(hPt);
125 void PrintMountPoints(const char *path)
127 char volumeUniqueName[128];
128 BOOL res = GetVolumeNameForVolumeMountPointA(path, volumeUniqueName, 128);
133 char buf[BUFSIZE]; // buffer for unique volume identifiers
134 HANDLE hVol; // handle for the volume scan
136 // Open a scan for volumes.
137 hVol = FindFirstVolumeA(buf, BUFSIZE );
139 // Shall we error out?
140 if (hVol == INVALID_HANDLE_VALUE) {
145 if (!strcmp(buf, volumeUniqueName)) {
146 PrintMountPointsForVolume(hVol, path, buf);
148 } while (FindNextVolumeA(hVol, buf, BUFSIZE));
150 FindVolumeClose(hVol);
153 void PrintChangeInfo(char *rootPath, FILE_NOTIFY_INFORMATION *info)
155 char FileNameBuffer[_MAX_PATH];
156 int converted = WideCharToMultiByte(CP_ACP, 0, info->FileName, info->FileNameLength/sizeof(WCHAR), FileNameBuffer, _MAX_PATH-1, NULL, NULL);
157 FileNameBuffer[converted] = '\0';
159 if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
163 else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
167 else if (info->Action == FILE_ACTION_MODIFIED)
173 return; // unknown command
176 EnterCriticalSection(&csOutput);
178 printf("%s", rootPath);
179 puts(FileNameBuffer);
181 LeaveCriticalSection(&csOutput);
184 DWORD WINAPI WatcherThread(void *param)
186 WatchRootInfo *info = (WatchRootInfo *) param;
188 OVERLAPPED overlapped;
189 memset(&overlapped, 0, sizeof(overlapped));
190 overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
193 sprintf_s(rootPath, 8, "%c:\\", info->driveLetter);
194 HANDLE hRootDir = CreateFileA(rootPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
195 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
197 int buffer_size = 10240;
198 char *buffer = new char[buffer_size];
201 handles [0] = info->hStopEvent;
202 handles [1] = overlapped.hEvent;
205 int rcDir = ReadDirectoryChangesW(hRootDir, buffer, buffer_size, TRUE,
206 FILE_NOTIFY_CHANGE_FILE_NAME |
207 FILE_NOTIFY_CHANGE_DIR_NAME |
208 FILE_NOTIFY_CHANGE_ATTRIBUTES |
209 FILE_NOTIFY_CHANGE_SIZE |
210 FILE_NOTIFY_CHANGE_LAST_WRITE,
216 info->bFailed = true;
220 int rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
221 if (rc == WAIT_OBJECT_0)
225 if (rc == WAIT_OBJECT_0+1)
227 FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *) buffer;
230 PrintChangeInfo(rootPath, info);
231 if (!info->NextEntryOffset)
233 info = (FILE_NOTIFY_INFORMATION *) ((char *) info + info->NextEntryOffset);
237 CloseHandle(overlapped.hEvent);
238 CloseHandle(hRootDir);
243 void MarkAllRootsUnused()
245 for(int i=0; i<ROOT_COUNT; i++)
247 watchRootInfos [i].bUsed = false;
251 void StartRoot(WatchRootInfo *info)
253 info->hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
254 info->hThread = CreateThread(NULL, 0, &WatcherThread, info, 0, NULL);
255 info->bInitialized = true;
258 void StopRoot(WatchRootInfo *info)
260 SetEvent(info->hStopEvent);
261 WaitForSingleObject(info->hThread, INFINITE);
262 CloseHandle(info->hThread);
263 CloseHandle(info->hStopEvent);
264 info->bInitialized = false;
269 bool haveUsedRoots = false;
270 for(int i=0; i<ROOT_COUNT; i++)
272 if (watchRootInfos [i].bInitialized && (!watchRootInfos [i].bUsed || watchRootInfos [i].bFailed))
274 StopRoot(&watchRootInfos [i]);
275 watchRootInfos [i].bFailed = false;
277 if (watchRootInfos [i].bUsed)
281 haveUsedRoots = true;
282 EnterCriticalSection(&csOutput);
283 puts("UNWATCHEABLE");
287 sprintf_s(rootPath, 8, "%c:\\", watchRootInfos [i].driveLetter);
288 if (!IsWatchable(rootPath))
293 if (!watchRootInfos [i].bInitialized)
295 StartRoot(&watchRootInfos [i]);
297 PrintMountPoints(rootPath);
304 LeaveCriticalSection(&csOutput);
308 int _tmain(int argc, _TCHAR* argv[])
310 InitializeCriticalSection(&csOutput);
312 for(int i=0; i<26; i++)
314 watchRootInfos [i].driveLetter = 'A' + i;
315 watchRootInfos [i].bInitialized = false;
316 watchRootInfos [i].bUsed = false;
322 if (!gets_s(buffer, sizeof(buffer)-1))
325 if (!strcmp(buffer, "ROOTS"))
327 MarkAllRootsUnused();
331 if (!gets_s(buffer, sizeof(buffer)-1))
336 if (buffer [0] == '#')
338 int driveLetterPos = 0;
339 _strupr_s(buffer, sizeof(buffer)-1);
340 char driveLetter = buffer[0];
341 if (driveLetter == '|')
342 driveLetter = buffer[1];
343 if (driveLetter >= 'A' && driveLetter <= 'Z')
345 watchRootInfos [driveLetter-'A'].bUsed = true;
353 if (!strcmp(buffer, "EXIT"))
357 MarkAllRootsUnused();
360 DeleteCriticalSection(&csOutput);