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