[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / dir.c
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(GetProcessHeap(), 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(GetProcessHeap(), 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 ULONG EaLength = 0;
233 PVOID EaBuffer = NULL;
234 BOOL ReparsePoint = FALSE;
235 IO_STATUS_BLOCK IoStatusBlock;
236 FILE_EA_INFORMATION FileEaInfo;
237 OBJECT_ATTRIBUTES ObjectAttributes;
238 FILE_BASIC_INFORMATION FileBasicInfo;
239 PREPARSE_DATA_BUFFER ReparseDataBuffer;
240 HANDLE TemplateHandle, DirectoryHandle;
241 FILE_ATTRIBUTE_TAG_INFORMATION FileTagInfo;
242 UNICODE_STRING NtPathU, NtTemplatePathU, NewDirectory;
243 RTL_RELATIVE_NAME_U RelativeName, TemplateRelativeName;
244 PWSTR TemplateBuffer, PathUBuffer, FilePart, SubstituteName;
245
246 /* Get relative name of the template */
247 if (!RtlDosPathNameToRelativeNtPathName_U(lpTemplateDirectory, &NtTemplatePathU, NULL, &TemplateRelativeName))
248 {
249 SetLastError(ERROR_PATH_NOT_FOUND);
250 return FALSE;
251 }
252
253 /* Save buffer for further freeing */
254 TemplateBuffer = NtTemplatePathU.Buffer;
255
256 /* If we have relative name (and root dir), use them instead */
257 if (TemplateRelativeName.RelativeName.Length != 0)
258 {
259 NtTemplatePathU.Length = TemplateRelativeName.RelativeName.Length;
260 NtTemplatePathU.MaximumLength = TemplateRelativeName.RelativeName.MaximumLength;
261 NtTemplatePathU.Buffer = TemplateRelativeName.RelativeName.Buffer;
262 }
263 else
264 {
265 TemplateRelativeName.ContainingDirectory = NULL;
266 }
267
268 InitializeObjectAttributes(&ObjectAttributes,
269 &NtTemplatePathU,
270 OBJ_CASE_INSENSITIVE,
271 NULL,
272 NULL);
273
274 /* Open template directory */
275 Status = NtOpenFile(&TemplateHandle,
276 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA,
277 &ObjectAttributes,
278 &IoStatusBlock,
279 FILE_SHARE_READ | FILE_SHARE_WRITE,
280 FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_FOR_BACKUP_INTENT);
281 if (!NT_SUCCESS(Status))
282 {
283 if (Status != STATUS_INVALID_PARAMETER)
284 {
285 RtlReleaseRelativeName(&TemplateRelativeName);
286 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
287 BaseSetLastNTError(Status);
288 return FALSE;
289 }
290
291 OpenWithoutReparseSupport:
292 /* Opening failed due to lacking reparse points support in the FSD, try without */
293 Status = NtOpenFile(&TemplateHandle,
294 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA,
295 &ObjectAttributes,
296 &IoStatusBlock,
297 FILE_SHARE_READ | FILE_SHARE_WRITE,
298 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
299
300 if (!NT_SUCCESS(Status))
301 {
302 RtlReleaseRelativeName(&TemplateRelativeName);
303 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
304 BaseSetLastNTError(Status);
305 return FALSE;
306 }
307
308 /* Request file attributes */
309 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
310 Status = NtQueryInformationFile(TemplateHandle,
311 &IoStatusBlock,
312 &FileBasicInfo,
313 sizeof(FileBasicInfo),
314 FileBasicInformation);
315 if (!NT_SUCCESS(Status))
316 {
317 RtlReleaseRelativeName(&TemplateRelativeName);
318 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
319 CloseHandle(TemplateHandle);
320 BaseSetLastNTError(Status);
321 return FALSE;
322
323 }
324 }
325 else
326 {
327 /* Request file attributes */
328 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
329 Status = NtQueryInformationFile(TemplateHandle,
330 &IoStatusBlock,
331 &FileBasicInfo,
332 sizeof(FileBasicInfo),
333 FileBasicInformation);
334 if (!NT_SUCCESS(Status))
335 {
336 RtlReleaseRelativeName(&TemplateRelativeName);
337 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
338 CloseHandle(TemplateHandle);
339 BaseSetLastNTError(Status);
340 return FALSE;
341
342 }
343
344 /* If it is a reparse point, then get information about it */
345 if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
346 {
347 Status = NtQueryInformationFile(TemplateHandle,
348 &IoStatusBlock,
349 &FileTagInfo,
350 sizeof(FileTagInfo),
351 FileAttributeTagInformation);
352 if (!NT_SUCCESS(Status))
353 {
354 RtlReleaseRelativeName(&TemplateRelativeName);
355 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
356 CloseHandle(TemplateHandle);
357 BaseSetLastNTError(Status);
358 return FALSE;
359 }
360
361 /* Only mount points are supported, retry without if anything different */
362 if (FileTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
363 {
364 CloseHandle(TemplateHandle);
365 goto OpenWithoutReparseSupport;
366 }
367
368 /* Mark we are playing with a reparse point */
369 ReparsePoint = TRUE;
370 }
371 }
372
373 /* Get relative name of the directory */
374 if (!RtlDosPathNameToRelativeNtPathName_U(lpNewDirectory, &NtPathU, NULL, &RelativeName))
375 {
376 RtlReleaseRelativeName(&TemplateRelativeName);
377 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
378 NtClose(TemplateHandle);
379 SetLastError(ERROR_PATH_NOT_FOUND);
380 return FALSE;
381 }
382
383 /* Save its buffer for further freeing */
384 PathUBuffer = NtPathU.Buffer;
385
386 /* Template & directory can't be the same */
387 if (RtlEqualUnicodeString(&NtPathU,
388 &NtTemplatePathU,
389 TRUE))
390 {
391 RtlReleaseRelativeName(&RelativeName);
392 RtlReleaseRelativeName(&TemplateRelativeName);
393 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
394 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
395 NtClose(TemplateHandle);
396 SetLastError(ERROR_INVALID_NAME);
397 return FALSE;
398 }
399
400 RtlReleaseRelativeName(&TemplateRelativeName);
401 RtlFreeHeap(GetProcessHeap(), 0, TemplateBuffer);
402
403 /* Check if path length is < MAX_PATH (with space for file name).
404 * If not, prefix is required.
405 */
406 if (NtPathU.Length > (MAX_PATH - SFN_LENGTH) * sizeof(WCHAR) && lpNewDirectory[0] != L'\\' &&
407 lpNewDirectory[1] != L'\\' && lpNewDirectory[2] != L'?' && lpNewDirectory[3] != L'\\')
408 {
409 /* Get file name position and full path length */
410 Length = GetFullPathNameW(lpNewDirectory, 0, NULL, &FilePart);
411 if (Length == 0)
412 {
413 RtlReleaseRelativeName(&RelativeName);
414 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
415 CloseHandle(TemplateHandle);
416 SetLastError(ERROR_FILENAME_EXCED_RANGE);
417 return FALSE;
418 }
419
420 /* Keep place for 8.3 file name */
421 Length += SFN_LENGTH;
422 /* No prefix, so, must be smaller than MAX_PATH */
423 if (Length > MAX_PATH)
424 {
425 RtlReleaseRelativeName(&RelativeName);
426 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
427 CloseHandle(TemplateHandle);
428 SetLastError(ERROR_FILENAME_EXCED_RANGE);
429 return FALSE;
430 }
431 }
432
433 /* If we have relative name (and root dir), use them instead */
434 if (RelativeName.RelativeName.Length != 0)
435 {
436 NtPathU.Length = RelativeName.RelativeName.Length;
437 NtPathU.MaximumLength = RelativeName.RelativeName.MaximumLength;
438 NtPathU.Buffer = RelativeName.RelativeName.Buffer;
439 }
440 else
441 {
442 RelativeName.ContainingDirectory = NULL;
443 }
444
445 /* Get extended attributes */
446 Status = NtQueryInformationFile(TemplateHandle,
447 &IoStatusBlock,
448 &FileEaInfo,
449 sizeof(FileEaInfo),
450 FileEaInformation);
451 if (!NT_SUCCESS(Status))
452 {
453 RtlReleaseRelativeName(&RelativeName);
454 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
455 CloseHandle(TemplateHandle);
456 BaseSetLastNTError(Status);
457 return FALSE;
458 }
459
460 /* Start reading extended attributes */
461 if (FileEaInfo.EaSize != 0)
462 {
463 for (EaLength = FileEaInfo.EaSize * 2; ; EaLength = EaLength * 2)
464 {
465 /* Allocate buffer for reading */
466 EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, EaLength);
467 if (!EaBuffer)
468 {
469 RtlReleaseRelativeName(&RelativeName);
470 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
471 CloseHandle(TemplateHandle);
472 BaseSetLastNTError(STATUS_NO_MEMORY);
473 return FALSE;
474 }
475
476 /* Query EAs */
477 Status = NtQueryEaFile(TemplateHandle,
478 &IoStatusBlock,
479 EaBuffer,
480 EaLength,
481 FALSE,
482 NULL,
483 0,
484 NULL,
485 TRUE);
486 if (!NT_SUCCESS(Status))
487 {
488 RtlFreeHeap(GetProcessHeap(), 0, EaBuffer);
489 IoStatusBlock.Information = 0;
490 }
491
492 /* If we don't fail because of too small buffer, stop here */
493 if (Status != STATUS_BUFFER_OVERFLOW &&
494 Status != STATUS_BUFFER_TOO_SMALL)
495 {
496 EaLength = IoStatusBlock.Information;
497 break;
498 }
499 }
500 }
501
502 InitializeObjectAttributes(&ObjectAttributes,
503 &NtPathU,
504 OBJ_CASE_INSENSITIVE,
505 RelativeName.ContainingDirectory,
506 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL));
507
508 /* Ensure attributes are valid */
509 FileBasicInfo.FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS;
510
511 /* Create the new directory */
512 Status = NtCreateFile(&DirectoryHandle,
513 FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES |
514 FILE_READ_ATTRIBUTES | (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? FILE_ADD_FILE : 0),
515 &ObjectAttributes,
516 &IoStatusBlock,
517 NULL,
518 FileBasicInfo.FileAttributes,
519 FILE_SHARE_READ | FILE_SHARE_WRITE,
520 FILE_CREATE,
521 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
522 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
523 EaBuffer,
524 EaLength);
525 if (!NT_SUCCESS(Status))
526 {
527 if (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED)
528 {
529 /* If creation failed, it might be because FSD doesn't support reparse points
530 * Retry without asking for such support in case template is not a reparse point
531 */
532 if (!ReparsePoint)
533 {
534 Status = NtCreateFile(&DirectoryHandle,
535 FILE_LIST_DIRECTORY | SYNCHRONIZE |
536 FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES,
537 &ObjectAttributes,
538 &IoStatusBlock,
539 NULL,
540 FileBasicInfo.FileAttributes,
541 FILE_SHARE_READ | FILE_SHARE_WRITE,
542 FILE_CREATE,
543 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
544 FILE_OPEN_FOR_BACKUP_INTENT,
545 EaBuffer,
546 EaLength);
547 }
548 else
549 {
550 RtlReleaseRelativeName(&RelativeName);
551 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
552 if (EaBuffer)
553 {
554 RtlFreeHeap(GetProcessHeap(), 0, EaBuffer);
555 }
556 CloseHandle(TemplateHandle);
557 BaseSetLastNTError(Status);
558 return FALSE;
559 }
560 }
561 }
562
563 RtlReleaseRelativeName(&RelativeName);
564 RtlFreeHeap(GetProcessHeap(), 0, PathUBuffer);
565 if (EaBuffer)
566 {
567 RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer);
568 }
569
570 if (!NT_SUCCESS(Status))
571 {
572 NtClose(TemplateHandle);
573 if (RtlIsDosDeviceName_U(lpNewDirectory))
574 {
575 Status = STATUS_NOT_A_DIRECTORY;
576 }
577 BaseSetLastNTError(Status);
578 return FALSE;
579 }
580
581 /* If template is a reparse point, copy reparse data */
582 if (ReparsePoint)
583 {
584 ReparseDataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
585 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
586 if (!ReparseDataBuffer)
587 {
588 NtClose(TemplateHandle);
589 NtClose(DirectoryHandle);
590 SetLastError(STATUS_NO_MEMORY);
591 return FALSE;
592 }
593
594 /* First query data */
595 Status = NtFsControlFile(TemplateHandle,
596 NULL,
597 NULL,
598 NULL,
599 &IoStatusBlock,
600 FSCTL_GET_REPARSE_POINT,
601 NULL,
602 0,
603 ReparseDataBuffer,
604 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
605 if (!NT_SUCCESS(Status))
606 {
607 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer);
608 NtClose(TemplateHandle);
609 NtClose(DirectoryHandle);
610 SetLastError(Status);
611 return FALSE;
612 }
613
614 /* Once again, ensure it is a mount point */
615 if (ReparseDataBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
616 {
617 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer);
618 NtClose(TemplateHandle);
619 NtClose(DirectoryHandle);
620 SetLastError(STATUS_OBJECT_NAME_INVALID);
621 return FALSE;
622 }
623
624 /* Get volume name */
625 SubstituteName = (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer +
626 ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset);
627 if (IS_VOLUME_NAME(SubstituteName, ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength))
628 {
629 /* Prepare to define a new mount point for that volume */
630 RtlInitUnicodeString(&NewDirectory, lpNewDirectory);
631 NewDirectory.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewDirectory.Length + 2 * sizeof(WCHAR));
632 if (!NewDirectory.Buffer)
633 {
634 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
635 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer);
636 NtClose(TemplateHandle);
637 NtClose(DirectoryHandle);
638 return FALSE;
639 }
640
641 RtlCopyMemory(&NewDirectory.Buffer, lpNewDirectory, NewDirectory.Length);
642 if (NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] != L'\\')
643 {
644 NewDirectory.Buffer[NewDirectory.Length / sizeof(WCHAR)] = L'\\';
645 NewDirectory.Buffer[(NewDirectory.Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
646 }
647
648 /* Define a new mount point for that volume */
649 SetVolumeMountPointW(NewDirectory.Buffer, SubstituteName);
650
651 RtlFreeHeap(RtlGetProcessHeap(), 0, NewDirectory.Buffer);
652 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer);
653 NtClose(TemplateHandle);
654 NtClose(DirectoryHandle);
655 return TRUE;
656 }
657
658 /* Otherwise copy data raw */
659 Status = NtFsControlFile(DirectoryHandle,
660 NULL,
661 NULL,
662 NULL,
663 &IoStatusBlock,
664 FSCTL_SET_REPARSE_POINT,
665 ReparseDataBuffer,
666 ReparseDataBuffer->ReparseDataLength +
667 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer),
668 NULL,
669 0);
670
671 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseDataBuffer);
672 NtClose(TemplateHandle);
673 NtClose(DirectoryHandle);
674
675 if (NT_SUCCESS(Status))
676 {
677 return TRUE;
678 }
679
680 BaseSetLastNTError(Status);
681 return FALSE;
682 }
683 /* In case it's not a reparse point, handle streams on the file */
684 else
685 {
686 PVOID StreamInfo;
687 ULONG StreamSize = 0x1000;
688 for (;;)
689 {
690 StreamInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, StreamSize);
691 if (!StreamInfo)
692 {
693 BaseMarkFileForDelete(DirectoryHandle, FileBasicInfo.FileAttributes);
694 SetLastError(STATUS_NO_MEMORY);
695 break;
696 }
697
698 /* Query stream information */
699 Status = NtQueryInformationFile(TemplateHandle,
700 &IoStatusBlock,
701 StreamInfo,
702 StreamSize,
703 FileStreamInformation);
704 if (!NT_SUCCESS(Status))
705 {
706 RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo);
707 StreamInfo = NULL;
708 StreamSize << 1;
709 }
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 (StreamInfo)
722 {
723 RtlFreeHeap(RtlGetProcessHeap(), 0, StreamInfo);
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, StreamInfo);
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 */