filewatcher supports substed drives and junction points (IDEADEV-41841, IDEADEV-41842)
authorDmitry Jemerov <yole@jetbrains.com>
Fri, 4 Dec 2009 13:45:35 +0000 (16:45 +0300)
committerDmitry Jemerov <yole@jetbrains.com>
Fri, 4 Dec 2009 13:47:37 +0000 (16:47 +0300)
bin/win/fsnotifier.exe
native/fileWatcher/fileWatcher3.cpp
native/fileWatcher/fileWatcher3.rc
native/fileWatcher/fileWatcher3.vcproj

index 265c94f7ea03858b484eac5f2a33ad9ba4914db5..47c7139daf391295344774176a38f452ac4a081f 100644 (file)
Binary files a/bin/win/fsnotifier.exe and b/bin/win/fsnotifier.exe differ
index 97c32fdbfafb9a121b1b15d489dab62a3fb8a18c..77b8f36f8e40b604bdd1bcdb4960c4ef4a05d227 100644 (file)
@@ -25,12 +25,28 @@ struct WatchRootInfo {
        bool bFailed;
 };
 
+struct WatchRoot {
+       char *path;
+       WatchRoot *next;
+};
+
 const int ROOT_COUNT = 26;
 
 WatchRootInfo watchRootInfos[ROOT_COUNT];
 
+WatchRoot *firstWatchRoot = NULL;
+
 CRITICAL_SECTION csOutput;
 
+void NormalizeSlashes(char *path, char slash)
+{
+       for(char *p=path; *p; p++)
+               if (*p == '\\' || *p == '/') 
+                       *p = slash;
+}
+
+// -- Watchable root checks ---------------------------------------------------
+
 bool IsNetworkDrive(const char *name) 
 {
     const int BUF_SIZE = 1024;
@@ -48,22 +64,6 @@ bool IsNetworkDrive(const char *name)
     return result == NO_ERROR;
 }
 
-bool IsSubstedDrive(const char* name) 
-{
-    char deviceName[3] = {name[0], name[1], 0};
-    const int BUF_SIZE = 1024;
-    char targetPath[BUF_SIZE];
-
-    DWORD result = QueryDosDeviceA(deviceName, targetPath, BUF_SIZE);
-    if (result == 0) {
-        return false;
-    }
-    else {
-        bool result = (targetPath[0] == '\\' && targetPath[1] == '?' && targetPath[2] == '?');
-        return result;
-    }
-}
-
 bool IsUnwatchableFS(const char *path)
 {
     char volumeName[MAX_PATH];
@@ -89,16 +89,87 @@ bool IsWatchable(const char *path)
 {
        if (IsNetworkDrive(path))
                return false;
-       if (IsSubstedDrive(path))
-               return false;
        if (IsUnwatchableFS(path))
                return false;
        return true;
 }
 
+// -- Substed drive checks ----------------------------------------------------
+
+void PrintRemapForSubstDrive(char driveLetter) 
+{
+    const int BUF_SIZE = 1024;
+    char targetPath[BUF_SIZE];
+
+       char rootPath[8];
+       sprintf_s(rootPath, 8, "%c:", driveLetter);
+
+    DWORD result = QueryDosDeviceA(rootPath, targetPath, BUF_SIZE);
+    if (result == 0) {
+        return;
+    }
+    else 
+       {
+        if (targetPath[0] == '\\' && targetPath[1] == '?' && targetPath[2] == '?' && targetPath[3] == '\\')
+               {
+                       // example path: \??\C:\jetbrains\idea
+                       NormalizeSlashes(targetPath, '/');
+                       printf("%c:\n%s\n", driveLetter, targetPath+4);
+               }
+    }
+}
+
+void PrintRemapForSubstDrives()
+{
+       for(int i=0; i<ROOT_COUNT; i++)
+       {
+               if (watchRootInfos [i].bUsed)
+               {
+                       PrintRemapForSubstDrive(watchRootInfos [i].driveLetter);
+               }
+       }
+}
+
+// -- Mount point enumeration -------------------------------------------------
+
 const int BUFSIZE = 1024;
 
-void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
+void PrintDirectoryReparsePoint(const char *path)
+{
+       int size = strlen(path)+2;
+       char *directory = (char *) malloc(size);
+       strcpy_s(directory, size, path);
+       NormalizeSlashes(directory, '\\');
+       if (directory [strlen(directory)-1] != '\\')
+               strcat_s(directory, size, "\\");
+               
+       char volumeName[_MAX_PATH];
+       int rc = GetVolumeNameForVolumeMountPointA(directory, volumeName, sizeof(volumeName));
+       if (rc)
+       {
+               char volumePathNames[_MAX_PATH];
+               DWORD returnLength;
+               rc = GetVolumePathNamesForVolumeNameA(volumeName, volumePathNames, sizeof(volumePathNames), &returnLength);
+               if (rc)
+               {
+                       char *p = volumePathNames;
+                       while(*p)
+                       {
+                               if (_stricmp(p, directory))   // if it's not the path we've already found
+                               {
+                                       NormalizeSlashes(directory, '/');
+                                       NormalizeSlashes(p, '/');
+                                       puts(directory);
+                                       puts(p);
+                               }
+                               p += strlen(p)+1;
+                       }
+               }
+       }
+       free(directory);
+}
+
+bool PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
 {
     HANDLE hPt;                  // handle for mount point scan
     char Path[BUFSIZE];          // string buffer for mount points
@@ -112,7 +183,7 @@ void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
     // mount points, which are implemented using reparse points. 
 
     if (! (dwSysFlags & FILE_SUPPORTS_REPARSE_POINTS)) {
-       return;
+       return true;
     } 
 
     // Start processing mount points on this volume. 
@@ -124,23 +195,27 @@ void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
 
     // Shall we error out?
     if (hPt == INVALID_HANDLE_VALUE) {
-        return;
+               return GetLastError() != ERROR_ACCESS_DENIED;
     } 
 
     // Process the volume mount point.
+       char *buf = new char[MAX_PATH];
     do {
-               printf("%s%s\n", volumePath, Path);
+               strcpy_s(buf, MAX_PATH, volumePath);
+               strcat_s(buf, MAX_PATH, Path);
+               PrintDirectoryReparsePoint(buf);
     } while (FindNextVolumeMountPointA(hPt, Path, BUFSIZE));
 
     FindVolumeMountPointClose(hPt);
+       return true;
 }
 
-void PrintMountPoints(const char *path)
+bool PrintMountPoints(const char *path)
 {
        char volumeUniqueName[128];
        BOOL res = GetVolumeNameForVolumeMountPointA(path, volumeUniqueName, 128);
        if (!res) {
-        return;
+        return false;
     }
    
     char buf[BUFSIZE];            // buffer for unique volume identifiers
@@ -151,18 +226,59 @@ void PrintMountPoints(const char *path)
 
     // Shall we error out?
     if (hVol == INVALID_HANDLE_VALUE) {
-        return;
+        return false;
     }
 
-    do {
+    bool success = true;
+       do {
        if (!strcmp(buf, volumeUniqueName)) {
-                  PrintMountPointsForVolume(hVol, path, buf);
+                  success = PrintMountPointsForVolume(hVol, path, buf);
+                  if (!success) break;
           }
     } while (FindNextVolumeA(hVol, buf, BUFSIZE));
 
     FindVolumeClose(hVol);
+       return success;
+}
+
+// -- Searching for mount points in watch roots (fallback) --------------------
+
+void PrintDirectoryReparsePoints(const char *path)
+{
+       char *const buf = _strdup(path);
+       while(strchr(buf, '/'))
+       {
+               DWORD attributes = GetFileAttributesA(buf);
+               if (attributes == INVALID_FILE_ATTRIBUTES)
+                       break;
+               if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+               {
+                       PrintDirectoryReparsePoint(buf);
+               }
+               char *pSlash = strrchr(buf, '/');
+               if (pSlash)
+               {
+                       *pSlash = '\0';
+               }
+       }
+       free(buf);
+}
+
+// This is called if we got an ERROR_ACCESS_DENIED when trying to enumerate all mount points for volume.
+// In this case, we walk the directory tree up from each watch root, and look at each parent directory 
+// to check whether it's a reparse point.
+void PrintWatchRootReparsePoints()
+{
+       WatchRoot *pWatchRoot = firstWatchRoot;
+       while(pWatchRoot)
+       {
+               PrintDirectoryReparsePoints(pWatchRoot->path);
+               pWatchRoot = pWatchRoot->next;
+       }
 }
 
+// -- Watcher thread ----------------------------------------------------------
+
 void PrintChangeInfo(char *rootPath, FILE_NOTIFY_INFORMATION *info)
 {
        char FileNameBuffer[_MAX_PATH];
@@ -253,6 +369,8 @@ DWORD WINAPI WatcherThread(void *param)
        return 0;
 }
 
+// -- Roots update ------------------------------------------------------------
+
 void MarkAllRootsUnused()
 {
        for(int i=0; i<ROOT_COUNT; i++)
@@ -306,20 +424,53 @@ void UpdateRoots()
        }
        EnterCriticalSection(&csOutput);
        fprintf(stdout, "%s", infoBuffer);
+       puts("#\nREMAP");
+       PrintRemapForSubstDrives();
+       bool printedMountPoints = true;
        for(int i=0; i<ROOT_COUNT; i++)
        {
                if (watchRootInfos [i].bUsed)
                {
                        char rootPath[8];
                        sprintf_s(rootPath, 8, "%c:\\", watchRootInfos [i].driveLetter);
-                       PrintMountPoints(rootPath);
+                       if (!PrintMountPoints(rootPath))
+                               printedMountPoints = false;
                }
        }
+       if (!printedMountPoints)
+       {
+               PrintWatchRootReparsePoints();
+       }
        puts("#");
        fflush(stdout);
        LeaveCriticalSection(&csOutput);
 }
 
+void AddWatchRoot(const char *path)
+{
+       WatchRoot *watchRoot = (WatchRoot *) malloc(sizeof(WatchRoot));
+       watchRoot->next = NULL;
+       watchRoot->path = _strdup(path);
+       watchRoot->next = firstWatchRoot;
+       firstWatchRoot = watchRoot;
+}
+
+void FreeWatchRootsList()
+{
+       WatchRoot *pWatchRoot = firstWatchRoot;
+       WatchRoot *pNext;
+       while(pWatchRoot)
+       {
+               pNext = pWatchRoot->next;
+               free(pWatchRoot->path);
+               free(pWatchRoot);
+               pWatchRoot=pNext;
+       }
+       firstWatchRoot = NULL;
+}
+
+// -- Main - filewatcher protocol ---------------------------------------------
+
 int _tmain(int argc, _TCHAR* argv[])
 {
        InitializeCriticalSection(&csOutput);
@@ -340,6 +491,7 @@ int _tmain(int argc, _TCHAR* argv[])
                if (!strcmp(buffer, "ROOTS"))
                {
                        MarkAllRootsUnused();
+                       FreeWatchRootsList();
                        bool failed = false;
                        while(true)
                        {
@@ -351,10 +503,14 @@ int _tmain(int argc, _TCHAR* argv[])
                                if (buffer [0] == '#')
                                        break;
                                int driveLetterPos = 0;
+                               char *pDriveLetter = buffer;
+                               if (*pDriveLetter == '|')
+                                       pDriveLetter++;
+
+                               AddWatchRoot(pDriveLetter);
+
                                _strupr_s(buffer, sizeof(buffer)-1);
-                               char driveLetter = buffer[0];
-                               if (driveLetter == '|')
-                                       driveLetter = buffer[1];
+                               char driveLetter = *pDriveLetter;
                                if (driveLetter >= 'A' && driveLetter <= 'Z')
                                {
                                        watchRootInfos [driveLetter-'A'].bUsed = true;
index 4956f6958f9c21a7e9dc69bde6a586c5054922c6..a2ed61e673e361fb9e3bec26a62c020667a2a4a1 100644 (file)
@@ -73,7 +73,7 @@ BEGIN
             VALUE "FileDescription", "File System Notification Processor"
             VALUE "FileVersion", "1, 0, 0, 1"
             VALUE "InternalName", "fsnotifier"
-            VALUE "LegalCopyright", "Copyright (C) 2008 JetBrains, Inc."
+            VALUE "LegalCopyright", "Copyright (C) 2008-09 JetBrains, Inc."
             VALUE "OriginalFilename", "fsnotifier.exe"
             VALUE "ProductName", "IntelliJ IDEA"
             VALUE "ProductVersion", "1, 0, 0, 1"
index e1b797fdd423357d6cb1bea3ba8587be4c7a0514..14ec211e54f3bab3f9e9a6f078e49901b05d0e30 100644 (file)
@@ -62,7 +62,7 @@
                                Name="VCLinkerTool"
                                AdditionalDependencies="mpr.lib"
                                OutputFile="$(OutDir)\fsnotifier.exe"
-                               LinkIncremental="2"
+                               LinkIncremental="1"
                                GenerateDebugInformation="true"
                                SubSystem="1"
                                TargetMachine="1"