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