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