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