xref: /reactos/dll/win32/kernel32/client/file/disk.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            dll/win32/kernel32/client/file/disk.c
5  * PURPOSE:         Disk and Drive functions
6  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
7  *                  Erik Bos, Alexandre Julliard :
8  *                      GetLogicalDriveStringsA,
9  *                      GetLogicalDriveStringsW, GetLogicalDrives
10  * UPDATE HISTORY:
11  *                  Created 01/11/98
12  */
13 //WINE copyright notice:
14 /*
15  * DOS drives handling functions
16  *
17  * Copyright 1993 Erik Bos
18  * Copyright 1996 Alexandre Julliard
19  */
20 
21 #include <k32.h>
22 #include <strsafe.h>
23 
24 #define NDEBUG
25 #include <debug.h>
26 DEBUG_CHANNEL(kernel32file);
27 
28 #define MAX_DOS_DRIVES 26
29 
30 HANDLE
31 WINAPI
32 InternalOpenDirW(IN LPCWSTR DirName,
33                  IN BOOLEAN Write)
34 {
35     UNICODE_STRING NtPathU;
36     OBJECT_ATTRIBUTES ObjectAttributes;
37     NTSTATUS errCode;
38     IO_STATUS_BLOCK IoStatusBlock;
39     HANDLE hFile;
40 
41     if (!RtlDosPathNameToNtPathName_U(DirName, &NtPathU, NULL, NULL))
42     {
43         WARN("Invalid path\n");
44         SetLastError(ERROR_BAD_PATHNAME);
45         return INVALID_HANDLE_VALUE;
46     }
47 
48     InitializeObjectAttributes(&ObjectAttributes,
49                                &NtPathU,
50                                OBJ_CASE_INSENSITIVE,
51                                NULL,
52                                NULL);
53 
54     errCode = NtCreateFile(&hFile,
55                            Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
56                            &ObjectAttributes,
57                            &IoStatusBlock,
58                            NULL,
59                            0,
60                            FILE_SHARE_READ | FILE_SHARE_WRITE,
61                            FILE_OPEN,
62                            0,
63                            NULL,
64                            0);
65 
66     RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer);
67 
68     if (!NT_SUCCESS(errCode))
69     {
70         BaseSetLastNTError(errCode);
71         return INVALID_HANDLE_VALUE;
72     }
73 
74     return hFile;
75 }
76 
77 /*
78  * @implemented
79  */
80 /* Synced to Wine-2008/12/28 */
81 DWORD
82 WINAPI
83 GetLogicalDriveStringsA(IN DWORD nBufferLength,
84                         IN LPSTR lpBuffer)
85 {
86     DWORD drive, count;
87     DWORD dwDriveMap;
88     LPSTR p;
89 
90     dwDriveMap = GetLogicalDrives();
91 
92     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
93     {
94         if (dwDriveMap & (1<<drive))
95             count++;
96     }
97 
98 
99     if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1);
100 
101     p = lpBuffer;
102 
103     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
104         if (dwDriveMap & (1<<drive))
105         {
106             *p++ = 'A' + (UCHAR)drive;
107             *p++ = ':';
108             *p++ = '\\';
109             *p++ = '\0';
110         }
111     *p = '\0';
112 
113     return (count * 4);
114 }
115 
116 /*
117  * @implemented
118  */
119 /* Synced to Wine-2008/12/28 */
120 DWORD
121 WINAPI
122 GetLogicalDriveStringsW(IN DWORD nBufferLength,
123                         IN LPWSTR lpBuffer)
124 {
125     DWORD drive, count;
126     DWORD dwDriveMap;
127     LPWSTR p;
128 
129     dwDriveMap = GetLogicalDrives();
130 
131     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
132     {
133         if (dwDriveMap & (1<<drive))
134             count++;
135     }
136 
137     if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1);
138 
139     p = lpBuffer;
140     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
141         if (dwDriveMap & (1<<drive))
142         {
143             *p++ = (WCHAR)('A' + drive);
144             *p++ = (WCHAR)':';
145             *p++ = (WCHAR)'\\';
146             *p++ = (WCHAR)'\0';
147         }
148     *p = (WCHAR)'\0';
149 
150     return (count * 4);
151 }
152 
153 /*
154  * @implemented
155  */
156 /* Synced to Wine-? */
157 DWORD
158 WINAPI
159 GetLogicalDrives(VOID)
160 {
161     NTSTATUS Status;
162     PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
163 
164     /* Get the Device Map for this Process */
165     Status = NtQueryInformationProcess(NtCurrentProcess(),
166                                        ProcessDeviceMap,
167                                        &ProcessDeviceMapInfo,
168                                        sizeof(ProcessDeviceMapInfo),
169                                        NULL);
170 
171     /* Return the Drive Map */
172     if (!NT_SUCCESS(Status))
173     {
174         BaseSetLastNTError(Status);
175         return 0;
176     }
177 
178     return ProcessDeviceMapInfo.Query.DriveMap;
179 }
180 
181 /*
182  * @implemented
183  */
184 BOOL
185 WINAPI
186 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName,
187                   OUT LPDWORD lpSectorsPerCluster,
188                   OUT LPDWORD lpBytesPerSector,
189                   OUT LPDWORD lpNumberOfFreeClusters,
190                   OUT LPDWORD lpTotalNumberOfClusters)
191 {
192     PWCHAR RootPathNameW=NULL;
193 
194     if (lpRootPathName)
195     {
196         if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
197             return FALSE;
198     }
199 
200     return GetDiskFreeSpaceW (RootPathNameW,
201                               lpSectorsPerCluster,
202                               lpBytesPerSector,
203                               lpNumberOfFreeClusters,
204                               lpTotalNumberOfClusters);
205 }
206 
207 /*
208  * @implemented
209  */
210 BOOL
211 WINAPI
212 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName,
213                   OUT LPDWORD lpSectorsPerCluster,
214                   OUT LPDWORD lpBytesPerSector,
215                   OUT LPDWORD lpNumberOfFreeClusters,
216                   OUT LPDWORD lpTotalNumberOfClusters)
217 {
218     FILE_FS_SIZE_INFORMATION FileFsSize;
219     IO_STATUS_BLOCK IoStatusBlock;
220     WCHAR RootPathName[MAX_PATH];
221     HANDLE hFile;
222     NTSTATUS errCode;
223 
224     if (lpRootPathName)
225     {
226         wcsncpy (RootPathName, lpRootPathName, 3);
227     }
228     else
229     {
230         GetCurrentDirectoryW (MAX_PATH, RootPathName);
231     }
232     RootPathName[3] = 0;
233 
234     hFile = InternalOpenDirW(RootPathName, FALSE);
235     if (INVALID_HANDLE_VALUE == hFile)
236     {
237         SetLastError(ERROR_PATH_NOT_FOUND);
238         return FALSE;
239     }
240 
241     errCode = NtQueryVolumeInformationFile(hFile,
242                                            &IoStatusBlock,
243                                            &FileFsSize,
244                                            sizeof(FILE_FS_SIZE_INFORMATION),
245                                            FileFsSizeInformation);
246     if (!NT_SUCCESS(errCode))
247     {
248         CloseHandle(hFile);
249         BaseSetLastNTError (errCode);
250         return FALSE;
251     }
252 
253     if (lpSectorsPerCluster)
254         *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit;
255     if (lpBytesPerSector)
256         *lpBytesPerSector = FileFsSize.BytesPerSector;
257     if (lpNumberOfFreeClusters)
258         *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.u.LowPart;
259     if (lpTotalNumberOfClusters)
260         *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.u.LowPart;
261     CloseHandle(hFile);
262 
263     return TRUE;
264 }
265 
266 /*
267  * @implemented
268  */
269 BOOL
270 WINAPI
271 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL,
272                     OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller,
273                     OUT PULARGE_INTEGER lpTotalNumberOfBytes,
274                     OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes)
275 {
276     PWCHAR DirectoryNameW=NULL;
277 
278     if (lpDirectoryName)
279     {
280         if (!(DirectoryNameW = FilenameA2W(lpDirectoryName, FALSE)))
281             return FALSE;
282     }
283 
284     return GetDiskFreeSpaceExW (DirectoryNameW ,
285                                 lpFreeBytesAvailableToCaller,
286                                 lpTotalNumberOfBytes,
287                                 lpTotalNumberOfFreeBytes);
288 }
289 
290 /*
291  * @implemented
292  */
293 BOOL
294 WINAPI
295 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL,
296                     OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller,
297                     OUT PULARGE_INTEGER lpTotalNumberOfBytes,
298                     OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes)
299 {
300     union
301     {
302         FILE_FS_SIZE_INFORMATION FsSize;
303         FILE_FS_FULL_SIZE_INFORMATION FsFullSize;
304     } FsInfo;
305     IO_STATUS_BLOCK IoStatusBlock;
306     ULARGE_INTEGER BytesPerCluster;
307     HANDLE hFile;
308     NTSTATUS Status;
309 
310     if (lpDirectoryName == NULL)
311         lpDirectoryName = L"\\";
312 
313     hFile = InternalOpenDirW(lpDirectoryName, FALSE);
314     if (INVALID_HANDLE_VALUE == hFile)
315     {
316         return FALSE;
317     }
318 
319     if (lpFreeBytesAvailableToCaller != NULL || lpTotalNumberOfBytes != NULL)
320     {
321         /* To get the free space available to the user associated with the
322            current thread, try FileFsFullSizeInformation. If this is not
323            supported by the file system, fall back to FileFsSize */
324 
325         Status = NtQueryVolumeInformationFile(hFile,
326                                               &IoStatusBlock,
327                                               &FsInfo.FsFullSize,
328                                               sizeof(FsInfo.FsFullSize),
329                                               FileFsFullSizeInformation);
330 
331         if (NT_SUCCESS(Status))
332         {
333             /* Close the handle before returning data
334                to avoid a handle leak in case of a fault! */
335             CloseHandle(hFile);
336 
337             BytesPerCluster.QuadPart =
338                 FsInfo.FsFullSize.BytesPerSector * FsInfo.FsFullSize.SectorsPerAllocationUnit;
339 
340             if (lpFreeBytesAvailableToCaller != NULL)
341             {
342                 lpFreeBytesAvailableToCaller->QuadPart =
343                     BytesPerCluster.QuadPart * FsInfo.FsFullSize.CallerAvailableAllocationUnits.QuadPart;
344             }
345 
346             if (lpTotalNumberOfBytes != NULL)
347             {
348                 lpTotalNumberOfBytes->QuadPart =
349                     BytesPerCluster.QuadPart * FsInfo.FsFullSize.TotalAllocationUnits.QuadPart;
350             }
351 
352             if (lpTotalNumberOfFreeBytes != NULL)
353             {
354                 lpTotalNumberOfFreeBytes->QuadPart =
355                     BytesPerCluster.QuadPart * FsInfo.FsFullSize.ActualAvailableAllocationUnits.QuadPart;
356             }
357 
358             return TRUE;
359         }
360     }
361 
362     Status = NtQueryVolumeInformationFile(hFile,
363                                           &IoStatusBlock,
364                                           &FsInfo.FsSize,
365                                           sizeof(FsInfo.FsSize),
366                                           FileFsSizeInformation);
367 
368     /* Close the handle before returning data
369        to avoid a handle leak in case of a fault! */
370     CloseHandle(hFile);
371 
372     if (!NT_SUCCESS(Status))
373     {
374         BaseSetLastNTError (Status);
375         return FALSE;
376     }
377 
378     BytesPerCluster.QuadPart =
379         FsInfo.FsSize.BytesPerSector * FsInfo.FsSize.SectorsPerAllocationUnit;
380 
381     if (lpFreeBytesAvailableToCaller)
382     {
383         lpFreeBytesAvailableToCaller->QuadPart =
384             BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
385     }
386 
387     if (lpTotalNumberOfBytes)
388     {
389         lpTotalNumberOfBytes->QuadPart =
390             BytesPerCluster.QuadPart * FsInfo.FsSize.TotalAllocationUnits.QuadPart;
391     }
392 
393     if (lpTotalNumberOfFreeBytes)
394     {
395         lpTotalNumberOfFreeBytes->QuadPart =
396             BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
397     }
398 
399     return TRUE;
400 }
401 
402 /*
403  * @implemented
404  */
405 UINT
406 WINAPI
407 GetDriveTypeA(IN LPCSTR lpRootPathName)
408 {
409     PWCHAR RootPathNameW;
410 
411     if (!lpRootPathName)
412         return GetDriveTypeW(NULL);
413 
414     if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
415         return DRIVE_UNKNOWN;
416 
417     return GetDriveTypeW(RootPathNameW);
418 }
419 
420 /*
421  * @implemented
422  */
423 UINT
424 WINAPI
425 GetDriveTypeW(IN LPCWSTR lpRootPathName)
426 {
427     FILE_FS_DEVICE_INFORMATION FileFsDevice;
428     OBJECT_ATTRIBUTES ObjectAttributes;
429     IO_STATUS_BLOCK IoStatusBlock;
430     UNICODE_STRING PathName;
431     HANDLE FileHandle;
432     NTSTATUS Status;
433     PWSTR CurrentDir = NULL;
434     PCWSTR lpRootPath;
435 
436     if (!lpRootPathName)
437     {
438         /* If NULL is passed, use current directory path */
439         DWORD BufferSize = GetCurrentDirectoryW(0, NULL);
440         CurrentDir = HeapAlloc(GetProcessHeap(), 0, BufferSize * sizeof(WCHAR));
441         if (!CurrentDir)
442             return DRIVE_UNKNOWN;
443         if (!GetCurrentDirectoryW(BufferSize, CurrentDir))
444         {
445             HeapFree(GetProcessHeap(), 0, CurrentDir);
446             return DRIVE_UNKNOWN;
447         }
448 
449         if (wcslen(CurrentDir) > 3)
450             CurrentDir[3] = 0;
451 
452         lpRootPath = CurrentDir;
453     }
454     else
455     {
456         size_t Length = wcslen(lpRootPathName);
457 
458         TRACE("lpRootPathName: %S\n", lpRootPathName);
459 
460         lpRootPath = lpRootPathName;
461         if (Length == 2)
462         {
463             WCHAR DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]);
464 
465             if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':')
466             {
467                 Length = (Length + 2) * sizeof(WCHAR);
468 
469                 CurrentDir = HeapAlloc(GetProcessHeap(), 0, Length);
470                 if (!CurrentDir)
471                     return DRIVE_UNKNOWN;
472 
473                 StringCbPrintfW(CurrentDir, Length, L"%s\\", lpRootPathName);
474 
475                 lpRootPath = CurrentDir;
476             }
477         }
478     }
479 
480     TRACE("lpRootPath: %S\n", lpRootPath);
481 
482     if (!RtlDosPathNameToNtPathName_U(lpRootPath, &PathName, NULL, NULL))
483     {
484         if (CurrentDir != NULL)
485             HeapFree(GetProcessHeap(), 0, CurrentDir);
486 
487         return DRIVE_NO_ROOT_DIR;
488     }
489 
490     TRACE("PathName: %S\n", PathName.Buffer);
491 
492     if (CurrentDir != NULL)
493         HeapFree(GetProcessHeap(), 0, CurrentDir);
494 
495     if (PathName.Buffer[(PathName.Length >> 1) - 1] != L'\\')
496     {
497         return DRIVE_NO_ROOT_DIR;
498     }
499 
500     InitializeObjectAttributes(&ObjectAttributes,
501                                &PathName,
502                                OBJ_CASE_INSENSITIVE,
503                                NULL,
504                                NULL);
505 
506     Status = NtOpenFile(&FileHandle,
507                         FILE_READ_ATTRIBUTES | SYNCHRONIZE,
508                         &ObjectAttributes,
509                         &IoStatusBlock,
510                         FILE_SHARE_READ | FILE_SHARE_WRITE,
511                         FILE_SYNCHRONOUS_IO_NONALERT);
512 
513     RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer);
514     if (!NT_SUCCESS(Status))
515         return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */
516 
517     Status = NtQueryVolumeInformationFile(FileHandle,
518                                           &IoStatusBlock,
519                                           &FileFsDevice,
520                                           sizeof(FILE_FS_DEVICE_INFORMATION),
521                                           FileFsDeviceInformation);
522     NtClose(FileHandle);
523     if (!NT_SUCCESS(Status))
524     {
525         return 0;
526     }
527 
528     switch (FileFsDevice.DeviceType)
529     {
530         case FILE_DEVICE_CD_ROM:
531         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
532             return DRIVE_CDROM;
533         case FILE_DEVICE_VIRTUAL_DISK:
534             return DRIVE_RAMDISK;
535         case FILE_DEVICE_NETWORK_FILE_SYSTEM:
536             return DRIVE_REMOTE;
537         case FILE_DEVICE_DISK:
538         case FILE_DEVICE_DISK_FILE_SYSTEM:
539             if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE)
540                 return DRIVE_REMOTE;
541             if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA)
542                 return DRIVE_REMOVABLE;
543         return DRIVE_FIXED;
544     }
545 
546     ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice.DeviceType);
547 
548     return DRIVE_UNKNOWN;
549 }
550 
551 /* EOF */
552