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