1b7d1e3a9d8cc4ad93dff6649ced4d4f87317436
[reactos.git] / 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
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 PCSTR RootPath;
193 PUNICODE_STRING RootPathU;
194
195 RootPath = lpRootPathName;
196 if (RootPath == NULL)
197 {
198 RootPath = "\\";
199 }
200
201 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath);
202 if (RootPathU == NULL)
203 {
204 return FALSE;
205 }
206
207 return GetDiskFreeSpaceW(RootPathU->Buffer, lpSectorsPerCluster,
208 lpBytesPerSector, lpNumberOfFreeClusters,
209 lpTotalNumberOfClusters);
210 }
211
212 /*
213 * @implemented
214 */
215 BOOL
216 WINAPI
217 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName,
218 OUT LPDWORD lpSectorsPerCluster,
219 OUT LPDWORD lpBytesPerSector,
220 OUT LPDWORD lpNumberOfFreeClusters,
221 OUT LPDWORD lpTotalNumberOfClusters)
222 {
223 BOOL Below2GB;
224 PCWSTR RootPath;
225 NTSTATUS Status;
226 HANDLE RootHandle;
227 UNICODE_STRING FileName;
228 IO_STATUS_BLOCK IoStatusBlock;
229 OBJECT_ATTRIBUTES ObjectAttributes;
230 FILE_FS_SIZE_INFORMATION FileFsSize;
231
232 /* If no path provided, get root path */
233 RootPath = lpRootPathName;
234 if (lpRootPathName == NULL)
235 {
236 RootPath = L"\\";
237 }
238
239 /* Convert the path to NT path */
240 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL))
241 {
242 SetLastError(ERROR_PATH_NOT_FOUND);
243 return FALSE;
244 }
245
246 /* Open it for disk space query! */
247 InitializeObjectAttributes(&ObjectAttributes, &FileName,
248 OBJ_CASE_INSENSITIVE, NULL, NULL);
249 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock,
250 FILE_SHARE_READ | FILE_SHARE_WRITE,
251 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY);
252 if (!NT_SUCCESS(Status))
253 {
254 BaseSetLastNTError(Status);
255 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
256 if (lpBytesPerSector != NULL)
257 {
258 *lpBytesPerSector = 0;
259 }
260
261 return FALSE;
262 }
263
264 /* We don't need the name any longer */
265 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
266
267 /* Query disk space! */
268 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsSize,
269 sizeof(FILE_FS_SIZE_INFORMATION),
270 FileFsSizeInformation);
271 NtClose(RootHandle);
272 if (!NT_SUCCESS(Status))
273 {
274 BaseSetLastNTError(Status);
275 return FALSE;
276 }
277
278 /* Are we in some compatibility mode where size must be below 2GB? */
279 Below2GB = ((NtCurrentPeb()->AppCompatFlags.LowPart & GetDiskFreeSpace2GB) == GetDiskFreeSpace2GB);
280
281 /* If we're to overflow output, make sure we return the maximum */
282 if (FileFsSize.TotalAllocationUnits.HighPart != 0)
283 {
284 FileFsSize.TotalAllocationUnits.LowPart = -1;
285 }
286
287 if (FileFsSize.AvailableAllocationUnits.HighPart != 0)
288 {
289 FileFsSize.AvailableAllocationUnits.LowPart = -1;
290 }
291
292 /* Return what user asked for */
293 if (lpSectorsPerCluster != NULL)
294 {
295 *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit;
296 }
297
298 if (lpBytesPerSector != NULL)
299 {
300 *lpBytesPerSector = FileFsSize.BytesPerSector;
301 }
302
303 if (lpNumberOfFreeClusters != NULL)
304 {
305 if (!Below2GB)
306 {
307 *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.LowPart;
308 }
309 /* If we have to remain below 2GB... */
310 else
311 {
312 DWORD FreeClusters;
313
314 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
315 FreeClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector);
316 /* If that's higher than what was queried, then return the queried value, it's OK! */
317 if (FreeClusters > FileFsSize.AvailableAllocationUnits.LowPart)
318 {
319 FreeClusters = FileFsSize.AvailableAllocationUnits.LowPart;
320 }
321
322 *lpNumberOfFreeClusters = FreeClusters;
323 }
324 }
325
326 if (lpTotalNumberOfClusters != NULL)
327 {
328 if (!Below2GB)
329 {
330 *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.LowPart;
331 }
332 /* If we have to remain below 2GB... */
333 else
334 {
335 DWORD TotalClusters;
336
337 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
338 TotalClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector);
339 /* If that's higher than what was queried, then return the queried value, it's OK! */
340 if (TotalClusters > FileFsSize.TotalAllocationUnits.LowPart)
341 {
342 TotalClusters = FileFsSize.TotalAllocationUnits.LowPart;
343 }
344
345 *lpTotalNumberOfClusters = TotalClusters;
346 }
347 }
348
349 return TRUE;
350 }
351
352 /*
353 * @implemented
354 */
355 BOOL
356 WINAPI
357 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL,
358 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller,
359 OUT PULARGE_INTEGER lpTotalNumberOfBytes,
360 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes)
361 {
362 PCSTR RootPath;
363 PUNICODE_STRING RootPathU;
364
365 RootPath = lpDirectoryName;
366 if (RootPath == NULL)
367 {
368 RootPath = "\\";
369 }
370
371 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath);
372 if (RootPathU == NULL)
373 {
374 return FALSE;
375 }
376
377 return GetDiskFreeSpaceExW(RootPathU->Buffer, lpFreeBytesAvailableToCaller,
378 lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
379 }
380
381 /*
382 * @implemented
383 */
384 BOOL
385 WINAPI
386 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL,
387 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller,
388 OUT PULARGE_INTEGER lpTotalNumberOfBytes,
389 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes)
390 {
391 PCWSTR RootPath;
392 NTSTATUS Status;
393 HANDLE RootHandle;
394 UNICODE_STRING FileName;
395 DWORD BytesPerAllocationUnit;
396 IO_STATUS_BLOCK IoStatusBlock;
397 OBJECT_ATTRIBUTES ObjectAttributes;
398 FILE_FS_SIZE_INFORMATION FileFsSize;
399
400 /* If no path provided, get root path */
401 RootPath = lpDirectoryName;
402 if (lpDirectoryName == NULL)
403 {
404 RootPath = L"\\";
405 }
406
407 /* Convert the path to NT path */
408 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL))
409 {
410 SetLastError(ERROR_PATH_NOT_FOUND);
411 return FALSE;
412 }
413
414 /* Open it for disk space query! */
415 InitializeObjectAttributes(&ObjectAttributes, &FileName,
416 OBJ_CASE_INSENSITIVE, NULL, NULL);
417 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock,
418 FILE_SHARE_READ | FILE_SHARE_WRITE,
419 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY);
420 if (!NT_SUCCESS(Status))
421 {
422 BaseSetLastNTError(Status);
423 /* If error conversion lead to file not found, override to use path not found
424 * which is more accurate
425 */
426 if (GetLastError() == ERROR_FILE_NOT_FOUND)
427 {
428 SetLastError(ERROR_PATH_NOT_FOUND);
429 }
430
431 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
432
433 return FALSE;
434 }
435
436 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
437
438 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */
439 if (lpTotalNumberOfFreeBytes != NULL)
440 {
441 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize;
442
443 /* Issue the full fs size request */
444 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsFullSize,
445 sizeof(FILE_FS_FULL_SIZE_INFORMATION),
446 FileFsFullSizeInformation);
447 /* If it succeed, complete out buffers */
448 if (NT_SUCCESS(Status))
449 {
450 /* We can close here, we'll return */
451 NtClose(RootHandle);
452
453 /* Compute the size of an AU */
454 BytesPerAllocationUnit = FileFsFullSize.SectorsPerAllocationUnit * FileFsFullSize.BytesPerSector;
455
456 /* And then return what was asked */
457 if (lpFreeBytesAvailableToCaller != NULL)
458 {
459 lpFreeBytesAvailableToCaller->QuadPart = FileFsFullSize.CallerAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
460 }
461
462 if (lpTotalNumberOfBytes != NULL)
463 {
464 lpTotalNumberOfBytes->QuadPart = FileFsFullSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit;
465 }
466
467 /* No need to check for nullness ;-) */
468 lpTotalNumberOfFreeBytes->QuadPart = FileFsFullSize.ActualAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
469
470 return TRUE;
471 }
472 }
473
474 /* Otherwise, fallback to normal size information */
475 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock,
476 &FileFsSize, sizeof(FILE_FS_SIZE_INFORMATION),
477 FileFsSizeInformation);
478 NtClose(RootHandle);
479 if (!NT_SUCCESS(Status))
480 {
481 BaseSetLastNTError(Status);
482 return FALSE;
483 }
484
485 /* Compute the size of an AU */
486 BytesPerAllocationUnit = FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector;
487
488 /* And then return what was asked, available is free, the same! */
489 if (lpFreeBytesAvailableToCaller != NULL)
490 {
491 lpFreeBytesAvailableToCaller->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
492 }
493
494 if (lpTotalNumberOfBytes != NULL)
495 {
496 lpTotalNumberOfBytes->QuadPart = FileFsSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit;
497 }
498
499 if (lpTotalNumberOfFreeBytes != NULL)
500 {
501 lpTotalNumberOfFreeBytes->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit;
502 }
503
504 return TRUE;
505 }
506
507 /*
508 * @implemented
509 */
510 UINT
511 WINAPI
512 GetDriveTypeA(IN LPCSTR lpRootPathName)
513 {
514 PWCHAR RootPathNameW;
515
516 if (!lpRootPathName)
517 return GetDriveTypeW(NULL);
518
519 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
520 return DRIVE_UNKNOWN;
521
522 return GetDriveTypeW(RootPathNameW);
523 }
524
525 /*
526 * @implemented
527 */
528 UINT
529 WINAPI
530 GetDriveTypeW(IN LPCWSTR lpRootPathName)
531 {
532 FILE_FS_DEVICE_INFORMATION FileFsDevice;
533 OBJECT_ATTRIBUTES ObjectAttributes;
534 IO_STATUS_BLOCK IoStatusBlock;
535 UNICODE_STRING PathName;
536 HANDLE FileHandle;
537 NTSTATUS Status;
538 PWSTR CurrentDir = NULL;
539 PCWSTR lpRootPath;
540
541 if (!lpRootPathName)
542 {
543 /* If NULL is passed, use current directory path */
544 DWORD BufferSize = GetCurrentDirectoryW(0, NULL);
545 CurrentDir = HeapAlloc(GetProcessHeap(), 0, BufferSize * sizeof(WCHAR));
546 if (!CurrentDir)
547 return DRIVE_UNKNOWN;
548 if (!GetCurrentDirectoryW(BufferSize, CurrentDir))
549 {
550 HeapFree(GetProcessHeap(), 0, CurrentDir);
551 return DRIVE_UNKNOWN;
552 }
553
554 if (wcslen(CurrentDir) > 3)
555 CurrentDir[3] = 0;
556
557 lpRootPath = CurrentDir;
558 }
559 else
560 {
561 size_t Length = wcslen(lpRootPathName);
562
563 TRACE("lpRootPathName: %S\n", lpRootPathName);
564
565 lpRootPath = lpRootPathName;
566 if (Length == 2)
567 {
568 WCHAR DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]);
569
570 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':')
571 {
572 Length = (Length + 2) * sizeof(WCHAR);
573
574 CurrentDir = HeapAlloc(GetProcessHeap(), 0, Length);
575 if (!CurrentDir)
576 return DRIVE_UNKNOWN;
577
578 StringCbPrintfW(CurrentDir, Length, L"%s\\", lpRootPathName);
579
580 lpRootPath = CurrentDir;
581 }
582 }
583 }
584
585 TRACE("lpRootPath: %S\n", lpRootPath);
586
587 if (!RtlDosPathNameToNtPathName_U(lpRootPath, &PathName, NULL, NULL))
588 {
589 if (CurrentDir != NULL)
590 HeapFree(GetProcessHeap(), 0, CurrentDir);
591
592 return DRIVE_NO_ROOT_DIR;
593 }
594
595 TRACE("PathName: %S\n", PathName.Buffer);
596
597 if (CurrentDir != NULL)
598 HeapFree(GetProcessHeap(), 0, CurrentDir);
599
600 if (PathName.Buffer[(PathName.Length >> 1) - 1] != L'\\')
601 {
602 return DRIVE_NO_ROOT_DIR;
603 }
604
605 InitializeObjectAttributes(&ObjectAttributes,
606 &PathName,
607 OBJ_CASE_INSENSITIVE,
608 NULL,
609 NULL);
610
611 Status = NtOpenFile(&FileHandle,
612 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
613 &ObjectAttributes,
614 &IoStatusBlock,
615 FILE_SHARE_READ | FILE_SHARE_WRITE,
616 FILE_SYNCHRONOUS_IO_NONALERT);
617
618 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer);
619 if (!NT_SUCCESS(Status))
620 return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */
621
622 Status = NtQueryVolumeInformationFile(FileHandle,
623 &IoStatusBlock,
624 &FileFsDevice,
625 sizeof(FILE_FS_DEVICE_INFORMATION),
626 FileFsDeviceInformation);
627 NtClose(FileHandle);
628 if (!NT_SUCCESS(Status))
629 {
630 return 0;
631 }
632
633 switch (FileFsDevice.DeviceType)
634 {
635 case FILE_DEVICE_CD_ROM:
636 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
637 return DRIVE_CDROM;
638 case FILE_DEVICE_VIRTUAL_DISK:
639 return DRIVE_RAMDISK;
640 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
641 return DRIVE_REMOTE;
642 case FILE_DEVICE_DISK:
643 case FILE_DEVICE_DISK_FILE_SYSTEM:
644 if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE)
645 return DRIVE_REMOTE;
646 if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA)
647 return DRIVE_REMOVABLE;
648 return DRIVE_FIXED;
649 }
650
651 ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice.DeviceType);
652
653 return DRIVE_UNKNOWN;
654 }
655
656 /* EOF */