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 27 #define MAX_DOS_DRIVES 26 28 29 /* 30 * @implemented 31 */ 32 /* Synced to Wine-2008/12/28 */ 33 DWORD 34 WINAPI 35 GetLogicalDriveStringsA(IN DWORD nBufferLength, 36 IN LPSTR lpBuffer) 37 { 38 DWORD drive, count; 39 DWORD dwDriveMap; 40 LPSTR p; 41 42 dwDriveMap = GetLogicalDrives(); 43 44 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 45 { 46 if (dwDriveMap & (1<<drive)) 47 count++; 48 } 49 50 51 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 52 53 p = lpBuffer; 54 55 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 56 if (dwDriveMap & (1<<drive)) 57 { 58 *p++ = 'A' + (UCHAR)drive; 59 *p++ = ':'; 60 *p++ = '\\'; 61 *p++ = '\0'; 62 } 63 *p = '\0'; 64 65 return (count * 4); 66 } 67 68 /* 69 * @implemented 70 */ 71 /* Synced to Wine-2008/12/28 */ 72 DWORD 73 WINAPI 74 GetLogicalDriveStringsW(IN DWORD nBufferLength, 75 IN LPWSTR lpBuffer) 76 { 77 DWORD drive, count; 78 DWORD dwDriveMap; 79 LPWSTR p; 80 81 dwDriveMap = GetLogicalDrives(); 82 83 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 84 { 85 if (dwDriveMap & (1<<drive)) 86 count++; 87 } 88 89 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 90 91 p = lpBuffer; 92 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 93 if (dwDriveMap & (1<<drive)) 94 { 95 *p++ = (WCHAR)('A' + drive); 96 *p++ = (WCHAR)':'; 97 *p++ = (WCHAR)'\\'; 98 *p++ = (WCHAR)'\0'; 99 } 100 *p = (WCHAR)'\0'; 101 102 return (count * 4); 103 } 104 105 /* 106 * @implemented 107 */ 108 /* Synced to Wine-? */ 109 DWORD 110 WINAPI 111 GetLogicalDrives(VOID) 112 { 113 NTSTATUS Status; 114 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo; 115 116 /* Get the Device Map for this Process */ 117 Status = NtQueryInformationProcess(NtCurrentProcess(), 118 ProcessDeviceMap, 119 &ProcessDeviceMapInfo, 120 sizeof(ProcessDeviceMapInfo), 121 NULL); 122 123 /* Return the Drive Map */ 124 if (!NT_SUCCESS(Status)) 125 { 126 BaseSetLastNTError(Status); 127 return 0; 128 } 129 130 if (ProcessDeviceMapInfo.Query.DriveMap == 0) 131 { 132 SetLastError(ERROR_SUCCESS); 133 } 134 135 return ProcessDeviceMapInfo.Query.DriveMap; 136 } 137 138 /* 139 * @implemented 140 */ 141 BOOL 142 WINAPI 143 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName, 144 OUT LPDWORD lpSectorsPerCluster, 145 OUT LPDWORD lpBytesPerSector, 146 OUT LPDWORD lpNumberOfFreeClusters, 147 OUT LPDWORD lpTotalNumberOfClusters) 148 { 149 PCSTR RootPath; 150 PUNICODE_STRING RootPathU; 151 152 RootPath = lpRootPathName; 153 if (RootPath == NULL) 154 { 155 RootPath = "\\"; 156 } 157 158 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath); 159 if (RootPathU == NULL) 160 { 161 return FALSE; 162 } 163 164 return GetDiskFreeSpaceW(RootPathU->Buffer, lpSectorsPerCluster, 165 lpBytesPerSector, lpNumberOfFreeClusters, 166 lpTotalNumberOfClusters); 167 } 168 169 /* 170 * @implemented 171 */ 172 BOOL 173 WINAPI 174 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName, 175 OUT LPDWORD lpSectorsPerCluster, 176 OUT LPDWORD lpBytesPerSector, 177 OUT LPDWORD lpNumberOfFreeClusters, 178 OUT LPDWORD lpTotalNumberOfClusters) 179 { 180 BOOL Below2GB; 181 PCWSTR RootPath; 182 NTSTATUS Status; 183 HANDLE RootHandle; 184 UNICODE_STRING FileName; 185 IO_STATUS_BLOCK IoStatusBlock; 186 OBJECT_ATTRIBUTES ObjectAttributes; 187 FILE_FS_SIZE_INFORMATION FileFsSize; 188 189 /* If no path provided, get root path */ 190 RootPath = lpRootPathName; 191 if (lpRootPathName == NULL) 192 { 193 RootPath = L"\\"; 194 } 195 196 /* Convert the path to NT path */ 197 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL)) 198 { 199 SetLastError(ERROR_PATH_NOT_FOUND); 200 return FALSE; 201 } 202 203 /* Open it for disk space query! */ 204 InitializeObjectAttributes(&ObjectAttributes, &FileName, 205 OBJ_CASE_INSENSITIVE, NULL, NULL); 206 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 207 FILE_SHARE_READ | FILE_SHARE_WRITE, 208 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY); 209 if (!NT_SUCCESS(Status)) 210 { 211 BaseSetLastNTError(Status); 212 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 213 if (lpBytesPerSector != NULL) 214 { 215 *lpBytesPerSector = 0; 216 } 217 218 return FALSE; 219 } 220 221 /* We don't need the name any longer */ 222 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 223 224 /* Query disk space! */ 225 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsSize, 226 sizeof(FILE_FS_SIZE_INFORMATION), 227 FileFsSizeInformation); 228 NtClose(RootHandle); 229 if (!NT_SUCCESS(Status)) 230 { 231 BaseSetLastNTError(Status); 232 return FALSE; 233 } 234 235 /* Are we in some compatibility mode where size must be below 2GB? */ 236 Below2GB = ((NtCurrentPeb()->AppCompatFlags.LowPart & GetDiskFreeSpace2GB) == GetDiskFreeSpace2GB); 237 238 /* If we're to overflow output, make sure we return the maximum */ 239 if (FileFsSize.TotalAllocationUnits.HighPart != 0) 240 { 241 FileFsSize.TotalAllocationUnits.LowPart = -1; 242 } 243 244 if (FileFsSize.AvailableAllocationUnits.HighPart != 0) 245 { 246 FileFsSize.AvailableAllocationUnits.LowPart = -1; 247 } 248 249 /* Return what user asked for */ 250 if (lpSectorsPerCluster != NULL) 251 { 252 *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit; 253 } 254 255 if (lpBytesPerSector != NULL) 256 { 257 *lpBytesPerSector = FileFsSize.BytesPerSector; 258 } 259 260 if (lpNumberOfFreeClusters != NULL) 261 { 262 if (!Below2GB) 263 { 264 *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; 265 } 266 /* If we have to remain below 2GB... */ 267 else 268 { 269 DWORD FreeClusters; 270 271 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ 272 FreeClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); 273 /* If that's higher than what was queried, then return the queried value, it's OK! */ 274 if (FreeClusters > FileFsSize.AvailableAllocationUnits.LowPart) 275 { 276 FreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; 277 } 278 279 *lpNumberOfFreeClusters = FreeClusters; 280 } 281 } 282 283 if (lpTotalNumberOfClusters != NULL) 284 { 285 if (!Below2GB) 286 { 287 *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.LowPart; 288 } 289 /* If we have to remain below 2GB... */ 290 else 291 { 292 DWORD TotalClusters; 293 294 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ 295 TotalClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); 296 /* If that's higher than what was queried, then return the queried value, it's OK! */ 297 if (TotalClusters > FileFsSize.TotalAllocationUnits.LowPart) 298 { 299 TotalClusters = FileFsSize.TotalAllocationUnits.LowPart; 300 } 301 302 *lpTotalNumberOfClusters = TotalClusters; 303 } 304 } 305 306 return TRUE; 307 } 308 309 /* 310 * @implemented 311 */ 312 BOOL 313 WINAPI 314 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL, 315 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 316 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 317 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 318 { 319 PCSTR RootPath; 320 PUNICODE_STRING RootPathU; 321 322 RootPath = lpDirectoryName; 323 if (RootPath == NULL) 324 { 325 RootPath = "\\"; 326 } 327 328 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath); 329 if (RootPathU == NULL) 330 { 331 return FALSE; 332 } 333 334 return GetDiskFreeSpaceExW(RootPathU->Buffer, lpFreeBytesAvailableToCaller, 335 lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); 336 } 337 338 /* 339 * @implemented 340 */ 341 BOOL 342 WINAPI 343 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL, 344 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 345 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 346 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 347 { 348 PCWSTR RootPath; 349 NTSTATUS Status; 350 HANDLE RootHandle; 351 UNICODE_STRING FileName; 352 DWORD BytesPerAllocationUnit; 353 IO_STATUS_BLOCK IoStatusBlock; 354 OBJECT_ATTRIBUTES ObjectAttributes; 355 FILE_FS_SIZE_INFORMATION FileFsSize; 356 357 /* If no path provided, get root path */ 358 RootPath = lpDirectoryName; 359 if (lpDirectoryName == NULL) 360 { 361 RootPath = L"\\"; 362 } 363 364 /* Convert the path to NT path */ 365 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL)) 366 { 367 SetLastError(ERROR_PATH_NOT_FOUND); 368 return FALSE; 369 } 370 371 /* Open it for disk space query! */ 372 InitializeObjectAttributes(&ObjectAttributes, &FileName, 373 OBJ_CASE_INSENSITIVE, NULL, NULL); 374 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 375 FILE_SHARE_READ | FILE_SHARE_WRITE, 376 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY); 377 if (!NT_SUCCESS(Status)) 378 { 379 BaseSetLastNTError(Status); 380 /* If error conversion lead to file not found, override to use path not found 381 * which is more accurate 382 */ 383 if (GetLastError() == ERROR_FILE_NOT_FOUND) 384 { 385 SetLastError(ERROR_PATH_NOT_FOUND); 386 } 387 388 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 389 390 return FALSE; 391 } 392 393 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 394 395 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */ 396 if (lpTotalNumberOfFreeBytes != NULL) 397 { 398 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize; 399 400 /* Issue the full fs size request */ 401 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsFullSize, 402 sizeof(FILE_FS_FULL_SIZE_INFORMATION), 403 FileFsFullSizeInformation); 404 /* If it succeed, complete out buffers */ 405 if (NT_SUCCESS(Status)) 406 { 407 /* We can close here, we'll return */ 408 NtClose(RootHandle); 409 410 /* Compute the size of an AU */ 411 BytesPerAllocationUnit = FileFsFullSize.SectorsPerAllocationUnit * FileFsFullSize.BytesPerSector; 412 413 /* And then return what was asked */ 414 if (lpFreeBytesAvailableToCaller != NULL) 415 { 416 lpFreeBytesAvailableToCaller->QuadPart = FileFsFullSize.CallerAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 417 } 418 419 if (lpTotalNumberOfBytes != NULL) 420 { 421 lpTotalNumberOfBytes->QuadPart = FileFsFullSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit; 422 } 423 424 /* No need to check for nullness ;-) */ 425 lpTotalNumberOfFreeBytes->QuadPart = FileFsFullSize.ActualAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 426 427 return TRUE; 428 } 429 } 430 431 /* Otherwise, fallback to normal size information */ 432 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, 433 &FileFsSize, sizeof(FILE_FS_SIZE_INFORMATION), 434 FileFsSizeInformation); 435 NtClose(RootHandle); 436 if (!NT_SUCCESS(Status)) 437 { 438 BaseSetLastNTError(Status); 439 return FALSE; 440 } 441 442 /* Compute the size of an AU */ 443 BytesPerAllocationUnit = FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector; 444 445 /* And then return what was asked, available is free, the same! */ 446 if (lpFreeBytesAvailableToCaller != NULL) 447 { 448 lpFreeBytesAvailableToCaller->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 449 } 450 451 if (lpTotalNumberOfBytes != NULL) 452 { 453 lpTotalNumberOfBytes->QuadPart = FileFsSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit; 454 } 455 456 if (lpTotalNumberOfFreeBytes != NULL) 457 { 458 lpTotalNumberOfFreeBytes->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 459 } 460 461 return TRUE; 462 } 463 464 /* 465 * @implemented 466 */ 467 UINT 468 WINAPI 469 GetDriveTypeA(IN LPCSTR lpRootPathName) 470 { 471 PWSTR RootPathU; 472 473 if (lpRootPathName != NULL) 474 { 475 PUNICODE_STRING RootPathUStr; 476 477 RootPathUStr = Basep8BitStringToStaticUnicodeString(lpRootPathName); 478 if (RootPathUStr == NULL) 479 { 480 return DRIVE_NO_ROOT_DIR; 481 } 482 483 RootPathU = RootPathUStr->Buffer; 484 } 485 else 486 { 487 RootPathU = NULL; 488 } 489 490 return GetDriveTypeW(RootPathU); 491 } 492 493 /* 494 * @implemented 495 */ 496 UINT 497 WINAPI 498 GetDriveTypeW(IN LPCWSTR lpRootPathName) 499 { 500 BOOL RetryOpen; 501 PCWSTR RootPath; 502 NTSTATUS Status; 503 WCHAR DriveLetter; 504 HANDLE RootHandle; 505 IO_STATUS_BLOCK IoStatusBlock; 506 OBJECT_ATTRIBUTES ObjectAttributes; 507 UNICODE_STRING PathName, VolumeString; 508 FILE_FS_DEVICE_INFORMATION FileFsDevice; 509 WCHAR Buffer[MAX_PATH], VolumeName[MAX_PATH]; 510 511 /* If no path, get one */ 512 if (lpRootPathName == NULL) 513 { 514 RootPath = Buffer; 515 /* This will be current drive (<letter>:\ - drop the rest)*/ 516 if (RtlGetCurrentDirectory_U(sizeof(Buffer), Buffer) > 3 * sizeof(WCHAR)) 517 { 518 Buffer[3] = UNICODE_NULL; 519 } 520 } 521 else 522 { 523 /* Handle broken value */ 524 if (lpRootPathName == (PVOID)-1) 525 { 526 return DRIVE_UNKNOWN; 527 } 528 529 RootPath = lpRootPathName; 530 /* If provided path is 2-len, it might be a drive letter... */ 531 if (wcslen(lpRootPathName) == 2) 532 { 533 /* Check it! */ 534 DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]); 535 /* That's a drive letter! */ 536 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':') 537 { 538 /* Make it a volume */ 539 Buffer[0] = DriveLetter; 540 Buffer[1] = L':'; 541 Buffer[2] = L'\\'; 542 Buffer[3] = UNICODE_NULL; 543 RootPath = Buffer; 544 } 545 } 546 } 547 548 /* If the provided looks like a DOS device... Like <letter>:\<0> */ 549 DriveLetter = RtlUpcaseUnicodeChar(RootPath[0]); 550 /* We'll take the quick path! 551 * We'll find the device type looking at the device map (and types ;-)) 552 * associated with the current process 553 */ 554 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && RootPath[1] == L':' && 555 RootPath[2] == L'\\' && RootPath[3] == UNICODE_NULL) 556 { 557 USHORT Index; 558 PROCESS_DEVICEMAP_INFORMATION DeviceMap; 559 560 /* Query the device map */ 561 Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, 562 &DeviceMap, 563 sizeof(PROCESS_DEVICEMAP_INFORMATION), 564 NULL); 565 /* Zero output if we failed */ 566 if (!NT_SUCCESS(Status)) 567 { 568 RtlZeroMemory(&DeviceMap, sizeof(PROCESS_DEVICEMAP_INFORMATION)); 569 } 570 571 /* Get our index in the device map */ 572 Index = DriveLetter - L'A'; 573 /* Check we're in the device map (bit set) */ 574 if (((1 << Index) & DeviceMap.Query.DriveMap) != 0) 575 { 576 /* Validate device type and return it */ 577 if (DeviceMap.Query.DriveType[Index] >= DRIVE_REMOVABLE && 578 DeviceMap.Query.DriveType[Index] <= DRIVE_RAMDISK) 579 { 580 return DeviceMap.Query.DriveType[Index]; 581 } 582 /* Otherwise, return we don't know the type */ 583 else 584 { 585 return DRIVE_UNKNOWN; 586 } 587 } 588 589 /* We couldn't find ourselves, do it the slow way */ 590 } 591 592 /* No path provided, use root */ 593 if (lpRootPathName == NULL) 594 { 595 RootPath = L"\\"; 596 } 597 598 /* Convert to NT path */ 599 if (!RtlDosPathNameToNtPathName_U(RootPath, &PathName, NULL, NULL)) 600 { 601 return DRIVE_NO_ROOT_DIR; 602 } 603 604 /* If not a directory, fail, we need a volume */ 605 if (PathName.Buffer[(PathName.Length / sizeof(WCHAR)) - 1] != L'\\') 606 { 607 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 608 return DRIVE_NO_ROOT_DIR; 609 } 610 611 /* Let's probe for it, by forcing open failure! */ 612 RetryOpen = TRUE; 613 InitializeObjectAttributes(&ObjectAttributes, &PathName, 614 OBJ_CASE_INSENSITIVE, NULL, NULL); 615 Status = NtOpenFile(&RootHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, 616 &ObjectAttributes, &IoStatusBlock, 617 FILE_SHARE_READ | FILE_SHARE_WRITE, 618 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 619 /* It properly failed! */ 620 if (Status == STATUS_FILE_IS_A_DIRECTORY) 621 { 622 /* It might be a mount point, then, query for target */ 623 if (BasepGetVolumeNameFromReparsePoint(lpRootPathName, VolumeName, MAX_PATH, NULL)) 624 { 625 /* We'll reopen the target */ 626 RtlInitUnicodeString(&VolumeString, VolumeName); 627 VolumeName[1] = L'?'; 628 VolumeString.Length -= sizeof(WCHAR); 629 InitializeObjectAttributes(&ObjectAttributes, &VolumeString, 630 OBJ_CASE_INSENSITIVE, NULL, NULL); 631 } 632 } 633 else 634 { 635 /* heh. It worked? Or failed for whatever other reason? 636 * Check we have a directory if we get farther in path 637 */ 638 PathName.Length += sizeof(WCHAR); 639 if (IsThisARootDirectory(0, &PathName)) 640 { 641 /* Yes? Heh, then it's fine, keep our current handle */ 642 RetryOpen = FALSE; 643 } 644 else 645 { 646 /* Then, retry to open without forcing non directory type */ 647 PathName.Length -= sizeof(WCHAR); 648 if (NT_SUCCESS(Status)) 649 { 650 NtClose(RootHandle); 651 } 652 } 653 } 654 655 /* Now, we retry without forcing file type - should work now */ 656 if (RetryOpen) 657 { 658 Status = NtOpenFile(&RootHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, 659 &ObjectAttributes, &IoStatusBlock, 660 FILE_SHARE_READ | FILE_SHARE_WRITE, 661 FILE_SYNCHRONOUS_IO_NONALERT); 662 } 663 664 /* We don't need path any longer */ 665 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 666 if (!NT_SUCCESS(Status)) 667 { 668 return DRIVE_NO_ROOT_DIR; 669 } 670 671 /* Query the device for its type */ 672 Status = NtQueryVolumeInformationFile(RootHandle, 673 &IoStatusBlock, 674 &FileFsDevice, 675 sizeof(FILE_FS_DEVICE_INFORMATION), 676 FileFsDeviceInformation); 677 /* No longer required */ 678 NtClose(RootHandle); 679 if (!NT_SUCCESS(Status)) 680 { 681 return DRIVE_UNKNOWN; 682 } 683 684 /* Do we have a remote device? Return so! */ 685 if ((FileFsDevice.Characteristics & FILE_REMOTE_DEVICE) == FILE_REMOTE_DEVICE) 686 { 687 return DRIVE_REMOTE; 688 } 689 690 /* Check the device type */ 691 switch (FileFsDevice.DeviceType) 692 { 693 /* CDROM, easy */ 694 case FILE_DEVICE_CD_ROM: 695 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: 696 return DRIVE_CDROM; 697 698 /* Disk... */ 699 case FILE_DEVICE_DISK: 700 case FILE_DEVICE_DISK_FILE_SYSTEM: 701 /* Removable media? Floppy is one */ 702 if ((FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA) == FILE_REMOVABLE_MEDIA || 703 (FileFsDevice.Characteristics & FILE_FLOPPY_DISKETTE) == FILE_FLOPPY_DISKETTE) 704 { 705 return DRIVE_REMOVABLE; 706 } 707 else 708 { 709 return DRIVE_FIXED; 710 } 711 712 /* Easy cases */ 713 case FILE_DEVICE_NETWORK: 714 case FILE_DEVICE_NETWORK_FILE_SYSTEM: 715 return DRIVE_REMOTE; 716 717 case FILE_DEVICE_VIRTUAL_DISK: 718 return DRIVE_RAMDISK; 719 } 720 721 /* Nothing matching, just fail */ 722 return DRIVE_UNKNOWN; 723 } 724 725 /* EOF */ 726