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