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)
10 /* INCLUDES ******************************************************************/
16 /* Short File Name length in chars (8.3) */
19 /* Match a volume name like:
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] == '}')
31 /* FIXME - Get it out of here */
32 typedef struct _REPARSE_DATA_BUFFER
{
34 USHORT ReparseDataLength
;
38 USHORT SubstituteNameOffset
;
39 USHORT SubstituteNameLength
;
40 USHORT PrintNameOffset
;
41 USHORT PrintNameLength
;
44 } SymbolicLinkReparseBuffer
;
46 USHORT SubstituteNameOffset
;
47 USHORT SubstituteNameLength
;
48 USHORT PrintNameOffset
;
49 USHORT PrintNameLength
;
51 } MountPointReparseBuffer
;
54 } GenericReparseBuffer
;
56 } REPARSE_DATA_BUFFER
, *PREPARSE_DATA_BUFFER
;
58 typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION
{
61 } FILE_ATTRIBUTE_TAG_INFORMATION
, *PFILE_ATTRIBUTE_TAG_INFORMATION
;
63 /* FUNCTIONS *****************************************************************/
70 CreateDirectoryA(IN LPCSTR lpPathName
,
71 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
73 PUNICODE_STRING PathNameW
;
75 PathNameW
= Basep8BitStringToStaticUnicodeString(lpPathName
);
81 return CreateDirectoryW(PathNameW
->Buffer
,
82 lpSecurityAttributes
);
90 CreateDirectoryExA(IN LPCSTR lpTemplateDirectory
,
91 IN LPCSTR lpNewDirectory
,
92 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
94 PUNICODE_STRING TemplateDirectoryW
;
95 UNICODE_STRING NewDirectoryW
;
98 TemplateDirectoryW
= Basep8BitStringToStaticUnicodeString(lpTemplateDirectory
);
99 if (!TemplateDirectoryW
)
104 if (!Basep8BitStringToDynamicUnicodeString(&NewDirectoryW
, lpNewDirectory
))
109 ret
= CreateDirectoryExW(TemplateDirectoryW
->Buffer
,
110 NewDirectoryW
.Buffer
,
111 lpSecurityAttributes
);
113 RtlFreeUnicodeString(&NewDirectoryW
);
123 CreateDirectoryW(IN LPCWSTR lpPathName
,
124 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
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
;
135 /* Get relative name */
136 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName
, &NtPathU
, NULL
, &RelativeName
))
138 SetLastError(ERROR_PATH_NOT_FOUND
);
142 /* Check if path length is < MAX_PATH (with space for file name).
143 * If not, prefix is required.
145 if (NtPathU
.Length
> (MAX_PATH
- SFN_LENGTH
) * sizeof(WCHAR
) && lpPathName
[0] != L
'\\' &&
146 lpPathName
[1] != L
'\\' && lpPathName
[2] != L
'?' && lpPathName
[3] != L
'\\')
148 /* Get file name position and full path length */
149 Length
= GetFullPathNameW(lpPathName
, 0, NULL
, &FilePart
);
152 RtlReleaseRelativeName(&RelativeName
);
153 RtlFreeHeap(GetProcessHeap(), 0, NtPathU
.Buffer
);
154 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
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
)
163 RtlReleaseRelativeName(&RelativeName
);
164 RtlFreeHeap(GetProcessHeap(), 0, NtPathU
.Buffer
);
165 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
170 /* Save buffer to allow later freeing */
171 PathUBuffer
= NtPathU
.Buffer
;
173 /* If we have relative name (and root dir), use them instead */
174 if (RelativeName
.RelativeName
.Length
!= 0)
176 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
177 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
178 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
182 RelativeName
.ContainingDirectory
= NULL
;
185 InitializeObjectAttributes(&ObjectAttributes
,
187 OBJ_CASE_INSENSITIVE
,
188 RelativeName
.ContainingDirectory
,
189 (lpSecurityAttributes
? lpSecurityAttributes
->lpSecurityDescriptor
: NULL
));
191 Status
= NtCreateFile(&DirectoryHandle
,
192 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
196 FILE_ATTRIBUTE_NORMAL
,
197 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
199 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT
,
203 RtlReleaseRelativeName(&RelativeName
);
204 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
206 if (NT_SUCCESS(Status
))
208 NtClose(DirectoryHandle
);
212 if (RtlIsDosDeviceName_U(lpPathName
))
214 Status
= STATUS_NOT_A_DIRECTORY
;
217 BaseSetLastNTError(Status
);
226 CreateDirectoryExW(IN LPCWSTR lpTemplateDirectory
,
227 IN LPCWSTR lpNewDirectory
,
228 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
233 PVOID EaBuffer
= NULL
;
234 BOOL ReparsePoint
= FALSE
;
235 IO_STATUS_BLOCK IoStatusBlock
;
236 FILE_EA_INFORMATION FileEaInfo
;
237 OBJECT_ATTRIBUTES ObjectAttributes
;
238 FILE_BASIC_INFORMATION FileBasicInfo
;
239 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
240 HANDLE TemplateHandle
, DirectoryHandle
;
241 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo
;
242 UNICODE_STRING NtPathU
, NtTemplatePathU
, NewDirectory
;
243 RTL_RELATIVE_NAME_U RelativeName
, TemplateRelativeName
;
244 PWSTR TemplateBuffer
, PathUBuffer
, FilePart
, SubstituteName
;
246 /* Get relative name of the template */
247 if (!RtlDosPathNameToRelativeNtPathName_U(lpTemplateDirectory
, &NtTemplatePathU
, NULL
, &TemplateRelativeName
))
249 SetLastError(ERROR_PATH_NOT_FOUND
);
253 /* Save buffer for further freeing */
254 TemplateBuffer
= NtTemplatePathU
.Buffer
;
256 /* If we have relative name (and root dir), use them instead */
257 if (TemplateRelativeName
.RelativeName
.Length
!= 0)
259 NtTemplatePathU
.Length
= TemplateRelativeName
.RelativeName
.Length
;
260 NtTemplatePathU
.MaximumLength
= TemplateRelativeName
.RelativeName
.MaximumLength
;
261 NtTemplatePathU
.Buffer
= TemplateRelativeName
.RelativeName
.Buffer
;
265 TemplateRelativeName
.ContainingDirectory
= NULL
;
268 InitializeObjectAttributes(&ObjectAttributes
,
270 OBJ_CASE_INSENSITIVE
,
274 /* Open template directory */
275 Status
= NtOpenFile(&TemplateHandle
,
276 FILE_LIST_DIRECTORY
| FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
279 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
280 FILE_DIRECTORY_FILE
| FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT
);
281 if (!NT_SUCCESS(Status
))
283 if (Status
!= STATUS_INVALID_PARAMETER
)
285 RtlReleaseRelativeName(&TemplateRelativeName
);
286 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
287 BaseSetLastNTError(Status
);
291 OpenWithoutReparseSupport
:
292 /* Opening failed due to lacking reparse points support in the FSD, try without */
293 Status
= NtOpenFile(&TemplateHandle
,
294 FILE_LIST_DIRECTORY
| FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
297 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
298 FILE_DIRECTORY_FILE
| FILE_OPEN_FOR_BACKUP_INTENT
);
300 if (!NT_SUCCESS(Status
))
302 RtlReleaseRelativeName(&TemplateRelativeName
);
303 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
304 BaseSetLastNTError(Status
);
308 /* Request file attributes */
309 FileBasicInfo
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
310 Status
= NtQueryInformationFile(TemplateHandle
,
313 sizeof(FileBasicInfo
),
314 FileBasicInformation
);
315 if (!NT_SUCCESS(Status
))
317 RtlReleaseRelativeName(&TemplateRelativeName
);
318 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
319 CloseHandle(TemplateHandle
);
320 BaseSetLastNTError(Status
);
327 /* Request file attributes */
328 FileBasicInfo
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
329 Status
= NtQueryInformationFile(TemplateHandle
,
332 sizeof(FileBasicInfo
),
333 FileBasicInformation
);
334 if (!NT_SUCCESS(Status
))
336 RtlReleaseRelativeName(&TemplateRelativeName
);
337 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
338 CloseHandle(TemplateHandle
);
339 BaseSetLastNTError(Status
);
344 /* If it is a reparse point, then get information about it */
345 if (FileBasicInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
347 Status
= NtQueryInformationFile(TemplateHandle
,
351 FileAttributeTagInformation
);
352 if (!NT_SUCCESS(Status
))
354 RtlReleaseRelativeName(&TemplateRelativeName
);
355 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
356 CloseHandle(TemplateHandle
);
357 BaseSetLastNTError(Status
);
361 /* Only mount points are supported, retry without if anything different */
362 if (FileTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
364 CloseHandle(TemplateHandle
);
365 goto OpenWithoutReparseSupport
;
368 /* Mark we are playing with a reparse point */
373 /* Get relative name of the directory */
374 if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory
, &NtPathU
, NULL
, &RelativeName
))
376 RtlReleaseRelativeName(&TemplateRelativeName
);
377 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
378 NtClose(TemplateHandle
);
379 SetLastError(ERROR_PATH_NOT_FOUND
);
383 /* Save its buffer for further freeing */
384 PathUBuffer
= NtPathU
.Buffer
;
386 /* Template & directory can't be the same */
387 if (RtlEqualUnicodeString(&NtPathU
,
391 RtlReleaseRelativeName(&RelativeName
);
392 RtlReleaseRelativeName(&TemplateRelativeName
);
393 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
394 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
395 NtClose(TemplateHandle
);
396 SetLastError(ERROR_INVALID_NAME
);
400 RtlReleaseRelativeName(&TemplateRelativeName
);
401 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer
);
403 /* Check if path length is < MAX_PATH (with space for file name).
404 * If not, prefix is required.
406 if (NtPathU
.Length
> (MAX_PATH
- SFN_LENGTH
) * sizeof(WCHAR
) && lpNewDirectory
[0] != L
'\\' &&
407 lpNewDirectory
[1] != L
'\\' && lpNewDirectory
[2] != L
'?' && lpNewDirectory
[3] != L
'\\')
409 /* Get file name position and full path length */
410 Length
= GetFullPathNameW(lpNewDirectory
, 0, NULL
, &FilePart
);
413 RtlReleaseRelativeName(&RelativeName
);
414 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
415 CloseHandle(TemplateHandle
);
416 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
420 /* Keep place for 8.3 file name */
421 Length
+= SFN_LENGTH
;
422 /* No prefix, so, must be smaller than MAX_PATH */
423 if (Length
> MAX_PATH
)
425 RtlReleaseRelativeName(&RelativeName
);
426 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
427 CloseHandle(TemplateHandle
);
428 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
433 /* If we have relative name (and root dir), use them instead */
434 if (RelativeName
.RelativeName
.Length
!= 0)
436 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
437 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
438 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
442 RelativeName
.ContainingDirectory
= NULL
;
445 /* Get extended attributes */
446 Status
= NtQueryInformationFile(TemplateHandle
,
451 if (!NT_SUCCESS(Status
))
453 RtlReleaseRelativeName(&RelativeName
);
454 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
455 CloseHandle(TemplateHandle
);
456 BaseSetLastNTError(Status
);
460 /* Start reading extended attributes */
461 if (FileEaInfo
.EaSize
!= 0)
463 for (EaLength
= FileEaInfo
.EaSize
* 2; ; EaLength
= EaLength
* 2)
465 /* Allocate buffer for reading */
466 EaBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, EaLength
);
469 RtlReleaseRelativeName(&RelativeName
);
470 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
471 CloseHandle(TemplateHandle
);
472 BaseSetLastNTError(STATUS_NO_MEMORY
);
477 Status
= NtQueryEaFile(TemplateHandle
,
486 if (!NT_SUCCESS(Status
))
488 RtlFreeHeap(GetProcessHeap(), 0, EaBuffer
);
489 IoStatusBlock
.Information
= 0;
492 /* If we don't fail because of too small buffer, stop here */
493 if (Status
!= STATUS_BUFFER_OVERFLOW
&&
494 Status
!= STATUS_BUFFER_TOO_SMALL
)
496 EaLength
= IoStatusBlock
.Information
;
502 InitializeObjectAttributes(&ObjectAttributes
,
504 OBJ_CASE_INSENSITIVE
,
505 RelativeName
.ContainingDirectory
,
506 (lpSecurityAttributes
? lpSecurityAttributes
->lpSecurityDescriptor
: NULL
));
508 /* Ensure attributes are valid */
509 FileBasicInfo
.FileAttributes
&= FILE_ATTRIBUTE_VALID_FLAGS
;
511 /* Create the new directory */
512 Status
= NtCreateFile(&DirectoryHandle
,
513 FILE_LIST_DIRECTORY
| SYNCHRONIZE
| FILE_WRITE_ATTRIBUTES
|
514 FILE_READ_ATTRIBUTES
| (FileBasicInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
? FILE_ADD_FILE
: 0),
518 FileBasicInfo
.FileAttributes
,
519 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
521 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
522 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
,
525 if (!NT_SUCCESS(Status
))
527 if (Status
== STATUS_INVALID_PARAMETER
|| Status
== STATUS_ACCESS_DENIED
)
529 /* If creation failed, it might be because FSD doesn't support reparse points
530 * Retry without asking for such support in case template is not a reparse point
534 Status
= NtCreateFile(&DirectoryHandle
,
535 FILE_LIST_DIRECTORY
| SYNCHRONIZE
|
536 FILE_WRITE_ATTRIBUTES
| FILE_READ_ATTRIBUTES
,
540 FileBasicInfo
.FileAttributes
,
541 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
543 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
544 FILE_OPEN_FOR_BACKUP_INTENT
,
550 RtlReleaseRelativeName(&RelativeName
);
551 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
554 RtlFreeHeap(GetProcessHeap(), 0, EaBuffer
);
556 CloseHandle(TemplateHandle
);
557 BaseSetLastNTError(Status
);
563 RtlReleaseRelativeName(&RelativeName
);
564 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer
);
567 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer
);
570 if (!NT_SUCCESS(Status
))
572 NtClose(TemplateHandle
);
573 if (RtlIsDosDeviceName_U(lpNewDirectory
))
575 Status
= STATUS_NOT_A_DIRECTORY
;
577 BaseSetLastNTError(Status
);
581 /* If template is a reparse point, copy reparse data */
584 ReparseDataBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
585 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
586 if (!ReparseDataBuffer
)
588 NtClose(TemplateHandle
);
589 NtClose(DirectoryHandle
);
590 SetLastError(STATUS_NO_MEMORY
);
594 /* First query data */
595 Status
= NtFsControlFile(TemplateHandle
,
600 FSCTL_GET_REPARSE_POINT
,
604 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
605 if (!NT_SUCCESS(Status
))
607 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
608 NtClose(TemplateHandle
);
609 NtClose(DirectoryHandle
);
610 SetLastError(Status
);
614 /* Once again, ensure it is a mount point */
615 if (ReparseDataBuffer
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
617 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
618 NtClose(TemplateHandle
);
619 NtClose(DirectoryHandle
);
620 SetLastError(STATUS_OBJECT_NAME_INVALID
);
624 /* Get volume name */
625 SubstituteName
= (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
626 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
);
627 if (IS_VOLUME_NAME(SubstituteName
, ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
))
629 /* Prepare to define a new mount point for that volume */
630 RtlInitUnicodeString(&NewDirectory
, lpNewDirectory
);
631 NewDirectory
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewDirectory
.Length
+ 2 * sizeof(WCHAR
));
632 if (!NewDirectory
.Buffer
)
634 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
635 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
636 NtClose(TemplateHandle
);
637 NtClose(DirectoryHandle
);
641 RtlCopyMemory(&NewDirectory
.Buffer
, lpNewDirectory
, NewDirectory
.Length
);
642 if (NewDirectory
.Buffer
[NewDirectory
.Length
/ sizeof(WCHAR
)] != L
'\\')
644 NewDirectory
.Buffer
[NewDirectory
.Length
/ sizeof(WCHAR
)] = L
'\\';
645 NewDirectory
.Buffer
[(NewDirectory
.Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
648 /* Define a new mount point for that volume */
649 SetVolumeMountPointW(NewDirectory
.Buffer
, SubstituteName
);
651 RtlFreeHeap(RtlGetProcessHeap(), 0, NewDirectory
.Buffer
);
652 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
653 NtClose(TemplateHandle
);
654 NtClose(DirectoryHandle
);
658 /* Otherwise copy data raw */
659 Status
= NtFsControlFile(DirectoryHandle
,
664 FSCTL_SET_REPARSE_POINT
,
666 ReparseDataBuffer
->ReparseDataLength
+
667 FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
.PathBuffer
),
671 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
672 NtClose(TemplateHandle
);
673 NtClose(DirectoryHandle
);
675 if (NT_SUCCESS(Status
))
680 BaseSetLastNTError(Status
);
683 /* In case it's not a reparse point, handle streams on the file */
687 ULONG StreamSize
= 0x1000;
690 StreamInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize
);
693 BaseMarkFileForDelete(DirectoryHandle
, FileBasicInfo
.FileAttributes
);
694 SetLastError(STATUS_NO_MEMORY
);
698 /* Query stream information */
699 Status
= NtQueryInformationFile(TemplateHandle
,
703 FileStreamInformation
);
704 if (!NT_SUCCESS(Status
))
706 RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo
);
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
)
719 if (!NT_SUCCESS(Status
) || IoStatusBlock
.Information
== 0)
723 RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo
);
726 NtClose(TemplateHandle
);
727 NtClose(DirectoryHandle
);
733 DPRINT1("Warning: streams copying is unimplemented!\n");
734 RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo
);
735 NtClose(TemplateHandle
);
736 NtClose(DirectoryHandle
);
747 RemoveDirectoryA(IN LPCSTR lpPathName
)
749 PUNICODE_STRING PathNameW
;
751 PathNameW
= Basep8BitStringToStaticUnicodeString(lpPathName
);
757 return RemoveDirectoryW(PathNameW
->Buffer
);
765 RemoveDirectoryW(IN LPCWSTR lpPathName
)
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
;
779 /* Get relative name */
780 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName
, &NtPathU
, NULL
, &RelativeName
))
782 SetLastError(ERROR_PATH_NOT_FOUND
);
786 /* Save buffer to allow later freeing */
787 PathUBuffer
= NtPathU
.Buffer
;
789 /* If we have relative name (and root dir), use them instead */
790 if (RelativeName
.RelativeName
.Length
!= 0)
792 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
793 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
794 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
798 RelativeName
.ContainingDirectory
= NULL
;
801 InitializeObjectAttributes(&ObjectAttributes
,
803 OBJ_CASE_INSENSITIVE
,
804 RelativeName
.ContainingDirectory
,
807 /* Try to open directory */
808 Status
= NtOpenFile(&DirectoryHandle
,
809 DELETE
| SYNCHRONIZE
| FAILED_ACCESS_ACE_FLAG
,
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
))
817 /* We only accept failure for reparse points not being supported */
818 if (Status
!= STATUS_INVALID_PARAMETER
)
823 /* Try to open, with reparse points support */
824 Status
= NtOpenFile(&DirectoryHandle
,
825 DELETE
| SYNCHRONIZE
,
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
))
836 /* Success, mark directory */
837 goto MarkFileForDelete
;
840 /* Get information about file (and reparse point) */
841 Status
= NtQueryInformationFile(DirectoryHandle
,
845 FileAttributeTagInformation
);
846 if (!NT_SUCCESS(Status
))
848 /* FSD might not support querying reparse points information */
849 if (Status
!= STATUS_NOT_IMPLEMENTED
&&
850 Status
!= STATUS_INVALID_PARAMETER
)
855 /* If that's the case, then just delete directory */
856 goto MarkFileForDelete
;
859 /* If that's not a reparse point, nothing more to do than just delete */
860 if (!(FileTagInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
862 goto MarkFileForDelete
;
865 /* Check if that's a mount point */
866 if (FileTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
869 NtClose(DirectoryHandle
);
871 /* So, try to reopen directory, ignoring mount point */
872 Status
= NtOpenFile(&DirectoryHandle
,
873 DELETE
| SYNCHRONIZE
,
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
))
881 /* It succeed, we can safely delete directory (and ignore reparse point) */
882 goto MarkFileForDelete
;
885 /* If it failed, only allow case where IO mount point was ignored */
886 if (Status
!= STATUS_IO_REPARSE_TAG_NOT_HANDLED
)
891 /* Reopen with reparse point support */
892 Status
= NtOpenFile(&DirectoryHandle
,
893 DELETE
| SYNCHRONIZE
,
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
))
901 /* And mark for delete */
902 goto MarkFileForDelete
;
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
)
913 RtlReleaseRelativeName(&RelativeName
);
914 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
915 NtClose(DirectoryHandle
);
916 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
921 if (!DeviceIoControl(DirectoryHandle
,
922 FSCTL_GET_REPARSE_POINT
,
925 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
,
929 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
930 goto MarkFileForDelete
;
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
))
938 /* This is not a volume, we can safely delete */
939 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
940 goto MarkFileForDelete
;
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
)
948 RtlReleaseRelativeName(&RelativeName
);
949 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
950 NtClose(DirectoryHandle
);
951 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
955 RtlCopyMemory(&PathName
.Buffer
, lpPathName
, PathName
.Length
);
956 if (PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] != L
'\\')
958 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] = L
'\\';
959 PathName
.Buffer
[(PathName
.Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
962 /* Delete mount point for that volume */
963 DeleteVolumeMountPointW(PathName
.Buffer
);
964 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
965 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
967 /* And mark directory for delete */
969 RtlReleaseRelativeName(&RelativeName
);
970 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
973 FileDispInfo
.DeleteFile
= TRUE
;
974 Status
= NtSetInformationFile(DirectoryHandle
,
977 sizeof(FILE_DISPOSITION_INFORMATION
),
978 FileDispositionInformation
);
979 NtClose(DirectoryHandle
);
981 if (!NT_SUCCESS(Status
))
983 BaseSetLastNTError (Status
);
990 NtClose(DirectoryHandle
);
993 RtlReleaseRelativeName(&RelativeName
);
994 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
995 BaseSetLastNTError(Status
);