- Merge from trunk up to r45543
[reactos.git] / dll / win32 / kernel32 / file / dir.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/dir.c
6 * PURPOSE: Directory functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /*
13 * NOTES: Changed to using ZwCreateFile
14 */
15
16 /* INCLUDES ******************************************************************/
17
18 #include <k32.h>
19 #include <wine/debug.h>
20
21 WINE_DEFAULT_DEBUG_CHANNEL(kernel32file);
22
23 UNICODE_STRING DllDirectory = {0, 0, NULL};
24
25 /* FUNCTIONS *****************************************************************/
26
27 /*
28 * @implemented
29 */
30 BOOL
31 WINAPI
32 CreateDirectoryA (
33 LPCSTR lpPathName,
34 LPSECURITY_ATTRIBUTES lpSecurityAttributes
35 )
36 {
37 PWCHAR PathNameW;
38
39 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
40 return FALSE;
41
42 return CreateDirectoryW (PathNameW,
43 lpSecurityAttributes);
44 }
45
46
47 /*
48 * @implemented
49 */
50 BOOL
51 WINAPI
52 CreateDirectoryExA (
53 LPCSTR lpTemplateDirectory,
54 LPCSTR lpNewDirectory,
55 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
56 {
57 PWCHAR TemplateDirectoryW;
58 PWCHAR NewDirectoryW;
59 BOOL ret;
60
61 if (!(TemplateDirectoryW = FilenameA2W(lpTemplateDirectory, TRUE)))
62 return FALSE;
63
64 if (!(NewDirectoryW = FilenameA2W(lpNewDirectory, FALSE)))
65 {
66 RtlFreeHeap (RtlGetProcessHeap (),
67 0,
68 TemplateDirectoryW);
69 return FALSE;
70 }
71
72 ret = CreateDirectoryExW (TemplateDirectoryW,
73 NewDirectoryW,
74 lpSecurityAttributes);
75
76 RtlFreeHeap (RtlGetProcessHeap (),
77 0,
78 TemplateDirectoryW);
79
80 return ret;
81 }
82
83
84 /*
85 * @implemented
86 */
87 BOOL
88 WINAPI
89 CreateDirectoryW (
90 LPCWSTR lpPathName,
91 LPSECURITY_ATTRIBUTES lpSecurityAttributes
92 )
93 {
94 OBJECT_ATTRIBUTES ObjectAttributes;
95 IO_STATUS_BLOCK IoStatusBlock;
96 UNICODE_STRING NtPathU;
97 HANDLE DirectoryHandle = NULL;
98 NTSTATUS Status;
99
100 TRACE ("lpPathName %S lpSecurityAttributes %p\n",
101 lpPathName, lpSecurityAttributes);
102
103 if (!RtlDosPathNameToNtPathName_U (lpPathName,
104 &NtPathU,
105 NULL,
106 NULL))
107 {
108 SetLastError(ERROR_PATH_NOT_FOUND);
109 return FALSE;
110 }
111
112 InitializeObjectAttributes(&ObjectAttributes,
113 &NtPathU,
114 OBJ_CASE_INSENSITIVE,
115 NULL,
116 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL));
117
118 Status = NtCreateFile (&DirectoryHandle,
119 FILE_LIST_DIRECTORY | SYNCHRONIZE,
120 &ObjectAttributes,
121 &IoStatusBlock,
122 NULL,
123 FILE_ATTRIBUTE_NORMAL,
124 FILE_SHARE_READ | FILE_SHARE_WRITE,
125 FILE_CREATE,
126 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
127 NULL,
128 0);
129
130 RtlFreeHeap (RtlGetProcessHeap (),
131 0,
132 NtPathU.Buffer);
133
134 if (!NT_SUCCESS(Status))
135 {
136 WARN("NtCreateFile failed with Status %lx\n", Status);
137 SetLastErrorByStatus(Status);
138 return FALSE;
139 }
140
141 NtClose (DirectoryHandle);
142
143 return TRUE;
144 }
145
146
147 /*
148 * @implemented
149 */
150 BOOL
151 WINAPI
152 CreateDirectoryExW (
153 LPCWSTR lpTemplateDirectory,
154 LPCWSTR lpNewDirectory,
155 LPSECURITY_ATTRIBUTES lpSecurityAttributes
156 )
157 {
158 OBJECT_ATTRIBUTES ObjectAttributes;
159 IO_STATUS_BLOCK IoStatusBlock;
160 UNICODE_STRING NtPathU, NtTemplatePathU;
161 HANDLE DirectoryHandle = NULL;
162 HANDLE TemplateHandle = NULL;
163 FILE_EA_INFORMATION EaInformation;
164 FILE_BASIC_INFORMATION FileBasicInfo;
165 NTSTATUS Status;
166 ULONG OpenOptions, CreateOptions;
167 ACCESS_MASK DesiredAccess;
168 BOOLEAN ReparsePoint = FALSE;
169 PVOID EaBuffer = NULL;
170 ULONG EaLength = 0;
171
172 OpenOptions = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT |
173 FILE_OPEN_FOR_BACKUP_INTENT;
174 CreateOptions = FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT;
175 DesiredAccess = FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES |
176 FILE_READ_ATTRIBUTES;
177
178 TRACE ("lpTemplateDirectory %ws lpNewDirectory %ws lpSecurityAttributes %p\n",
179 lpTemplateDirectory, lpNewDirectory, lpSecurityAttributes);
180
181 /*
182 * Translate the template directory path
183 */
184
185 if (!RtlDosPathNameToNtPathName_U (lpTemplateDirectory,
186 &NtTemplatePathU,
187 NULL,
188 NULL))
189 {
190 SetLastError(ERROR_PATH_NOT_FOUND);
191 return FALSE;
192 }
193
194 InitializeObjectAttributes(&ObjectAttributes,
195 &NtTemplatePathU,
196 OBJ_CASE_INSENSITIVE,
197 NULL,
198 NULL);
199
200 /*
201 * Open the template directory
202 */
203
204 OpenTemplateDir:
205 Status = NtOpenFile (&TemplateHandle,
206 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA,
207 &ObjectAttributes,
208 &IoStatusBlock,
209 FILE_SHARE_READ | FILE_SHARE_WRITE,
210 OpenOptions);
211 if (!NT_SUCCESS(Status))
212 {
213 if (Status == STATUS_INVALID_PARAMETER &&
214 (OpenOptions & FILE_OPEN_REPARSE_POINT))
215 {
216 /* Some FSs (FAT) don't support reparse points, try opening
217 the directory without FILE_OPEN_REPARSE_POINT */
218 OpenOptions &= ~FILE_OPEN_REPARSE_POINT;
219
220 TRACE("Reparse points not supported, try with less options\n");
221
222 /* try again */
223 goto OpenTemplateDir;
224 }
225 else
226 {
227 WARN("Failed to open the template directory: 0x%x\n", Status);
228 goto CleanupNoNtPath;
229 }
230 }
231
232 /*
233 * Translate the new directory path and check if they're the same
234 */
235
236 if (!RtlDosPathNameToNtPathName_U (lpNewDirectory,
237 &NtPathU,
238 NULL,
239 NULL))
240 {
241 Status = STATUS_OBJECT_PATH_NOT_FOUND;
242 goto CleanupNoNtPath;
243 }
244
245 if (RtlEqualUnicodeString(&NtPathU,
246 &NtTemplatePathU,
247 TRUE))
248 {
249 WARN("Both directory paths are the same!\n");
250 Status = STATUS_OBJECT_NAME_INVALID;
251 goto Cleanup;
252 }
253
254 InitializeObjectAttributes(&ObjectAttributes,
255 &NtPathU,
256 OBJ_CASE_INSENSITIVE,
257 NULL,
258 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL));
259
260 /*
261 * Query the basic file attributes from the template directory
262 */
263
264 /* Make sure FILE_ATTRIBUTE_NORMAL is used in case the information
265 isn't set by the FS */
266 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
267 Status = NtQueryInformationFile(TemplateHandle,
268 &IoStatusBlock,
269 &FileBasicInfo,
270 sizeof(FILE_BASIC_INFORMATION),
271 FileBasicInformation);
272 if (!NT_SUCCESS(Status))
273 {
274 WARN("Failed to query the basic directory attributes\n");
275 goto Cleanup;
276 }
277
278 /* clear the reparse point attribute if present. We're going to set the
279 reparse point later which will cause the attribute to be set */
280 if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
281 {
282 FileBasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
283
284 /* writing the extended attributes requires the FILE_WRITE_DATA
285 access right */
286 DesiredAccess |= FILE_WRITE_DATA;
287
288 CreateOptions |= FILE_OPEN_REPARSE_POINT;
289 ReparsePoint = TRUE;
290 }
291
292 /*
293 * Read the Extended Attributes if present
294 */
295
296 for (;;)
297 {
298 Status = NtQueryInformationFile(TemplateHandle,
299 &IoStatusBlock,
300 &EaInformation,
301 sizeof(FILE_EA_INFORMATION),
302 FileEaInformation);
303 if (NT_SUCCESS(Status) && (EaInformation.EaSize != 0))
304 {
305 EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
306 0,
307 EaInformation.EaSize);
308 if (EaBuffer == NULL)
309 {
310 Status = STATUS_INSUFFICIENT_RESOURCES;
311 break;
312 }
313
314 Status = NtQueryEaFile(TemplateHandle,
315 &IoStatusBlock,
316 EaBuffer,
317 EaInformation.EaSize,
318 FALSE,
319 NULL,
320 0,
321 NULL,
322 TRUE);
323
324 if (NT_SUCCESS(Status))
325 {
326 /* we successfully read the extended attributes */
327 EaLength = EaInformation.EaSize;
328 break;
329 }
330 else
331 {
332 RtlFreeHeap(RtlGetProcessHeap(),
333 0,
334 EaBuffer);
335 EaBuffer = NULL;
336
337 if (Status != STATUS_BUFFER_TOO_SMALL)
338 {
339 /* unless we just allocated not enough memory, break the loop
340 and just continue without copying extended attributes */
341 break;
342 }
343 }
344 }
345 else
346 {
347 /* failure or no extended attributes present, break the loop */
348 break;
349 }
350 }
351
352 if (!NT_SUCCESS(Status))
353 {
354 WARN("Querying the EA data failed: 0x%x\n", Status);
355 goto Cleanup;
356 }
357
358 /*
359 * Create the new directory
360 */
361
362 Status = NtCreateFile (&DirectoryHandle,
363 DesiredAccess,
364 &ObjectAttributes,
365 &IoStatusBlock,
366 NULL,
367 FileBasicInfo.FileAttributes,
368 FILE_SHARE_READ | FILE_SHARE_WRITE,
369 FILE_CREATE,
370 CreateOptions,
371 EaBuffer,
372 EaLength);
373 if (!NT_SUCCESS(Status))
374 {
375 if (ReparsePoint &&
376 (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED))
377 {
378 /* The FS doesn't seem to support reparse points... */
379 WARN("Cannot copy the hardlink, destination doesn\'t support reparse points!\n");
380 }
381
382 goto Cleanup;
383 }
384
385 if (ReparsePoint)
386 {
387 /*
388 * Copy the reparse point
389 */
390
391 PREPARSE_GUID_DATA_BUFFER ReparseDataBuffer =
392 (PREPARSE_GUID_DATA_BUFFER)RtlAllocateHeap(RtlGetProcessHeap(),
393 0,
394 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
395
396 if (ReparseDataBuffer == NULL)
397 {
398 Status = STATUS_INSUFFICIENT_RESOURCES;
399 goto Cleanup;
400 }
401
402 /* query the size of the reparse data buffer structure */
403 Status = NtFsControlFile(TemplateHandle,
404 NULL,
405 NULL,
406 NULL,
407 &IoStatusBlock,
408 FSCTL_GET_REPARSE_POINT,
409 NULL,
410 0,
411 ReparseDataBuffer,
412 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
413 if (NT_SUCCESS(Status))
414 {
415 /* write the reparse point */
416 Status = NtFsControlFile(DirectoryHandle,
417 NULL,
418 NULL,
419 NULL,
420 &IoStatusBlock,
421 FSCTL_SET_REPARSE_POINT,
422 ReparseDataBuffer,
423 MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
424 NULL,
425 0);
426 }
427
428 RtlFreeHeap(RtlGetProcessHeap(),
429 0,
430 ReparseDataBuffer);
431
432 if (!NT_SUCCESS(Status))
433 {
434 /* fail, we were unable to read the reparse point data! */
435 WARN("Querying or setting the reparse point failed: 0x%x\n", Status);
436 goto Cleanup;
437 }
438 }
439 else
440 {
441 /*
442 * Copy alternate file streams, if existing
443 */
444
445 /* FIXME - enumerate and copy the file streams */
446 }
447
448 /*
449 * We successfully created the directory and copied all information
450 * from the template directory
451 */
452 Status = STATUS_SUCCESS;
453
454 Cleanup:
455 RtlFreeHeap (RtlGetProcessHeap (),
456 0,
457 NtPathU.Buffer);
458
459 CleanupNoNtPath:
460 if (TemplateHandle != NULL)
461 {
462 NtClose(TemplateHandle);
463 }
464
465 RtlFreeHeap (RtlGetProcessHeap (),
466 0,
467 NtTemplatePathU.Buffer);
468
469 /* free the he extended attributes buffer */
470 if (EaBuffer != NULL)
471 {
472 RtlFreeHeap (RtlGetProcessHeap (),
473 0,
474 EaBuffer);
475 }
476
477 if (DirectoryHandle != NULL)
478 {
479 NtClose(DirectoryHandle);
480 }
481
482 if (!NT_SUCCESS(Status))
483 {
484 SetLastErrorByStatus(Status);
485 return FALSE;
486 }
487
488 return TRUE;
489 }
490
491
492 /*
493 * @implemented
494 */
495 BOOL
496 WINAPI
497 RemoveDirectoryA (
498 LPCSTR lpPathName
499 )
500 {
501 PWCHAR PathNameW;
502
503 TRACE("RemoveDirectoryA(%s)\n",lpPathName);
504
505 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
506 return FALSE;
507
508 return RemoveDirectoryW (PathNameW);
509 }
510
511
512 /*
513 * @implemented
514 */
515 BOOL
516 WINAPI
517 RemoveDirectoryW (
518 LPCWSTR lpPathName
519 )
520 {
521 FILE_DISPOSITION_INFORMATION FileDispInfo;
522 OBJECT_ATTRIBUTES ObjectAttributes;
523 IO_STATUS_BLOCK IoStatusBlock;
524 UNICODE_STRING NtPathU;
525 HANDLE DirectoryHandle = NULL;
526 NTSTATUS Status;
527
528 TRACE("lpPathName %S\n", lpPathName);
529
530 if (!RtlDosPathNameToNtPathName_U (lpPathName,
531 &NtPathU,
532 NULL,
533 NULL))
534 {
535 SetLastError(ERROR_PATH_NOT_FOUND);
536 return FALSE;
537 }
538
539 InitializeObjectAttributes(&ObjectAttributes,
540 &NtPathU,
541 OBJ_CASE_INSENSITIVE,
542 NULL,
543 NULL);
544
545 TRACE("NtPathU '%S'\n", NtPathU.Buffer);
546
547 Status = NtOpenFile(&DirectoryHandle,
548 DELETE | SYNCHRONIZE,
549 &ObjectAttributes,
550 &IoStatusBlock,
551 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
552 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
553
554 RtlFreeUnicodeString(&NtPathU);
555
556 if (!NT_SUCCESS(Status))
557 {
558 WARN("Status 0x%08x\n", Status);
559 SetLastErrorByStatus (Status);
560 return FALSE;
561 }
562
563 FileDispInfo.DeleteFile = TRUE;
564
565 Status = NtSetInformationFile (DirectoryHandle,
566 &IoStatusBlock,
567 &FileDispInfo,
568 sizeof(FILE_DISPOSITION_INFORMATION),
569 FileDispositionInformation);
570 NtClose(DirectoryHandle);
571
572 if (!NT_SUCCESS(Status))
573 {
574 SetLastErrorByStatus (Status);
575 return FALSE;
576 }
577
578 return TRUE;
579 }
580
581
582 /*
583 * @implemented
584 */
585 DWORD
586 WINAPI
587 GetFullPathNameA (
588 LPCSTR lpFileName,
589 DWORD nBufferLength,
590 LPSTR lpBuffer,
591 LPSTR *lpFilePart
592 )
593 {
594 WCHAR BufferW[MAX_PATH];
595 PWCHAR FileNameW;
596 DWORD ret;
597 LPWSTR FilePartW = NULL;
598
599 TRACE("GetFullPathNameA(lpFileName %s, nBufferLength %d, lpBuffer %p, "
600 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
601
602 if (!lpFileName)
603 {
604 SetLastError(ERROR_INVALID_PARAMETER);
605 return 0;
606 }
607
608 if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
609 return 0;
610
611 ret = GetFullPathNameW(FileNameW, MAX_PATH, BufferW, &FilePartW);
612
613 if (!ret)
614 return 0;
615
616 if (ret > MAX_PATH)
617 {
618 SetLastError(ERROR_FILENAME_EXCED_RANGE);
619 return 0;
620 }
621
622 ret = FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
623
624 if (ret < nBufferLength && lpFilePart)
625 {
626 /* if the path closed with '\', FilePart is NULL */
627 if (!FilePartW)
628 *lpFilePart=NULL;
629 else
630 *lpFilePart = (FilePartW - BufferW) + lpBuffer;
631 }
632
633 TRACE("GetFullPathNameA ret: lpBuffer %s lpFilePart %s\n",
634 lpBuffer, (lpFilePart == NULL) ? "NULL" : *lpFilePart);
635
636 return ret;
637 }
638
639
640 /*
641 * @implemented
642 */
643 DWORD
644 WINAPI
645 GetFullPathNameW (
646 LPCWSTR lpFileName,
647 DWORD nBufferLength,
648 LPWSTR lpBuffer,
649 LPWSTR *lpFilePart
650 )
651 {
652 ULONG Length;
653
654 TRACE("GetFullPathNameW(lpFileName %S, nBufferLength %d, lpBuffer %p, "
655 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
656
657 Length = RtlGetFullPathName_U ((LPWSTR)lpFileName,
658 nBufferLength * sizeof(WCHAR),
659 lpBuffer,
660 lpFilePart);
661
662 TRACE("GetFullPathNameW ret: lpBuffer %S lpFilePart %S Length %ld\n",
663 lpBuffer, (lpFilePart == NULL) ? L"NULL" : *lpFilePart, Length / sizeof(WCHAR));
664
665 return Length/sizeof(WCHAR);
666 }
667
668
669 /*
670 * NOTE: Copied from Wine.
671 * @implemented
672 */
673 DWORD
674 WINAPI
675 GetShortPathNameA (
676 LPCSTR longpath,
677 LPSTR shortpath,
678 DWORD shortlen
679 )
680 {
681 PWCHAR LongPathW;
682 WCHAR ShortPathW[MAX_PATH];
683 DWORD ret;
684
685 if (!longpath)
686 {
687 SetLastError(ERROR_INVALID_PARAMETER);
688 return 0;
689 }
690
691 if (!(LongPathW = FilenameA2W(longpath, FALSE)))
692 return 0;
693
694 ret = GetShortPathNameW(LongPathW, ShortPathW, MAX_PATH);
695
696 if (!ret)
697 return 0;
698
699 if (ret > MAX_PATH)
700 {
701 SetLastError(ERROR_FILENAME_EXCED_RANGE);
702 return 0;
703 }
704
705 return FilenameW2A_FitOrFail(shortpath, shortlen, ShortPathW, ret+1);
706 }
707
708
709 /*
710 * NOTE: Copied from Wine.
711 * @implemented
712 */
713 DWORD
714 WINAPI
715 GetShortPathNameW (
716 LPCWSTR longpath,
717 LPWSTR shortpath,
718 DWORD shortlen
719 )
720 {
721 WCHAR tmpshortpath[MAX_PATH];
722 LPCWSTR p;
723 DWORD sp = 0, lp = 0;
724 DWORD tmplen;
725 WIN32_FIND_DATAW wfd;
726 HANDLE goit;
727 UNICODE_STRING ustr;
728 WCHAR ustr_buf[8+1+3+1];
729
730 TRACE("GetShortPathNameW: %S\n",longpath);
731
732 if (!longpath)
733 {
734 SetLastError(ERROR_INVALID_PARAMETER);
735 return 0;
736 }
737 if (!longpath[0])
738 {
739 SetLastError(ERROR_BAD_PATHNAME);
740 return 0;
741 }
742
743 /* check for drive letter */
744 if (longpath[1] == ':' )
745 {
746 tmpshortpath[0] = longpath[0];
747 tmpshortpath[1] = ':';
748 sp = lp = 2;
749 }
750
751 ustr.Buffer = ustr_buf;
752 ustr.Length = 0;
753 ustr.MaximumLength = sizeof(ustr_buf);
754
755 while (longpath[lp])
756 {
757 /* check for path delimiters and reproduce them */
758 if (longpath[lp] == '\\' || longpath[lp] == '/')
759 {
760 if (!sp || tmpshortpath[sp-1] != '\\')
761 {
762 /* strip double "\\" */
763 tmpshortpath[sp] = '\\';
764 sp++;
765 }
766 tmpshortpath[sp] = 0; /* terminate string */
767 lp++;
768 continue;
769 }
770
771 for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
772 tmplen = p - (longpath + lp);
773 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
774 /* Check, if the current element is a valid dos name */
775 if (tmplen <= 8+1+3+1)
776 {
777 BOOLEAN spaces;
778 memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
779 ustr_buf[tmplen] = '\0';
780 ustr.Length = (USHORT)tmplen * sizeof(WCHAR);
781 if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
782 {
783 sp += tmplen;
784 lp += tmplen;
785 continue;
786 }
787 }
788
789 /* Check if the file exists and use the existing short file name */
790 goit = FindFirstFileW(tmpshortpath, &wfd);
791 if (goit == INVALID_HANDLE_VALUE) goto notfound;
792 FindClose(goit);
793 lstrcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
794 sp += lstrlenW(tmpshortpath + sp);
795 lp += tmplen;
796 }
797 tmpshortpath[sp] = 0;
798
799 tmplen = lstrlenW(tmpshortpath) + 1;
800 if (tmplen <= shortlen)
801 {
802 lstrcpyW(shortpath, tmpshortpath);
803 tmplen--; /* length without 0 */
804 }
805
806 return tmplen;
807
808 notfound:
809 SetLastError ( ERROR_FILE_NOT_FOUND );
810 return 0;
811 }
812
813
814 /*
815 * @implemented
816 */
817 DWORD
818 WINAPI
819 SearchPathA (
820 LPCSTR lpPath,
821 LPCSTR lpFileName,
822 LPCSTR lpExtension,
823 DWORD nBufferLength,
824 LPSTR lpBuffer,
825 LPSTR *lpFilePart
826 )
827 {
828 UNICODE_STRING PathU = { 0, 0, NULL };
829 UNICODE_STRING FileNameU = { 0, 0, NULL };
830 UNICODE_STRING ExtensionU = { 0, 0, NULL };
831 UNICODE_STRING BufferU = { 0, 0, NULL };
832 ANSI_STRING Path;
833 ANSI_STRING FileName;
834 ANSI_STRING Extension;
835 ANSI_STRING Buffer;
836 PWCHAR FilePartW;
837 DWORD RetValue = 0;
838 NTSTATUS Status = STATUS_SUCCESS;
839
840 RtlInitAnsiString (&Path,
841 (LPSTR)lpPath);
842 RtlInitAnsiString (&FileName,
843 (LPSTR)lpFileName);
844 RtlInitAnsiString (&Extension,
845 (LPSTR)lpExtension);
846
847 /* convert ansi (or oem) strings to unicode */
848 if (bIsFileApiAnsi)
849 {
850 Status = RtlAnsiStringToUnicodeString (&PathU,
851 &Path,
852 TRUE);
853 if (!NT_SUCCESS(Status))
854 goto Cleanup;
855
856 Status = RtlAnsiStringToUnicodeString (&FileNameU,
857 &FileName,
858 TRUE);
859 if (!NT_SUCCESS(Status))
860 goto Cleanup;
861
862 Status = RtlAnsiStringToUnicodeString (&ExtensionU,
863 &Extension,
864 TRUE);
865 if (!NT_SUCCESS(Status))
866 goto Cleanup;
867 }
868 else
869 {
870 Status = RtlOemStringToUnicodeString (&PathU,
871 &Path,
872 TRUE);
873 if (!NT_SUCCESS(Status))
874 goto Cleanup;
875 Status = RtlOemStringToUnicodeString (&FileNameU,
876 &FileName,
877 TRUE);
878 if (!NT_SUCCESS(Status))
879 goto Cleanup;
880
881 Status = RtlOemStringToUnicodeString (&ExtensionU,
882 &Extension,
883 TRUE);
884 if (!NT_SUCCESS(Status))
885 goto Cleanup;
886 }
887
888 BufferU.MaximumLength = min(nBufferLength * sizeof(WCHAR), USHRT_MAX);
889 BufferU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
890 0,
891 BufferU.MaximumLength);
892 if (BufferU.Buffer == NULL)
893 {
894 Status = STATUS_NO_MEMORY;
895 goto Cleanup;
896 }
897
898 Buffer.MaximumLength = min(nBufferLength, USHRT_MAX);
899 Buffer.Buffer = lpBuffer;
900
901 RetValue = SearchPathW (NULL == lpPath ? NULL : PathU.Buffer,
902 NULL == lpFileName ? NULL : FileNameU.Buffer,
903 NULL == lpExtension ? NULL : ExtensionU.Buffer,
904 nBufferLength,
905 BufferU.Buffer,
906 &FilePartW);
907
908 if (0 != RetValue)
909 {
910 BufferU.Length = wcslen(BufferU.Buffer) * sizeof(WCHAR);
911 /* convert ansi (or oem) string to unicode */
912 if (bIsFileApiAnsi)
913 Status = RtlUnicodeStringToAnsiString(&Buffer,
914 &BufferU,
915 FALSE);
916 else
917 Status = RtlUnicodeStringToOemString(&Buffer,
918 &BufferU,
919 FALSE);
920
921 if (NT_SUCCESS(Status) && Buffer.Buffer)
922 {
923 /* nul-terminate ascii string */
924 Buffer.Buffer[BufferU.Length / sizeof(WCHAR)] = '\0';
925
926 if (NULL != lpFilePart && BufferU.Length != 0)
927 {
928 *lpFilePart = strrchr (lpBuffer, '\\') + 1;
929 }
930 }
931 }
932
933 Cleanup:
934 RtlFreeHeap (RtlGetProcessHeap (),
935 0,
936 PathU.Buffer);
937 RtlFreeHeap (RtlGetProcessHeap (),
938 0,
939 FileNameU.Buffer);
940 RtlFreeHeap (RtlGetProcessHeap (),
941 0,
942 ExtensionU.Buffer);
943 RtlFreeHeap (RtlGetProcessHeap (),
944 0,
945 BufferU.Buffer);
946
947 if (!NT_SUCCESS(Status))
948 {
949 SetLastErrorByStatus(Status);
950 return 0;
951 }
952
953 return RetValue;
954 }
955
956
957 /***********************************************************************
958 * ContainsPath (Wine name: contains_pathW)
959 *
960 * Check if the file name contains a path; helper for SearchPathW.
961 * A relative path is not considered a path unless it starts with ./ or ../
962 */
963 static
964 BOOL
965 ContainsPath(LPCWSTR name)
966 {
967 if (RtlDetermineDosPathNameType_U(name) != RtlPathTypeRelative) return TRUE;
968 if (name[0] != '.') return FALSE;
969 if (name[1] == '/' || name[1] == '\\') return TRUE;
970 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
971 }
972
973
974 /*
975 * @implemented
976 */
977 DWORD
978 WINAPI
979 SearchPathW(LPCWSTR lpPath,
980 LPCWSTR lpFileName,
981 LPCWSTR lpExtension,
982 DWORD nBufferLength,
983 LPWSTR lpBuffer,
984 LPWSTR *lpFilePart)
985 {
986 DWORD ret = 0;
987
988 /* If the name contains an explicit path, ignore the path */
989 if (ContainsPath(lpFileName))
990 {
991 /* try first without extension */
992 if (RtlDoesFileExists_U(lpFileName))
993 return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
994
995 if (lpExtension)
996 {
997 LPCWSTR p = wcsrchr(lpFileName, '.');
998 if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
999 lpExtension = NULL; /* Ignore the specified extension */
1000 }
1001
1002 /* Allocate a buffer for the file name and extension */
1003 if (lpExtension)
1004 {
1005 LPWSTR tmp;
1006 DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
1007
1008 if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
1009 {
1010 SetLastError(ERROR_OUTOFMEMORY);
1011 return 0;
1012 }
1013 wcscpy(tmp, lpFileName);
1014 wcscat(tmp, lpExtension);
1015 if (RtlDoesFileExists_U(tmp))
1016 ret = GetFullPathNameW(tmp, nBufferLength, lpBuffer, lpFilePart);
1017 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1018 }
1019 }
1020 else if (lpPath && lpPath[0]) /* search in the specified path */
1021 {
1022 ret = RtlDosSearchPath_U(lpPath,
1023 lpFileName,
1024 lpExtension,
1025 nBufferLength * sizeof(WCHAR),
1026 lpBuffer,
1027 lpFilePart) / sizeof(WCHAR);
1028 }
1029 else /* search in the default path */
1030 {
1031 WCHAR *DllPath = GetDllLoadPath(NULL);
1032
1033 if (DllPath)
1034 {
1035 ret = RtlDosSearchPath_U(DllPath,
1036 lpFileName,
1037 lpExtension,
1038 nBufferLength * sizeof(WCHAR),
1039 lpBuffer,
1040 lpFilePart) / sizeof(WCHAR);
1041 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
1042 }
1043 else
1044 {
1045 SetLastError(ERROR_OUTOFMEMORY);
1046 return 0;
1047 }
1048 }
1049
1050 if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
1051
1052 return ret;
1053 }
1054
1055 /*
1056 * @implemented
1057 */
1058 BOOL
1059 WINAPI
1060 SetDllDirectoryW(
1061 LPCWSTR lpPathName
1062 )
1063 {
1064 UNICODE_STRING PathName;
1065
1066 RtlInitUnicodeString(&PathName, lpPathName);
1067
1068 RtlEnterCriticalSection(&DllLock);
1069 if(PathName.Length > 0)
1070 {
1071 if(PathName.Length + sizeof(WCHAR) <= DllDirectory.MaximumLength)
1072 {
1073 RtlCopyUnicodeString(&DllDirectory, &PathName);
1074 }
1075 else
1076 {
1077 RtlFreeUnicodeString(&DllDirectory);
1078 if(!(DllDirectory.Buffer = (PWSTR)RtlAllocateHeap(RtlGetProcessHeap(),
1079 0,
1080 PathName.Length + sizeof(WCHAR))))
1081 {
1082 RtlLeaveCriticalSection(&DllLock);
1083 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1084 return FALSE;
1085 }
1086 DllDirectory.Length = 0;
1087 DllDirectory.MaximumLength = PathName.Length + sizeof(WCHAR);
1088
1089 RtlCopyUnicodeString(&DllDirectory, &PathName);
1090 }
1091 }
1092 else
1093 {
1094 RtlFreeUnicodeString(&DllDirectory);
1095 }
1096 RtlLeaveCriticalSection(&DllLock);
1097
1098 return TRUE;
1099 }
1100
1101 /*
1102 * @implemented
1103 */
1104 BOOL
1105 WINAPI
1106 SetDllDirectoryA(
1107 LPCSTR lpPathName /* can be NULL */
1108 )
1109 {
1110 PWCHAR PathNameW=NULL;
1111
1112 if(lpPathName)
1113 {
1114 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
1115 return FALSE;
1116 }
1117
1118 return SetDllDirectoryW(PathNameW);
1119 }
1120
1121 /*
1122 * @implemented
1123 */
1124 DWORD
1125 WINAPI
1126 GetDllDirectoryW(
1127 DWORD nBufferLength,
1128 LPWSTR lpBuffer
1129 )
1130 {
1131 DWORD Ret;
1132
1133 RtlEnterCriticalSection(&DllLock);
1134 if(nBufferLength > 0)
1135 {
1136 Ret = DllDirectory.Length / sizeof(WCHAR);
1137 if(Ret > nBufferLength - 1)
1138 {
1139 Ret = nBufferLength - 1;
1140 }
1141
1142 if(Ret > 0)
1143 {
1144 RtlCopyMemory(lpBuffer, DllDirectory.Buffer, Ret * sizeof(WCHAR));
1145 }
1146 lpBuffer[Ret] = L'\0';
1147 }
1148 else
1149 {
1150 /* include termination character, even if the string is empty! */
1151 Ret = (DllDirectory.Length / sizeof(WCHAR)) + 1;
1152 }
1153 RtlLeaveCriticalSection(&DllLock);
1154
1155 return Ret;
1156 }
1157
1158 /*
1159 * @implemented
1160 */
1161 DWORD
1162 WINAPI
1163 GetDllDirectoryA(
1164 DWORD nBufferLength,
1165 LPSTR lpBuffer
1166 )
1167 {
1168 WCHAR BufferW[MAX_PATH];
1169 DWORD ret;
1170
1171 ret = GetDllDirectoryW(MAX_PATH, BufferW);
1172
1173 if (!ret)
1174 return 0;
1175
1176 if (ret > MAX_PATH)
1177 {
1178 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1179 return 0;
1180 }
1181
1182 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1183 }
1184
1185
1186 /*
1187 * @implemented
1188 */
1189 BOOL WINAPI
1190 NeedCurrentDirectoryForExePathW(LPCWSTR ExeName)
1191 {
1192 static const WCHAR env_name[] = {'N','o','D','e','f','a','u','l','t',
1193 'C','u','r','r','e','n','t',
1194 'D','i','r','e','c','t','o','r','y',
1195 'I','n','E','x','e','P','a','t','h',0};
1196 WCHAR env_val;
1197
1198 /* MSDN mentions some 'registry location'. We do not use registry. */
1199 FIXME("(%s): partial stub\n", debugstr_w(ExeName));
1200
1201 if (wcschr(ExeName, L'\\'))
1202 return TRUE;
1203
1204 /* Check the existence of the variable, not value */
1205 if (!GetEnvironmentVariableW( env_name, &env_val, 1 ))
1206 return TRUE;
1207
1208 return FALSE;
1209 }
1210
1211
1212 /*
1213 * @implemented
1214 */
1215 BOOL WINAPI
1216 NeedCurrentDirectoryForExePathA(LPCSTR ExeName)
1217 {
1218 WCHAR *ExeNameW;
1219
1220 if (!(ExeNameW = FilenameA2W(ExeName, FALSE)))
1221 return TRUE;
1222
1223 return NeedCurrentDirectoryForExePathW(ExeNameW);
1224 }
1225
1226
1227
1228
1229
1230 /***********************************************************************
1231 * @implemented
1232 *
1233 * GetLongPathNameW (KERNEL32.@)
1234 *
1235 * NOTES
1236 * observed (Win2000):
1237 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1238 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1239 */
1240 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1241 {
1242 #define MAX_PATHNAME_LEN 1024
1243
1244 WCHAR tmplongpath[MAX_PATHNAME_LEN];
1245 LPCWSTR p;
1246 DWORD sp = 0, lp = 0;
1247 DWORD tmplen;
1248 BOOL unixabsolute;
1249 WIN32_FIND_DATAW wfd;
1250 HANDLE goit;
1251
1252 if (!shortpath)
1253 {
1254 SetLastError(ERROR_INVALID_PARAMETER);
1255 return 0;
1256 }
1257 if (!shortpath[0])
1258 {
1259 SetLastError(ERROR_PATH_NOT_FOUND);
1260 return 0;
1261 }
1262
1263 TRACE("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen);
1264
1265 if (shortpath[0] == '\\' && shortpath[1] == '\\')
1266 {
1267 WARN("ERR: UNC pathname %s\n", shortpath);
1268 lstrcpynW( longpath, shortpath, longlen );
1269 return wcslen(longpath);
1270 }
1271 unixabsolute = (shortpath[0] == '/');
1272 /* check for drive letter */
1273 if (!unixabsolute && shortpath[1] == ':' )
1274 {
1275 tmplongpath[0] = shortpath[0];
1276 tmplongpath[1] = ':';
1277 lp = sp = 2;
1278 }
1279
1280 while (shortpath[sp])
1281 {
1282 /* check for path delimiters and reproduce them */
1283 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
1284 {
1285 if (!lp || tmplongpath[lp-1] != '\\')
1286 {
1287 /* strip double "\\" */
1288 tmplongpath[lp++] = '\\';
1289 }
1290 tmplongpath[lp] = 0; /* terminate string */
1291 sp++;
1292 continue;
1293 }
1294
1295 p = shortpath + sp;
1296 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
1297 {
1298 tmplongpath[lp++] = *p++;
1299 tmplongpath[lp++] = *p++;
1300 }
1301 for (; *p && *p != '/' && *p != '\\'; p++);
1302 tmplen = p - (shortpath + sp);
1303 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
1304 /* Check if the file exists and use the existing file name */
1305 goit = FindFirstFileW(tmplongpath, &wfd);
1306 if (goit == INVALID_HANDLE_VALUE)
1307 {
1308 TRACE("not found %s!\n", tmplongpath);
1309 SetLastError ( ERROR_FILE_NOT_FOUND );
1310 return 0;
1311 }
1312 FindClose(goit);
1313 wcscpy(tmplongpath + lp, wfd.cFileName);
1314 lp += wcslen(tmplongpath + lp);
1315 sp += tmplen;
1316 }
1317 tmplen = wcslen(shortpath) - 1;
1318 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
1319 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
1320 tmplongpath[lp++] = shortpath[tmplen];
1321 tmplongpath[lp] = 0;
1322
1323 tmplen = wcslen(tmplongpath) + 1;
1324 if (tmplen <= longlen)
1325 {
1326 wcscpy(longpath, tmplongpath);
1327 TRACE("returning %s\n", longpath);
1328 tmplen--; /* length without 0 */
1329 }
1330
1331 return tmplen;
1332 }
1333
1334
1335
1336 /***********************************************************************
1337 * GetLongPathNameA (KERNEL32.@)
1338 */
1339 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1340 {
1341 WCHAR *shortpathW;
1342 WCHAR longpathW[MAX_PATH];
1343 DWORD ret;
1344
1345 TRACE("GetLongPathNameA %s, %i\n",shortpath,longlen );
1346
1347 if (!(shortpathW = FilenameA2W( shortpath, FALSE )))
1348 return 0;
1349
1350 ret = GetLongPathNameW(shortpathW, longpathW, MAX_PATH);
1351
1352 if (!ret) return 0;
1353 if (ret > MAX_PATH)
1354 {
1355 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1356 return 0;
1357 }
1358
1359 return FilenameW2A_FitOrFail(longpath, longlen, longpathW, ret+1 );
1360 }
1361
1362 /* EOF */