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 23 #define NDEBUG 24 #include <debug.h> 25 26 #define MAX_DOS_DRIVES 26 27 28 /* 29 * @implemented 30 */ 31 /* Synced to Wine-2008/12/28 */ 32 DWORD 33 WINAPI 34 GetLogicalDriveStringsA(IN DWORD nBufferLength, 35 IN LPSTR lpBuffer) 36 { 37 DWORD drive, count; 38 DWORD dwDriveMap; 39 LPSTR p; 40 41 dwDriveMap = GetLogicalDrives(); 42 43 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 44 { 45 if (dwDriveMap & (1<<drive)) 46 count++; 47 } 48 49 50 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 51 52 p = lpBuffer; 53 54 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 55 if (dwDriveMap & (1<<drive)) 56 { 57 *p++ = 'A' + (UCHAR)drive; 58 *p++ = ':'; 59 *p++ = '\\'; 60 *p++ = '\0'; 61 } 62 *p = '\0'; 63 64 return (count * 4); 65 } 66 67 /* 68 * @implemented 69 */ 70 /* Synced to Wine-2008/12/28 */ 71 DWORD 72 WINAPI 73 GetLogicalDriveStringsW(IN DWORD nBufferLength, 74 IN LPWSTR lpBuffer) 75 { 76 DWORD drive, count; 77 DWORD dwDriveMap; 78 LPWSTR p; 79 80 dwDriveMap = GetLogicalDrives(); 81 82 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 83 { 84 if (dwDriveMap & (1<<drive)) 85 count++; 86 } 87 88 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 89 90 p = lpBuffer; 91 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 92 if (dwDriveMap & (1<<drive)) 93 { 94 *p++ = (WCHAR)('A' + drive); 95 *p++ = (WCHAR)':'; 96 *p++ = (WCHAR)'\\'; 97 *p++ = (WCHAR)'\0'; 98 } 99 *p = (WCHAR)'\0'; 100 101 return (count * 4); 102 } 103 104 /* 105 * @implemented 106 */ 107 /* Synced to Wine-? */ 108 DWORD 109 WINAPI 110 GetLogicalDrives(VOID) 111 { 112 NTSTATUS Status; 113 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo; 114 115 /* Get the Device Map for this Process */ 116 Status = NtQueryInformationProcess(NtCurrentProcess(), 117 ProcessDeviceMap, 118 &ProcessDeviceMapInfo.Query, 119 sizeof(ProcessDeviceMapInfo.Query), 120 NULL); 121 122 /* Return the Drive Map */ 123 if (!NT_SUCCESS(Status)) 124 { 125 BaseSetLastNTError(Status); 126 return 0; 127 } 128 129 if (ProcessDeviceMapInfo.Query.DriveMap == 0) 130 { 131 SetLastError(ERROR_SUCCESS); 132 } 133 134 return ProcessDeviceMapInfo.Query.DriveMap; 135 } 136 137 /* 138 * @implemented 139 */ 140 BOOL 141 WINAPI 142 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName, 143 OUT LPDWORD lpSectorsPerCluster, 144 OUT LPDWORD lpBytesPerSector, 145 OUT LPDWORD lpNumberOfFreeClusters, 146 OUT LPDWORD lpTotalNumberOfClusters) 147 { 148 PCSTR RootPath; 149 PUNICODE_STRING RootPathU; 150 151 RootPath = lpRootPathName; 152 if (RootPath == NULL) 153 { 154 RootPath = "\\"; 155 } 156 157 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath); 158 if (RootPathU == NULL) 159 { 160 return FALSE; 161 } 162 163 return GetDiskFreeSpaceW(RootPathU->Buffer, lpSectorsPerCluster, 164 lpBytesPerSector, lpNumberOfFreeClusters, 165 lpTotalNumberOfClusters); 166 } 167 168 /* 169 * @implemented 170 */ 171 BOOL 172 WINAPI 173 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName, 174 OUT LPDWORD lpSectorsPerCluster, 175 OUT LPDWORD lpBytesPerSector, 176 OUT LPDWORD lpNumberOfFreeClusters, 177 OUT LPDWORD lpTotalNumberOfClusters) 178 { 179 BOOL Below2GB; 180 PCWSTR RootPath; 181 NTSTATUS Status; 182 HANDLE RootHandle; 183 UNICODE_STRING FileName; 184 IO_STATUS_BLOCK IoStatusBlock; 185 OBJECT_ATTRIBUTES ObjectAttributes; 186 FILE_FS_SIZE_INFORMATION FileFsSize; 187 188 /* If no path provided, get root path */ 189 RootPath = lpRootPathName; 190 if (lpRootPathName == NULL) 191 { 192 RootPath = L"\\"; 193 } 194 195 /* Convert the path to NT path */ 196 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL)) 197 { 198 SetLastError(ERROR_PATH_NOT_FOUND); 199 return FALSE; 200 } 201 202 /* Open it for disk space query! */ 203 InitializeObjectAttributes(&ObjectAttributes, &FileName, 204 OBJ_CASE_INSENSITIVE, NULL, NULL); 205 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 206 FILE_SHARE_READ | FILE_SHARE_WRITE, 207 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY); 208 if (!NT_SUCCESS(Status)) 209 { 210 BaseSetLastNTError(Status); 211 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 212 if (lpBytesPerSector != NULL) 213 { 214 *lpBytesPerSector = 0; 215 } 216 217 return FALSE; 218 } 219 220 /* We don't need the name any longer */ 221 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 222 223 /* Query disk space! */ 224 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsSize, 225 sizeof(FILE_FS_SIZE_INFORMATION), 226 FileFsSizeInformation); 227 NtClose(RootHandle); 228 if (!NT_SUCCESS(Status)) 229 { 230 BaseSetLastNTError(Status); 231 return FALSE; 232 } 233 234 /* Are we in some compatibility mode where size must be below 2GB? */ 235 Below2GB = ((NtCurrentPeb()->AppCompatFlags.LowPart & GetDiskFreeSpace2GB) == GetDiskFreeSpace2GB); 236 237 /* If we're to overflow output, make sure we return the maximum */ 238 if (FileFsSize.TotalAllocationUnits.HighPart != 0) 239 { 240 FileFsSize.TotalAllocationUnits.LowPart = -1; 241 } 242 243 if (FileFsSize.AvailableAllocationUnits.HighPart != 0) 244 { 245 FileFsSize.AvailableAllocationUnits.LowPart = -1; 246 } 247 248 /* Return what user asked for */ 249 if (lpSectorsPerCluster != NULL) 250 { 251 *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit; 252 } 253 254 if (lpBytesPerSector != NULL) 255 { 256 *lpBytesPerSector = FileFsSize.BytesPerSector; 257 } 258 259 if (lpNumberOfFreeClusters != NULL) 260 { 261 if (!Below2GB) 262 { 263 *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; 264 } 265 /* If we have to remain below 2GB... */ 266 else 267 { 268 DWORD FreeClusters; 269 270 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ 271 FreeClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); 272 /* If that's higher than what was queried, then return the queried value, it's OK! */ 273 if (FreeClusters > FileFsSize.AvailableAllocationUnits.LowPart) 274 { 275 FreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; 276 } 277 278 *lpNumberOfFreeClusters = FreeClusters; 279 } 280 } 281 282 if (lpTotalNumberOfClusters != NULL) 283 { 284 if (!Below2GB) 285 { 286 *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.LowPart; 287 } 288 /* If we have to remain below 2GB... */ 289 else 290 { 291 DWORD TotalClusters; 292 293 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ 294 TotalClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); 295 /* If that's higher than what was queried, then return the queried value, it's OK! */ 296 if (TotalClusters > FileFsSize.TotalAllocationUnits.LowPart) 297 { 298 TotalClusters = FileFsSize.TotalAllocationUnits.LowPart; 299 } 300 301 *lpTotalNumberOfClusters = TotalClusters; 302 } 303 } 304 305 return TRUE; 306 } 307 308 /* 309 * @implemented 310 */ 311 BOOL 312 WINAPI 313 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL, 314 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 315 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 316 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 317 { 318 PCSTR RootPath; 319 PUNICODE_STRING RootPathU; 320 321 RootPath = lpDirectoryName; 322 if (RootPath == NULL) 323 { 324 RootPath = "\\"; 325 } 326 327 RootPathU = Basep8BitStringToStaticUnicodeString(RootPath); 328 if (RootPathU == NULL) 329 { 330 return FALSE; 331 } 332 333 return GetDiskFreeSpaceExW(RootPathU->Buffer, lpFreeBytesAvailableToCaller, 334 lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); 335 } 336 337 /* 338 * @implemented 339 */ 340 BOOL 341 WINAPI 342 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL, 343 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 344 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 345 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 346 { 347 PCWSTR RootPath; 348 NTSTATUS Status; 349 HANDLE RootHandle; 350 UNICODE_STRING FileName; 351 DWORD BytesPerAllocationUnit; 352 IO_STATUS_BLOCK IoStatusBlock; 353 OBJECT_ATTRIBUTES ObjectAttributes; 354 FILE_FS_SIZE_INFORMATION FileFsSize; 355 356 /* If no path provided, get root path */ 357 RootPath = lpDirectoryName; 358 if (lpDirectoryName == NULL) 359 { 360 RootPath = L"\\"; 361 } 362 363 /* Convert the path to NT path */ 364 if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL)) 365 { 366 SetLastError(ERROR_PATH_NOT_FOUND); 367 return FALSE; 368 } 369 370 /* Open it for disk space query! */ 371 InitializeObjectAttributes(&ObjectAttributes, &FileName, 372 OBJ_CASE_INSENSITIVE, NULL, NULL); 373 Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 374 FILE_SHARE_READ | FILE_SHARE_WRITE, 375 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY); 376 if (!NT_SUCCESS(Status)) 377 { 378 BaseSetLastNTError(Status); 379 /* If error conversion lead to file not found, override to use path not found 380 * which is more accurate 381 */ 382 if (GetLastError() == ERROR_FILE_NOT_FOUND) 383 { 384 SetLastError(ERROR_PATH_NOT_FOUND); 385 } 386 387 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 388 389 return FALSE; 390 } 391 392 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); 393 394 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */ 395 if (lpTotalNumberOfFreeBytes != NULL) 396 { 397 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize; 398 399 /* Issue the full fs size request */ 400 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsFullSize, 401 sizeof(FILE_FS_FULL_SIZE_INFORMATION), 402 FileFsFullSizeInformation); 403 /* If it succeed, complete out buffers */ 404 if (NT_SUCCESS(Status)) 405 { 406 /* We can close here, we'll return */ 407 NtClose(RootHandle); 408 409 /* Compute the size of an AU */ 410 BytesPerAllocationUnit = FileFsFullSize.SectorsPerAllocationUnit * FileFsFullSize.BytesPerSector; 411 412 /* And then return what was asked */ 413 if (lpFreeBytesAvailableToCaller != NULL) 414 { 415 lpFreeBytesAvailableToCaller->QuadPart = FileFsFullSize.CallerAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 416 } 417 418 if (lpTotalNumberOfBytes != NULL) 419 { 420 lpTotalNumberOfBytes->QuadPart = FileFsFullSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit; 421 } 422 423 /* No need to check for nullness ;-) */ 424 lpTotalNumberOfFreeBytes->QuadPart = FileFsFullSize.ActualAvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 425 426 return TRUE; 427 } 428 } 429 430 /* Otherwise, fallback to normal size information */ 431 Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, 432 &FileFsSize, sizeof(FILE_FS_SIZE_INFORMATION), 433 FileFsSizeInformation); 434 NtClose(RootHandle); 435 if (!NT_SUCCESS(Status)) 436 { 437 BaseSetLastNTError(Status); 438 return FALSE; 439 } 440 441 /* Compute the size of an AU */ 442 BytesPerAllocationUnit = FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector; 443 444 /* And then return what was asked, available is free, the same! */ 445 if (lpFreeBytesAvailableToCaller != NULL) 446 { 447 lpFreeBytesAvailableToCaller->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 448 } 449 450 if (lpTotalNumberOfBytes != NULL) 451 { 452 lpTotalNumberOfBytes->QuadPart = FileFsSize.TotalAllocationUnits.QuadPart * BytesPerAllocationUnit; 453 } 454 455 if (lpTotalNumberOfFreeBytes != NULL) 456 { 457 lpTotalNumberOfFreeBytes->QuadPart = FileFsSize.AvailableAllocationUnits.QuadPart * BytesPerAllocationUnit; 458 } 459 460 return TRUE; 461 } 462 463 /* 464 * @implemented 465 */ 466 UINT 467 WINAPI 468 GetDriveTypeA(IN LPCSTR lpRootPathName) 469 { 470 PWSTR RootPathU; 471 472 if (lpRootPathName != NULL) 473 { 474 PUNICODE_STRING RootPathUStr; 475 476 RootPathUStr = Basep8BitStringToStaticUnicodeString(lpRootPathName); 477 if (RootPathUStr == NULL) 478 { 479 return DRIVE_NO_ROOT_DIR; 480 } 481 482 RootPathU = RootPathUStr->Buffer; 483 } 484 else 485 { 486 RootPathU = NULL; 487 } 488 489 return GetDriveTypeW(RootPathU); 490 } 491 492 /* 493 * @implemented 494 */ 495 UINT 496 WINAPI 497 GetDriveTypeW(IN LPCWSTR lpRootPathName) 498 { 499 BOOL RetryOpen; 500 PCWSTR RootPath; 501 NTSTATUS Status; 502 WCHAR DriveLetter; 503 HANDLE RootHandle; 504 IO_STATUS_BLOCK IoStatusBlock; 505 OBJECT_ATTRIBUTES ObjectAttributes; 506 UNICODE_STRING PathName, VolumeString; 507 FILE_FS_DEVICE_INFORMATION FileFsDevice; 508 WCHAR Buffer[MAX_PATH], VolumeName[MAX_PATH]; 509 510 /* If no path, get one */ 511 if (lpRootPathName == NULL) 512 { 513 RootPath = Buffer; 514 /* This will be current drive (<letter>:\ - drop the rest)*/ 515 if (RtlGetCurrentDirectory_U(sizeof(Buffer), Buffer) > 3 * sizeof(WCHAR)) 516 { 517 Buffer[3] = UNICODE_NULL; 518 } 519 } 520 else 521 { 522 /* Handle broken value */ 523 if (lpRootPathName == (PVOID)-1) 524 { 525 return DRIVE_UNKNOWN; 526 } 527 528 RootPath = lpRootPathName; 529 /* If provided path is 2-len, it might be a drive letter... */ 530 if (wcslen(lpRootPathName) == 2) 531 { 532 /* Check it! */ 533 DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]); 534 /* That's a drive letter! */ 535 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':') 536 { 537 /* Make it a volume */ 538 Buffer[0] = DriveLetter; 539 Buffer[1] = L':'; 540 Buffer[2] = L'\\'; 541 Buffer[3] = UNICODE_NULL; 542 RootPath = Buffer; 543 } 544 } 545 } 546 547 /* If the provided looks like a DOS device... Like <letter>:\<0> */ 548 DriveLetter = RtlUpcaseUnicodeChar(RootPath[0]); 549 /* We'll take the quick path! 550 * We'll find the device type looking at the device map (and types ;-)) 551 * associated with the current process 552 */ 553 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && RootPath[1] == L':' && 554 RootPath[2] == L'\\' && RootPath[3] == UNICODE_NULL) 555 { 556 USHORT Index; 557 PROCESS_DEVICEMAP_INFORMATION DeviceMap; 558 559 /* Query the device map */ 560 Status = NtQueryInformationProcess(NtCurrentProcess(), 561 ProcessDeviceMap, 562 &DeviceMap.Query, 563 sizeof(DeviceMap.Query), 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