Sync with trunk (r48545)
[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 #define NDEBUG
20 #include <debug.h>
21 static ULONG gDebugChannel = 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[0] != '/' && 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)
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 if (!lpFileName)
841 {
842 SetLastError(ERROR_INVALID_PARAMETER);
843 return 0;
844 }
845
846 RtlInitAnsiString (&Path,
847 (LPSTR)lpPath);
848 RtlInitAnsiString (&FileName,
849 (LPSTR)lpFileName);
850 RtlInitAnsiString (&Extension,
851 (LPSTR)lpExtension);
852
853 /* convert ansi (or oem) strings to unicode */
854 if (bIsFileApiAnsi)
855 {
856 Status = RtlAnsiStringToUnicodeString (&PathU,
857 &Path,
858 TRUE);
859 if (!NT_SUCCESS(Status))
860 goto Cleanup;
861
862 Status = RtlAnsiStringToUnicodeString (&FileNameU,
863 &FileName,
864 TRUE);
865 if (!NT_SUCCESS(Status))
866 goto Cleanup;
867
868 Status = RtlAnsiStringToUnicodeString (&ExtensionU,
869 &Extension,
870 TRUE);
871 if (!NT_SUCCESS(Status))
872 goto Cleanup;
873 }
874 else
875 {
876 Status = RtlOemStringToUnicodeString (&PathU,
877 &Path,
878 TRUE);
879 if (!NT_SUCCESS(Status))
880 goto Cleanup;
881 Status = RtlOemStringToUnicodeString (&FileNameU,
882 &FileName,
883 TRUE);
884 if (!NT_SUCCESS(Status))
885 goto Cleanup;
886
887 Status = RtlOemStringToUnicodeString (&ExtensionU,
888 &Extension,
889 TRUE);
890 if (!NT_SUCCESS(Status))
891 goto Cleanup;
892 }
893
894 BufferU.MaximumLength = min(nBufferLength * sizeof(WCHAR), USHRT_MAX);
895 BufferU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
896 0,
897 BufferU.MaximumLength);
898 if (BufferU.Buffer == NULL)
899 {
900 Status = STATUS_NO_MEMORY;
901 goto Cleanup;
902 }
903
904 Buffer.MaximumLength = min(nBufferLength, USHRT_MAX);
905 Buffer.Buffer = lpBuffer;
906
907 RetValue = SearchPathW (NULL == lpPath ? NULL : PathU.Buffer,
908 NULL == lpFileName ? NULL : FileNameU.Buffer,
909 NULL == lpExtension ? NULL : ExtensionU.Buffer,
910 nBufferLength,
911 BufferU.Buffer,
912 &FilePartW);
913
914 if (0 != RetValue)
915 {
916 BufferU.Length = wcslen(BufferU.Buffer) * sizeof(WCHAR);
917 /* convert ansi (or oem) string to unicode */
918 if (bIsFileApiAnsi)
919 Status = RtlUnicodeStringToAnsiString(&Buffer,
920 &BufferU,
921 FALSE);
922 else
923 Status = RtlUnicodeStringToOemString(&Buffer,
924 &BufferU,
925 FALSE);
926
927 if (NT_SUCCESS(Status) && Buffer.Buffer)
928 {
929 /* nul-terminate ascii string */
930 Buffer.Buffer[BufferU.Length / sizeof(WCHAR)] = '\0';
931
932 if (NULL != lpFilePart && BufferU.Length != 0)
933 {
934 *lpFilePart = strrchr (lpBuffer, '\\') + 1;
935 }
936 }
937 }
938
939 Cleanup:
940 RtlFreeHeap (RtlGetProcessHeap (),
941 0,
942 PathU.Buffer);
943 RtlFreeHeap (RtlGetProcessHeap (),
944 0,
945 FileNameU.Buffer);
946 RtlFreeHeap (RtlGetProcessHeap (),
947 0,
948 ExtensionU.Buffer);
949 RtlFreeHeap (RtlGetProcessHeap (),
950 0,
951 BufferU.Buffer);
952
953 if (!NT_SUCCESS(Status))
954 {
955 SetLastErrorByStatus(Status);
956 return 0;
957 }
958
959 return RetValue;
960 }
961
962
963 /***********************************************************************
964 * ContainsPath (Wine name: contains_pathW)
965 *
966 * Check if the file name contains a path; helper for SearchPathW.
967 * A relative path is not considered a path unless it starts with ./ or ../
968 */
969 static
970 BOOL
971 ContainsPath(LPCWSTR name)
972 {
973 if (RtlDetermineDosPathNameType_U(name) != RtlPathTypeRelative) return TRUE;
974 if (name[0] != '.') return FALSE;
975 if (name[1] == '/' || name[1] == '\\' || name[1] == '\0') return TRUE;
976 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
977 }
978
979
980 /*
981 * @implemented
982 */
983 DWORD
984 WINAPI
985 SearchPathW(LPCWSTR lpPath,
986 LPCWSTR lpFileName,
987 LPCWSTR lpExtension,
988 DWORD nBufferLength,
989 LPWSTR lpBuffer,
990 LPWSTR *lpFilePart)
991 {
992 DWORD ret = 0;
993
994 if (!lpFileName || !lpFileName[0])
995 {
996 SetLastError(ERROR_INVALID_PARAMETER);
997 return 0;
998 }
999
1000 /* If the name contains an explicit path, ignore the path */
1001 if (ContainsPath(lpFileName))
1002 {
1003 /* try first without extension */
1004 if (RtlDoesFileExists_U(lpFileName))
1005 return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
1006
1007 if (lpExtension)
1008 {
1009 LPCWSTR p = wcsrchr(lpFileName, '.');
1010 if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
1011 lpExtension = NULL; /* Ignore the specified extension */
1012 }
1013
1014 /* Allocate a buffer for the file name and extension */
1015 if (lpExtension)
1016 {
1017 LPWSTR tmp;
1018 DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
1019
1020 if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
1021 {
1022 SetLastError(ERROR_OUTOFMEMORY);
1023 return 0;
1024 }
1025 wcscpy(tmp, lpFileName);
1026 wcscat(tmp, lpExtension);
1027 if (RtlDoesFileExists_U(tmp))
1028 ret = GetFullPathNameW(tmp, nBufferLength, lpBuffer, lpFilePart);
1029 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1030 }
1031 }
1032 else if (lpPath && lpPath[0]) /* search in the specified path */
1033 {
1034 ret = RtlDosSearchPath_U(lpPath,
1035 lpFileName,
1036 lpExtension,
1037 nBufferLength * sizeof(WCHAR),
1038 lpBuffer,
1039 lpFilePart) / sizeof(WCHAR);
1040 }
1041 else /* search in the default path */
1042 {
1043 WCHAR *DllPath = GetDllLoadPath(NULL);
1044
1045 if (DllPath)
1046 {
1047 ret = RtlDosSearchPath_U(DllPath,
1048 lpFileName,
1049 lpExtension,
1050 nBufferLength * sizeof(WCHAR),
1051 lpBuffer,
1052 lpFilePart) / sizeof(WCHAR);
1053 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
1054 }
1055 else
1056 {
1057 SetLastError(ERROR_OUTOFMEMORY);
1058 return 0;
1059 }
1060 }
1061
1062 if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
1063
1064 return ret;
1065 }
1066
1067 /*
1068 * @implemented
1069 */
1070 BOOL
1071 WINAPI
1072 SetDllDirectoryW(
1073 LPCWSTR lpPathName
1074 )
1075 {
1076 UNICODE_STRING PathName;
1077
1078 RtlInitUnicodeString(&PathName, lpPathName);
1079
1080 RtlEnterCriticalSection(&DllLock);
1081 if(PathName.Length > 0)
1082 {
1083 if(PathName.Length + sizeof(WCHAR) <= DllDirectory.MaximumLength)
1084 {
1085 RtlCopyUnicodeString(&DllDirectory, &PathName);
1086 }
1087 else
1088 {
1089 RtlFreeUnicodeString(&DllDirectory);
1090 if(!(DllDirectory.Buffer = (PWSTR)RtlAllocateHeap(RtlGetProcessHeap(),
1091 0,
1092 PathName.Length + sizeof(WCHAR))))
1093 {
1094 RtlLeaveCriticalSection(&DllLock);
1095 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1096 return FALSE;
1097 }
1098 DllDirectory.Length = 0;
1099 DllDirectory.MaximumLength = PathName.Length + sizeof(WCHAR);
1100
1101 RtlCopyUnicodeString(&DllDirectory, &PathName);
1102 }
1103 }
1104 else
1105 {
1106 RtlFreeUnicodeString(&DllDirectory);
1107 }
1108 RtlLeaveCriticalSection(&DllLock);
1109
1110 return TRUE;
1111 }
1112
1113 /*
1114 * @implemented
1115 */
1116 BOOL
1117 WINAPI
1118 SetDllDirectoryA(
1119 LPCSTR lpPathName /* can be NULL */
1120 )
1121 {
1122 PWCHAR PathNameW=NULL;
1123
1124 if(lpPathName)
1125 {
1126 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
1127 return FALSE;
1128 }
1129
1130 return SetDllDirectoryW(PathNameW);
1131 }
1132
1133 /*
1134 * @implemented
1135 */
1136 DWORD
1137 WINAPI
1138 GetDllDirectoryW(
1139 DWORD nBufferLength,
1140 LPWSTR lpBuffer
1141 )
1142 {
1143 DWORD Ret;
1144
1145 RtlEnterCriticalSection(&DllLock);
1146 if(nBufferLength > 0)
1147 {
1148 Ret = DllDirectory.Length / sizeof(WCHAR);
1149 if(Ret > nBufferLength - 1)
1150 {
1151 Ret = nBufferLength - 1;
1152 }
1153
1154 if(Ret > 0)
1155 {
1156 RtlCopyMemory(lpBuffer, DllDirectory.Buffer, Ret * sizeof(WCHAR));
1157 }
1158 lpBuffer[Ret] = L'\0';
1159 }
1160 else
1161 {
1162 /* include termination character, even if the string is empty! */
1163 Ret = (DllDirectory.Length / sizeof(WCHAR)) + 1;
1164 }
1165 RtlLeaveCriticalSection(&DllLock);
1166
1167 return Ret;
1168 }
1169
1170 /*
1171 * @implemented
1172 */
1173 DWORD
1174 WINAPI
1175 GetDllDirectoryA(
1176 DWORD nBufferLength,
1177 LPSTR lpBuffer
1178 )
1179 {
1180 WCHAR BufferW[MAX_PATH];
1181 DWORD ret;
1182
1183 ret = GetDllDirectoryW(MAX_PATH, BufferW);
1184
1185 if (!ret)
1186 return 0;
1187
1188 if (ret > MAX_PATH)
1189 {
1190 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1191 return 0;
1192 }
1193
1194 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1195 }
1196
1197
1198 /*
1199 * @implemented
1200 */
1201 BOOL WINAPI
1202 NeedCurrentDirectoryForExePathW(LPCWSTR ExeName)
1203 {
1204 static const WCHAR env_name[] = {'N','o','D','e','f','a','u','l','t',
1205 'C','u','r','r','e','n','t',
1206 'D','i','r','e','c','t','o','r','y',
1207 'I','n','E','x','e','P','a','t','h',0};
1208 WCHAR env_val;
1209
1210 /* MSDN mentions some 'registry location'. We do not use registry. */
1211 FIXME("(%s): partial stub\n", debugstr_w(ExeName));
1212
1213 if (wcschr(ExeName, L'\\'))
1214 return TRUE;
1215
1216 /* Check the existence of the variable, not value */
1217 if (!GetEnvironmentVariableW( env_name, &env_val, 1 ))
1218 return TRUE;
1219
1220 return FALSE;
1221 }
1222
1223
1224 /*
1225 * @implemented
1226 */
1227 BOOL WINAPI
1228 NeedCurrentDirectoryForExePathA(LPCSTR ExeName)
1229 {
1230 WCHAR *ExeNameW;
1231
1232 if (!(ExeNameW = FilenameA2W(ExeName, FALSE)))
1233 return TRUE;
1234
1235 return NeedCurrentDirectoryForExePathW(ExeNameW);
1236 }
1237
1238
1239
1240
1241
1242 /***********************************************************************
1243 * @implemented
1244 *
1245 * GetLongPathNameW (KERNEL32.@)
1246 *
1247 * NOTES
1248 * observed (Win2000):
1249 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1250 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1251 */
1252 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1253 {
1254 #define MAX_PATHNAME_LEN 1024
1255
1256 WCHAR tmplongpath[MAX_PATHNAME_LEN];
1257 LPCWSTR p;
1258 DWORD sp = 0, lp = 0;
1259 DWORD tmplen;
1260 BOOL unixabsolute;
1261 WIN32_FIND_DATAW wfd;
1262 HANDLE goit;
1263
1264 if (!shortpath)
1265 {
1266 SetLastError(ERROR_INVALID_PARAMETER);
1267 return 0;
1268 }
1269 if (!shortpath[0])
1270 {
1271 SetLastError(ERROR_PATH_NOT_FOUND);
1272 return 0;
1273 }
1274
1275 TRACE("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen);
1276
1277 if (shortpath[0] == '\\' && shortpath[1] == '\\')
1278 {
1279 WARN("ERR: UNC pathname %s\n", shortpath);
1280 lstrcpynW( longpath, shortpath, longlen );
1281 return wcslen(longpath);
1282 }
1283 unixabsolute = (shortpath[0] == '/');
1284 /* check for drive letter */
1285 if (!unixabsolute && shortpath[1] == ':' )
1286 {
1287 tmplongpath[0] = shortpath[0];
1288 tmplongpath[1] = ':';
1289 lp = sp = 2;
1290 }
1291
1292 while (shortpath[sp])
1293 {
1294 /* check for path delimiters and reproduce them */
1295 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
1296 {
1297 if (!lp || tmplongpath[lp-1] != '\\')
1298 {
1299 /* strip double "\\" */
1300 tmplongpath[lp++] = '\\';
1301 }
1302 tmplongpath[lp] = 0; /* terminate string */
1303 sp++;
1304 continue;
1305 }
1306
1307 p = shortpath + sp;
1308 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
1309 {
1310 tmplongpath[lp++] = *p++;
1311 tmplongpath[lp++] = *p++;
1312 }
1313 for (; *p && *p != '/' && *p != '\\'; p++);
1314 tmplen = p - (shortpath + sp);
1315 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
1316 /* Check if the file exists and use the existing file name */
1317 goit = FindFirstFileW(tmplongpath, &wfd);
1318 if (goit == INVALID_HANDLE_VALUE)
1319 {
1320 TRACE("not found %s!\n", tmplongpath);
1321 SetLastError ( ERROR_FILE_NOT_FOUND );
1322 return 0;
1323 }
1324 FindClose(goit);
1325 wcscpy(tmplongpath + lp, wfd.cFileName);
1326 lp += wcslen(tmplongpath + lp);
1327 sp += tmplen;
1328 }
1329 tmplen = wcslen(shortpath) - 1;
1330 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
1331 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
1332 tmplongpath[lp++] = shortpath[tmplen];
1333 tmplongpath[lp] = 0;
1334
1335 tmplen = wcslen(tmplongpath) + 1;
1336 if (tmplen <= longlen)
1337 {
1338 wcscpy(longpath, tmplongpath);
1339 TRACE("returning %s\n", longpath);
1340 tmplen--; /* length without 0 */
1341 }
1342
1343 return tmplen;
1344 }
1345
1346
1347
1348 /***********************************************************************
1349 * GetLongPathNameA (KERNEL32.@)
1350 */
1351 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1352 {
1353 WCHAR *shortpathW;
1354 WCHAR longpathW[MAX_PATH];
1355 DWORD ret;
1356
1357 TRACE("GetLongPathNameA %s, %i\n",shortpath,longlen );
1358
1359 if (!(shortpathW = FilenameA2W( shortpath, FALSE )))
1360 return 0;
1361
1362 ret = GetLongPathNameW(shortpathW, longpathW, MAX_PATH);
1363
1364 if (!ret) return 0;
1365 if (ret > MAX_PATH)
1366 {
1367 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1368 return 0;
1369 }
1370
1371 return FilenameW2A_FitOrFail(longpath, longlen, longpathW, ret+1 );
1372 }
1373
1374 /* EOF */