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