increase buffer size for reading stdin in fsnotifier (IDEADEV-31286)
[idea/community.git] / tools / fileWatcher3 / fileWatcher3.cpp
1 // fileWatcher3.cpp : Defines the entry point for the console application.
2 //
3
4 #include "stdafx.h"
5
6 struct WatchRootInfo {
7         char driveLetter;
8         HANDLE hThread;
9         HANDLE hStopEvent;
10         bool bInitialized;
11         bool bUsed;
12         bool bFailed;
13 };
14
15 const int ROOT_COUNT = 26;
16
17 WatchRootInfo watchRootInfos[ROOT_COUNT];
18
19 CRITICAL_SECTION csOutput;
20
21 bool IsNetworkDrive(const char *name) 
22 {
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;
27
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
33     );
34
35     return result == NO_ERROR;
36 }
37
38 bool IsSubstedDrive(const char* name) 
39 {
40     char deviceName[3] = {name[0], name[1], 0};
41     const int BUF_SIZE = 1024;
42     char targetPath[BUF_SIZE];
43
44     DWORD result = QueryDosDeviceA(deviceName, targetPath, BUF_SIZE);
45     if (result == 0) {
46         return false;
47     }
48     else {
49         bool result = (targetPath[0] == '\\' && targetPath[1] == '?' && targetPath[2] == '?');
50         return result;
51     }
52 }
53
54 bool IsUnwatchableFS(const char *path)
55 {
56     char volumeName[MAX_PATH];
57         char fsName[MAX_PATH];
58         DWORD fsFlags;
59         DWORD maxComponentLength;
60         SetErrorMode(SEM_FAILCRITICALERRORS);
61         if (!GetVolumeInformationA(path, volumeName, MAX_PATH-1, NULL, &maxComponentLength, &fsFlags, fsName, MAX_PATH-1))
62                 return false;
63         if (strcmp(fsName, "NTFS") && strcmp(fsName, "FAT") && strcmp(fsName, "FAT32"))
64                 return true;
65
66     if (!strcmp(fsName, "NTFS") && maxComponentLength != 255 && !(fsFlags & FILE_SUPPORTS_REPARSE_POINTS))
67         {
68                 // SAMBA reports itself as NTFS
69                 return true;
70         }
71
72         return false;
73 }
74
75 bool IsWatchable(const char *path)
76 {
77         if (IsNetworkDrive(path))
78                 return false;
79         if (IsSubstedDrive(path))
80                 return false;
81         if (IsUnwatchableFS(path))
82                 return false;
83         return true;
84 }
85
86 const int BUFSIZE = 1024;
87
88 void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
89 {
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];
94
95     // Is this volume NTFS? 
96     GetVolumeInformationA(Buf, NULL, 0, NULL, NULL, &dwSysFlags, FileSysNameBuf, BUFSIZE);
97
98     // Detect support for reparse points, and therefore for volume 
99     // mount points, which are implemented using reparse points. 
100
101     if (! (dwSysFlags & FILE_SUPPORTS_REPARSE_POINTS)) {
102        return;
103     } 
104
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
110     );
111
112     // Shall we error out?
113     if (hPt == INVALID_HANDLE_VALUE) {
114         return;
115     } 
116
117     // Process the volume mount point.
118     do {
119                 printf("%s%s\n", volumePath, Path);
120     } while (FindNextVolumeMountPointA(hPt, Path, BUFSIZE));
121
122     FindVolumeMountPointClose(hPt);
123 }
124
125 void PrintMountPoints(const char *path)
126 {
127         char volumeUniqueName[128];
128         BOOL res = GetVolumeNameForVolumeMountPointA(path, volumeUniqueName, 128);
129         if (!res) {
130         return;
131     }
132    
133     char buf[BUFSIZE];            // buffer for unique volume identifiers
134     HANDLE hVol;                  // handle for the volume scan
135
136     // Open a scan for volumes.
137     hVol = FindFirstVolumeA(buf, BUFSIZE );
138
139     // Shall we error out?
140     if (hVol == INVALID_HANDLE_VALUE) {
141         return;
142     }
143
144     do {
145        if (!strcmp(buf, volumeUniqueName)) {
146                    PrintMountPointsForVolume(hVol, path, buf);
147            }
148     } while (FindNextVolumeA(hVol, buf, BUFSIZE));
149
150     FindVolumeClose(hVol);
151 }
152
153 void PrintChangeInfo(char *rootPath, FILE_NOTIFY_INFORMATION *info)
154 {
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';
158         char *command;
159         if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
160         {
161                 command = "CREATE";
162         }
163         else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
164         {
165                 command = "DELETE";
166         }
167         else if (info->Action == FILE_ACTION_MODIFIED)
168         {
169                 command = "CHANGE";     
170         }
171         else
172         {
173                 return;  // unknown command
174         }
175
176         EnterCriticalSection(&csOutput);
177         puts(command);
178         printf("%s", rootPath);
179         puts(FileNameBuffer);
180         fflush(stdout);
181         LeaveCriticalSection(&csOutput);
182 }
183
184 DWORD WINAPI WatcherThread(void *param)
185 {
186         WatchRootInfo *info = (WatchRootInfo *) param;
187
188         OVERLAPPED overlapped;
189         memset(&overlapped, 0, sizeof(overlapped));
190         overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
191
192         char rootPath[8];
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);
196
197         int buffer_size = 10240;
198         char *buffer = new char[buffer_size];
199
200         HANDLE handles [2];
201         handles [0] = info->hStopEvent;
202         handles [1] = overlapped.hEvent;
203         while(true)
204         {
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,
211                         NULL,
212                         &overlapped, 
213                         NULL);
214                 if (rcDir == 0)
215                 {
216                         info->bFailed = true;
217                         break;
218                 }
219
220                 int rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
221                 if (rc == WAIT_OBJECT_0)
222                 {
223                         break;
224                 }
225                 if (rc == WAIT_OBJECT_0+1)
226                 {
227                         FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *) buffer;
228                         while(true) 
229                         {
230                                 PrintChangeInfo(rootPath, info);
231                                 if (!info->NextEntryOffset)
232                                         break;
233                                 info = (FILE_NOTIFY_INFORMATION *) ((char *) info + info->NextEntryOffset);
234                         }
235                 }
236         }
237         CloseHandle(overlapped.hEvent);
238         CloseHandle(hRootDir);
239         delete[] buffer;
240         return 0;
241 }
242
243 void MarkAllRootsUnused()
244 {
245         for(int i=0; i<ROOT_COUNT; i++)
246         {
247                 watchRootInfos [i].bUsed = false;
248         }
249 }
250
251 void StartRoot(WatchRootInfo *info)
252 {
253         info->hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
254         info->hThread = CreateThread(NULL, 0, &WatcherThread, info, 0, NULL);
255         info->bInitialized = true;
256 }
257
258 void StopRoot(WatchRootInfo *info)
259 {
260         SetEvent(info->hStopEvent);
261         WaitForSingleObject(info->hThread, INFINITE);
262         CloseHandle(info->hThread);
263         CloseHandle(info->hStopEvent);
264         info->bInitialized = false;
265 }
266
267 void UpdateRoots()
268 {
269         bool haveUsedRoots = false;
270         for(int i=0; i<ROOT_COUNT; i++)
271         {
272                 if (watchRootInfos [i].bInitialized && (!watchRootInfos [i].bUsed || watchRootInfos [i].bFailed))
273                 {
274                         StopRoot(&watchRootInfos [i]);
275                         watchRootInfos [i].bFailed = false;
276                 }
277                 if (watchRootInfos [i].bUsed)
278                 {
279                         if (!haveUsedRoots)
280                         {
281                                 haveUsedRoots = true;
282                                 EnterCriticalSection(&csOutput);
283                                 puts("UNWATCHEABLE");
284                         }
285
286                         char rootPath[8];
287                         sprintf_s(rootPath, 8, "%c:\\", watchRootInfos [i].driveLetter);
288                         if (!IsWatchable(rootPath))
289                         {
290                                 puts(rootPath);
291                                 continue;
292                         }
293                         if (!watchRootInfos [i].bInitialized)
294                         {
295                                 StartRoot(&watchRootInfos [i]);
296                         }
297                         PrintMountPoints(rootPath);
298                 }
299         }
300         if (haveUsedRoots)
301         {
302                 puts("#");
303                 fflush(stdout);
304                 LeaveCriticalSection(&csOutput);
305         }
306 }
307
308 int _tmain(int argc, _TCHAR* argv[])
309 {
310         InitializeCriticalSection(&csOutput);
311
312         for(int i=0; i<26; i++)
313         {
314                 watchRootInfos [i].driveLetter = 'A' + i;
315                 watchRootInfos [i].bInitialized = false;
316                 watchRootInfos [i].bUsed = false;
317         }
318
319         char buffer[8192];
320         while(true)
321         {
322                 if (!gets_s(buffer, sizeof(buffer)-1))
323                         break;
324
325                 if (!strcmp(buffer, "ROOTS"))
326                 {
327                         MarkAllRootsUnused();
328                         bool failed = false;
329                         while(true)
330                         {
331                                 if (!gets_s(buffer, sizeof(buffer)-1))
332                                 {
333                                         failed = true;
334                                         break;
335                                 }
336                                 if (buffer [0] == '#')
337                                         break;
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')
344                                 {
345                                         watchRootInfos [driveLetter-'A'].bUsed = true;
346                                 }
347                         }
348                         if (failed)
349                                 break;
350
351                         UpdateRoots();
352                 }
353                 if (!strcmp(buffer, "EXIT"))
354                         break;
355         }
356
357         MarkAllRootsUnused();
358         UpdateRoots();
359         
360         DeleteCriticalSection(&csOutput);
361 }