reshuffling of dlls
[reactos.git] / reactos / lib / 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
20 #define NDEBUG
21 #include "../include/debug.h"
22
23 UNICODE_STRING DllDirectory = {0, 0, NULL};
24
25 /* FUNCTIONS *****************************************************************/
26
27 /*
28 * @implemented
29 */
30 BOOL
31 STDCALL
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 STDCALL
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 STDCALL
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;
98 NTSTATUS Status;
99
100 DPRINT ("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 FILE_OPEN_FOR_BACKUP_INTENT,
121 &ObjectAttributes,
122 &IoStatusBlock,
123 NULL,
124 FILE_ATTRIBUTE_NORMAL,
125 FILE_SHARE_READ | FILE_SHARE_WRITE,
126 FILE_CREATE,
127 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
128 NULL,
129 0);
130
131 RtlFreeHeap (RtlGetProcessHeap (),
132 0,
133 NtPathU.Buffer);
134
135 if (!NT_SUCCESS(Status))
136 {
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 STDCALL
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 DPRINT ("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 &NtPathU,
196 OBJ_CASE_INSENSITIVE,
197 NULL,
198 (lpSecurityAttributes ? lpSecurityAttributes->lpSecurityDescriptor : NULL));
199
200 InitializeObjectAttributes(&ObjectAttributes,
201 &NtTemplatePathU,
202 OBJ_CASE_INSENSITIVE,
203 NULL,
204 NULL);
205
206 /*
207 * Open the template directory
208 */
209
210 OpenTemplateDir:
211 Status = NtOpenFile (&TemplateHandle,
212 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA,
213 &ObjectAttributes,
214 &IoStatusBlock,
215 FILE_SHARE_READ | FILE_SHARE_WRITE,
216 OpenOptions);
217 if (!NT_SUCCESS(Status))
218 {
219 if (Status == STATUS_INVALID_PARAMETER &&
220 (OpenOptions & FILE_OPEN_REPARSE_POINT))
221 {
222 /* Some FSs (FAT) don't support reparse points, try opening
223 the directory without FILE_OPEN_REPARSE_POINT */
224 OpenOptions &= ~FILE_OPEN_REPARSE_POINT;
225
226 DPRINT("Reparse points not supported, try with less options\n");
227
228 /* try again */
229 goto OpenTemplateDir;
230 }
231 else
232 {
233 DPRINT1("Failed to open the template directory: 0x%x\n", Status);
234 goto CleanupNoNtPath;
235 }
236 }
237
238 /*
239 * Translate the new directory path and check if they're the same
240 */
241
242 if (!RtlDosPathNameToNtPathName_U (lpNewDirectory,
243 &NtPathU,
244 NULL,
245 NULL))
246 {
247 Status = STATUS_OBJECT_PATH_NOT_FOUND;
248 goto CleanupNoNtPath;
249 }
250
251 if (RtlEqualUnicodeString(&NtPathU,
252 &NtTemplatePathU,
253 TRUE))
254 {
255 DPRINT1("Both directory paths are the same!\n");
256 Status = STATUS_OBJECT_NAME_INVALID;
257 goto Cleanup;
258 }
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 DPRINT1("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 DPRINT1("Querying the EA data failed: 0x%x\n", Status);
355 goto Cleanup;
356 }
357
358 /*
359 * Create the new directory
360 */
361 Status = NtCreateFile (&DirectoryHandle,
362 DesiredAccess,
363 &ObjectAttributes,
364 &IoStatusBlock,
365 NULL,
366 FileBasicInfo.FileAttributes,
367 FILE_SHARE_READ | FILE_SHARE_WRITE,
368 FILE_CREATE,
369 CreateOptions,
370 EaBuffer,
371 EaLength);
372 if (!NT_SUCCESS(Status))
373 {
374 if (ReparsePoint &&
375 (Status == STATUS_INVALID_PARAMETER || Status == STATUS_ACCESS_DENIED))
376 {
377 /* The FS doesn't seem to support reparse points... */
378 DPRINT1("Cannot copy the hardlink, destination doesn\'t support reparse points!\n");
379 }
380
381 goto Cleanup;
382 }
383
384 if (ReparsePoint)
385 {
386 /*
387 * Copy the reparse point
388 */
389
390 PREPARSE_GUID_DATA_BUFFER ReparseDataBuffer =
391 (PREPARSE_GUID_DATA_BUFFER)RtlAllocateHeap(RtlGetProcessHeap(),
392 0,
393 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
394
395 if (ReparseDataBuffer == NULL)
396 {
397 Status = STATUS_INSUFFICIENT_RESOURCES;
398 goto Cleanup;
399 }
400
401 /* query the size of the reparse data buffer structure */
402 Status = NtFsControlFile(TemplateHandle,
403 NULL,
404 NULL,
405 NULL,
406 &IoStatusBlock,
407 FSCTL_GET_REPARSE_POINT,
408 NULL,
409 0,
410 ReparseDataBuffer,
411 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
412 if (NT_SUCCESS(Status))
413 {
414 /* write the reparse point */
415 Status = NtFsControlFile(DirectoryHandle,
416 NULL,
417 NULL,
418 NULL,
419 &IoStatusBlock,
420 FSCTL_SET_REPARSE_POINT,
421 ReparseDataBuffer,
422 MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
423 NULL,
424 0);
425 }
426
427 RtlFreeHeap(RtlGetProcessHeap(),
428 0,
429 ReparseDataBuffer);
430
431 if (!NT_SUCCESS(Status))
432 {
433 /* fail, we were unable to read the reparse point data! */
434 DPRINT1("Querying or setting the reparse point failed: 0x%x\n", Status);
435 goto Cleanup;
436 }
437 }
438 else
439 {
440 /*
441 * Copy alternate file streams, if existing
442 */
443
444 /* FIXME - enumerate and copy the file streams */
445 }
446
447 /*
448 * We successfully created the directory and copied all information
449 * from the template directory
450 */
451 Status = STATUS_SUCCESS;
452
453 Cleanup:
454 RtlFreeHeap (RtlGetProcessHeap (),
455 0,
456 NtPathU.Buffer);
457
458 CleanupNoNtPath:
459 if (TemplateHandle != NULL)
460 {
461 NtClose(TemplateHandle);
462 }
463
464 RtlFreeHeap (RtlGetProcessHeap (),
465 0,
466 NtTemplatePathU.Buffer);
467
468 /* free the he extended attributes buffer */
469 if (EaBuffer != NULL)
470 {
471 RtlFreeHeap (RtlGetProcessHeap (),
472 0,
473 EaBuffer);
474 }
475
476 if (DirectoryHandle != NULL)
477 {
478 NtClose(DirectoryHandle);
479 }
480
481 if (!NT_SUCCESS(Status))
482 {
483 SetLastErrorByStatus(Status);
484 return FALSE;
485 }
486
487 return TRUE;
488 }
489
490
491 /*
492 * @implemented
493 */
494 BOOL
495 STDCALL
496 RemoveDirectoryA (
497 LPCSTR lpPathName
498 )
499 {
500 PWCHAR PathNameW;
501
502 DPRINT("RemoveDirectoryA(%s)\n",lpPathName);
503
504 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
505 return FALSE;
506
507 return RemoveDirectoryW (PathNameW);
508 }
509
510
511 /*
512 * @implemented
513 */
514 BOOL
515 STDCALL
516 RemoveDirectoryW (
517 LPCWSTR lpPathName
518 )
519 {
520 FILE_DISPOSITION_INFORMATION FileDispInfo;
521 OBJECT_ATTRIBUTES ObjectAttributes;
522 IO_STATUS_BLOCK IoStatusBlock;
523 UNICODE_STRING NtPathU;
524 HANDLE DirectoryHandle;
525 NTSTATUS Status;
526
527 DPRINT("lpPathName %S\n", lpPathName);
528
529 if (!RtlDosPathNameToNtPathName_U (lpPathName,
530 &NtPathU,
531 NULL,
532 NULL))
533 return FALSE;
534
535 InitializeObjectAttributes(&ObjectAttributes,
536 &NtPathU,
537 OBJ_CASE_INSENSITIVE,
538 NULL,
539 NULL);
540
541 DPRINT("NtPathU '%S'\n", NtPathU.Buffer);
542
543 Status = NtCreateFile (&DirectoryHandle,
544 DELETE,
545 &ObjectAttributes,
546 &IoStatusBlock,
547 NULL,
548 FILE_ATTRIBUTE_DIRECTORY, /* 0x7 */
549 0,
550 FILE_OPEN,
551 FILE_DIRECTORY_FILE, /* 0x204021 */
552 NULL,
553 0);
554
555 RtlFreeHeap (RtlGetProcessHeap (),
556 0,
557 NtPathU.Buffer);
558
559 if (!NT_SUCCESS(Status))
560 {
561 CHECKPOINT;
562 SetLastErrorByStatus (Status);
563 return FALSE;
564 }
565
566 FileDispInfo.DeleteFile = TRUE;
567
568 Status = NtSetInformationFile (DirectoryHandle,
569 &IoStatusBlock,
570 &FileDispInfo,
571 sizeof(FILE_DISPOSITION_INFORMATION),
572 FileDispositionInformation);
573 NtClose(DirectoryHandle);
574
575 if (!NT_SUCCESS(Status))
576 {
577 SetLastErrorByStatus (Status);
578 return FALSE;
579 }
580
581 return TRUE;
582 }
583
584
585 /*
586 * @implemented
587 */
588 DWORD
589 STDCALL
590 GetFullPathNameA (
591 LPCSTR lpFileName,
592 DWORD nBufferLength,
593 LPSTR lpBuffer,
594 LPSTR *lpFilePart
595 )
596 {
597 WCHAR BufferW[MAX_PATH];
598 PWCHAR FileNameW;
599 DWORD ret;
600 LPWSTR FilePartW = NULL;
601
602 DPRINT("GetFullPathNameA(lpFileName %s, nBufferLength %d, lpBuffer %p, "
603 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
604
605 if (!lpFileName)
606 {
607 SetLastError(ERROR_INVALID_PARAMETER);
608 return 0;
609 }
610
611 if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
612 return 0;
613
614 ret = GetFullPathNameW(FileNameW, MAX_PATH, BufferW, &FilePartW);
615
616 if (!ret)
617 return 0;
618
619 if (ret > MAX_PATH)
620 {
621 SetLastError(ERROR_FILENAME_EXCED_RANGE);
622 return 0;
623 }
624
625 ret = FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
626
627 if (ret < nBufferLength && lpFilePart)
628 {
629 /* if the path closed with '\', FilePart is NULL */
630 if (!FilePartW)
631 *lpFilePart=NULL;
632 else
633 *lpFilePart = (FilePartW - BufferW) + lpBuffer;
634 }
635
636 DPRINT("GetFullPathNameA ret: lpBuffer %s lpFilePart %s\n",
637 lpBuffer, (lpFilePart == NULL) ? "NULL" : *lpFilePart);
638
639 return ret;
640 }
641
642
643 /*
644 * @implemented
645 */
646 DWORD
647 STDCALL
648 GetFullPathNameW (
649 LPCWSTR lpFileName,
650 DWORD nBufferLength,
651 LPWSTR lpBuffer,
652 LPWSTR *lpFilePart
653 )
654 {
655 ULONG Length;
656
657 DPRINT("GetFullPathNameW(lpFileName %S, nBufferLength %d, lpBuffer %p, "
658 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
659
660 Length = RtlGetFullPathName_U ((LPWSTR)lpFileName,
661 nBufferLength * sizeof(WCHAR),
662 lpBuffer,
663 lpFilePart);
664
665 DPRINT("GetFullPathNameW ret: lpBuffer %S lpFilePart %S Length %ld\n",
666 lpBuffer, (lpFilePart == NULL) ? L"NULL" : *lpFilePart, Length / sizeof(WCHAR));
667
668 return Length/sizeof(WCHAR);
669 }
670
671
672 /*
673 * NOTE: Copied from Wine.
674 * @implemented
675 */
676 DWORD
677 STDCALL
678 GetShortPathNameA (
679 LPCSTR longpath,
680 LPSTR shortpath,
681 DWORD shortlen
682 )
683 {
684 PWCHAR LongPathW;
685 WCHAR ShortPathW[MAX_PATH];
686 DWORD ret;
687
688 if (!longpath)
689 {
690 SetLastError(ERROR_INVALID_PARAMETER);
691 return 0;
692 }
693
694 if (!(LongPathW = FilenameA2W(longpath, FALSE)))
695 return 0;
696
697 ret = GetShortPathNameW(LongPathW, ShortPathW, MAX_PATH);
698
699 if (!ret)
700 return 0;
701
702 if (ret > MAX_PATH)
703 {
704 SetLastError(ERROR_FILENAME_EXCED_RANGE);
705 return 0;
706 }
707
708 return FilenameW2A_FitOrFail(shortpath, shortlen, ShortPathW, ret+1);
709 }
710
711
712 /*
713 * NOTE: Copied from Wine.
714 * @implemented
715 */
716 DWORD
717 STDCALL
718 GetShortPathNameW (
719 LPCWSTR longpath,
720 LPWSTR shortpath,
721 DWORD shortlen
722 )
723 {
724 WCHAR tmpshortpath[MAX_PATH];
725 LPCWSTR p;
726 DWORD sp = 0, lp = 0;
727 DWORD tmplen;
728 WIN32_FIND_DATAW wfd;
729 HANDLE goit;
730 UNICODE_STRING ustr;
731 WCHAR ustr_buf[8+1+3+1];
732
733 DPRINT("GetShortPathNameW: %S\n",longpath);
734
735 if (!longpath)
736 {
737 SetLastError(ERROR_INVALID_PARAMETER);
738 return 0;
739 }
740 if (!longpath[0])
741 {
742 SetLastError(ERROR_BAD_PATHNAME);
743 return 0;
744 }
745
746 /* check for drive letter */
747 if (longpath[1] == ':' )
748 {
749 tmpshortpath[0] = longpath[0];
750 tmpshortpath[1] = ':';
751 sp = lp = 2;
752 }
753
754 ustr.Buffer = ustr_buf;
755 ustr.Length = 0;
756 ustr.MaximumLength = sizeof(ustr_buf);
757
758 while (longpath[lp])
759 {
760 /* check for path delimiters and reproduce them */
761 if (longpath[lp] == '\\' || longpath[lp] == '/')
762 {
763 if (!sp || tmpshortpath[sp-1] != '\\')
764 {
765 /* strip double "\\" */
766 tmpshortpath[sp] = '\\';
767 sp++;
768 }
769 tmpshortpath[sp] = 0; /* terminate string */
770 lp++;
771 continue;
772 }
773
774 for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
775 tmplen = p - (longpath + lp);
776 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
777 /* Check, if the current element is a valid dos name */
778 if (tmplen <= 8+1+3+1)
779 {
780 BOOLEAN spaces;
781 memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
782 ustr_buf[tmplen] = '\0';
783 ustr.Length = tmplen * sizeof(WCHAR);
784 if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
785 {
786 sp += tmplen;
787 lp += tmplen;
788 continue;
789 }
790 }
791
792 /* Check if the file exists and use the existing short file name */
793 goit = FindFirstFileW(tmpshortpath, &wfd);
794 if (goit == INVALID_HANDLE_VALUE) goto notfound;
795 FindClose(goit);
796 lstrcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
797 sp += lstrlenW(tmpshortpath + sp);
798 lp += tmplen;
799 }
800 tmpshortpath[sp] = 0;
801
802 tmplen = lstrlenW(tmpshortpath) + 1;
803 if (tmplen <= shortlen)
804 {
805 lstrcpyW(shortpath, tmpshortpath);
806 tmplen--; /* length without 0 */
807 }
808
809 return tmplen;
810
811 notfound:
812 SetLastError ( ERROR_FILE_NOT_FOUND );
813 return 0;
814 }
815
816
817 /*
818 * @implemented
819 */
820 DWORD
821 STDCALL
822 SearchPathA (
823 LPCSTR lpPath,
824 LPCSTR lpFileName,
825 LPCSTR lpExtension,
826 DWORD nBufferLength,
827 LPSTR lpBuffer,
828 LPSTR *lpFilePart
829 )
830 {
831 UNICODE_STRING PathU = {0};
832 UNICODE_STRING FileNameU = {0};
833 UNICODE_STRING ExtensionU = {0};
834 UNICODE_STRING BufferU = {0};
835 ANSI_STRING Path;
836 ANSI_STRING FileName;
837 ANSI_STRING Extension;
838 ANSI_STRING Buffer;
839 PWCHAR FilePartW;
840 DWORD RetValue = 0;
841 NTSTATUS Status = STATUS_SUCCESS;
842
843 RtlInitAnsiString (&Path,
844 (LPSTR)lpPath);
845 RtlInitAnsiString (&FileName,
846 (LPSTR)lpFileName);
847 RtlInitAnsiString (&Extension,
848 (LPSTR)lpExtension);
849
850 /* convert ansi (or oem) strings to unicode */
851 if (bIsFileApiAnsi)
852 {
853 Status = RtlAnsiStringToUnicodeString (&PathU,
854 &Path,
855 TRUE);
856 if (!NT_SUCCESS(Status))
857 goto Cleanup;
858
859 Status = RtlAnsiStringToUnicodeString (&FileNameU,
860 &FileName,
861 TRUE);
862 if (!NT_SUCCESS(Status))
863 goto Cleanup;
864
865 Status = RtlAnsiStringToUnicodeString (&ExtensionU,
866 &Extension,
867 TRUE);
868 if (!NT_SUCCESS(Status))
869 goto Cleanup;
870 }
871 else
872 {
873 Status = RtlOemStringToUnicodeString (&PathU,
874 &Path,
875 TRUE);
876 if (!NT_SUCCESS(Status))
877 goto Cleanup;
878 Status = RtlOemStringToUnicodeString (&FileNameU,
879 &FileName,
880 TRUE);
881 if (!NT_SUCCESS(Status))
882 goto Cleanup;
883
884 Status = RtlOemStringToUnicodeString (&ExtensionU,
885 &Extension,
886 TRUE);
887 if (!NT_SUCCESS(Status))
888 goto Cleanup;
889 }
890
891 BufferU.MaximumLength = nBufferLength * sizeof(WCHAR);
892 BufferU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
893 0,
894 BufferU.MaximumLength);
895 if (BufferU.Buffer == NULL)
896 {
897 Status = STATUS_NO_MEMORY;
898 goto Cleanup;
899 }
900
901 Buffer.MaximumLength = nBufferLength;
902 Buffer.Buffer = lpBuffer;
903
904 RetValue = SearchPathW (NULL == lpPath ? NULL : PathU.Buffer,
905 NULL == lpFileName ? NULL : FileNameU.Buffer,
906 NULL == lpExtension ? NULL : ExtensionU.Buffer,
907 nBufferLength,
908 BufferU.Buffer,
909 &FilePartW);
910
911 if (0 != RetValue)
912 {
913 BufferU.Length = wcslen(BufferU.Buffer) * sizeof(WCHAR);
914 /* convert ansi (or oem) string to unicode */
915 if (bIsFileApiAnsi)
916 RtlUnicodeStringToAnsiString (&Buffer,
917 &BufferU,
918 FALSE);
919 else
920 RtlUnicodeStringToOemString (&Buffer,
921 &BufferU,
922 FALSE);
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 Cleanup:
933 RtlFreeHeap (RtlGetProcessHeap (),
934 0,
935 PathU.Buffer);
936 RtlFreeHeap (RtlGetProcessHeap (),
937 0,
938 FileNameU.Buffer);
939 RtlFreeHeap (RtlGetProcessHeap (),
940 0,
941 ExtensionU.Buffer);
942 RtlFreeHeap (RtlGetProcessHeap (),
943 0,
944 BufferU.Buffer);
945
946 if (!NT_SUCCESS(Status))
947 {
948 SetLastErrorByStatus(Status);
949 return 0;
950 }
951
952 return RetValue;
953 }
954
955
956 /*
957 * @implemented
958 */
959 DWORD
960 STDCALL
961 SearchPathW (
962 LPCWSTR lpPath,
963 LPCWSTR lpFileName,
964 LPCWSTR lpExtension,
965 DWORD nBufferLength,
966 LPWSTR lpBuffer,
967 LPWSTR *lpFilePart
968 )
969 /*
970 * FUNCTION: Searches for the specified file
971 * ARGUMENTS:
972 * lpPath = Points to a null-terminated string that specified the
973 * path to be searched. If this parameters is NULL then
974 * the following directories are searched
975 * The directory from which the application loaded
976 * The current directory
977 * The system directory
978 * The 16-bit system directory
979 * The windows directory
980 * The directories listed in the PATH environment
981 * variable
982 * lpFileName = Specifies the filename to search for
983 * lpExtension = Points to the null-terminated string that specifies
984 * an extension to be added to the filename when
985 * searching for the file. The first character of the
986 * filename extension must be a period (.). The
987 * extension is only added if the specified filename
988 * doesn't end with an extension
989 *
990 * If the filename extension is not required or if the
991 * filename contains an extension, this parameters can be
992 * NULL
993 * nBufferLength = The length in characters of the buffer for output
994 * lpBuffer = Points to the buffer for the valid path and filename of
995 * file found
996 * lpFilePart = Points to the last component of the valid path and
997 * filename
998 * RETURNS: On success, the length, in characters, of the string copied to the
999 * buffer
1000 * On failure, zero.
1001 */
1002 {
1003 DWORD retCode = 0;
1004 ULONG pos, len;
1005 PWCHAR EnvironmentBufferW = NULL;
1006 PWCHAR AppPathW = NULL;
1007 WCHAR Buffer;
1008 BOOL HasExtension;
1009 LPCWSTR p;
1010 PWCHAR Name;
1011
1012 DPRINT("SearchPath\n");
1013
1014 HasExtension = FALSE;
1015 p = lpFileName + wcslen(lpFileName);
1016 while (lpFileName < p &&
1017 L'\\' != *(p - 1) &&
1018 L'/' != *(p - 1) &&
1019 L':' != *(p - 1))
1020 {
1021 HasExtension = HasExtension || L'.' == *(p - 1);
1022 p--;
1023 }
1024 if (lpFileName < p)
1025 {
1026 if (HasExtension || NULL == lpExtension)
1027 {
1028 Name = (PWCHAR) lpFileName;
1029 }
1030 else
1031 {
1032 Name = RtlAllocateHeap(GetProcessHeap(),
1033 HEAP_GENERATE_EXCEPTIONS,
1034 (wcslen(lpFileName) + wcslen(lpExtension) + 1)
1035 * sizeof(WCHAR));
1036 if (NULL == Name)
1037 {
1038 SetLastError(ERROR_OUTOFMEMORY);
1039 return 0;
1040 }
1041 wcscat(wcscpy(Name, lpFileName), lpExtension);
1042 }
1043 if (RtlDoesFileExists_U(Name))
1044 {
1045 retCode = RtlGetFullPathName_U (Name,
1046 nBufferLength * sizeof(WCHAR),
1047 lpBuffer,
1048 lpFilePart);
1049 }
1050 if (Name != lpFileName)
1051 {
1052 RtlFreeHeap(GetProcessHeap(), 0, Name);
1053 }
1054 }
1055 else
1056 {
1057 if (lpPath == NULL)
1058 {
1059
1060 AppPathW = (PWCHAR) RtlAllocateHeap(RtlGetProcessHeap(),
1061 HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY,
1062 MAX_PATH * sizeof(WCHAR));
1063 if (AppPathW == NULL)
1064 {
1065 SetLastError(ERROR_OUTOFMEMORY);
1066 return 0;
1067 }
1068
1069
1070 wcscat (AppPathW, NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer);
1071
1072 len = wcslen (AppPathW);
1073
1074 while (len && AppPathW[len - 1] != L'\\')
1075 len--;
1076
1077 if (len) AppPathW[len-1] = L'\0';
1078
1079 len = GetEnvironmentVariableW(L"PATH", &Buffer, 0);
1080 len += 1 + GetCurrentDirectoryW(0, &Buffer);
1081 len += 1 + GetSystemDirectoryW(&Buffer, 0);
1082 len += 1 + GetWindowsDirectoryW(&Buffer, 0);
1083 len += 1 + wcslen(AppPathW) * sizeof(WCHAR);
1084
1085 EnvironmentBufferW = (PWCHAR) RtlAllocateHeap(RtlGetProcessHeap(),
1086 HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY,
1087 len * sizeof(WCHAR));
1088 if (EnvironmentBufferW == NULL)
1089 {
1090 RtlFreeHeap(RtlGetProcessHeap(), 0, AppPathW);
1091 SetLastError(ERROR_OUTOFMEMORY);
1092 return 0;
1093 }
1094
1095 pos = GetCurrentDirectoryW(len, EnvironmentBufferW);
1096 EnvironmentBufferW[pos++] = L';';
1097 EnvironmentBufferW[pos] = 0;
1098 pos += GetSystemDirectoryW(&EnvironmentBufferW[pos], len - pos);
1099 EnvironmentBufferW[pos++] = L';';
1100 EnvironmentBufferW[pos] = 0;
1101 pos += GetWindowsDirectoryW(&EnvironmentBufferW[pos], len - pos);
1102 EnvironmentBufferW[pos++] = L';';
1103 EnvironmentBufferW[pos] = 0;
1104 pos += GetEnvironmentVariableW(L"PATH", &EnvironmentBufferW[pos], len - pos);
1105 EnvironmentBufferW[pos++] = L';';
1106 EnvironmentBufferW[pos] = 0;
1107 wcscat (EnvironmentBufferW, AppPathW);
1108
1109 RtlFreeHeap (RtlGetProcessHeap (),
1110 0,
1111 AppPathW);
1112
1113 lpPath = EnvironmentBufferW;
1114
1115 }
1116
1117 retCode = RtlDosSearchPath_U ((PWCHAR)lpPath, (PWCHAR)lpFileName, (PWCHAR)lpExtension,
1118 nBufferLength * sizeof(WCHAR), lpBuffer, lpFilePart);
1119
1120 if (EnvironmentBufferW != NULL)
1121 {
1122 RtlFreeHeap(GetProcessHeap(), 0, EnvironmentBufferW);
1123 }
1124 if (retCode == 0)
1125 {
1126 SetLastError(ERROR_FILE_NOT_FOUND);
1127 }
1128 }
1129 return retCode / sizeof(WCHAR);
1130 }
1131
1132 /*
1133 * @implemented
1134 */
1135 BOOL
1136 STDCALL
1137 SetDllDirectoryW(
1138 LPCWSTR lpPathName
1139 )
1140 {
1141 UNICODE_STRING PathName;
1142
1143 RtlInitUnicodeString(&PathName, lpPathName);
1144
1145 RtlEnterCriticalSection(&DllLock);
1146 if(PathName.Length > 0)
1147 {
1148 if(PathName.Length + sizeof(WCHAR) <= DllDirectory.MaximumLength)
1149 {
1150 RtlCopyUnicodeString(&DllDirectory, &PathName);
1151 }
1152 else
1153 {
1154 RtlFreeUnicodeString(&DllDirectory);
1155 if(!(DllDirectory.Buffer = (PWSTR)RtlAllocateHeap(RtlGetProcessHeap(),
1156 0,
1157 PathName.Length + sizeof(WCHAR))))
1158 {
1159 RtlLeaveCriticalSection(&DllLock);
1160 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1161 return FALSE;
1162 }
1163 DllDirectory.Length = 0;
1164 DllDirectory.MaximumLength = PathName.Length + sizeof(WCHAR);
1165
1166 RtlCopyUnicodeString(&DllDirectory, &PathName);
1167 }
1168 }
1169 else
1170 {
1171 RtlFreeUnicodeString(&DllDirectory);
1172 }
1173 RtlLeaveCriticalSection(&DllLock);
1174
1175 return TRUE;
1176 }
1177
1178 /*
1179 * @implemented
1180 */
1181 BOOL
1182 STDCALL
1183 SetDllDirectoryA(
1184 LPCSTR lpPathName /* can be NULL */
1185 )
1186 {
1187 PWCHAR PathNameW=NULL;
1188
1189 if(lpPathName)
1190 {
1191 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
1192 return FALSE;
1193 }
1194
1195 return SetDllDirectoryW(PathNameW);
1196 }
1197
1198 /*
1199 * @implemented
1200 */
1201 DWORD
1202 STDCALL
1203 GetDllDirectoryW(
1204 DWORD nBufferLength,
1205 LPWSTR lpBuffer
1206 )
1207 {
1208 DWORD Ret;
1209
1210 RtlEnterCriticalSection(&DllLock);
1211 if(nBufferLength > 0)
1212 {
1213 Ret = DllDirectory.Length / sizeof(WCHAR);
1214 if(Ret > nBufferLength - 1)
1215 {
1216 Ret = nBufferLength - 1;
1217 }
1218
1219 if(Ret > 0)
1220 {
1221 RtlCopyMemory(lpBuffer, DllDirectory.Buffer, Ret * sizeof(WCHAR));
1222 }
1223 lpBuffer[Ret] = L'\0';
1224 }
1225 else
1226 {
1227 /* include termination character, even if the string is empty! */
1228 Ret = (DllDirectory.Length / sizeof(WCHAR)) + 1;
1229 }
1230 RtlLeaveCriticalSection(&DllLock);
1231
1232 return Ret;
1233 }
1234
1235 /*
1236 * @implemented
1237 */
1238 DWORD
1239 STDCALL
1240 GetDllDirectoryA(
1241 DWORD nBufferLength,
1242 LPSTR lpBuffer
1243 )
1244 {
1245 WCHAR BufferW[MAX_PATH];
1246 DWORD ret;
1247
1248 ret = GetDllDirectoryW(MAX_PATH, BufferW);
1249
1250 if (!ret)
1251 return 0;
1252
1253 if (ret > MAX_PATH)
1254 {
1255 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1256 return 0;
1257 }
1258
1259 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1260 }
1261
1262
1263 /*
1264 * @implemented
1265 */
1266 BOOL STDCALL
1267 NeedCurrentDirectoryForExePathW(LPCWSTR ExeName)
1268 {
1269 return (wcschr(ExeName,
1270 L'\\') != NULL);
1271 }
1272
1273
1274 /*
1275 * @implemented
1276 */
1277 BOOL STDCALL
1278 NeedCurrentDirectoryForExePathA(LPCSTR ExeName)
1279 {
1280 return (strchr(ExeName,
1281 '\\') != NULL);
1282 }
1283
1284
1285
1286
1287
1288 /***********************************************************************
1289 * @implemented
1290 *
1291 * GetLongPathNameW (KERNEL32.@)
1292 *
1293 * NOTES
1294 * observed (Win2000):
1295 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1296 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1297 */
1298 DWORD STDCALL GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1299 {
1300 #define MAX_PATHNAME_LEN 1024
1301
1302 WCHAR tmplongpath[MAX_PATHNAME_LEN];
1303 LPCWSTR p;
1304 DWORD sp = 0, lp = 0;
1305 DWORD tmplen;
1306 BOOL unixabsolute = (shortpath[0] == '/');
1307 WIN32_FIND_DATAW wfd;
1308 HANDLE goit;
1309
1310 if (!shortpath)
1311 {
1312 SetLastError(ERROR_INVALID_PARAMETER);
1313 return 0;
1314 }
1315 if (!shortpath[0])
1316 {
1317 SetLastError(ERROR_PATH_NOT_FOUND);
1318 return 0;
1319 }
1320
1321 DPRINT("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen);
1322
1323 if (shortpath[0] == '\\' && shortpath[1] == '\\')
1324 {
1325 DPRINT1("ERR: UNC pathname %s\n", shortpath);
1326 lstrcpynW( longpath, shortpath, longlen );
1327 return wcslen(longpath);
1328 }
1329
1330 /* check for drive letter */
1331 if (!unixabsolute && shortpath[1] == ':' )
1332 {
1333 tmplongpath[0] = shortpath[0];
1334 tmplongpath[1] = ':';
1335 lp = sp = 2;
1336 }
1337
1338 while (shortpath[sp])
1339 {
1340 /* check for path delimiters and reproduce them */
1341 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
1342 {
1343 if (!lp || tmplongpath[lp-1] != '\\')
1344 {
1345 /* strip double "\\" */
1346 tmplongpath[lp++] = '\\';
1347 }
1348 tmplongpath[lp] = 0; /* terminate string */
1349 sp++;
1350 continue;
1351 }
1352
1353 p = shortpath + sp;
1354 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
1355 {
1356 tmplongpath[lp++] = *p++;
1357 tmplongpath[lp++] = *p++;
1358 }
1359 for (; *p && *p != '/' && *p != '\\'; p++);
1360 tmplen = p - (shortpath + sp);
1361 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
1362 /* Check if the file exists and use the existing file name */
1363 goit = FindFirstFileW(tmplongpath, &wfd);
1364 if (goit == INVALID_HANDLE_VALUE)
1365 {
1366 DPRINT("not found %s!\n", tmplongpath);
1367 SetLastError ( ERROR_FILE_NOT_FOUND );
1368 return 0;
1369 }
1370 FindClose(goit);
1371 wcscpy(tmplongpath + lp, wfd.cFileName);
1372 lp += wcslen(tmplongpath + lp);
1373 sp += tmplen;
1374 }
1375 tmplen = wcslen(shortpath) - 1;
1376 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
1377 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
1378 tmplongpath[lp++] = shortpath[tmplen];
1379 tmplongpath[lp] = 0;
1380
1381 tmplen = wcslen(tmplongpath) + 1;
1382 if (tmplen <= longlen)
1383 {
1384 wcscpy(longpath, tmplongpath);
1385 DPRINT("returning %s\n", longpath);
1386 tmplen--; /* length without 0 */
1387 }
1388
1389 return tmplen;
1390 }
1391
1392
1393
1394 /***********************************************************************
1395 * GetLongPathNameA (KERNEL32.@)
1396 */
1397 DWORD STDCALL GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1398 {
1399 WCHAR *shortpathW;
1400 WCHAR longpathW[MAX_PATH];
1401 DWORD ret;
1402
1403 DPRINT("GetLongPathNameA %s, %i\n",shortpath,longlen );
1404
1405 if (!(shortpathW = FilenameA2W( shortpath, FALSE )))
1406 return 0;
1407
1408 ret = GetLongPathNameW(shortpathW, longpathW, MAX_PATH);
1409
1410 if (!ret) return 0;
1411 if (ret > MAX_PATH)
1412 {
1413 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1414 return 0;
1415 }
1416
1417 return FilenameW2A_FitOrFail(longpath, longlen, longpathW, ret+1 );
1418 }
1419
1420 /* EOF */