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, 119 sizeof(ProcessDeviceMapInfo), 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(), ProcessDeviceMap, 561 &DeviceMap, 562 sizeof(PROCESS_DEVICEMAP_INFORMATION), 563 NULL); 564 /* Zero output if we failed */ 565 if (!NT_SUCCESS(Status)) 566 { 567 RtlZeroMemory(&DeviceMap, sizeof(PROCESS_DEVICEMAP_INFORMATION)); 568 } 569 570 /* Get our index in the device map */ 571 Index = DriveLetter - L'A'; 572 /* Check we're in the device map (bit set) */ 573 if (((1 << Index) & DeviceMap.Query.DriveMap) != 0) 574 { 575 /* Validate device type and return it */ 576 if (DeviceMap.Query.DriveType[Index] >= DRIVE_REMOVABLE && 577 DeviceMap.Query.DriveType[Index] <= DRIVE_RAMDISK) 578 { 579 return DeviceMap.Query.DriveType[Index]; 580 } 581 /* Otherwise, return we don't know the type */ 582 else 583 { 584 return DRIVE_UNKNOWN; 585 } 586 } 587 588 /* We couldn't find ourselves, do it the slow way */ 589 } 590 591 /* No path provided, use root */ 592 if (lpRootPathName == NULL) 593 { 594 RootPath = L"\\"; 595 } 596 597 /* Convert to NT path */ 598 if (!RtlDosPathNameToNtPathName_U(RootPath, &PathName, NULL, NULL)) 599 { 600 return DRIVE_NO_ROOT_DIR; 601 } 602 603 /* If not a directory, fail, we need a volume */ 604 if (PathName.Buffer[(PathName.Length / sizeof(WCHAR)) - 1] != L'\\') 605 { 606 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 607 return DRIVE_NO_ROOT_DIR; 608 } 609 610 /* Let's probe for it, by forcing open failure! */ 611 RetryOpen = TRUE; 612 InitializeObjectAttributes(&ObjectAttributes, &PathName, 613 OBJ_CASE_INSENSITIVE, NULL, NULL); 614 Status = NtOpenFile(&RootHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, 615 &ObjectAttributes, &IoStatusBlock, 616 FILE_SHARE_READ | FILE_SHARE_WRITE, 617 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 618 /* It properly failed! */ 619 if (Status == STATUS_FILE_IS_A_DIRECTORY) 620 { 621 /* It might be a mount point, then, query for target */ 622 if (BasepGetVolumeNameFromReparsePoint(lpRootPathName, VolumeName, MAX_PATH, NULL)) 623 { 624 /* We'll reopen the target */ 625 RtlInitUnicodeString(&VolumeString, VolumeName); 626 VolumeName[1] = L'?'; 627 VolumeString.Length -= sizeof(WCHAR); 628 InitializeObjectAttributes(&ObjectAttributes, &VolumeString, 629 OBJ_CASE_INSENSITIVE, NULL, NULL); 630 } 631 } 632 else 633 { 634 /* heh. It worked? Or failed for whatever other reason? 635 * Check we have a directory if we get farther in path 636 */ 637 PathName.Length += sizeof(WCHAR); 638 if (IsThisARootDirectory(0, &PathName)) 639 { 640 /* Yes? Heh, then it's fine, keep our current handle */ 641 RetryOpen = FALSE; 642 } 643 else 644 { 645 /* Then, retry to open without forcing non directory type */ 646 PathName.Length -= sizeof(WCHAR); 647 if (NT_SUCCESS(Status)) 648 { 649 NtClose(RootHandle); 650 } 651 } 652 } 653 654 /* Now, we retry without forcing file type - should work now */ 655 if (RetryOpen) 656 { 657 Status = NtOpenFile(&RootHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, 658 &ObjectAttributes, &IoStatusBlock, 659 FILE_SHARE_READ | FILE_SHARE_WRITE, 660 FILE_SYNCHRONOUS_IO_NONALERT); 661 } 662 663 /* We don't need path any longer */ 664 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 665 if (!NT_SUCCESS(Status)) 666 { 667 return DRIVE_NO_ROOT_DIR; 668 } 669 670 /* Query the device for its type */ 671 Status = NtQueryVolumeInformationFile(RootHandle, 672 &IoStatusBlock, 673 &FileFsDevice, 674 sizeof(FILE_FS_DEVICE_INFORMATION), 675 FileFsDeviceInformation); 676 /* No longer required */ 677 NtClose(RootHandle); 678 if (!NT_SUCCESS(Status)) 679 { 680 return DRIVE_UNKNOWN; 681 } 682 683 /* Do we have a remote device? Return so! */ 684 if ((FileFsDevice.Characteristics & FILE_REMOTE_DEVICE) == FILE_REMOTE_DEVICE) 685 { 686 return DRIVE_REMOTE; 687 } 688 689 /* Check the device type */ 690 switch (FileFsDevice.DeviceType) 691 { 692 /* CDROM, easy */ 693 case FILE_DEVICE_CD_ROM: 694 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: 695 return DRIVE_CDROM; 696 697 /* Disk... */ 698 case FILE_DEVICE_DISK: 699 case FILE_DEVICE_DISK_FILE_SYSTEM: 700 /* Removable media? Floppy is one */ 701 if ((FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA) == FILE_REMOVABLE_MEDIA || 702 (FileFsDevice.Characteristics & FILE_FLOPPY_DISKETTE) == FILE_FLOPPY_DISKETTE) 703 { 704 return DRIVE_REMOVABLE; 705 } 706 else 707 { 708 return DRIVE_FIXED; 709 } 710 711 /* Easy cases */ 712 case FILE_DEVICE_NETWORK: 713 case FILE_DEVICE_NETWORK_FILE_SYSTEM: 714 return DRIVE_REMOTE; 715 716 case FILE_DEVICE_VIRTUAL_DISK: 717 return DRIVE_RAMDISK; 718 } 719 720 /* Nothing matching, just fail */ 721 return DRIVE_UNKNOWN; 722 } 723 724 /* EOF */ 725