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 HANDLE 31 WINAPI 32 InternalOpenDirW(IN LPCWSTR DirName, 33 IN BOOLEAN Write) 34 { 35 UNICODE_STRING NtPathU; 36 OBJECT_ATTRIBUTES ObjectAttributes; 37 NTSTATUS errCode; 38 IO_STATUS_BLOCK IoStatusBlock; 39 HANDLE hFile; 40 41 if (!RtlDosPathNameToNtPathName_U(DirName, &NtPathU, NULL, NULL)) 42 { 43 WARN("Invalid path\n"); 44 SetLastError(ERROR_BAD_PATHNAME); 45 return INVALID_HANDLE_VALUE; 46 } 47 48 InitializeObjectAttributes(&ObjectAttributes, 49 &NtPathU, 50 OBJ_CASE_INSENSITIVE, 51 NULL, 52 NULL); 53 54 errCode = NtCreateFile(&hFile, 55 Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ, 56 &ObjectAttributes, 57 &IoStatusBlock, 58 NULL, 59 0, 60 FILE_SHARE_READ | FILE_SHARE_WRITE, 61 FILE_OPEN, 62 0, 63 NULL, 64 0); 65 66 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer); 67 68 if (!NT_SUCCESS(errCode)) 69 { 70 BaseSetLastNTError(errCode); 71 return INVALID_HANDLE_VALUE; 72 } 73 74 return hFile; 75 } 76 77 /* 78 * @implemented 79 */ 80 /* Synced to Wine-2008/12/28 */ 81 DWORD 82 WINAPI 83 GetLogicalDriveStringsA(IN DWORD nBufferLength, 84 IN LPSTR lpBuffer) 85 { 86 DWORD drive, count; 87 DWORD dwDriveMap; 88 LPSTR p; 89 90 dwDriveMap = GetLogicalDrives(); 91 92 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 93 { 94 if (dwDriveMap & (1<<drive)) 95 count++; 96 } 97 98 99 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 100 101 p = lpBuffer; 102 103 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 104 if (dwDriveMap & (1<<drive)) 105 { 106 *p++ = 'A' + (UCHAR)drive; 107 *p++ = ':'; 108 *p++ = '\\'; 109 *p++ = '\0'; 110 } 111 *p = '\0'; 112 113 return (count * 4); 114 } 115 116 /* 117 * @implemented 118 */ 119 /* Synced to Wine-2008/12/28 */ 120 DWORD 121 WINAPI 122 GetLogicalDriveStringsW(IN DWORD nBufferLength, 123 IN LPWSTR lpBuffer) 124 { 125 DWORD drive, count; 126 DWORD dwDriveMap; 127 LPWSTR p; 128 129 dwDriveMap = GetLogicalDrives(); 130 131 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) 132 { 133 if (dwDriveMap & (1<<drive)) 134 count++; 135 } 136 137 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1); 138 139 p = lpBuffer; 140 for (drive = 0; drive < MAX_DOS_DRIVES; drive++) 141 if (dwDriveMap & (1<<drive)) 142 { 143 *p++ = (WCHAR)('A' + drive); 144 *p++ = (WCHAR)':'; 145 *p++ = (WCHAR)'\\'; 146 *p++ = (WCHAR)'\0'; 147 } 148 *p = (WCHAR)'\0'; 149 150 return (count * 4); 151 } 152 153 /* 154 * @implemented 155 */ 156 /* Synced to Wine-? */ 157 DWORD 158 WINAPI 159 GetLogicalDrives(VOID) 160 { 161 NTSTATUS Status; 162 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo; 163 164 /* Get the Device Map for this Process */ 165 Status = NtQueryInformationProcess(NtCurrentProcess(), 166 ProcessDeviceMap, 167 &ProcessDeviceMapInfo, 168 sizeof(ProcessDeviceMapInfo), 169 NULL); 170 171 /* Return the Drive Map */ 172 if (!NT_SUCCESS(Status)) 173 { 174 BaseSetLastNTError(Status); 175 return 0; 176 } 177 178 return ProcessDeviceMapInfo.Query.DriveMap; 179 } 180 181 /* 182 * @implemented 183 */ 184 BOOL 185 WINAPI 186 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName, 187 OUT LPDWORD lpSectorsPerCluster, 188 OUT LPDWORD lpBytesPerSector, 189 OUT LPDWORD lpNumberOfFreeClusters, 190 OUT LPDWORD lpTotalNumberOfClusters) 191 { 192 PWCHAR RootPathNameW=NULL; 193 194 if (lpRootPathName) 195 { 196 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE))) 197 return FALSE; 198 } 199 200 return GetDiskFreeSpaceW (RootPathNameW, 201 lpSectorsPerCluster, 202 lpBytesPerSector, 203 lpNumberOfFreeClusters, 204 lpTotalNumberOfClusters); 205 } 206 207 /* 208 * @implemented 209 */ 210 BOOL 211 WINAPI 212 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName, 213 OUT LPDWORD lpSectorsPerCluster, 214 OUT LPDWORD lpBytesPerSector, 215 OUT LPDWORD lpNumberOfFreeClusters, 216 OUT LPDWORD lpTotalNumberOfClusters) 217 { 218 FILE_FS_SIZE_INFORMATION FileFsSize; 219 IO_STATUS_BLOCK IoStatusBlock; 220 WCHAR RootPathName[MAX_PATH]; 221 HANDLE hFile; 222 NTSTATUS errCode; 223 224 if (lpRootPathName) 225 { 226 wcsncpy (RootPathName, lpRootPathName, 3); 227 } 228 else 229 { 230 GetCurrentDirectoryW (MAX_PATH, RootPathName); 231 } 232 RootPathName[3] = 0; 233 234 hFile = InternalOpenDirW(RootPathName, FALSE); 235 if (INVALID_HANDLE_VALUE == hFile) 236 { 237 SetLastError(ERROR_PATH_NOT_FOUND); 238 return FALSE; 239 } 240 241 errCode = NtQueryVolumeInformationFile(hFile, 242 &IoStatusBlock, 243 &FileFsSize, 244 sizeof(FILE_FS_SIZE_INFORMATION), 245 FileFsSizeInformation); 246 if (!NT_SUCCESS(errCode)) 247 { 248 CloseHandle(hFile); 249 BaseSetLastNTError (errCode); 250 return FALSE; 251 } 252 253 if (lpSectorsPerCluster) 254 *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit; 255 if (lpBytesPerSector) 256 *lpBytesPerSector = FileFsSize.BytesPerSector; 257 if (lpNumberOfFreeClusters) 258 *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.u.LowPart; 259 if (lpTotalNumberOfClusters) 260 *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.u.LowPart; 261 CloseHandle(hFile); 262 263 return TRUE; 264 } 265 266 /* 267 * @implemented 268 */ 269 BOOL 270 WINAPI 271 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL, 272 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 273 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 274 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 275 { 276 PWCHAR DirectoryNameW=NULL; 277 278 if (lpDirectoryName) 279 { 280 if (!(DirectoryNameW = FilenameA2W(lpDirectoryName, FALSE))) 281 return FALSE; 282 } 283 284 return GetDiskFreeSpaceExW (DirectoryNameW , 285 lpFreeBytesAvailableToCaller, 286 lpTotalNumberOfBytes, 287 lpTotalNumberOfFreeBytes); 288 } 289 290 /* 291 * @implemented 292 */ 293 BOOL 294 WINAPI 295 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL, 296 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, 297 OUT PULARGE_INTEGER lpTotalNumberOfBytes, 298 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) 299 { 300 union 301 { 302 FILE_FS_SIZE_INFORMATION FsSize; 303 FILE_FS_FULL_SIZE_INFORMATION FsFullSize; 304 } FsInfo; 305 IO_STATUS_BLOCK IoStatusBlock; 306 ULARGE_INTEGER BytesPerCluster; 307 HANDLE hFile; 308 NTSTATUS Status; 309 310 if (lpDirectoryName == NULL) 311 lpDirectoryName = L"\\"; 312 313 hFile = InternalOpenDirW(lpDirectoryName, FALSE); 314 if (INVALID_HANDLE_VALUE == hFile) 315 { 316 return FALSE; 317 } 318 319 if (lpFreeBytesAvailableToCaller != NULL || lpTotalNumberOfBytes != NULL) 320 { 321 /* To get the free space available to the user associated with the 322 current thread, try FileFsFullSizeInformation. If this is not 323 supported by the file system, fall back to FileFsSize */ 324 325 Status = NtQueryVolumeInformationFile(hFile, 326 &IoStatusBlock, 327 &FsInfo.FsFullSize, 328 sizeof(FsInfo.FsFullSize), 329 FileFsFullSizeInformation); 330 331 if (NT_SUCCESS(Status)) 332 { 333 /* Close the handle before returning data 334 to avoid a handle leak in case of a fault! */ 335 CloseHandle(hFile); 336 337 BytesPerCluster.QuadPart = 338 FsInfo.FsFullSize.BytesPerSector * FsInfo.FsFullSize.SectorsPerAllocationUnit; 339 340 if (lpFreeBytesAvailableToCaller != NULL) 341 { 342 lpFreeBytesAvailableToCaller->QuadPart = 343 BytesPerCluster.QuadPart * FsInfo.FsFullSize.CallerAvailableAllocationUnits.QuadPart; 344 } 345 346 if (lpTotalNumberOfBytes != NULL) 347 { 348 lpTotalNumberOfBytes->QuadPart = 349 BytesPerCluster.QuadPart * FsInfo.FsFullSize.TotalAllocationUnits.QuadPart; 350 } 351 352 if (lpTotalNumberOfFreeBytes != NULL) 353 { 354 lpTotalNumberOfFreeBytes->QuadPart = 355 BytesPerCluster.QuadPart * FsInfo.FsFullSize.ActualAvailableAllocationUnits.QuadPart; 356 } 357 358 return TRUE; 359 } 360 } 361 362 Status = NtQueryVolumeInformationFile(hFile, 363 &IoStatusBlock, 364 &FsInfo.FsSize, 365 sizeof(FsInfo.FsSize), 366 FileFsSizeInformation); 367 368 /* Close the handle before returning data 369 to avoid a handle leak in case of a fault! */ 370 CloseHandle(hFile); 371 372 if (!NT_SUCCESS(Status)) 373 { 374 BaseSetLastNTError (Status); 375 return FALSE; 376 } 377 378 BytesPerCluster.QuadPart = 379 FsInfo.FsSize.BytesPerSector * FsInfo.FsSize.SectorsPerAllocationUnit; 380 381 if (lpFreeBytesAvailableToCaller) 382 { 383 lpFreeBytesAvailableToCaller->QuadPart = 384 BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart; 385 } 386 387 if (lpTotalNumberOfBytes) 388 { 389 lpTotalNumberOfBytes->QuadPart = 390 BytesPerCluster.QuadPart * FsInfo.FsSize.TotalAllocationUnits.QuadPart; 391 } 392 393 if (lpTotalNumberOfFreeBytes) 394 { 395 lpTotalNumberOfFreeBytes->QuadPart = 396 BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart; 397 } 398 399 return TRUE; 400 } 401 402 /* 403 * @implemented 404 */ 405 UINT 406 WINAPI 407 GetDriveTypeA(IN LPCSTR lpRootPathName) 408 { 409 PWCHAR RootPathNameW; 410 411 if (!lpRootPathName) 412 return GetDriveTypeW(NULL); 413 414 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE))) 415 return DRIVE_UNKNOWN; 416 417 return GetDriveTypeW(RootPathNameW); 418 } 419 420 /* 421 * @implemented 422 */ 423 UINT 424 WINAPI 425 GetDriveTypeW(IN LPCWSTR lpRootPathName) 426 { 427 FILE_FS_DEVICE_INFORMATION FileFsDevice; 428 OBJECT_ATTRIBUTES ObjectAttributes; 429 IO_STATUS_BLOCK IoStatusBlock; 430 UNICODE_STRING PathName; 431 HANDLE FileHandle; 432 NTSTATUS Status; 433 PWSTR CurrentDir = NULL; 434 PCWSTR lpRootPath; 435 436 if (!lpRootPathName) 437 { 438 /* If NULL is passed, use current directory path */ 439 DWORD BufferSize = GetCurrentDirectoryW(0, NULL); 440 CurrentDir = HeapAlloc(GetProcessHeap(), 0, BufferSize * sizeof(WCHAR)); 441 if (!CurrentDir) 442 return DRIVE_UNKNOWN; 443 if (!GetCurrentDirectoryW(BufferSize, CurrentDir)) 444 { 445 HeapFree(GetProcessHeap(), 0, CurrentDir); 446 return DRIVE_UNKNOWN; 447 } 448 449 if (wcslen(CurrentDir) > 3) 450 CurrentDir[3] = 0; 451 452 lpRootPath = CurrentDir; 453 } 454 else 455 { 456 size_t Length = wcslen(lpRootPathName); 457 458 TRACE("lpRootPathName: %S\n", lpRootPathName); 459 460 lpRootPath = lpRootPathName; 461 if (Length == 2) 462 { 463 WCHAR DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]); 464 465 if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':') 466 { 467 Length = (Length + 2) * sizeof(WCHAR); 468 469 CurrentDir = HeapAlloc(GetProcessHeap(), 0, Length); 470 if (!CurrentDir) 471 return DRIVE_UNKNOWN; 472 473 StringCbPrintfW(CurrentDir, Length, L"%s\\", lpRootPathName); 474 475 lpRootPath = CurrentDir; 476 } 477 } 478 } 479 480 TRACE("lpRootPath: %S\n", lpRootPath); 481 482 if (!RtlDosPathNameToNtPathName_U(lpRootPath, &PathName, NULL, NULL)) 483 { 484 if (CurrentDir != NULL) 485 HeapFree(GetProcessHeap(), 0, CurrentDir); 486 487 return DRIVE_NO_ROOT_DIR; 488 } 489 490 TRACE("PathName: %S\n", PathName.Buffer); 491 492 if (CurrentDir != NULL) 493 HeapFree(GetProcessHeap(), 0, CurrentDir); 494 495 if (PathName.Buffer[(PathName.Length >> 1) - 1] != L'\\') 496 { 497 return DRIVE_NO_ROOT_DIR; 498 } 499 500 InitializeObjectAttributes(&ObjectAttributes, 501 &PathName, 502 OBJ_CASE_INSENSITIVE, 503 NULL, 504 NULL); 505 506 Status = NtOpenFile(&FileHandle, 507 FILE_READ_ATTRIBUTES | SYNCHRONIZE, 508 &ObjectAttributes, 509 &IoStatusBlock, 510 FILE_SHARE_READ | FILE_SHARE_WRITE, 511 FILE_SYNCHRONOUS_IO_NONALERT); 512 513 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 514 if (!NT_SUCCESS(Status)) 515 return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */ 516 517 Status = NtQueryVolumeInformationFile(FileHandle, 518 &IoStatusBlock, 519 &FileFsDevice, 520 sizeof(FILE_FS_DEVICE_INFORMATION), 521 FileFsDeviceInformation); 522 NtClose(FileHandle); 523 if (!NT_SUCCESS(Status)) 524 { 525 return 0; 526 } 527 528 switch (FileFsDevice.DeviceType) 529 { 530 case FILE_DEVICE_CD_ROM: 531 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: 532 return DRIVE_CDROM; 533 case FILE_DEVICE_VIRTUAL_DISK: 534 return DRIVE_RAMDISK; 535 case FILE_DEVICE_NETWORK_FILE_SYSTEM: 536 return DRIVE_REMOTE; 537 case FILE_DEVICE_DISK: 538 case FILE_DEVICE_DISK_FILE_SYSTEM: 539 if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE) 540 return DRIVE_REMOTE; 541 if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA) 542 return DRIVE_REMOVABLE; 543 return DRIVE_FIXED; 544 } 545 546 ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice.DeviceType); 547 548 return DRIVE_UNKNOWN; 549 } 550 551 /* EOF */ 552