2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 struct WatchRootInfo
{
28 const int ROOT_COUNT
= 26;
30 WatchRootInfo watchRootInfos
[ROOT_COUNT
];
32 CRITICAL_SECTION csOutput
;
34 bool IsNetworkDrive(const char *name
)
36 const int BUF_SIZE
= 1024;
37 char buffer
[BUF_SIZE
];
38 UNIVERSAL_NAME_INFO
* uni
= (UNIVERSAL_NAME_INFO
*) buffer
;
39 DWORD size
= BUF_SIZE
;
41 DWORD result
= WNetGetUniversalNameA(
42 name
, // path for network resource
43 UNIVERSAL_NAME_INFO_LEVEL
, // level of information
44 buffer
, // name buffer
45 &size
// size of buffer
48 return result
== NO_ERROR
;
51 bool IsSubstedDrive(const char* name
)
53 char deviceName
[3] = {name
[0], name
[1], 0};
54 const int BUF_SIZE
= 1024;
55 char targetPath
[BUF_SIZE
];
57 DWORD result
= QueryDosDeviceA(deviceName
, targetPath
, BUF_SIZE
);
62 bool result
= (targetPath
[0] == '\\' && targetPath
[1] == '?' && targetPath
[2] == '?');
67 bool IsUnwatchableFS(const char *path
)
69 char volumeName
[MAX_PATH
];
70 char fsName
[MAX_PATH
];
72 DWORD maxComponentLength
;
73 SetErrorMode(SEM_FAILCRITICALERRORS
);
74 if (!GetVolumeInformationA(path
, volumeName
, MAX_PATH
-1, NULL
, &maxComponentLength
, &fsFlags
, fsName
, MAX_PATH
-1))
76 if (strcmp(fsName
, "NTFS") && strcmp(fsName
, "FAT") && strcmp(fsName
, "FAT32"))
79 if (!strcmp(fsName
, "NTFS") && maxComponentLength
!= 255 && !(fsFlags
& FILE_SUPPORTS_REPARSE_POINTS
))
81 // SAMBA reports itself as NTFS
88 bool IsWatchable(const char *path
)
90 if (IsNetworkDrive(path
))
92 if (IsSubstedDrive(path
))
94 if (IsUnwatchableFS(path
))
99 const int BUFSIZE
= 1024;
101 void PrintMountPointsForVolume(HANDLE hVol
, const char* volumePath
, char *Buf
)
103 HANDLE hPt
; // handle for mount point scan
104 char Path
[BUFSIZE
]; // string buffer for mount points
105 DWORD dwSysFlags
; // flags that describe the file system
106 char FileSysNameBuf
[BUFSIZE
];
108 // Is this volume NTFS?
109 GetVolumeInformationA(Buf
, NULL
, 0, NULL
, NULL
, &dwSysFlags
, FileSysNameBuf
, BUFSIZE
);
111 // Detect support for reparse points, and therefore for volume
112 // mount points, which are implemented using reparse points.
114 if (! (dwSysFlags
& FILE_SUPPORTS_REPARSE_POINTS
)) {
118 // Start processing mount points on this volume.
119 hPt
= FindFirstVolumeMountPointA(
120 Buf
, // root path of volume to be scanned
121 Path
, // pointer to output string
122 BUFSIZE
// size of output buffer
125 // Shall we error out?
126 if (hPt
== INVALID_HANDLE_VALUE
) {
130 // Process the volume mount point.
132 printf("%s%s\n", volumePath
, Path
);
133 } while (FindNextVolumeMountPointA(hPt
, Path
, BUFSIZE
));
135 FindVolumeMountPointClose(hPt
);
138 void PrintMountPoints(const char *path
)
140 char volumeUniqueName
[128];
141 BOOL res
= GetVolumeNameForVolumeMountPointA(path
, volumeUniqueName
, 128);
146 char buf
[BUFSIZE
]; // buffer for unique volume identifiers
147 HANDLE hVol
; // handle for the volume scan
149 // Open a scan for volumes.
150 hVol
= FindFirstVolumeA(buf
, BUFSIZE
);
152 // Shall we error out?
153 if (hVol
== INVALID_HANDLE_VALUE
) {
158 if (!strcmp(buf
, volumeUniqueName
)) {
159 PrintMountPointsForVolume(hVol
, path
, buf
);
161 } while (FindNextVolumeA(hVol
, buf
, BUFSIZE
));
163 FindVolumeClose(hVol
);
166 void PrintChangeInfo(char *rootPath
, FILE_NOTIFY_INFORMATION
*info
)
168 char FileNameBuffer
[_MAX_PATH
];
169 int converted
= WideCharToMultiByte(CP_ACP
, 0, info
->FileName
, info
->FileNameLength
/sizeof(WCHAR
), FileNameBuffer
, _MAX_PATH
-1, NULL
, NULL
);
170 FileNameBuffer
[converted
] = '\0';
172 if (info
->Action
== FILE_ACTION_ADDED
|| info
->Action
== FILE_ACTION_RENAMED_OLD_NAME
)
176 else if (info
->Action
== FILE_ACTION_REMOVED
|| info
->Action
== FILE_ACTION_RENAMED_OLD_NAME
)
180 else if (info
->Action
== FILE_ACTION_MODIFIED
)
186 return; // unknown command
189 EnterCriticalSection(&csOutput
);
191 printf("%s", rootPath
);
192 puts(FileNameBuffer
);
194 LeaveCriticalSection(&csOutput
);
197 DWORD WINAPI
WatcherThread(void *param
)
199 WatchRootInfo
*info
= (WatchRootInfo
*) param
;
201 OVERLAPPED overlapped
;
202 memset(&overlapped
, 0, sizeof(overlapped
));
203 overlapped
.hEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
206 sprintf_s(rootPath
, 8, "%c:\\", info
->driveLetter
);
207 HANDLE hRootDir
= CreateFileA(rootPath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
208 NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
, NULL
);
210 int buffer_size
= 10240;
211 char *buffer
= new char[buffer_size
];
214 handles
[0] = info
->hStopEvent
;
215 handles
[1] = overlapped
.hEvent
;
218 int rcDir
= ReadDirectoryChangesW(hRootDir
, buffer
, buffer_size
, TRUE
,
219 FILE_NOTIFY_CHANGE_FILE_NAME
|
220 FILE_NOTIFY_CHANGE_DIR_NAME
|
221 FILE_NOTIFY_CHANGE_ATTRIBUTES
|
222 FILE_NOTIFY_CHANGE_SIZE
|
223 FILE_NOTIFY_CHANGE_LAST_WRITE
,
229 info
->bFailed
= true;
233 int rc
= WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
234 if (rc
== WAIT_OBJECT_0
)
238 if (rc
== WAIT_OBJECT_0
+1)
240 FILE_NOTIFY_INFORMATION
*info
= (FILE_NOTIFY_INFORMATION
*) buffer
;
243 PrintChangeInfo(rootPath
, info
);
244 if (!info
->NextEntryOffset
)
246 info
= (FILE_NOTIFY_INFORMATION
*) ((char *) info
+ info
->NextEntryOffset
);
250 CloseHandle(overlapped
.hEvent
);
251 CloseHandle(hRootDir
);
256 void MarkAllRootsUnused()
258 for(int i
=0; i
<ROOT_COUNT
; i
++)
260 watchRootInfos
[i
].bUsed
= false;
264 void StartRoot(WatchRootInfo
*info
)
266 info
->hStopEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
267 info
->hThread
= CreateThread(NULL
, 0, &WatcherThread
, info
, 0, NULL
);
268 info
->bInitialized
= true;
271 void StopRoot(WatchRootInfo
*info
)
273 SetEvent(info
->hStopEvent
);
274 WaitForSingleObject(info
->hThread
, INFINITE
);
275 CloseHandle(info
->hThread
);
276 CloseHandle(info
->hStopEvent
);
277 info
->bInitialized
= false;
282 char infoBuffer
[256];
283 strcpy_s(infoBuffer
, "UNWATCHEABLE\n");
284 for(int i
=0; i
<ROOT_COUNT
; i
++)
286 if (watchRootInfos
[i
].bInitialized
&& (!watchRootInfos
[i
].bUsed
|| watchRootInfos
[i
].bFailed
))
288 StopRoot(&watchRootInfos
[i
]);
289 watchRootInfos
[i
].bFailed
= false;
291 if (watchRootInfos
[i
].bUsed
)
294 sprintf_s(rootPath
, 8, "%c:\\", watchRootInfos
[i
].driveLetter
);
295 if (!IsWatchable(rootPath
))
297 strcat_s(infoBuffer
, rootPath
);
298 strcat_s(infoBuffer
, "\n");
301 if (!watchRootInfos
[i
].bInitialized
)
303 StartRoot(&watchRootInfos
[i
]);
307 EnterCriticalSection(&csOutput
);
308 fprintf(stdout
, "%s", infoBuffer
);
309 for(int i
=0; i
<ROOT_COUNT
; i
++)
311 if (watchRootInfos
[i
].bUsed
)
314 sprintf_s(rootPath
, 8, "%c:\\", watchRootInfos
[i
].driveLetter
);
315 PrintMountPoints(rootPath
);
320 LeaveCriticalSection(&csOutput
);
323 int _tmain(int argc
, _TCHAR
* argv
[])
325 InitializeCriticalSection(&csOutput
);
327 for(int i
=0; i
<26; i
++)
329 watchRootInfos
[i
].driveLetter
= 'A' + i
;
330 watchRootInfos
[i
].bInitialized
= false;
331 watchRootInfos
[i
].bUsed
= false;
337 if (!gets_s(buffer
, sizeof(buffer
)-1))
340 if (!strcmp(buffer
, "ROOTS"))
342 MarkAllRootsUnused();
346 if (!gets_s(buffer
, sizeof(buffer
)-1))
351 if (buffer
[0] == '#')
353 int driveLetterPos
= 0;
354 _strupr_s(buffer
, sizeof(buffer
)-1);
355 char driveLetter
= buffer
[0];
356 if (driveLetter
== '|')
357 driveLetter
= buffer
[1];
358 if (driveLetter
>= 'A' && driveLetter
<= 'Z')
360 watchRootInfos
[driveLetter
-'A'].bUsed
= true;
368 if (!strcmp(buffer
, "EXIT"))
372 MarkAllRootsUnused();
375 DeleteCriticalSection(&csOutput
);