[KERNEL32]: Refactor a bit GetDiskFreeSpaceExW(), no ground breaking changes
authorPierre Schweitzer <pierre@reactos.org>
Wed, 4 Oct 2017 21:00:12 +0000 (23:00 +0200)
committerPierre Schweitzer <pierre@reactos.org>
Thu, 5 Oct 2017 20:14:44 +0000 (22:14 +0200)
dll/win32/kernel32/client/file/disk.c

index 5854b23..1ba1b73 100644 (file)
@@ -383,103 +383,117 @@ GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL,
                     OUT PULARGE_INTEGER lpTotalNumberOfBytes,
                     OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes)
 {
-    union
-    {
-        FILE_FS_SIZE_INFORMATION FsSize;
-        FILE_FS_FULL_SIZE_INFORMATION FsFullSize;
-    } FsInfo;
-    IO_STATUS_BLOCK IoStatusBlock;
-    ULARGE_INTEGER BytesPerCluster;
-    HANDLE hFile;
+    PCWSTR RootPath;
     NTSTATUS Status;
+    HANDLE RootHandle;
+    UNICODE_STRING FileName;
+    DWORD BytesPerAllocationUnit;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    FILE_FS_SIZE_INFORMATION FileFsSize;
 
+    /* If no path provided, get root path */
+    RootPath = lpDirectoryName;
     if (lpDirectoryName == NULL)
-        lpDirectoryName = L"\\";
+    {
+        RootPath = L"\\";
+    }
 
-    hFile = InternalOpenDirW(lpDirectoryName, FALSE);
-    if (INVALID_HANDLE_VALUE == hFile)
+    /* Convert the path to NT path */
+    if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL))
     {
+        SetLastError(ERROR_PATH_NOT_FOUND);
         return FALSE;
     }
 
-    if (lpFreeBytesAvailableToCaller != NULL || lpTotalNumberOfBytes != NULL)
+    /* Open it for disk space query! */
+    InitializeObjectAttributes(&ObjectAttributes, &FileName,
+                               OBJ_CASE_INSENSITIVE, NULL, NULL);
+    Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY);
+    if (!NT_SUCCESS(Status))
     {
-        /* To get the free space available to the user associated with the
-           current thread, try FileFsFullSizeInformation. If this is not
-           supported by the file system, fall back to FileFsSize */
+        BaseSetLastNTError(Status);
+        /* If error conversion lead to file not found, override to use path not found
+         * which is more accurate
+         */
+        if (GetLastError() == ERROR_FILE_NOT_FOUND)
+        {
+            SetLastError(ERROR_PATH_NOT_FOUND);
+        }
 
-        Status = NtQueryVolumeInformationFile(hFile,
-                                              &IoStatusBlock,
-                                              &FsInfo.FsFullSize,
-                                              sizeof(FsInfo.FsFullSize),
-                                              FileFsFullSizeInformation);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
+
+        return FALSE;
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
 
+    /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */
+    if (lpTotalNumberOfFreeBytes != NULL)
+    {
+        FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize;
+
+        /* Issue the full fs size request */
+        Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsFullSize,
+                                              sizeof(FILE_FS_FULL_SIZE_INFORMATION),
+                                              FileFsFullSizeInformation);
+        /* If it succeed, complete out buffers */
         if (NT_SUCCESS(Status))
         {
-            /* Close the handle before returning data
-               to avoid a handle leak in case of a fault! */
-            CloseHandle(hFile);
+            /* We can close here, we'll return */
+            NtClose(RootHandle);
 
-            BytesPerCluster.QuadPart =
-                FsInfo.FsFullSize.BytesPerSector * FsInfo.FsFullSize.SectorsPerAllocationUnit;
+            /* Compute the size of an AU */
+            BytesPerAllocationUnit = FileFsFullSize.SectorsPerAllocationUnit * FileFsFullSize.BytesPerSector;
 
+            /* And then return what was asked */
             if (lpFreeBytesAvailableToCaller != NULL)
             {
-                lpFreeBytesAvailableToCaller->QuadPart =
-                    BytesPerCluster.QuadPart * FsInfo.FsFullSize.CallerAvailableAllocationUnits.QuadPart;
+                lpFreeBytesAvailableToCaller->QuadPart = FileFsFullSize.CallerAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
             }
 
             if (lpTotalNumberOfBytes != NULL)
             {
-                lpTotalNumberOfBytes->QuadPart =
-                    BytesPerCluster.QuadPart * FsInfo.FsFullSize.TotalAllocationUnits.QuadPart;
+                lpTotalNumberOfBytes->QuadPart = FileFsFullSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit;
             }
 
-            if (lpTotalNumberOfFreeBytes != NULL)
-            {
-                lpTotalNumberOfFreeBytes->QuadPart =
-                    BytesPerCluster.QuadPart * FsInfo.FsFullSize.ActualAvailableAllocationUnits.QuadPart;
-            }
+            /* No need to check for nullness ;-) */
+            lpTotalNumberOfFreeBytes->QuadPart = FileFsFullSize.ActualAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
 
             return TRUE;
         }
     }
 
-    Status = NtQueryVolumeInformationFile(hFile,
-                                          &IoStatusBlock,
-                                          &FsInfo.FsSize,
-                                          sizeof(FsInfo.FsSize),
+    /* Otherwise, fallback to normal size information */
+    Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock,
+                                          &FileFsSize, sizeof(FILE_FS_SIZE_INFORMATION),
                                           FileFsSizeInformation);
-
-    /* Close the handle before returning data
-       to avoid a handle leak in case of a fault! */
-    CloseHandle(hFile);
-
+    NtClose(RootHandle);
     if (!NT_SUCCESS(Status))
     {
-        BaseSetLastNTError (Status);
+        BaseSetLastNTError(Status);
         return FALSE;
     }
 
-    BytesPerCluster.QuadPart =
-        FsInfo.FsSize.BytesPerSector * FsInfo.FsSize.SectorsPerAllocationUnit;
+    /* Compute the size of an AU */
+    BytesPerAllocationUnit = FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector;
 
-    if (lpFreeBytesAvailableToCaller)
+    /* And then return what was asked, available is free, the same! */
+    if (lpFreeBytesAvailableToCaller != NULL)
     {
-        lpFreeBytesAvailableToCaller->QuadPart =
-            BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
+        lpFreeBytesAvailableToCaller->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
     }
 
-    if (lpTotalNumberOfBytes)
+    if (lpTotalNumberOfBytes != NULL)
     {
-        lpTotalNumberOfBytes->QuadPart =
-            BytesPerCluster.QuadPart * FsInfo.FsSize.TotalAllocationUnits.QuadPart;
+        lpTotalNumberOfBytes->QuadPart = FileFsSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit;
     }
 
-    if (lpTotalNumberOfFreeBytes)
+    if (lpTotalNumberOfFreeBytes != NULL)
     {
-        lpTotalNumberOfFreeBytes->QuadPart =
-            BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
+        lpTotalNumberOfFreeBytes->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
     }
 
     return TRUE;