6ba996c345a5d410b841d82ac0a5b0e889b6ee2c
[reactos.git] / dll / win32 / kernel32 / client / file / dir.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/dir.c
6 * PURPOSE: Directory functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /*
13 * NOTES: Changed to using ZwCreateFile
14 */
15
16 /* INCLUDES ******************************************************************/
17
18 #include <k32.h>
19 #define NDEBUG
20 #include <debug.h>
21 DEBUG_CHANNEL(kernel32file);
22
23 UNICODE_STRING BaseDllDirectory = {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 BaseSetLastNTError(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 BaseSetLastNTError(Status);
485 return FALSE;
486 }
487
488 return TRUE;
489 }
490
491
492 /*
493 * @implemented
494 */
495 BOOL
496 WINAPI
497 RemoveDirectoryA (
498 LPCSTR lpPathName
499 )
500 {
501 PWCHAR PathNameW;
502
503 TRACE("RemoveDirectoryA(%s)\n",lpPathName);
504
505 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
506 return FALSE;
507
508 return RemoveDirectoryW (PathNameW);
509 }
510
511
512 /*
513 * @implemented
514 */
515 BOOL
516 WINAPI
517 RemoveDirectoryW (
518 LPCWSTR lpPathName
519 )
520 {
521 FILE_DISPOSITION_INFORMATION FileDispInfo;
522 OBJECT_ATTRIBUTES ObjectAttributes;
523 IO_STATUS_BLOCK IoStatusBlock;
524 UNICODE_STRING NtPathU;
525 HANDLE DirectoryHandle = NULL;
526 NTSTATUS Status;
527
528 TRACE("lpPathName %S\n", lpPathName);
529
530 if (!RtlDosPathNameToNtPathName_U (lpPathName,
531 &NtPathU,
532 NULL,
533 NULL))
534 {
535 SetLastError(ERROR_PATH_NOT_FOUND);
536 return FALSE;
537 }
538
539 InitializeObjectAttributes(&ObjectAttributes,
540 &NtPathU,
541 OBJ_CASE_INSENSITIVE,
542 NULL,
543 NULL);
544
545 TRACE("NtPathU '%S'\n", NtPathU.Buffer);
546
547 Status = NtOpenFile(&DirectoryHandle,
548 DELETE | SYNCHRONIZE,
549 &ObjectAttributes,
550 &IoStatusBlock,
551 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
552 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
553
554 RtlFreeUnicodeString(&NtPathU);
555
556 if (!NT_SUCCESS(Status))
557 {
558 WARN("Status 0x%08x\n", Status);
559 BaseSetLastNTError (Status);
560 return FALSE;
561 }
562
563 FileDispInfo.DeleteFile = TRUE;
564
565 Status = NtSetInformationFile (DirectoryHandle,
566 &IoStatusBlock,
567 &FileDispInfo,
568 sizeof(FILE_DISPOSITION_INFORMATION),
569 FileDispositionInformation);
570 NtClose(DirectoryHandle);
571
572 if (!NT_SUCCESS(Status))
573 {
574 BaseSetLastNTError (Status);
575 return FALSE;
576 }
577
578 return TRUE;
579 }
580
581
582 /*
583 * @implemented
584 */
585 DWORD
586 WINAPI
587 GetFullPathNameA (
588 LPCSTR lpFileName,
589 DWORD nBufferLength,
590 LPSTR lpBuffer,
591 LPSTR *lpFilePart
592 )
593 {
594 WCHAR BufferW[MAX_PATH];
595 PWCHAR FileNameW;
596 DWORD ret;
597 LPWSTR FilePartW = NULL;
598
599 TRACE("GetFullPathNameA(lpFileName %s, nBufferLength %d, lpBuffer %p, "
600 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
601
602 if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
603 return 0;
604
605 ret = GetFullPathNameW(FileNameW, MAX_PATH, BufferW, &FilePartW);
606
607 if (!ret)
608 return 0;
609
610 if (ret > MAX_PATH)
611 {
612 SetLastError(ERROR_FILENAME_EXCED_RANGE);
613 return 0;
614 }
615
616 ret = FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
617
618 if (ret < nBufferLength && lpFilePart)
619 {
620 /* if the path closed with '\', FilePart is NULL */
621 if (!FilePartW)
622 *lpFilePart=NULL;
623 else
624 *lpFilePart = (FilePartW - BufferW) + lpBuffer;
625 }
626
627 TRACE("GetFullPathNameA ret: lpBuffer %s lpFilePart %s\n",
628 lpBuffer, (lpFilePart == NULL) ? "NULL" : *lpFilePart);
629
630 return ret;
631 }
632
633
634 /*
635 * @implemented
636 */
637 DWORD
638 WINAPI
639 GetFullPathNameW (
640 LPCWSTR lpFileName,
641 DWORD nBufferLength,
642 LPWSTR lpBuffer,
643 LPWSTR *lpFilePart
644 )
645 {
646 ULONG Length;
647
648 TRACE("GetFullPathNameW(lpFileName %S, nBufferLength %d, lpBuffer %p, "
649 "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
650
651 Length = RtlGetFullPathName_U ((LPWSTR)lpFileName,
652 nBufferLength * sizeof(WCHAR),
653 lpBuffer,
654 lpFilePart);
655
656 TRACE("GetFullPathNameW ret: lpBuffer %S lpFilePart %S Length %ld\n",
657 lpBuffer, (lpFilePart == NULL) ? L"NULL" : *lpFilePart, Length / sizeof(WCHAR));
658
659 if (!lpFileName)
660 {
661 #if (WINVER >= _WIN32_WINNT_WIN7)
662 SetLastError(ERROR_INVALID_PARAMETER);
663 #endif
664 return 0;
665 }
666
667 return Length/sizeof(WCHAR);
668 }
669
670
671 /*
672 * NOTE: Copied from Wine.
673 * @implemented
674 */
675 DWORD
676 WINAPI
677 GetShortPathNameA (
678 LPCSTR longpath,
679 LPSTR shortpath,
680 DWORD shortlen
681 )
682 {
683 PWCHAR LongPathW;
684 WCHAR ShortPathW[MAX_PATH];
685 DWORD ret;
686
687 if (!longpath)
688 {
689 SetLastError(ERROR_INVALID_PARAMETER);
690 return 0;
691 }
692
693 if (!(LongPathW = FilenameA2W(longpath, FALSE)))
694 return 0;
695
696 ret = GetShortPathNameW(LongPathW, ShortPathW, MAX_PATH);
697
698 if (!ret)
699 return 0;
700
701 if (ret > MAX_PATH)
702 {
703 SetLastError(ERROR_FILENAME_EXCED_RANGE);
704 return 0;
705 }
706
707 return FilenameW2A_FitOrFail(shortpath, shortlen, ShortPathW, ret+1);
708 }
709
710
711 /*
712 * NOTE: Copied from Wine.
713 * @implemented
714 */
715 DWORD
716 WINAPI
717 GetShortPathNameW (
718 LPCWSTR longpath,
719 LPWSTR shortpath,
720 DWORD shortlen
721 )
722 {
723 WCHAR tmpshortpath[MAX_PATH];
724 LPCWSTR p;
725 DWORD sp = 0, lp = 0;
726 DWORD tmplen;
727 WIN32_FIND_DATAW wfd;
728 HANDLE goit;
729 UNICODE_STRING ustr;
730 WCHAR ustr_buf[8+1+3+1];
731
732 TRACE("GetShortPathNameW: %S\n",longpath);
733
734 if (!longpath)
735 {
736 SetLastError(ERROR_INVALID_PARAMETER);
737 return 0;
738 }
739 if (!longpath[0])
740 {
741 SetLastError(ERROR_BAD_PATHNAME);
742 return 0;
743 }
744
745 /* check for drive letter */
746 if (longpath[0] != '/' && longpath[1] == ':' )
747 {
748 tmpshortpath[0] = longpath[0];
749 tmpshortpath[1] = ':';
750 sp = lp = 2;
751 }
752
753 ustr.Buffer = ustr_buf;
754 ustr.Length = 0;
755 ustr.MaximumLength = sizeof(ustr_buf);
756
757 while (longpath[lp])
758 {
759 /* check for path delimiters and reproduce them */
760 if (longpath[lp] == '\\' || longpath[lp] == '/')
761 {
762 if (!sp || tmpshortpath[sp-1] != '\\')
763 {
764 /* strip double "\\" */
765 tmpshortpath[sp] = '\\';
766 sp++;
767 }
768 tmpshortpath[sp] = 0; /* terminate string */
769 lp++;
770 continue;
771 }
772
773 for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
774 tmplen = p - (longpath + lp);
775 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
776 /* Check, if the current element is a valid dos name */
777 if (tmplen <= 8+1+3)
778 {
779 BOOLEAN spaces;
780 memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
781 ustr_buf[tmplen] = '\0';
782 ustr.Length = (USHORT)tmplen * sizeof(WCHAR);
783 if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
784 {
785 sp += tmplen;
786 lp += tmplen;
787 continue;
788 }
789 }
790
791 /* Check if the file exists and use the existing short file name */
792 goit = FindFirstFileW(tmpshortpath, &wfd);
793 if (goit == INVALID_HANDLE_VALUE) goto notfound;
794 FindClose(goit);
795 lstrcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
796 sp += lstrlenW(tmpshortpath + sp);
797 lp += tmplen;
798 }
799 tmpshortpath[sp] = 0;
800
801 tmplen = lstrlenW(tmpshortpath) + 1;
802 if (tmplen <= shortlen)
803 {
804 lstrcpyW(shortpath, tmpshortpath);
805 tmplen--; /* length without 0 */
806 }
807
808 return tmplen;
809
810 notfound:
811 SetLastError ( ERROR_FILE_NOT_FOUND );
812 return 0;
813 }
814
815
816 /*
817 * @implemented
818 */
819 DWORD
820 WINAPI
821 SearchPathA (
822 LPCSTR lpPath,
823 LPCSTR lpFileName,
824 LPCSTR lpExtension,
825 DWORD nBufferLength,
826 LPSTR lpBuffer,
827 LPSTR *lpFilePart
828 )
829 {
830 UNICODE_STRING PathU = { 0, 0, NULL };
831 UNICODE_STRING FileNameU = { 0, 0, NULL };
832 UNICODE_STRING ExtensionU = { 0, 0, NULL };
833 UNICODE_STRING BufferU = { 0, 0, NULL };
834 ANSI_STRING Path;
835 ANSI_STRING FileName;
836 ANSI_STRING Extension;
837 ANSI_STRING Buffer;
838 PWCHAR FilePartW;
839 DWORD RetValue = 0;
840 NTSTATUS Status = STATUS_SUCCESS;
841
842 if (!lpFileName)
843 {
844 SetLastError(ERROR_INVALID_PARAMETER);
845 return 0;
846 }
847
848 RtlInitAnsiString (&Path,
849 (LPSTR)lpPath);
850 RtlInitAnsiString (&FileName,
851 (LPSTR)lpFileName);
852 RtlInitAnsiString (&Extension,
853 (LPSTR)lpExtension);
854
855 /* convert ansi (or oem) strings to unicode */
856 if (bIsFileApiAnsi)
857 {
858 Status = RtlAnsiStringToUnicodeString (&PathU,
859 &Path,
860 TRUE);
861 if (!NT_SUCCESS(Status))
862 goto Cleanup;
863
864 Status = RtlAnsiStringToUnicodeString (&FileNameU,
865 &FileName,
866 TRUE);
867 if (!NT_SUCCESS(Status))
868 goto Cleanup;
869
870 Status = RtlAnsiStringToUnicodeString (&ExtensionU,
871 &Extension,
872 TRUE);
873 if (!NT_SUCCESS(Status))
874 goto Cleanup;
875 }
876 else
877 {
878 Status = RtlOemStringToUnicodeString (&PathU,
879 &Path,
880 TRUE);
881 if (!NT_SUCCESS(Status))
882 goto Cleanup;
883 Status = RtlOemStringToUnicodeString (&FileNameU,
884 &FileName,
885 TRUE);
886 if (!NT_SUCCESS(Status))
887 goto Cleanup;
888
889 Status = RtlOemStringToUnicodeString (&ExtensionU,
890 &Extension,
891 TRUE);
892 if (!NT_SUCCESS(Status))
893 goto Cleanup;
894 }
895
896 BufferU.MaximumLength = min(nBufferLength * sizeof(WCHAR), USHRT_MAX);
897 BufferU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
898 0,
899 BufferU.MaximumLength);
900 if (BufferU.Buffer == NULL)
901 {
902 Status = STATUS_NO_MEMORY;
903 goto Cleanup;
904 }
905
906 Buffer.MaximumLength = min(nBufferLength, USHRT_MAX);
907 Buffer.Buffer = lpBuffer;
908
909 RetValue = SearchPathW (NULL == lpPath ? NULL : PathU.Buffer,
910 NULL == lpFileName ? NULL : FileNameU.Buffer,
911 NULL == lpExtension ? NULL : ExtensionU.Buffer,
912 nBufferLength,
913 BufferU.Buffer,
914 &FilePartW);
915
916 if (0 != RetValue)
917 {
918 BufferU.Length = wcslen(BufferU.Buffer) * sizeof(WCHAR);
919 /* convert ansi (or oem) string to unicode */
920 if (bIsFileApiAnsi)
921 Status = RtlUnicodeStringToAnsiString(&Buffer,
922 &BufferU,
923 FALSE);
924 else
925 Status = RtlUnicodeStringToOemString(&Buffer,
926 &BufferU,
927 FALSE);
928
929 if (NT_SUCCESS(Status) && Buffer.Buffer)
930 {
931 /* nul-terminate ascii string */
932 Buffer.Buffer[BufferU.Length / sizeof(WCHAR)] = '\0';
933
934 if (NULL != lpFilePart && BufferU.Length != 0)
935 {
936 *lpFilePart = strrchr (lpBuffer, '\\') + 1;
937 }
938 }
939 }
940
941 Cleanup:
942 RtlFreeHeap (RtlGetProcessHeap (),
943 0,
944 PathU.Buffer);
945 RtlFreeHeap (RtlGetProcessHeap (),
946 0,
947 FileNameU.Buffer);
948 RtlFreeHeap (RtlGetProcessHeap (),
949 0,
950 ExtensionU.Buffer);
951 RtlFreeHeap (RtlGetProcessHeap (),
952 0,
953 BufferU.Buffer);
954
955 if (!NT_SUCCESS(Status))
956 {
957 BaseSetLastNTError(Status);
958 return 0;
959 }
960
961 return RetValue;
962 }
963
964
965 /***********************************************************************
966 * ContainsPath (Wine name: contains_pathW)
967 *
968 * Check if the file name contains a path; helper for SearchPathW.
969 * A relative path is not considered a path unless it starts with ./ or ../
970 */
971 static
972 BOOL
973 ContainsPath(LPCWSTR name)
974 {
975 if (RtlDetermineDosPathNameType_U(name) != RtlPathTypeRelative) return TRUE;
976 if (name[0] != '.') return FALSE;
977 if (name[1] == '/' || name[1] == '\\' || name[1] == '\0') return TRUE;
978 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
979 }
980
981
982 /*
983 * @implemented
984 */
985 DWORD
986 WINAPI
987 SearchPathW(LPCWSTR lpPath,
988 LPCWSTR lpFileName,
989 LPCWSTR lpExtension,
990 DWORD nBufferLength,
991 LPWSTR lpBuffer,
992 LPWSTR *lpFilePart)
993 {
994 DWORD ret = 0;
995
996 if (!lpFileName || !lpFileName[0])
997 {
998 SetLastError(ERROR_INVALID_PARAMETER);
999 return 0;
1000 }
1001
1002 /* If the name contains an explicit path, ignore the path */
1003 if (ContainsPath(lpFileName))
1004 {
1005 /* try first without extension */
1006 if (RtlDoesFileExists_U(lpFileName))
1007 return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
1008
1009 if (lpExtension)
1010 {
1011 LPCWSTR p = wcsrchr(lpFileName, '.');
1012 if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
1013 lpExtension = NULL; /* Ignore the specified extension */
1014 }
1015
1016 /* Allocate a buffer for the file name and extension */
1017 if (lpExtension)
1018 {
1019 LPWSTR tmp;
1020 DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
1021
1022 if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
1023 {
1024 SetLastError(ERROR_OUTOFMEMORY);
1025 return 0;
1026 }
1027 wcscpy(tmp, lpFileName);
1028 wcscat(tmp, lpExtension);
1029 if (RtlDoesFileExists_U(tmp))
1030 ret = GetFullPathNameW(tmp, nBufferLength, lpBuffer, lpFilePart);
1031 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1032 }
1033 }
1034 else if (lpPath && lpPath[0]) /* search in the specified path */
1035 {
1036 ret = RtlDosSearchPath_U(lpPath,
1037 lpFileName,
1038 lpExtension,
1039 nBufferLength * sizeof(WCHAR),
1040 lpBuffer,
1041 lpFilePart) / sizeof(WCHAR);
1042 }
1043 else /* search in the default path */
1044 {
1045 WCHAR *DllPath = GetDllLoadPath(NULL);
1046
1047 if (DllPath)
1048 {
1049 ret = RtlDosSearchPath_U(DllPath,
1050 lpFileName,
1051 lpExtension,
1052 nBufferLength * sizeof(WCHAR),
1053 lpBuffer,
1054 lpFilePart) / sizeof(WCHAR);
1055 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
1056 }
1057 else
1058 {
1059 SetLastError(ERROR_OUTOFMEMORY);
1060 return 0;
1061 }
1062 }
1063
1064 if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
1065
1066 return ret;
1067 }
1068
1069 /*
1070 * @implemented
1071 */
1072 BOOL
1073 WINAPI
1074 SetDllDirectoryW(
1075 LPCWSTR lpPathName
1076 )
1077 {
1078 UNICODE_STRING PathName;
1079
1080 RtlInitUnicodeString(&PathName, lpPathName);
1081
1082 RtlEnterCriticalSection(&BaseDllDirectoryLock);
1083 if(PathName.Length > 0)
1084 {
1085 if(PathName.Length + sizeof(WCHAR) <= BaseDllDirectory.MaximumLength)
1086 {
1087 RtlCopyUnicodeString(&BaseDllDirectory, &PathName);
1088 }
1089 else
1090 {
1091 RtlFreeUnicodeString(&BaseDllDirectory);
1092 if(!(BaseDllDirectory.Buffer = (PWSTR)RtlAllocateHeap(RtlGetProcessHeap(),
1093 0,
1094 PathName.Length + sizeof(WCHAR))))
1095 {
1096 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
1097 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1098 return FALSE;
1099 }
1100 BaseDllDirectory.Length = 0;
1101 BaseDllDirectory.MaximumLength = PathName.Length + sizeof(WCHAR);
1102
1103 RtlCopyUnicodeString(&BaseDllDirectory, &PathName);
1104 }
1105 }
1106 else
1107 {
1108 RtlFreeUnicodeString(&BaseDllDirectory);
1109 }
1110 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
1111
1112 return TRUE;
1113 }
1114
1115 /*
1116 * @implemented
1117 */
1118 BOOL
1119 WINAPI
1120 SetDllDirectoryA(
1121 LPCSTR lpPathName /* can be NULL */
1122 )
1123 {
1124 PWCHAR PathNameW=NULL;
1125
1126 if(lpPathName)
1127 {
1128 if (!(PathNameW = FilenameA2W(lpPathName, FALSE)))
1129 return FALSE;
1130 }
1131
1132 return SetDllDirectoryW(PathNameW);
1133 }
1134
1135 /*
1136 * @implemented
1137 */
1138 DWORD
1139 WINAPI
1140 GetDllDirectoryW(
1141 DWORD nBufferLength,
1142 LPWSTR lpBuffer
1143 )
1144 {
1145 DWORD Ret;
1146
1147 RtlEnterCriticalSection(&BaseDllDirectoryLock);
1148 if(nBufferLength > 0)
1149 {
1150 Ret = BaseDllDirectory.Length / sizeof(WCHAR);
1151 if(Ret > nBufferLength - 1)
1152 {
1153 Ret = nBufferLength - 1;
1154 }
1155
1156 if(Ret > 0)
1157 {
1158 RtlCopyMemory(lpBuffer, BaseDllDirectory.Buffer, Ret * sizeof(WCHAR));
1159 }
1160 lpBuffer[Ret] = L'\0';
1161 }
1162 else
1163 {
1164 /* include termination character, even if the string is empty! */
1165 Ret = (BaseDllDirectory.Length / sizeof(WCHAR)) + 1;
1166 }
1167 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
1168
1169 return Ret;
1170 }
1171
1172 /*
1173 * @implemented
1174 */
1175 DWORD
1176 WINAPI
1177 GetDllDirectoryA(
1178 DWORD nBufferLength,
1179 LPSTR lpBuffer
1180 )
1181 {
1182 WCHAR BufferW[MAX_PATH];
1183 DWORD ret;
1184
1185 ret = GetDllDirectoryW(MAX_PATH, BufferW);
1186
1187 if (!ret)
1188 return 0;
1189
1190 if (ret > MAX_PATH)
1191 {
1192 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1193 return 0;
1194 }
1195
1196 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1197 }
1198
1199
1200 /*
1201 * @implemented
1202 */
1203 BOOL WINAPI
1204 NeedCurrentDirectoryForExePathW(LPCWSTR ExeName)
1205 {
1206 static const WCHAR env_name[] = {'N','o','D','e','f','a','u','l','t',
1207 'C','u','r','r','e','n','t',
1208 'D','i','r','e','c','t','o','r','y',
1209 'I','n','E','x','e','P','a','t','h',0};
1210 WCHAR env_val;
1211
1212 /* MSDN mentions some 'registry location'. We do not use registry. */
1213 FIXME("(%s): partial stub\n", debugstr_w(ExeName));
1214
1215 if (wcschr(ExeName, L'\\'))
1216 return TRUE;
1217
1218 /* Check the existence of the variable, not value */
1219 if (!GetEnvironmentVariableW( env_name, &env_val, 1 ))
1220 return TRUE;
1221
1222 return FALSE;
1223 }
1224
1225
1226 /*
1227 * @implemented
1228 */
1229 BOOL WINAPI
1230 NeedCurrentDirectoryForExePathA(LPCSTR ExeName)
1231 {
1232 WCHAR *ExeNameW;
1233
1234 if (!(ExeNameW = FilenameA2W(ExeName, FALSE)))
1235 return TRUE;
1236
1237 return NeedCurrentDirectoryForExePathW(ExeNameW);
1238 }
1239
1240
1241
1242
1243
1244 /***********************************************************************
1245 * @implemented
1246 *
1247 * GetLongPathNameW (KERNEL32.@)
1248 *
1249 * NOTES
1250 * observed (Win2000):
1251 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
1252 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
1253 */
1254 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
1255 {
1256 #define MAX_PATHNAME_LEN 1024
1257
1258 WCHAR tmplongpath[MAX_PATHNAME_LEN];
1259 LPCWSTR p;
1260 DWORD sp = 0, lp = 0;
1261 DWORD tmplen;
1262 BOOL unixabsolute;
1263 WIN32_FIND_DATAW wfd;
1264 HANDLE goit;
1265
1266 if (!shortpath)
1267 {
1268 SetLastError(ERROR_INVALID_PARAMETER);
1269 return 0;
1270 }
1271 if (!shortpath[0])
1272 {
1273 SetLastError(ERROR_PATH_NOT_FOUND);
1274 return 0;
1275 }
1276
1277 TRACE("GetLongPathNameW(%s,%p,%ld)\n", shortpath, longpath, longlen);
1278
1279 if (shortpath[0] == '\\' && shortpath[1] == '\\')
1280 {
1281 WARN("ERR: UNC pathname %s\n", shortpath);
1282 lstrcpynW( longpath, shortpath, longlen );
1283 return wcslen(longpath);
1284 }
1285 unixabsolute = (shortpath[0] == '/');
1286 /* check for drive letter */
1287 if (!unixabsolute && shortpath[1] == ':' )
1288 {
1289 tmplongpath[0] = shortpath[0];
1290 tmplongpath[1] = ':';
1291 lp = sp = 2;
1292 }
1293
1294 while (shortpath[sp])
1295 {
1296 /* check for path delimiters and reproduce them */
1297 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
1298 {
1299 if (!lp || tmplongpath[lp-1] != '\\')
1300 {
1301 /* strip double "\\" */
1302 tmplongpath[lp++] = '\\';
1303 }
1304 tmplongpath[lp] = 0; /* terminate string */
1305 sp++;
1306 continue;
1307 }
1308
1309 p = shortpath + sp;
1310 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
1311 {
1312 tmplongpath[lp++] = *p++;
1313 tmplongpath[lp++] = *p++;
1314 }
1315 for (; *p && *p != '/' && *p != '\\'; p++);
1316 tmplen = p - (shortpath + sp);
1317 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
1318 /* Check if the file exists and use the existing file name */
1319 goit = FindFirstFileW(tmplongpath, &wfd);
1320 if (goit == INVALID_HANDLE_VALUE)
1321 {
1322 TRACE("not found %s!\n", tmplongpath);
1323 SetLastError ( ERROR_FILE_NOT_FOUND );
1324 return 0;
1325 }
1326 FindClose(goit);
1327 wcscpy(tmplongpath + lp, wfd.cFileName);
1328 lp += wcslen(tmplongpath + lp);
1329 sp += tmplen;
1330 }
1331 tmplen = wcslen(shortpath) - 1;
1332 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
1333 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
1334 tmplongpath[lp++] = shortpath[tmplen];
1335 tmplongpath[lp] = 0;
1336
1337 tmplen = wcslen(tmplongpath) + 1;
1338 if (tmplen <= longlen)
1339 {
1340 wcscpy(longpath, tmplongpath);
1341 TRACE("returning %s\n", longpath);
1342 tmplen--; /* length without 0 */
1343 }
1344
1345 return tmplen;
1346 }
1347
1348
1349
1350 /***********************************************************************
1351 * GetLongPathNameA (KERNEL32.@)
1352 */
1353 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
1354 {
1355 WCHAR *shortpathW;
1356 WCHAR longpathW[MAX_PATH];
1357 DWORD ret;
1358
1359 TRACE("GetLongPathNameA %s, %i\n",shortpath,longlen );
1360
1361 if (!(shortpathW = FilenameA2W( shortpath, FALSE )))
1362 return 0;
1363
1364 ret = GetLongPathNameW(shortpathW, longpathW, MAX_PATH);
1365
1366 if (!ret) return 0;
1367 if (ret > MAX_PATH)
1368 {
1369 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1370 return 0;
1371 }
1372
1373 return FilenameW2A_FitOrFail(longpath, longlen, longpathW, ret+1 );
1374 }
1375
1376 /* EOF */