Bug 285157: Use GetFileAttributesEx if it exists. GetFileAttributesEx is
authorwtc%google.com
Sun, 27 Jan 2008 05:00:58 +0000
changeset 3918 61493b533829973b1922bcd90a1a1bc645bd8b41
parent 3917 48b798569e7632a54049cb60e93660ca5c8ad31b
child 3919 250567b7f8d5271621299203a7c6878db4e0a0a5
push idunknown
push userunknown
push dateunknown
bugs285157
Bug 285157: Use GetFileAttributesEx if it exists. GetFileAttributesEx is much faster than FindFirstFile. The patch is contributed by Jim Mathies <jmathies@mozilla.com>. r=wtc Modified files: ntio.c w95io.c
pr/src/md/windows/ntio.c
pr/src/md/windows/w95io.c
--- a/pr/src/md/windows/ntio.c
+++ b/pr/src/md/windows/ntio.c
@@ -3038,90 +3038,28 @@ IsRootDirectory(char *fn, size_t buflen)
         }
     }
     return rv;
 }
 
 PRInt32
 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
 {
-    HANDLE hFindFile;
-    WIN32_FIND_DATA findFileData;
-    char pathbuf[MAX_PATH + 1];
+    WIN32_FILE_ATTRIBUTE_DATA findFileData;
     
     if (NULL == fn || '\0' == *fn) {
         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
         return -1;
     }
 
-    /*
-     * FindFirstFile() expands wildcard characters.  So
-     * we make sure the pathname contains no wildcard.
-     */
-    if (NULL != _mbspbrk(fn, "?*")) {
-        PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+    if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) {
+        _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
         return -1;
     }
 
-    hFindFile = FindFirstFile(fn, &findFileData);
-    if (INVALID_HANDLE_VALUE == hFindFile) {
-        DWORD len;
-        char *filePart;
-
-        /*
-         * FindFirstFile() does not work correctly on root directories.
-         * It also doesn't work correctly on a pathname that ends in a
-         * slash.  So we first check to see if the pathname specifies a
-         * root directory.  If not, and if the pathname ends in a slash,
-         * we remove the final slash and try again.
-         */
-
-        /*
-         * If the pathname does not contain ., \, and /, it cannot be
-         * a root directory or a pathname that ends in a slash.
-         */
-        if (NULL == _mbspbrk(fn, ".\\/")) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
-        } 
-        len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
-                &filePart);
-        if (0 == len) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
-        }
-        if (len > sizeof(pathbuf)) {
-            PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
-            return -1;
-        }
-        if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
-            info->type = PR_FILE_DIRECTORY;
-            info->size = 0;
-            /*
-             * These timestamps don't make sense for root directories.
-             */
-            info->modifyTime = 0;
-            info->creationTime = 0;
-            return 0;
-        }
-        if (!IsPrevCharSlash(pathbuf, pathbuf + len)) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
-        } else {
-            pathbuf[len - 1] = '\0';
-            hFindFile = FindFirstFile(pathbuf, &findFileData);
-            if (INVALID_HANDLE_VALUE == hFindFile) {
-                _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-                return -1;
-            }
-        }
-    }
-
-    FindClose(hFindFile);
-
     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
         info->type = PR_FILE_DIRECTORY;
     } else {
         info->type = PR_FILE_FILE;
     }
 
     info->size = findFileData.nFileSizeHigh;
     info->size = (info->size << 32) + findFileData.nFileSizeLow;
--- a/pr/src/md/windows/w95io.c
+++ b/pr/src/md/windows/w95io.c
@@ -76,16 +76,22 @@ static DWORD dirAccessTable[] = {
  * This constant is used by _PR_FileTimeToPRTime().
  */
 #if defined(__MINGW32__)
 static const PRTime _pr_filetime_offset = 116444736000000000LL;
 #else
 static const PRTime _pr_filetime_offset = 116444736000000000i64;
 #endif
 
+typedef BOOL (WINAPI *GetFileAttributesExFn)(LPCTSTR,
+                                             GET_FILEEX_INFO_LEVELS,
+                                             LPVOID); 
+static GetFileAttributesExFn getFileAttributesEx;
+static void InitGetFileInfo(void);
+
 static void InitUnicodeSupport(void);
 
 static PRBool IsPrevCharSlash(const char *str, const char *current);
 
 void
 _PR_MD_INIT_IO()
 {
     WORD WSAVersion = 0x0101;
@@ -117,16 +123,18 @@ void
         rv = SystemTimeToFileTime(&systime, &filetime.ft);
         PR_ASSERT(0 != rv);
         PR_ASSERT(filetime.prt == _pr_filetime_offset);
     }
 #endif /* DEBUG */
 
     _PR_NT_InitSids();
 
+    InitGetFileInfo();
+
     InitUnicodeSupport();
 
     _PR_MD_InitSockets();
 }
 
 PRStatus
 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
 {
@@ -766,92 +774,138 @@ IsRootDirectory(char *fn, size_t buflen)
         /* restore the 'fn' buffer */
         if (slashAdded) {
             *--p = '\0';
         }
     }
     return rv;
 }
 
-PRInt32
-_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+/*
+ * InitGetFileInfo --
+ *
+ * Called during IO init. Checks for the existence of the system function
+ * GetFileAttributeEx, which when available is used in GETFILEINFO calls. 
+ * If the routine exists, then the address of the routine is stored in the
+ * variable getFileAttributesEx, which will be used to call the routine.
+ */
+static void InitGetFileInfo(void)
+{
+    HMODULE module;
+    module = GetModuleHandle("Kernel32.dll");
+    if (!module) {
+        PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
+                ("InitGetFileInfo: GetModuleHandle() failed: %d",
+                GetLastError()));
+        return;
+    }
+
+    getFileAttributesEx = (GetFileAttributesExFn)
+            GetProcAddress(module, "GetFileAttributesExA");
+}
+
+/*
+ * If GetFileAttributeEx doesn't exist, we call FindFirstFile as a
+ * fallback.
+ */
+static BOOL
+GetFileAttributesExFB(const char *fn, WIN32_FIND_DATA *findFileData)
 {
     HANDLE hFindFile;
-    WIN32_FIND_DATA findFileData;
-    char pathbuf[MAX_PATH + 1];
-    
-    if (NULL == fn || '\0' == *fn) {
-        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
-        return -1;
-    }
 
     /*
      * FindFirstFile() expands wildcard characters.  So
      * we make sure the pathname contains no wildcard.
      */
     if (NULL != _mbspbrk(fn, "?*")) {
-        PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
-        return -1;
+        SetLastError(ERROR_INVALID_NAME);
+        return FALSE;
     }
 
-    hFindFile = FindFirstFile(fn, &findFileData);
+    hFindFile = FindFirstFile(fn, findFileData);
     if (INVALID_HANDLE_VALUE == hFindFile) {
         DWORD len;
         char *filePart;
+        char pathbuf[MAX_PATH + 1];
 
         /*
          * FindFirstFile() does not work correctly on root directories.
          * It also doesn't work correctly on a pathname that ends in a
          * slash.  So we first check to see if the pathname specifies a
          * root directory.  If not, and if the pathname ends in a slash,
          * we remove the final slash and try again.
          */
 
         /*
          * If the pathname does not contain ., \, and /, it cannot be
          * a root directory or a pathname that ends in a slash.
          */
         if (NULL == _mbspbrk(fn, ".\\/")) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
+            return FALSE;
         } 
         len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
                 &filePart);
         if (0 == len) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
+            return FALSE;
         }
         if (len > sizeof(pathbuf)) {
-            PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
-            return -1;
+            SetLastError(ERROR_FILENAME_EXCED_RANGE);
+            return FALSE;
         }
         if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
-            info->type = PR_FILE_DIRECTORY;
-            info->size = 0;
+            findFileData->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+            /* The file size doesn't have a meaning for directories. */
+            findFileData->nFileSizeHigh = 0;
+            findFileData->nFileSizeLow = 0;
             /*
-             * These timestamps don't make sense for root directories.
+             * For a directory, these timestamps all specify when the
+             * directory is created.  The creation time doesn't make
+             * sense for root directories, so we set it to (NSPR) time 0.
              */
-            info->modifyTime = 0;
-            info->creationTime = 0;
-            return 0;
+            memcpy(&findFileData->ftCreationTime, &_pr_filetime_offset, 8);
+            findFileData->ftLastAccessTime = findFileData->ftCreationTime;
+            findFileData->ftLastWriteTime = findFileData->ftCreationTime;
+            return TRUE;
         }
         if (!IsPrevCharSlash(pathbuf, pathbuf + len)) {
-            _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-            return -1;
+            return FALSE;
         } else {
             pathbuf[len - 1] = '\0';
-            hFindFile = FindFirstFile(pathbuf, &findFileData);
+            hFindFile = FindFirstFile(pathbuf, findFileData);
             if (INVALID_HANDLE_VALUE == hFindFile) {
-                _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
-                return -1;
+                return FALSE;
             }
         }
     }
 
     FindClose(hFindFile);
+    return TRUE;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+    WIN32_FIND_DATA findFileData;
+    BOOL rv;
+    
+    if (NULL == fn || '\0' == *fn) {
+        PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+        return -1;
+    }
+
+    /* GetFileAttributesEx is supported on Win 2K and up. */
+    if (getFileAttributesEx) {
+        rv = getFileAttributesEx(fn, GetFileExInfoStandard, &findFileData);
+    } else {
+       rv = GetFileAttributesExFB(fn, &findFileData);
+    }
+    if (!rv) {
+        _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+        return -1;
+    }
 
     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
         info->type = PR_FILE_DIRECTORY;
     } else {
         info->type = PR_FILE_FILE;
     }
 
     info->size = findFileData.nFileSizeHigh;