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 char infoBuffer
[256];
270 strcpy_s(infoBuffer
, "UNWATCHEABLE\n");
271 for(int i
=0; i
<ROOT_COUNT
; i
++)
273 if (watchRootInfos
[i
].bInitialized
&& (!watchRootInfos
[i
].bUsed
|| watchRootInfos
[i
].bFailed
))
275 StopRoot(&watchRootInfos
[i
]);
276 watchRootInfos
[i
].bFailed
= false;
278 if (watchRootInfos
[i
].bUsed
)
281 sprintf_s(rootPath
, 8, "%c:\\", watchRootInfos
[i
].driveLetter
);
282 if (!IsWatchable(rootPath
))
284 strcat_s(infoBuffer
, rootPath
);
285 strcat_s(infoBuffer
, "\n");
288 if (!watchRootInfos
[i
].bInitialized
)
290 StartRoot(&watchRootInfos
[i
]);
294 EnterCriticalSection(&csOutput
);
295 fprintf(stdout
, "%s", infoBuffer
);
296 for(int i
=0; i
<ROOT_COUNT
; i
++)
298 if (watchRootInfos
[i
].bUsed
)
301 sprintf_s(rootPath
, 8, "%c:\\", watchRootInfos
[i
].driveLetter
);
302 PrintMountPoints(rootPath
);
307 LeaveCriticalSection(&csOutput
);
310 int _tmain(int argc
, _TCHAR
* argv
[])
312 InitializeCriticalSection(&csOutput
);
314 for(int i
=0; i
<26; i
++)
316 watchRootInfos
[i
].driveLetter
= 'A' + i
;
317 watchRootInfos
[i
].bInitialized
= false;
318 watchRootInfos
[i
].bUsed
= false;
324 if (!gets_s(buffer
, sizeof(buffer
)-1))
327 if (!strcmp(buffer
, "ROOTS"))
329 MarkAllRootsUnused();
333 if (!gets_s(buffer
, sizeof(buffer
)-1))
338 if (buffer
[0] == '#')
340 int driveLetterPos
= 0;
341 _strupr_s(buffer
, sizeof(buffer
)-1);
342 char driveLetter
= buffer
[0];
343 if (driveLetter
== '|')
344 driveLetter
= buffer
[1];
345 if (driveLetter
>= 'A' && driveLetter
<= 'Z')
347 watchRootInfos
[driveLetter
-'A'].bUsed
= true;
355 if (!strcmp(buffer
, "EXIT"))
359 MarkAllRootsUnused();
362 DeleteCriticalSection(&csOutput
);