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)
9 /* INCLUDES ******************************************************************/
15 /* Short File Name length in chars (8.3) */
18 /* Match a volume name like:
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] == '}')
30 /* FIXME - Get it out of here */
31 typedef struct _REPARSE_DATA_BUFFER
{
33 USHORT ReparseDataLength
;
37 USHORT SubstituteNameOffset
;
38 USHORT SubstituteNameLength
;
39 USHORT PrintNameOffset
;
40 USHORT PrintNameLength
;
43 } SymbolicLinkReparseBuffer
;
45 USHORT SubstituteNameOffset
;
46 USHORT SubstituteNameLength
;
47 USHORT PrintNameOffset
;
48 USHORT PrintNameLength
;
50 } MountPointReparseBuffer
;
53 } GenericReparseBuffer
;
55 } REPARSE_DATA_BUFFER
, *PREPARSE_DATA_BUFFER
;
57 /* FUNCTIONS *****************************************************************/
64 CreateDirectoryA(IN LPCSTR lpPathName
,
65 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
67 PUNICODE_STRING PathNameW
;
69 PathNameW
= Basep8BitStringToStaticUnicodeString(lpPathName
);
75 return CreateDirectoryW(PathNameW
->Buffer
,
76 lpSecurityAttributes
);
84 CreateDirectoryExA(IN LPCSTR lpTemplateDirectory
,
85 IN LPCSTR lpNewDirectory
,
86 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
88 PUNICODE_STRING TemplateDirectoryW
;
89 UNICODE_STRING NewDirectoryW
;
92 TemplateDirectoryW
= Basep8BitStringToStaticUnicodeString(lpTemplateDirectory
);
93 if (!TemplateDirectoryW
)
98 if (!Basep8BitStringToDynamicUnicodeString(&NewDirectoryW
, lpNewDirectory
))
103 ret
= CreateDirectoryExW(TemplateDirectoryW
->Buffer
,
104 NewDirectoryW
.Buffer
,
105 lpSecurityAttributes
);
107 RtlFreeUnicodeString(&NewDirectoryW
);
117 CreateDirectoryW(IN LPCWSTR lpPathName
,
118 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
122 HANDLE DirectoryHandle
;
123 UNICODE_STRING NtPathU
;
124 PWSTR PathUBuffer
, FilePart
;
125 IO_STATUS_BLOCK IoStatusBlock
;
126 RTL_RELATIVE_NAME_U RelativeName
;
127 OBJECT_ATTRIBUTES ObjectAttributes
;
129 /* Get relative name */
130 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName
, &NtPathU
, NULL
, &RelativeName
))
132 SetLastError(ERROR_PATH_NOT_FOUND
);
136 /* Check if path length is < MAX_PATH (with space for file name).
137 * If not, prefix is required.
139 if (NtPathU
.Length
> (MAX_PATH
- SFN_LENGTH
) * sizeof(WCHAR
) && lpPathName
[0] != L
'\\' &&
140 lpPathName
[1] != L
'\\' && lpPathName
[2] != L
'?' && lpPathName
[3] != L
'\\')
142 /* Get file name position and full path length */
143 Length
= GetFullPathNameW(lpPathName
, 0, NULL
, &FilePart
);
146 RtlReleaseRelativeName(&RelativeName
);
147 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU
.Buffer
);
148 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
152 /* Keep place for 8.3 file name */
153 Length
+= SFN_LENGTH
;
154 /* No prefix, so, must be smaller than MAX_PATH */
155 if (Length
> MAX_PATH
)
157 RtlReleaseRelativeName(&RelativeName
);
158 RtlFreeHeap(GetProcessHeap(), 0, NtPathU
.Buffer
);
159 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
164 /* Save buffer to allow later freeing */
165 PathUBuffer
= NtPathU
.Buffer
;
167 /* If we have relative name (and root dir), use them instead */
168 if (RelativeName
.RelativeName
.Length
!= 0)
170 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
171 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
172 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
176 RelativeName
.ContainingDirectory
= NULL
;
179 InitializeObjectAttributes(&ObjectAttributes
,
181 OBJ_CASE_INSENSITIVE
,
182 RelativeName
.ContainingDirectory
,
183 (lpSecurityAttributes
? lpSecurityAttributes
->lpSecurityDescriptor
: NULL
));
185 Status
= NtCreateFile(&DirectoryHandle
,
186 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
190 FILE_ATTRIBUTE_NORMAL
,
191 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
193 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT
,
197 RtlReleaseRelativeName(&RelativeName
);
198 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
200 if (NT_SUCCESS(Status
))
202 NtClose(DirectoryHandle
);
206 if (RtlIsDosDeviceName_U(lpPathName
))
208 Status
= STATUS_NOT_A_DIRECTORY
;
211 BaseSetLastNTError(Status
);
220 CreateDirectoryExW(IN LPCWSTR lpTemplateDirectory
,
221 IN LPCWSTR lpNewDirectory
,
222 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
226 PVOID EaBuffer
= NULL
;
227 BOOL ReparsePoint
= FALSE
;
228 IO_STATUS_BLOCK IoStatusBlock
;
229 FILE_EA_INFORMATION FileEaInfo
;
230 ULONG EaLength
= 0, StreamSize
;
231 OBJECT_ATTRIBUTES ObjectAttributes
;
232 FILE_BASIC_INFORMATION FileBasicInfo
;
233 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
234 HANDLE TemplateHandle
, DirectoryHandle
;
235 PFILE_STREAM_INFORMATION FileStreamInfo
;
236 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo
;
237 UNICODE_STRING NtPathU
, NtTemplatePathU
, NewDirectory
;
238 RTL_RELATIVE_NAME_U RelativeName
, TemplateRelativeName
;
239 PWSTR TemplateBuffer
, PathUBuffer
, FilePart
, SubstituteName
;
241 /* Get relative name of the template */
242 if (!RtlDosPathNameToRelativeNtPathName_U(lpTemplateDirectory
, &NtTemplatePathU
, NULL
, &TemplateRelativeName
))
244 SetLastError(ERROR_PATH_NOT_FOUND
);
248 /* Save buffer for further freeing */
249 TemplateBuffer
= NtTemplatePathU
.Buffer
;
251 /* If we have relative name (and root dir), use them instead */
252 if (TemplateRelativeName
.RelativeName
.Length
!= 0)
254 NtTemplatePathU
.Length
= TemplateRelativeName
.RelativeName
.Length
;
255 NtTemplatePathU
.MaximumLength
= TemplateRelativeName
.RelativeName
.MaximumLength
;
256 NtTemplatePathU
.Buffer
= TemplateRelativeName
.RelativeName
.Buffer
;
260 TemplateRelativeName
.ContainingDirectory
= NULL
;
263 InitializeObjectAttributes(&ObjectAttributes
,
265 OBJ_CASE_INSENSITIVE
,
269 /* Open template directory */
270 Status
= NtOpenFile(&TemplateHandle
,
271 FILE_LIST_DIRECTORY
| FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
274 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
275 FILE_DIRECTORY_FILE
| FILE_OPEN_REPARSE_POINT
| FILE_OPEN_FOR_BACKUP_INTENT
);
276 if (!NT_SUCCESS(Status
))
278 if (Status
!= STATUS_INVALID_PARAMETER
)
280 RtlReleaseRelativeName(&TemplateRelativeName
);
281 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
282 BaseSetLastNTError(Status
);
286 OpenWithoutReparseSupport
:
287 /* Opening failed due to lacking reparse points support in the FSD, try without */
288 Status
= NtOpenFile(&TemplateHandle
,
289 FILE_LIST_DIRECTORY
| FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
292 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
293 FILE_DIRECTORY_FILE
| FILE_OPEN_FOR_BACKUP_INTENT
);
295 if (!NT_SUCCESS(Status
))
297 RtlReleaseRelativeName(&TemplateRelativeName
);
298 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
299 BaseSetLastNTError(Status
);
303 /* Request file attributes */
304 FileBasicInfo
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
305 Status
= NtQueryInformationFile(TemplateHandle
,
308 sizeof(FileBasicInfo
),
309 FileBasicInformation
);
310 if (!NT_SUCCESS(Status
))
312 RtlReleaseRelativeName(&TemplateRelativeName
);
313 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
314 CloseHandle(TemplateHandle
);
315 BaseSetLastNTError(Status
);
322 /* Request file attributes */
323 FileBasicInfo
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
324 Status
= NtQueryInformationFile(TemplateHandle
,
327 sizeof(FileBasicInfo
),
328 FileBasicInformation
);
329 if (!NT_SUCCESS(Status
))
331 RtlReleaseRelativeName(&TemplateRelativeName
);
332 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
333 CloseHandle(TemplateHandle
);
334 BaseSetLastNTError(Status
);
339 /* If it is a reparse point, then get information about it */
340 if (FileBasicInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
342 Status
= NtQueryInformationFile(TemplateHandle
,
346 FileAttributeTagInformation
);
347 if (!NT_SUCCESS(Status
))
349 RtlReleaseRelativeName(&TemplateRelativeName
);
350 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
351 CloseHandle(TemplateHandle
);
352 BaseSetLastNTError(Status
);
356 /* Only mount points are supported, retry without if anything different */
357 if (FileTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
359 CloseHandle(TemplateHandle
);
360 goto OpenWithoutReparseSupport
;
363 /* Mark we are playing with a reparse point */
368 /* Get relative name of the directory */
369 if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory
, &NtPathU
, NULL
, &RelativeName
))
371 RtlReleaseRelativeName(&TemplateRelativeName
);
372 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
373 NtClose(TemplateHandle
);
374 SetLastError(ERROR_PATH_NOT_FOUND
);
378 /* Save its buffer for further freeing */
379 PathUBuffer
= NtPathU
.Buffer
;
381 /* Template & directory can't be the same */
382 if (RtlEqualUnicodeString(&NtPathU
,
386 RtlReleaseRelativeName(&RelativeName
);
387 RtlReleaseRelativeName(&TemplateRelativeName
);
388 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
389 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
390 NtClose(TemplateHandle
);
391 SetLastError(ERROR_INVALID_NAME
);
395 RtlReleaseRelativeName(&TemplateRelativeName
);
396 RtlFreeHeap(RtlGetProcessHeap(), 0, TemplateBuffer
);
398 /* Check if path length is < MAX_PATH (with space for file name).
399 * If not, prefix is required.
401 if (NtPathU
.Length
> (MAX_PATH
- SFN_LENGTH
) * sizeof(WCHAR
) && lpNewDirectory
[0] != L
'\\' &&
402 lpNewDirectory
[1] != L
'\\' && lpNewDirectory
[2] != L
'?' && lpNewDirectory
[3] != L
'\\')
404 /* Get file name position and full path length */
405 Length
= GetFullPathNameW(lpNewDirectory
, 0, NULL
, &FilePart
);
408 RtlReleaseRelativeName(&RelativeName
);
409 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
410 CloseHandle(TemplateHandle
);
411 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
415 /* Keep place for 8.3 file name */
416 Length
+= SFN_LENGTH
;
417 /* No prefix, so, must be smaller than MAX_PATH */
418 if (Length
> MAX_PATH
)
420 RtlReleaseRelativeName(&RelativeName
);
421 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
422 CloseHandle(TemplateHandle
);
423 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
428 /* If we have relative name (and root dir), use them instead */
429 if (RelativeName
.RelativeName
.Length
!= 0)
431 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
432 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
433 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
437 RelativeName
.ContainingDirectory
= NULL
;
440 /* Get extended attributes */
441 Status
= NtQueryInformationFile(TemplateHandle
,
446 if (!NT_SUCCESS(Status
))
448 RtlReleaseRelativeName(&RelativeName
);
449 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
450 CloseHandle(TemplateHandle
);
451 BaseSetLastNTError(Status
);
455 /* Start reading extended attributes */
456 if (FileEaInfo
.EaSize
!= 0)
458 for (EaLength
= FileEaInfo
.EaSize
* 2; ; EaLength
= EaLength
* 2)
460 /* Allocate buffer for reading */
461 EaBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, EaLength
);
464 RtlReleaseRelativeName(&RelativeName
);
465 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
466 CloseHandle(TemplateHandle
);
467 BaseSetLastNTError(STATUS_NO_MEMORY
);
472 Status
= NtQueryEaFile(TemplateHandle
,
481 if (!NT_SUCCESS(Status
))
483 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer
);
484 IoStatusBlock
.Information
= 0;
487 /* If we don't fail because of too small buffer, stop here */
488 if (Status
!= STATUS_BUFFER_OVERFLOW
&&
489 Status
!= STATUS_BUFFER_TOO_SMALL
)
491 EaLength
= IoStatusBlock
.Information
;
497 InitializeObjectAttributes(&ObjectAttributes
,
499 OBJ_CASE_INSENSITIVE
,
500 RelativeName
.ContainingDirectory
,
501 (lpSecurityAttributes
? lpSecurityAttributes
->lpSecurityDescriptor
: NULL
));
503 /* Ensure attributes are valid */
504 FileBasicInfo
.FileAttributes
&= FILE_ATTRIBUTE_VALID_FLAGS
;
506 /* Create the new directory */
507 Status
= NtCreateFile(&DirectoryHandle
,
508 FILE_LIST_DIRECTORY
| SYNCHRONIZE
| FILE_WRITE_ATTRIBUTES
|
509 FILE_READ_ATTRIBUTES
| (FileBasicInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
? FILE_ADD_FILE
: 0),
513 FileBasicInfo
.FileAttributes
,
514 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
516 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
517 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
,
520 if (!NT_SUCCESS(Status
))
522 if (Status
== STATUS_INVALID_PARAMETER
|| Status
== STATUS_ACCESS_DENIED
)
524 /* If creation failed, it might be because FSD doesn't support reparse points
525 * Retry without asking for such support in case template is not a reparse point
529 Status
= NtCreateFile(&DirectoryHandle
,
530 FILE_LIST_DIRECTORY
| SYNCHRONIZE
|
531 FILE_WRITE_ATTRIBUTES
| FILE_READ_ATTRIBUTES
,
535 FileBasicInfo
.FileAttributes
,
536 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
538 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
539 FILE_OPEN_FOR_BACKUP_INTENT
,
545 RtlReleaseRelativeName(&RelativeName
);
546 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
549 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer
);
551 CloseHandle(TemplateHandle
);
552 BaseSetLastNTError(Status
);
558 RtlReleaseRelativeName(&RelativeName
);
559 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
562 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer
);
565 if (!NT_SUCCESS(Status
))
567 NtClose(TemplateHandle
);
568 if (RtlIsDosDeviceName_U(lpNewDirectory
))
570 Status
= STATUS_NOT_A_DIRECTORY
;
572 BaseSetLastNTError(Status
);
576 /* If template is a reparse point, copy reparse data */
579 ReparseDataBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
580 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
581 if (!ReparseDataBuffer
)
583 NtClose(TemplateHandle
);
584 NtClose(DirectoryHandle
);
585 SetLastError(STATUS_NO_MEMORY
);
589 /* First query data */
590 Status
= NtFsControlFile(TemplateHandle
,
595 FSCTL_GET_REPARSE_POINT
,
599 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
600 if (!NT_SUCCESS(Status
))
602 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
603 NtClose(TemplateHandle
);
604 NtClose(DirectoryHandle
);
605 SetLastError(Status
);
609 /* Once again, ensure it is a mount point */
610 if (ReparseDataBuffer
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
612 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
613 NtClose(TemplateHandle
);
614 NtClose(DirectoryHandle
);
615 SetLastError(STATUS_OBJECT_NAME_INVALID
);
619 /* Get volume name */
620 SubstituteName
= (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
621 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
);
622 if (IS_VOLUME_NAME(SubstituteName
, ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
))
624 /* Prepare to define a new mount point for that volume */
625 RtlInitUnicodeString(&NewDirectory
, lpNewDirectory
);
626 NewDirectory
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewDirectory
.Length
+ 2 * sizeof(WCHAR
));
627 if (!NewDirectory
.Buffer
)
629 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
630 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
631 NtClose(TemplateHandle
);
632 NtClose(DirectoryHandle
);
636 RtlCopyMemory(&NewDirectory
.Buffer
, lpNewDirectory
, NewDirectory
.Length
);
637 if (NewDirectory
.Buffer
[NewDirectory
.Length
/ sizeof(WCHAR
)] != L
'\\')
639 NewDirectory
.Buffer
[NewDirectory
.Length
/ sizeof(WCHAR
)] = L
'\\';
640 NewDirectory
.Buffer
[(NewDirectory
.Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
643 /* Define a new mount point for that volume */
644 SetVolumeMountPointW(NewDirectory
.Buffer
, SubstituteName
);
646 RtlFreeHeap(RtlGetProcessHeap(), 0, NewDirectory
.Buffer
);
647 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
648 NtClose(TemplateHandle
);
649 NtClose(DirectoryHandle
);
653 /* Otherwise copy data raw */
654 Status
= NtFsControlFile(DirectoryHandle
,
659 FSCTL_SET_REPARSE_POINT
,
661 ReparseDataBuffer
->ReparseDataLength
+
662 FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
.PathBuffer
),
666 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
667 NtClose(TemplateHandle
);
668 NtClose(DirectoryHandle
);
670 if (NT_SUCCESS(Status
))
675 BaseSetLastNTError(Status
);
678 /* In case it's not a reparse point, handle streams on the file */
681 for (StreamSize
= 0x1000; ; StreamSize
= StreamSize
* 2)
683 FileStreamInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize
);
686 BaseMarkFileForDelete(DirectoryHandle
, FileBasicInfo
.FileAttributes
);
687 SetLastError(STATUS_NO_MEMORY
);
691 /* Query stream information */
692 Status
= NtQueryInformationFile(TemplateHandle
,
696 FileStreamInformation
);
697 if (NT_SUCCESS(Status
))
702 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo
);
703 FileStreamInfo
= NULL
;
705 /* If it failed, ensure that's not because of too small buffer */
706 if (Status
!= STATUS_BUFFER_OVERFLOW
&&
707 Status
!= STATUS_BUFFER_TOO_SMALL
)
713 if (!NT_SUCCESS(Status
) || IoStatusBlock
.Information
== 0)
717 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo
);
720 NtClose(TemplateHandle
);
721 NtClose(DirectoryHandle
);
727 DPRINT1("Warning: streams copying is unimplemented!\n");
728 RtlFreeHeap(RtlGetProcessHeap(), 0, FileStreamInfo
);
729 NtClose(TemplateHandle
);
730 NtClose(DirectoryHandle
);
741 RemoveDirectoryA(IN LPCSTR lpPathName
)
743 PUNICODE_STRING PathNameW
;
745 PathNameW
= Basep8BitStringToStaticUnicodeString(lpPathName
);
751 return RemoveDirectoryW(PathNameW
->Buffer
);
759 RemoveDirectoryW(IN LPCWSTR lpPathName
)
763 HANDLE DirectoryHandle
;
764 IO_STATUS_BLOCK IoStatusBlock
;
765 UNICODE_STRING NtPathU
, PathName
;
766 RTL_RELATIVE_NAME_U RelativeName
;
767 PWSTR PathUBuffer
, SubstituteName
;
768 OBJECT_ATTRIBUTES ObjectAttributes
;
769 PREPARSE_DATA_BUFFER ReparseDataBuffer
;
770 FILE_DISPOSITION_INFORMATION FileDispInfo
;
771 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo
;
773 /* Get relative name */
774 if (!RtlDosPathNameToRelativeNtPathName_U(lpPathName
, &NtPathU
, NULL
, &RelativeName
))
776 SetLastError(ERROR_PATH_NOT_FOUND
);
780 /* Save buffer to allow later freeing */
781 PathUBuffer
= NtPathU
.Buffer
;
783 /* If we have relative name (and root dir), use them instead */
784 if (RelativeName
.RelativeName
.Length
!= 0)
786 NtPathU
.Length
= RelativeName
.RelativeName
.Length
;
787 NtPathU
.MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
788 NtPathU
.Buffer
= RelativeName
.RelativeName
.Buffer
;
792 RelativeName
.ContainingDirectory
= NULL
;
795 InitializeObjectAttributes(&ObjectAttributes
,
797 OBJ_CASE_INSENSITIVE
,
798 RelativeName
.ContainingDirectory
,
801 /* Try to open directory */
802 Status
= NtOpenFile(&DirectoryHandle
,
803 DELETE
| SYNCHRONIZE
| FAILED_ACCESS_ACE_FLAG
,
806 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
807 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
808 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
);
809 if (!NT_SUCCESS(Status
))
811 /* We only accept failure for reparse points not being supported */
812 if (Status
!= STATUS_INVALID_PARAMETER
)
817 /* Try to open, with reparse points support */
818 Status
= NtOpenFile(&DirectoryHandle
,
819 DELETE
| SYNCHRONIZE
,
822 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
823 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
824 FILE_OPEN_FOR_BACKUP_INTENT
);
825 if (!NT_SUCCESS(Status
))
830 /* Success, mark directory */
831 goto MarkFileForDelete
;
834 /* Get information about file (and reparse point) */
835 Status
= NtQueryInformationFile(DirectoryHandle
,
839 FileAttributeTagInformation
);
840 if (!NT_SUCCESS(Status
))
842 /* FSD might not support querying reparse points information */
843 if (Status
!= STATUS_NOT_IMPLEMENTED
&&
844 Status
!= STATUS_INVALID_PARAMETER
)
849 /* If that's the case, then just delete directory */
850 goto MarkFileForDelete
;
853 /* If that's not a reparse point, nothing more to do than just delete */
854 if (!(FileTagInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
856 goto MarkFileForDelete
;
859 /* Check if that's a mount point */
860 if (FileTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
863 NtClose(DirectoryHandle
);
865 /* So, try to reopen directory, ignoring mount point */
866 Status
= NtOpenFile(&DirectoryHandle
,
867 DELETE
| SYNCHRONIZE
,
870 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
871 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
872 FILE_OPEN_FOR_BACKUP_INTENT
);
873 if (NT_SUCCESS(Status
))
875 /* It succeed, we can safely delete directory (and ignore reparse point) */
876 goto MarkFileForDelete
;
879 /* If it failed, only allow case where IO mount point was ignored */
880 if (Status
!= STATUS_IO_REPARSE_TAG_NOT_HANDLED
)
885 /* Reopen with reparse point support */
886 Status
= NtOpenFile(&DirectoryHandle
,
887 DELETE
| SYNCHRONIZE
,
890 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
891 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
892 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
);
893 if (NT_SUCCESS(Status
))
895 /* And mark for delete */
896 goto MarkFileForDelete
;
902 /* Here, we have a mount point, prepare to query information about it */
903 ReparseDataBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
904 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
905 if (!ReparseDataBuffer
)
907 RtlReleaseRelativeName(&RelativeName
);
908 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
909 NtClose(DirectoryHandle
);
910 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
915 if (!DeviceIoControl(DirectoryHandle
,
916 FSCTL_GET_REPARSE_POINT
,
919 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
,
923 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
924 goto MarkFileForDelete
;
927 /* Get volume name */
928 SubstituteName
= (PWSTR
)((ULONG_PTR
)ReparseDataBuffer
->MountPointReparseBuffer
.PathBuffer
+
929 ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
);
930 if (!IS_VOLUME_NAME(SubstituteName
, ReparseDataBuffer
->MountPointReparseBuffer
.SubstituteNameLength
))
932 /* This is not a volume, we can safely delete */
933 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
934 goto MarkFileForDelete
;
937 /* Prepare to delete mount point */
938 RtlInitUnicodeString(&PathName
, lpPathName
);
939 PathName
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathName
.Length
+ 2 * sizeof(WCHAR
));
940 if (!PathName
.Buffer
)
942 RtlReleaseRelativeName(&RelativeName
);
943 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
944 NtClose(DirectoryHandle
);
945 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
949 RtlCopyMemory(&PathName
.Buffer
, lpPathName
, PathName
.Length
);
950 if (PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] != L
'\\')
952 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] = L
'\\';
953 PathName
.Buffer
[(PathName
.Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
956 /* Delete mount point for that volume */
957 DeleteVolumeMountPointW(PathName
.Buffer
);
958 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
959 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer
);
961 /* And mark directory for delete */
963 RtlReleaseRelativeName(&RelativeName
);
964 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
967 FileDispInfo
.DeleteFile
= TRUE
;
968 Status
= NtSetInformationFile(DirectoryHandle
,
971 sizeof(FILE_DISPOSITION_INFORMATION
),
972 FileDispositionInformation
);
973 NtClose(DirectoryHandle
);
975 if (!NT_SUCCESS(Status
))
977 BaseSetLastNTError(Status
);
984 NtClose(DirectoryHandle
);
987 RtlReleaseRelativeName(&RelativeName
);
988 RtlFreeHeap(RtlGetProcessHeap(), 0, PathUBuffer
);
989 BaseSetLastNTError(Status
);