1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/dir.c 5 * PURPOSE: Directory functions 6 * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <k32.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* Short File Name length in chars (8.3) */ 16 #define SFN_LENGTH 12 17 18 /* Match a volume name like: 19 * \\?\Volume{GUID} 20 */ 21 #define IS_VOLUME_NAME(s, l) \ 22 ((l == 96 || (l == 98 && s[48] == '\\')) && \ 23 s[0] == '\\'&& (s[1] == '?' || s[1] == '\\') && \ 24 s[2] == '?' && s[3] == '\\' && s[4] == 'V' && \ 25 s[5] == 'o' && s[6] == 'l' && s[7] == 'u' && \ 26 s[8] == 'm' && s[9] == 'e' && s[10] == '{' && \ 27 s[19] == '-' && s[24] == '-' && s[29] == '-' && \ 28 s[34] == '-' && s[47] == '}') 29 30 /* FUNCTIONS *****************************************************************/ 31 32 /* 33 * @implemented 34 */ 35 BOOL 36 WINAPI 37 CreateDirectoryA(IN LPCSTR lpPathName, 38 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) 39 { 40 PUNICODE_STRING PathNameW; 41 42 PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName); 43 if (!PathNameW) 44 { 45 return FALSE; 46 } 47 48 return CreateDirectoryW(PathNameW->Buffer, 49 lpSecurityAttributes); 50 } 51 52 /* 53 * @implemented 54 */ 55 BOOL 56 WINAPI 57 CreateDirectoryExA(IN LPCSTR lpTemplateDirectory, 58 IN LPCSTR lpNewDirectory, 59 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) 60 { 61 PUNICODE_STRING TemplateDirectoryW; 62 UNICODE_STRING NewDirectoryW; 63 BOOL ret; 64 65 TemplateDirectoryW = Basep8BitStringToStaticUnicodeString(lpTemplateDirectory); 66 if (!TemplateDirectoryW) 67 { 68 return FALSE; 69 } 70 71 if (!Basep8BitStringToDynamicUnicodeString(&NewDirectoryW, lpNewDirectory)) 72 { 73 return FALSE; 74 } 75 76 ret = CreateDirectoryExW(TemplateDirectoryW->Buffer, 77 NewDirectoryW.Buffer, 78 lpSecurityAttributes); 79 80 RtlFreeUnicodeString(&NewDirectoryW); 81 82 return ret; 83 } 84 85 /* 86 * @implemented 87 */ 88 BOOL 89 WINAPI 90 CreateDirectoryW(IN LPCWSTR lpPathName, 91 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) 92 { 93 DWORD Length; 94 NTSTATUS Status; 95 HANDLE DirectoryHandle; 96 UNICODE_STRING NtPathU; 97 PWSTR PathUBuffer, FilePart; 98 IO_STATUS_BLOCK IoStatusBlock; 99 RTL_RELATIVE_NAME_U RelativeName; 100 OBJECT_ATTRIBUTES ObjectAttributes; 101 102 /* Get relative name */ 103 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName, &NtPathU, NULL, &RelativeName)) 104 { 105 SetLastError(ERROR_PATH_NOT_FOUND); 106 return FALSE; 107 } 108 109 /* Check if path length is < MAX_PATH (with space for file name). 110 * If not, prefix is required. 111 */ 112 if (NtPathU.Length > (MAX_PATH - SFN_LENGTH) * sizeof(WCHAR) && lpPathName[0] != L'\\' && 113 lpPathName[1] != L'\\' && lpPathName[2] != L'?' && lpPathName[3] != L'\\') 114 { 115 /* Get file name position and full path length */ 116 Length = GetFullPathNameW(lpPathName, 0, NULL, &FilePart); 117 if (Length == 0) 118 { 119 RtlReleaseRelativeName(&RelativeName); 120 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer); 121 SetLastError(ERROR_FILENAME_EXCED_RANGE); 122 return FALSE; 123 } 124 125 /* Keep place for 8.3 file name */ 126 Length += SFN_LENGTH; 127 /* No prefix, so, must be smaller than MAX_PATH */ 128 if (Length > MAX_PATH) 129 { 130 RtlReleaseRelativeName(&RelativeName); 131 RtlFreeHeap(GetProcessHeap(), 0, NtPathU.Buffer); 132 SetLastError(ERROR_FILENAME_EXCED_RANGE); 133 return FALSE; 134 } 135 } 136 137 /* Save buffer to allow later freeing */ 138 PathUBuffer = NtPathU.Buffer; 139 140 /* If we have relative name (and root dir), use them instead */ 141 if (RelativeName.RelativeName.Length != 0) 142 { 143 NtPathU.Length = RelativeName.RelativeName.Length; 144 NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; 145 NtPathU.Buffer = RelativeName.RelativeName.Buffer; 146 } 147 else 148 { 149 RelativeName.ContainingDirectory = NULL; 150 } 151 152 InitializeObjectAttributes(&ObjectAttributes, 153 &NtPathU, 154 OBJ_CASE_INSENSITIVE, 155 RelativeName.ContainingDirectory, 156 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL)); 157 158 Status = NtCreateFile(&DirectoryHandle, 159 FILE_LIST_DIRECTORY | SYNCHRONIZE, 160 &ObjectAttributes, 161 &IoStatusBlock, 162 NULL, 163 FILE_ATTRIBUTE_NORMAL, 164 FILE_SHARE_READ | FILE_SHARE_WRITE, 165 FILE_CREATE, 166 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, 167 NULL, 168 0); 169 170 RtlReleaseRelativeName(&RelativeName); 171 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 172 173 if (NT_SUCCESS(Status)) 174 { 175 NtClose(DirectoryHandle); 176 return TRUE; 177 } 178 179 if (RtlIsDosDeviceName_U(lpPathName)) 180 { 181 Status = STATUS_NOT_A_DIRECTORY; 182 } 183 184 BaseSetLastNTError(Status); 185 return FALSE; 186 } 187 188 /* 189 * @implemented 190 */ 191 BOOL 192 WINAPI 193 CreateDirectoryExW(IN LPCWSTR lpTemplateDirectory, 194 IN LPCWSTR lpNewDirectory, 195 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes) 196 { 197 DWORD Length; 198 NTSTATUS Status; 199 PVOID EaBuffer = NULL; 200 BOOL ReparsePoint = FALSE; 201 IO_STATUS_BLOCK IoStatusBlock; 202 FILE_EA_INFORMATION FileEaInfo; 203 ULONG EaLength = 0, StreamSize; 204 OBJECT_ATTRIBUTES ObjectAttributes; 205 FILE_BASIC_INFORMATION FileBasicInfo; 206 PREPARSE_DATA_BUFFER ReparseDataBuffer; 207 HANDLE TemplateHandle, DirectoryHandle; 208 PFILE_STREAM_INFORMATION FileStreamInfo; 209 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo; 210 UNICODE_STRING NtPathU, NtTemplatePathU, NewDirectory; 211 RTL_RELATIVE_NAME_U RelativeName, TemplateRelativeName; 212 PWSTR TemplateBuffer, PathUBuffer, FilePart, SubstituteName; 213 214 /* Get relative name of the template */ 215 if (!RtlDosPathNameToRelativeNtPathName_U(lpTemplateDirectory, &NtTemplatePathU, NULL, &TemplateRelativeName)) 216 { 217 SetLastError(ERROR_PATH_NOT_FOUND); 218 return FALSE; 219 } 220 221 /* Save buffer for further freeing */ 222 TemplateBuffer = NtTemplatePathU.Buffer; 223 224 /* If we have relative name (and root dir), use them instead */ 225 if (TemplateRelativeName.RelativeName.Length != 0) 226 { 227 NtTemplatePathU.Length = TemplateRelativeName.RelativeName.Length; 228 NtTemplatePathU.MaximumLength = TemplateRelativeName.RelativeName.MaximumLength; 229 NtTemplatePathU.Buffer = TemplateRelativeName.RelativeName.Buffer; 230 } 231 else 232 { 233 TemplateRelativeName.ContainingDirectory = NULL; 234 } 235 236 InitializeObjectAttributes(&ObjectAttributes, 237 &NtTemplatePathU, 238 OBJ_CASE_INSENSITIVE, 239 NULL, 240 NULL); 241 242 /* Open template directory */ 243 Status = NtOpenFile(&TemplateHandle, 244 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA, 245 &ObjectAttributes, 246 &IoStatusBlock, 247 FILE_SHARE_READ | FILE_SHARE_WRITE, 248 FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT); 249 if (!NT_SUCCESS(Status)) 250 { 251 if (Status != STATUS_INVALID_PARAMETER) 252 { 253 RtlReleaseRelativeName(&TemplateRelativeName); 254 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 255 BaseSetLastNTError(Status); 256 return FALSE; 257 } 258 259 OpenWithoutReparseSupport: 260 /* Opening failed due to lacking reparse points support in the FSD, try without */ 261 Status = NtOpenFile(&TemplateHandle, 262 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA, 263 &ObjectAttributes, 264 &IoStatusBlock, 265 FILE_SHARE_READ | FILE_SHARE_WRITE, 266 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); 267 268 if (!NT_SUCCESS(Status)) 269 { 270 RtlReleaseRelativeName(&TemplateRelativeName); 271 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 272 BaseSetLastNTError(Status); 273 return FALSE; 274 } 275 276 /* Request file attributes */ 277 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; 278 Status = NtQueryInformationFile(TemplateHandle, 279 &IoStatusBlock, 280 &FileBasicInfo, 281 sizeof(FileBasicInfo), 282 FileBasicInformation); 283 if (!NT_SUCCESS(Status)) 284 { 285 RtlReleaseRelativeName(&TemplateRelativeName); 286 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 287 CloseHandle(TemplateHandle); 288 BaseSetLastNTError(Status); 289 return FALSE; 290 291 } 292 } 293 else 294 { 295 /* Request file attributes */ 296 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; 297 Status = NtQueryInformationFile(TemplateHandle, 298 &IoStatusBlock, 299 &FileBasicInfo, 300 sizeof(FileBasicInfo), 301 FileBasicInformation); 302 if (!NT_SUCCESS(Status)) 303 { 304 RtlReleaseRelativeName(&TemplateRelativeName); 305 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 306 CloseHandle(TemplateHandle); 307 BaseSetLastNTError(Status); 308 return FALSE; 309 310 } 311 312 /* If it is a reparse point, then get information about it */ 313 if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 314 { 315 Status = NtQueryInformationFile(TemplateHandle, 316 &IoStatusBlock, 317 &FileTagInfo, 318 sizeof(FileTagInfo), 319 FileAttributeTagInformation); 320 if (!NT_SUCCESS(Status)) 321 { 322 RtlReleaseRelativeName(&TemplateRelativeName); 323 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 324 CloseHandle(TemplateHandle); 325 BaseSetLastNTError(Status); 326 return FALSE; 327 } 328 329 /* Only mount points are supported, retry without if anything different */ 330 if (FileTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 331 { 332 CloseHandle(TemplateHandle); 333 goto OpenWithoutReparseSupport; 334 } 335 336 /* Mark we are playing with a reparse point */ 337 ReparsePoint = TRUE; 338 } 339 } 340 341 /* Get relative name of the directory */ 342 if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory, &NtPathU, NULL, &RelativeName)) 343 { 344 RtlReleaseRelativeName(&TemplateRelativeName); 345 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 346 NtClose(TemplateHandle); 347 SetLastError(ERROR_PATH_NOT_FOUND); 348 return FALSE; 349 } 350 351 /* Save its buffer for further freeing */ 352 PathUBuffer = NtPathU.Buffer; 353 354 /* Template & directory can't be the same */ 355 if (RtlEqualUnicodeString(&NtPathU, 356 &NtTemplatePathU, 357 TRUE)) 358 { 359 RtlReleaseRelativeName(&RelativeName); 360 RtlReleaseRelativeName(&TemplateRelativeName); 361 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 362 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 363 NtClose(TemplateHandle); 364 SetLastError(ERROR_INVALID_NAME); 365 return FALSE; 366 } 367 368 RtlReleaseRelativeName(&TemplateRelativeName); 369 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer); 370 371 /* Check if path length is < MAX_PATH (with space for file name). 372 * If not, prefix is required. 373 */ 374 if (NtPathU.Length > (MAX_PATH - SFN_LENGTH) * sizeof(WCHAR) && lpNewDirectory[0] != L'\\' && 375 lpNewDirectory[1] != L'\\' && lpNewDirectory[2] != L'?' && lpNewDirectory[3] != L'\\') 376 { 377 /* Get file name position and full path length */ 378 Length = GetFullPathNameW(lpNewDirectory, 0, NULL, &FilePart); 379 if (Length == 0) 380 { 381 RtlReleaseRelativeName(&RelativeName); 382 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 383 CloseHandle(TemplateHandle); 384 SetLastError(ERROR_FILENAME_EXCED_RANGE); 385 return FALSE; 386 } 387 388 /* Keep place for 8.3 file name */ 389 Length += SFN_LENGTH; 390 /* No prefix, so, must be smaller than MAX_PATH */ 391 if (Length > MAX_PATH) 392 { 393 RtlReleaseRelativeName(&RelativeName); 394 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 395 CloseHandle(TemplateHandle); 396 SetLastError(ERROR_FILENAME_EXCED_RANGE); 397 return FALSE; 398 } 399 } 400 401 /* If we have relative name (and root dir), use them instead */ 402 if (RelativeName.RelativeName.Length != 0) 403 { 404 NtPathU.Length = RelativeName.RelativeName.Length; 405 NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; 406 NtPathU.Buffer = RelativeName.RelativeName.Buffer; 407 } 408 else 409 { 410 RelativeName.ContainingDirectory = NULL; 411 } 412 413 /* Get extended attributes */ 414 Status = NtQueryInformationFile(TemplateHandle, 415 &IoStatusBlock, 416 &FileEaInfo, 417 sizeof(FileEaInfo), 418 FileEaInformation); 419 if (!NT_SUCCESS(Status)) 420 { 421 RtlReleaseRelativeName(&RelativeName); 422 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 423 CloseHandle(TemplateHandle); 424 BaseSetLastNTError(Status); 425 return FALSE; 426 } 427 428 /* Start reading extended attributes */ 429 if (FileEaInfo.EaSize != 0) 430 { 431 for (EaLength = FileEaInfo.EaSize * 2; ; EaLength = EaLength * 2) 432 { 433 /* Allocate buffer for reading */ 434 EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, EaLength); 435 if (!EaBuffer) 436 { 437 RtlReleaseRelativeName(&RelativeName); 438 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 439 CloseHandle(TemplateHandle); 440 BaseSetLastNTError(STATUS_NO_MEMORY); 441 return FALSE; 442 } 443 444 /* Query EAs */ 445 Status = NtQueryEaFile(TemplateHandle, 446 &IoStatusBlock, 447 EaBuffer, 448 EaLength, 449 FALSE, 450 NULL, 451 0, 452 NULL, 453 TRUE); 454 if (!NT_SUCCESS(Status)) 455 { 456 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); 457 IoStatusBlock.Information = 0; 458 } 459 460 /* If we don't fail because of too small buffer, stop here */ 461 if (Status != STATUS_BUFFER_OVERFLOW && 462 Status != STATUS_BUFFER_TOO_SMALL) 463 { 464 EaLength = IoStatusBlock.Information; 465 break; 466 } 467 } 468 } 469 470 InitializeObjectAttributes(&ObjectAttributes, 471 &NtPathU, 472 OBJ_CASE_INSENSITIVE, 473 RelativeName.ContainingDirectory, 474 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL)); 475 476 /* Ensure attributes are valid */ 477 FileBasicInfo.FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; 478 479 /* Create the new directory */ 480 Status = NtCreateFile(&DirectoryHandle, 481 FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | 482 FILE_READ_ATTRIBUTES | (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? FILE_ADD_FILE : 0), 483 &ObjectAttributes, 484 &IoStatusBlock, 485 NULL, 486 FileBasicInfo.FileAttributes, 487 FILE_SHARE_READ | FILE_SHARE_WRITE, 488 FILE_CREATE, 489 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 490 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT, 491 EaBuffer, 492 EaLength); 493 if (!NT_SUCCESS(Status)) 494 { 495 if (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED) 496 { 497 /* If creation failed, it might be because FSD doesn't support reparse points 498 * Retry without asking for such support in case template is not a reparse point 499 */ 500 if (!ReparsePoint) 501 { 502 Status = NtCreateFile(&DirectoryHandle, 503 FILE_LIST_DIRECTORY | SYNCHRONIZE | 504 FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, 505 &ObjectAttributes, 506 &IoStatusBlock, 507 NULL, 508 FileBasicInfo.FileAttributes, 509 FILE_SHARE_READ | FILE_SHARE_WRITE, 510 FILE_CREATE, 511 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 512 FILE_OPEN_FOR_BACKUP_INTENT, 513 EaBuffer, 514 EaLength); 515 } 516 else 517 { 518 RtlReleaseRelativeName(&RelativeName); 519 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 520 if (EaBuffer) 521 { 522 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); 523 } 524 CloseHandle(TemplateHandle); 525 BaseSetLastNTError(Status); 526 return FALSE; 527 } 528 } 529 } 530 531 RtlReleaseRelativeName(&RelativeName); 532 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 533 if (EaBuffer) 534 { 535 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer); 536 } 537 538 if (!NT_SUCCESS(Status)) 539 { 540 NtClose(TemplateHandle); 541 if (RtlIsDosDeviceName_U(lpNewDirectory)) 542 { 543 Status = STATUS_NOT_A_DIRECTORY; 544 } 545 BaseSetLastNTError(Status); 546 return FALSE; 547 } 548 549 /* If template is a reparse point, copy reparse data */ 550 if (ReparsePoint) 551 { 552 ReparseDataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, 553 MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 554 if (!ReparseDataBuffer) 555 { 556 NtClose(TemplateHandle); 557 NtClose(DirectoryHandle); 558 SetLastError(STATUS_NO_MEMORY); 559 return FALSE; 560 } 561 562 /* First query data */ 563 Status = NtFsControlFile(TemplateHandle, 564 NULL, 565 NULL, 566 NULL, 567 &IoStatusBlock, 568 FSCTL_GET_REPARSE_POINT, 569 NULL, 570 0, 571 ReparseDataBuffer, 572 MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 573 if (!NT_SUCCESS(Status)) 574 { 575 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 576 NtClose(TemplateHandle); 577 NtClose(DirectoryHandle); 578 SetLastError(Status); 579 return FALSE; 580 } 581 582 /* Once again, ensure it is a mount point */ 583 if (ReparseDataBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 584 { 585 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 586 NtClose(TemplateHandle); 587 NtClose(DirectoryHandle); 588 SetLastError(STATUS_OBJECT_NAME_INVALID); 589 return FALSE; 590 } 591 592 /* Get volume name */ 593 SubstituteName = (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + 594 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset); 595 if (IS_VOLUME_NAME(SubstituteName, ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength)) 596 { 597 /* Prepare to define a new mount point for that volume */ 598 RtlInitUnicodeString(&NewDirectory, lpNewDirectory); 599 NewDirectory.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewDirectory.Length + 2 * sizeof(WCHAR)); 600 if (!NewDirectory.Buffer) 601 { 602 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 603 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 604 NtClose(TemplateHandle); 605 NtClose(DirectoryHandle); 606 return FALSE; 607 } 608 609 RtlCopyMemory(&NewDirectory.Buffer, lpNewDirectory, NewDirectory.Length); 610 if (NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] != L'\\') 611 { 612 NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] = L'\\'; 613 NewDirectory.Buffer[(NewDirectory.Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; 614 } 615 616 /* Define a new mount point for that volume */ 617 SetVolumeMountPointW(NewDirectory.Buffer, SubstituteName); 618 619 RtlFreeHeap(RtlGetProcessHeap(), 0, NewDirectory.Buffer); 620 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 621 NtClose(TemplateHandle); 622 NtClose(DirectoryHandle); 623 return TRUE; 624 } 625 626 /* Otherwise copy data raw */ 627 Status = NtFsControlFile(DirectoryHandle, 628 NULL, 629 NULL, 630 NULL, 631 &IoStatusBlock, 632 FSCTL_SET_REPARSE_POINT, 633 ReparseDataBuffer, 634 ReparseDataBuffer->ReparseDataLength + 635 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer), 636 NULL, 637 0); 638 639 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 640 NtClose(TemplateHandle); 641 NtClose(DirectoryHandle); 642 643 if (NT_SUCCESS(Status)) 644 { 645 return TRUE; 646 } 647 648 BaseSetLastNTError(Status); 649 return FALSE; 650 } 651 /* In case it's not a reparse point, handle streams on the file */ 652 else 653 { 654 for (StreamSize = 0x1000; ; StreamSize = StreamSize * 2) 655 { 656 FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize); 657 if (!FileStreamInfo) 658 { 659 BaseMarkFileForDelete(DirectoryHandle, FileBasicInfo.FileAttributes); 660 SetLastError(STATUS_NO_MEMORY); 661 break; 662 } 663 664 /* Query stream information */ 665 Status = NtQueryInformationFile(TemplateHandle, 666 &IoStatusBlock, 667 FileStreamInfo, 668 StreamSize, 669 FileStreamInformation); 670 if (NT_SUCCESS(Status)) 671 { 672 break; 673 } 674 675 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); 676 FileStreamInfo = NULL; 677 678 /* If it failed, ensure that's not because of too small buffer */ 679 if (Status != STATUS_BUFFER_OVERFLOW && 680 Status != STATUS_BUFFER_TOO_SMALL) 681 { 682 break; 683 } 684 } 685 686 if (!NT_SUCCESS(Status) || IoStatusBlock.Information == 0) 687 { 688 if (FileStreamInfo) 689 { 690 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); 691 } 692 693 NtClose(TemplateHandle); 694 NtClose(DirectoryHandle); 695 return TRUE; 696 } 697 698 #if 1 699 /* FIXME: TODO */ 700 DPRINT1("Warning: streams copying is unimplemented!\n"); 701 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo); 702 NtClose(TemplateHandle); 703 NtClose(DirectoryHandle); 704 #endif 705 return TRUE; 706 } 707 } 708 709 /* 710 * @implemented 711 */ 712 BOOL 713 WINAPI 714 RemoveDirectoryA(IN LPCSTR lpPathName) 715 { 716 PUNICODE_STRING PathNameW; 717 718 PathNameW = Basep8BitStringToStaticUnicodeString(lpPathName); 719 if (!PathNameW) 720 { 721 return FALSE; 722 } 723 724 return RemoveDirectoryW(PathNameW->Buffer); 725 } 726 727 /* 728 * @implemented 729 */ 730 BOOL 731 WINAPI 732 RemoveDirectoryW(IN LPCWSTR lpPathName) 733 { 734 NTSTATUS Status; 735 DWORD BytesReturned; 736 HANDLE DirectoryHandle; 737 IO_STATUS_BLOCK IoStatusBlock; 738 UNICODE_STRING NtPathU, PathName; 739 RTL_RELATIVE_NAME_U RelativeName; 740 PWSTR PathUBuffer, SubstituteName; 741 OBJECT_ATTRIBUTES ObjectAttributes; 742 PREPARSE_DATA_BUFFER ReparseDataBuffer; 743 FILE_DISPOSITION_INFORMATION FileDispInfo; 744 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo; 745 746 /* Get relative name */ 747 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName, &NtPathU, NULL, &RelativeName)) 748 { 749 SetLastError(ERROR_PATH_NOT_FOUND); 750 return FALSE; 751 } 752 753 /* Save buffer to allow later freeing */ 754 PathUBuffer = NtPathU.Buffer; 755 756 /* If we have relative name (and root dir), use them instead */ 757 if (RelativeName.RelativeName.Length != 0) 758 { 759 NtPathU.Length = RelativeName.RelativeName.Length; 760 NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength; 761 NtPathU.Buffer = RelativeName.RelativeName.Buffer; 762 } 763 else 764 { 765 RelativeName.ContainingDirectory = NULL; 766 } 767 768 InitializeObjectAttributes(&ObjectAttributes, 769 &NtPathU, 770 OBJ_CASE_INSENSITIVE, 771 RelativeName.ContainingDirectory, 772 NULL); 773 774 /* Try to open directory */ 775 Status = NtOpenFile(&DirectoryHandle, 776 DELETE | SYNCHRONIZE | FAILED_ACCESS_ACE_FLAG, 777 &ObjectAttributes, 778 &IoStatusBlock, 779 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 780 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 781 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); 782 if (!NT_SUCCESS(Status)) 783 { 784 /* We only accept failure for reparse points not being supported */ 785 if (Status != STATUS_INVALID_PARAMETER) 786 { 787 goto Cleanup; 788 } 789 790 /* Try to open, with reparse points support */ 791 Status = NtOpenFile(&DirectoryHandle, 792 DELETE | SYNCHRONIZE, 793 &ObjectAttributes, 794 &IoStatusBlock, 795 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 796 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 797 FILE_OPEN_FOR_BACKUP_INTENT); 798 if (!NT_SUCCESS(Status)) 799 { 800 goto Cleanup; 801 } 802 803 /* Success, mark directory */ 804 goto MarkFileForDelete; 805 } 806 807 /* Get information about file (and reparse point) */ 808 Status = NtQueryInformationFile(DirectoryHandle, 809 &IoStatusBlock, 810 &FileTagInfo, 811 sizeof(FileTagInfo), 812 FileAttributeTagInformation); 813 if (!NT_SUCCESS(Status)) 814 { 815 /* FSD might not support querying reparse points information */ 816 if (Status != STATUS_NOT_IMPLEMENTED && 817 Status != STATUS_INVALID_PARAMETER) 818 { 819 goto CleanupHandle; 820 } 821 822 /* If that's the case, then just delete directory */ 823 goto MarkFileForDelete; 824 } 825 826 /* If that's not a reparse point, nothing more to do than just delete */ 827 if (!(FileTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 828 { 829 goto MarkFileForDelete; 830 } 831 832 /* Check if that's a mount point */ 833 if (FileTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 834 { 835 /* It's not */ 836 NtClose(DirectoryHandle); 837 838 /* So, try to reopen directory, ignoring mount point */ 839 Status = NtOpenFile(&DirectoryHandle, 840 DELETE | SYNCHRONIZE, 841 &ObjectAttributes, 842 &IoStatusBlock, 843 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 844 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 845 FILE_OPEN_FOR_BACKUP_INTENT); 846 if (NT_SUCCESS(Status)) 847 { 848 /* It succeed, we can safely delete directory (and ignore reparse point) */ 849 goto MarkFileForDelete; 850 } 851 852 /* If it failed, only allow case where IO mount point was ignored */ 853 if (Status != STATUS_IO_REPARSE_TAG_NOT_HANDLED) 854 { 855 goto Cleanup; 856 } 857 858 /* Reopen with reparse point support */ 859 Status = NtOpenFile(&DirectoryHandle, 860 DELETE | SYNCHRONIZE, 861 &ObjectAttributes, 862 &IoStatusBlock, 863 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 864 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 865 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); 866 if (NT_SUCCESS(Status)) 867 { 868 /* And mark for delete */ 869 goto MarkFileForDelete; 870 } 871 872 goto Cleanup; 873 } 874 875 /* Here, we have a mount point, prepare to query information about it */ 876 ReparseDataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, 877 MAXIMUM_REPARSE_DATA_BUFFER_SIZE); 878 if (!ReparseDataBuffer) 879 { 880 RtlReleaseRelativeName(&RelativeName); 881 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 882 NtClose(DirectoryHandle); 883 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 884 return FALSE; 885 } 886 887 /* Query */ 888 if (!DeviceIoControl(DirectoryHandle, 889 FSCTL_GET_REPARSE_POINT, 890 NULL, 0, 891 ReparseDataBuffer, 892 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 893 &BytesReturned, 894 NULL)) 895 { 896 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 897 goto MarkFileForDelete; 898 } 899 900 /* Get volume name */ 901 SubstituteName = (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + 902 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset); 903 if (!IS_VOLUME_NAME(SubstituteName, ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength)) 904 { 905 /* This is not a volume, we can safely delete */ 906 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 907 goto MarkFileForDelete; 908 } 909 910 /* Prepare to delete mount point */ 911 RtlInitUnicodeString(&PathName, lpPathName); 912 PathName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathName.Length + 2 * sizeof(WCHAR)); 913 if (!PathName.Buffer) 914 { 915 RtlReleaseRelativeName(&RelativeName); 916 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 917 NtClose(DirectoryHandle); 918 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 919 return FALSE; 920 } 921 922 RtlCopyMemory(&PathName.Buffer, lpPathName, PathName.Length); 923 if (PathName.Buffer[PathName.Length / sizeof(WCHAR)] != L'\\') 924 { 925 PathName.Buffer[PathName.Length / sizeof(WCHAR)] = L'\\'; 926 PathName.Buffer[(PathName.Length / sizeof(WCHAR)) + 1] = UNICODE_NULL; 927 } 928 929 /* Delete mount point for that volume */ 930 DeleteVolumeMountPointW(PathName.Buffer); 931 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); 932 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer); 933 934 /* And mark directory for delete */ 935 MarkFileForDelete: 936 RtlReleaseRelativeName(&RelativeName); 937 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 938 939 /* Mark & set */ 940 FileDispInfo.DeleteFile = TRUE; 941 Status = NtSetInformationFile(DirectoryHandle, 942 &IoStatusBlock, 943 &FileDispInfo, 944 sizeof(FILE_DISPOSITION_INFORMATION), 945 FileDispositionInformation); 946 NtClose(DirectoryHandle); 947 948 if (!NT_SUCCESS(Status)) 949 { 950 BaseSetLastNTError(Status); 951 return FALSE; 952 } 953 954 return TRUE; 955 956 CleanupHandle: 957 NtClose(DirectoryHandle); 958 959 Cleanup: 960 RtlReleaseRelativeName(&RelativeName); 961 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer); 962 BaseSetLastNTError(Status); 963 return FALSE; 964 } 965 966 /* EOF */ 967