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