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