xref: /reactos/dll/win32/kernel32/client/file/dir.c (revision 40462c92)
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