[KERNEL32/RTL]: Fix build.
[reactos.git] / reactos / lib / rtl / path.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/path.c
5 * PURPOSE: Path and current directory functions
6 * PROGRAMMERS: Wine team
7 * Thomas Weidenmueller
8 * Gunnar Dalsnes
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <rtl.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* DEFINITONS and MACROS ******************************************************/
19
20 #define MAX_PFX_SIZE 16
21
22 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
23
24
25 /* GLOBALS ********************************************************************/
26
27 static const WCHAR DeviceRootW[] = L"\\\\.\\";
28
29 static const UNICODE_STRING _condev = RTL_CONSTANT_STRING(L"\\\\.\\CON");
30
31 static const UNICODE_STRING _unc = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
32
33 static const UNICODE_STRING _lpt = RTL_CONSTANT_STRING(L"LPT");
34
35 static const UNICODE_STRING _com = RTL_CONSTANT_STRING(L"COM");
36
37 static const UNICODE_STRING _prn = RTL_CONSTANT_STRING(L"PRN");
38
39 static const UNICODE_STRING _aux = RTL_CONSTANT_STRING(L"AUX");
40
41 static const UNICODE_STRING _con = RTL_CONSTANT_STRING(L"CON");
42
43 static const UNICODE_STRING _nul = RTL_CONSTANT_STRING(L"NUL");
44
45 /* FUNCTIONS *****************************************************************/
46
47 /*
48 * @implemented
49 */
50 VOID
51 NTAPI
52 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
53 {
54 /* Check if a directory reference was grabbed */
55 if (RelativeName->CurDirRef)
56 {
57 /* FIXME: Not yet supported */
58 UNIMPLEMENTED;
59 RelativeName->CurDirRef = NULL;
60 }
61 }
62
63 /*
64 * @implemented
65 */
66 ULONG
67 NTAPI
68 RtlGetLongestNtPathLength(VOID)
69 {
70 /*
71 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
72 * a mapped network drive), which is accessed through the DOS Global?? path.
73 * This is, and has always been equal to, 269 characters, except in Wine
74 * which claims this is 277. Go figure.
75 */
76 return (MAX_PATH + _unc.Length + sizeof(ANSI_NULL));
77 }
78
79
80 /*
81 * @implemented
82 *
83 */
84 ULONG
85 NTAPI
86 RtlDetermineDosPathNameType_U(IN PCWSTR Path)
87 {
88 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
89 ASSERT(Path != NULL);
90
91 if (IS_PATH_SEPARATOR(Path[0]))
92 {
93 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \xxx */
94 if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\xxx */
95 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\xxx */
96 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.xxxx */
97
98 return RtlPathTypeRootLocalDevice; /* \\. */
99 }
100 else
101 {
102 if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* xxx */
103 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\xxx */
104
105 return RtlPathTypeDriveRelative; /* x:xxx */
106 }
107 }
108
109
110 /* returns 0 if name is not valid DOS device name, or DWORD with
111 * offset in bytes to DOS device name from beginning of buffer in high word
112 * and size in bytes of DOS device name in low word */
113
114 /*
115 * @implemented
116 */
117 ULONG NTAPI
118 RtlIsDosDeviceName_U(PWSTR dos_name)
119 {
120 static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
121 static const WCHAR auxW[3] = {'A','U','X'};
122 static const WCHAR comW[3] = {'C','O','M'};
123 static const WCHAR conW[3] = {'C','O','N'};
124 static const WCHAR lptW[3] = {'L','P','T'};
125 static const WCHAR nulW[3] = {'N','U','L'};
126 static const WCHAR prnW[3] = {'P','R','N'};
127
128 const WCHAR *start, *end, *p;
129
130 switch(RtlDetermineDosPathNameType_U( dos_name ))
131 {
132 case RtlPathTypeUnknown:
133 case RtlPathTypeUncAbsolute:
134 return 0;
135 case RtlPathTypeLocalDevice:
136 if (!_wcsicmp( dos_name, consoleW ))
137 return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) ); /* 4 is length of \\.\ prefix */
138 return 0;
139 case RtlPathTypeDriveAbsolute:
140 case RtlPathTypeDriveRelative:
141 start = dos_name + 2; /* skip drive letter */
142 break;
143 default:
144 start = dos_name;
145 break;
146 }
147
148 /* find start of file name */
149 for (p = start; *p; p++) if (IS_PATH_SEPARATOR(*p)) start = p + 1;
150
151 /* truncate at extension and ':' */
152 for (end = start; *end; end++) if (*end == '.' || *end == ':') break;
153 end--;
154
155 /* remove trailing spaces */
156 while (end >= start && *end == ' ') end--;
157
158 /* now we have a potential device name between start and end, check it */
159 switch(end - start + 1)
160 {
161 case 3:
162 if (_wcsnicmp( start, auxW, 3 ) &&
163 _wcsnicmp( start, conW, 3 ) &&
164 _wcsnicmp( start, nulW, 3 ) &&
165 _wcsnicmp( start, prnW, 3 )) break;
166 return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
167 case 4:
168 if (_wcsnicmp( start, comW, 3 ) && _wcsnicmp( start, lptW, 3 )) break;
169 if (*end <= '0' || *end > '9') break;
170 return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
171 default: /* can't match anything */
172 break;
173 }
174 return 0;
175 }
176
177 /*
178 * @implemented
179 */
180 ULONG
181 NTAPI
182 RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
183 IN PWSTR Buffer)
184 {
185 ULONG Length, Bytes;
186 PCURDIR CurDir;
187 PWSTR CurDirName;
188 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
189
190 /* Lock the PEB to get the current directory */
191 RtlAcquirePebLock();
192 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
193
194 /* Get the buffer and character length */
195 CurDirName = CurDir->DosPath.Buffer;
196 Length = CurDir->DosPath.Length / sizeof(WCHAR);
197 ASSERT((CurDirName != NULL) && (Length > 0));
198
199 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
200 Bytes = Length * sizeof(WCHAR);
201 if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
202 {
203 /* Check if caller does not have enough space */
204 if (MaximumLength <= Bytes)
205 {
206 /* Call has no space for it, fail, add the trailing slash */
207 RtlReleasePebLock();
208 return Bytes + sizeof(L'\\');
209 }
210 }
211 else
212 {
213 /* Check if caller does not have enough space */
214 if (MaximumLength <= Bytes)
215 {
216 /* Call has no space for it, fail */
217 RtlReleasePebLock();
218 return Bytes;
219 }
220 }
221
222 /* Copy the buffer since we seem to have space */
223 RtlCopyMemory(Buffer, CurDirName, Bytes);
224
225 /* The buffer should end with a path separator */
226 ASSERT(Buffer[Length - 1] == L'\\');
227
228 /* Again check for our two cases (drive root vs path) */
229 if ((Length <= 1) || (Buffer[Length - 2] != L':'))
230 {
231 /* Replace the trailing slash with a null */
232 Buffer[Length - 1] = UNICODE_NULL;
233 --Length;
234 }
235 else
236 {
237 /* Append the null char since there's no trailing slash */
238 Buffer[Length] = UNICODE_NULL;
239 }
240
241 /* Release PEB lock */
242 RtlReleasePebLock();
243 DPRINT("CurrentDirectory %S\n", Buffer);
244 return Length * sizeof(WCHAR);
245 }
246
247 /*
248 * @implemented
249 */
250 NTSTATUS NTAPI
251 RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
252 {
253 UNICODE_STRING full;
254 FILE_FS_DEVICE_INFORMATION device_info;
255 OBJECT_ATTRIBUTES Attr;
256 IO_STATUS_BLOCK iosb;
257 PCURDIR cd;
258 NTSTATUS Status;
259 ULONG size;
260 HANDLE handle = NULL;
261 PWSTR ptr;
262
263 DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
264
265 full.Buffer = NULL;
266
267 RtlAcquirePebLock ();
268
269 cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
270
271 if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
272 {
273 RtlReleasePebLock ();
274 return STATUS_OBJECT_NAME_INVALID;
275 }
276
277 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
278
279 InitializeObjectAttributes (&Attr,
280 &full,
281 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
282 NULL,
283 NULL);
284
285 Status = ZwOpenFile (&handle,
286 SYNCHRONIZE | FILE_TRAVERSE,
287 &Attr,
288 &iosb,
289 FILE_SHARE_READ | FILE_SHARE_WRITE,
290 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
291
292 if (!NT_SUCCESS(Status))
293 {
294 RtlFreeUnicodeString( &full);
295 RtlReleasePebLock ();
296 return Status;
297 }
298
299 /* don't keep the directory handle open on removable media */
300 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
301 sizeof(device_info), FileFsDeviceInformation )) &&
302 (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
303 {
304 DPRINT1("don't keep the directory handle open on removable media\n");
305 ZwClose( handle );
306 handle = 0;
307 }
308
309 if (cd->Handle)
310 ZwClose(cd->Handle);
311 cd->Handle = handle;
312
313 /* append trailing \ if missing */
314 size = full.Length / sizeof(WCHAR);
315 ptr = full.Buffer;
316 ptr += 4; /* skip \??\ prefix */
317 size -= 4;
318
319 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
320 * So the nullterm is replaced with \
321 * -Gunnar
322 */
323 if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
324
325 memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
326 cd->DosPath.Buffer[size] = 0;
327 cd->DosPath.Length = size * sizeof(WCHAR);
328
329 RtlFreeUnicodeString( &full);
330 RtlReleasePebLock();
331
332 return STATUS_SUCCESS;
333 }
334
335
336 /******************************************************************
337 * collapse_path
338 *
339 * Helper for RtlGetFullPathName_U.
340 * Get rid of . and .. components in the path.
341 */
342 void FORCEINLINE collapse_path( WCHAR *path, UINT mark )
343 {
344 WCHAR *p, *next;
345
346 /* convert every / into a \ */
347 for (p = path; *p; p++) if (*p == '/') *p = '\\';
348
349 /* collapse duplicate backslashes */
350 next = path + max( 1, mark );
351 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
352 *next = 0;
353
354 p = path + mark;
355 while (*p)
356 {
357 if (*p == '.')
358 {
359 switch(p[1])
360 {
361 case '\\': /* .\ component */
362 next = p + 2;
363 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
364 continue;
365 case 0: /* final . */
366 if (p > path + mark) p--;
367 *p = 0;
368 continue;
369 case '.':
370 if (p[2] == '\\') /* ..\ component */
371 {
372 next = p + 3;
373 if (p > path + mark)
374 {
375 p--;
376 while (p > path + mark && p[-1] != '\\') p--;
377 }
378 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
379 continue;
380 }
381 else if (!p[2]) /* final .. */
382 {
383 if (p > path + mark)
384 {
385 p--;
386 while (p > path + mark && p[-1] != '\\') p--;
387 if (p > path + mark) p--;
388 }
389 *p = 0;
390 continue;
391 }
392 break;
393 }
394 }
395 /* skip to the next component */
396 while (*p && *p != '\\') p++;
397 if (*p == '\\')
398 {
399 /* remove last dot in previous dir name */
400 if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
401 else p++;
402 }
403 }
404
405 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
406 while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
407 *p = 0;
408 }
409
410
411
412 /******************************************************************
413 * skip_unc_prefix
414 *
415 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
416 */
417 static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
418 {
419 ptr += 2;
420 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */
421 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
422 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */
423 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
424 return ptr;
425 }
426
427
428 /******************************************************************
429 * get_full_path_helper
430 *
431 * Helper for RtlGetFullPathName_U
432 * Note: name and buffer are allowed to point to the same memory spot
433 */
434 static ULONG get_full_path_helper(
435 LPCWSTR name,
436 LPWSTR buffer,
437 ULONG size)
438 {
439 ULONG reqsize = 0, mark = 0, dep = 0, deplen;
440 LPWSTR ins_str = NULL;
441 LPCWSTR ptr;
442 const UNICODE_STRING* cd;
443 WCHAR tmp[4];
444
445 /* return error if name only consists of spaces */
446 for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
447 if (!*ptr) return 0;
448
449 RtlAcquirePebLock();
450
451 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
452 cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath;
453
454 switch (RtlDetermineDosPathNameType_U(name))
455 {
456 case RtlPathTypeUncAbsolute: /* \\foo */
457 ptr = skip_unc_prefix( name );
458 mark = (ptr - name);
459 break;
460
461 case RtlPathTypeLocalDevice: /* \\.\foo */
462 mark = 4;
463 break;
464
465 case RtlPathTypeDriveAbsolute: /* c:\foo */
466 reqsize = sizeof(WCHAR);
467 tmp[0] = towupper(name[0]);
468 ins_str = tmp;
469 dep = 1;
470 mark = 3;
471 break;
472
473 case RtlPathTypeDriveRelative: /* c:foo */
474 dep = 2;
475 if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
476 {
477 UNICODE_STRING var, val;
478
479 tmp[0] = '=';
480 tmp[1] = name[0];
481 tmp[2] = ':';
482 tmp[3] = '\0';
483 var.Length = 3 * sizeof(WCHAR);
484 var.MaximumLength = 4 * sizeof(WCHAR);
485 var.Buffer = tmp;
486 val.Length = 0;
487 val.MaximumLength = size;
488 val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
489 if (val.Buffer == NULL)
490 {
491 reqsize = 0;
492 goto done;
493 }
494
495 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
496 {
497 case STATUS_SUCCESS:
498 /* FIXME: Win2k seems to check that the environment variable actually points
499 * to an existing directory. If not, root of the drive is used
500 * (this seems also to be the only spot in RtlGetFullPathName that the
501 * existence of a part of a path is checked)
502 */
503 /* fall thru */
504 case STATUS_BUFFER_TOO_SMALL:
505 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
506 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
507 ins_str = val.Buffer;
508 break;
509 case STATUS_VARIABLE_NOT_FOUND:
510 reqsize = 3 * sizeof(WCHAR);
511 tmp[0] = name[0];
512 tmp[1] = ':';
513 tmp[2] = '\\';
514 ins_str = tmp;
515 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
516 break;
517 default:
518 DPRINT1("Unsupported status code\n");
519 RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
520 break;
521 }
522 mark = 3;
523 break;
524 }
525 /* fall through */
526
527 case RtlPathTypeRelative: /* foo */
528 reqsize = cd->Length;
529 ins_str = cd->Buffer;
530 if (cd->Buffer[1] != ':')
531 {
532 ptr = skip_unc_prefix( cd->Buffer );
533 mark = ptr - cd->Buffer;
534 }
535 else mark = 3;
536 break;
537
538 case RtlPathTypeRooted: /* \xxx */
539 #ifdef __WINE__
540 if (name[0] == '/') /* may be a Unix path */
541 {
542 const WCHAR *ptr = name;
543 int drive = find_drive_root( &ptr );
544 if (drive != -1)
545 {
546 reqsize = 3 * sizeof(WCHAR);
547 tmp[0] = 'A' + drive;
548 tmp[1] = ':';
549 tmp[2] = '\\';
550 ins_str = tmp;
551 mark = 3;
552 dep = ptr - name;
553 break;
554 }
555 }
556 #endif
557 if (cd->Buffer[1] == ':')
558 {
559 reqsize = 2 * sizeof(WCHAR);
560 tmp[0] = cd->Buffer[0];
561 tmp[1] = ':';
562 ins_str = tmp;
563 mark = 3;
564 }
565 else
566 {
567 ptr = skip_unc_prefix( cd->Buffer );
568 reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
569 mark = reqsize / sizeof(WCHAR);
570 ins_str = cd->Buffer;
571 }
572 break;
573
574 case RtlPathTypeRootLocalDevice: /* \\. */
575 reqsize = 4 * sizeof(WCHAR);
576 dep = 3;
577 tmp[0] = '\\';
578 tmp[1] = '\\';
579 tmp[2] = '.';
580 tmp[3] = '\\';
581 ins_str = tmp;
582 mark = 4;
583 break;
584
585 case RtlPathTypeUnknown:
586 goto done;
587 }
588
589 /* enough space ? */
590 deplen = wcslen(name + dep) * sizeof(WCHAR);
591 if (reqsize + deplen + sizeof(WCHAR) > size)
592 {
593 /* not enough space, return need size (including terminating '\0') */
594 reqsize += deplen + sizeof(WCHAR);
595 goto done;
596 }
597
598 memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
599 if (reqsize) memcpy(buffer, ins_str, reqsize);
600 reqsize += deplen;
601
602 if (ins_str != tmp && ins_str != cd->Buffer)
603 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
604
605 collapse_path( buffer, mark );
606 reqsize = wcslen(buffer) * sizeof(WCHAR);
607
608 done:
609 RtlReleasePebLock();
610 return reqsize;
611 }
612
613
614 /******************************************************************
615 * RtlGetFullPathName_U (NTDLL.@)
616 *
617 * Returns the number of bytes written to buffer (not including the
618 * terminating NULL) if the function succeeds, or the required number of bytes
619 * (including the terminating NULL) if the buffer is too small.
620 *
621 * file_part will point to the filename part inside buffer (except if we use
622 * DOS device name, in which case file_in_buf is NULL)
623 *
624 * @implemented
625 */
626 ULONG NTAPI RtlGetFullPathName_U(
627 const WCHAR* name,
628 ULONG size,
629 WCHAR* buffer,
630 WCHAR** file_part)
631 {
632 WCHAR* ptr;
633 ULONG dosdev;
634 ULONG reqsize;
635
636 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
637
638 if (!name || !*name) return 0;
639
640 if (file_part) *file_part = NULL;
641
642 /* check for DOS device name */
643 dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
644 if (dosdev)
645 {
646 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
647 DWORD sz = LOWORD(dosdev); /* in bytes */
648
649 if (8 + sz + 2 > size) return sz + 10;
650 wcscpy(buffer, DeviceRootW);
651 memmove(buffer + 4, name + offset, sz);
652 buffer[4 + sz / sizeof(WCHAR)] = '\0';
653 /* file_part isn't set in this case */
654 return sz + 8;
655 }
656
657 reqsize = get_full_path_helper(name, buffer, size);
658 if (!reqsize) return 0;
659 if (reqsize > size)
660 {
661 LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
662 if (tmp == NULL) return 0;
663 reqsize = get_full_path_helper(name, tmp, reqsize);
664 if (reqsize + sizeof(WCHAR) > size) /* it may have worked the second time */
665 {
666 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
667 return reqsize + sizeof(WCHAR);
668 }
669 memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
670 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
671 }
672
673 /* find file part */
674 if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
675 *file_part = ptr;
676 return reqsize;
677 }
678
679
680 /*
681 * @implemented
682 */
683 BOOLEAN NTAPI
684 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName,
685 OUT PUNICODE_STRING NtPathName,
686 OUT PCWSTR *NtFileNamePart,
687 OUT PRTL_RELATIVE_NAME_U DirectoryInfo)
688 {
689 UNICODE_STRING us;
690 PCURDIR cd;
691 ULONG Type;
692 ULONG Size;
693 ULONG Length;
694 ULONG tmpLength;
695 ULONG Offset;
696 WCHAR fullname[MAX_PATH + 1];
697 PWSTR Buffer = NULL;
698
699 RtlInitUnicodeString (&us, DosPathName);
700 if (us.Length > 8)
701 {
702 Buffer = us.Buffer;
703 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
704 if (Buffer[0] == L'\\' && Buffer[1] == L'\\' &&
705 Buffer[2] == L'?' && Buffer[3] == L'\\')
706 {
707 /* allocate the new string and simply copy it */
708 NtPathName->Length = us.Length;
709 NtPathName->MaximumLength = us.Length + sizeof(WCHAR);
710 NtPathName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
711 0,
712 NtPathName->MaximumLength);
713 if (NtPathName->Buffer == NULL)
714 {
715 return FALSE;
716 }
717
718 /* copy the string */
719 RtlCopyMemory(NtPathName->Buffer,
720 us.Buffer,
721 NtPathName->Length);
722 NtPathName->Buffer[us.Length / sizeof(WCHAR)] = L'\0';
723
724 /* change the \\?\ prefix to \??\ */
725 NtPathName->Buffer[1] = L'?';
726
727 if (NtFileNamePart != NULL)
728 {
729 PWSTR FilePart = NULL;
730 PWSTR s;
731
732 /* try to find the last separator */
733 s = NtPathName->Buffer + (NtPathName->Length / sizeof(WCHAR));
734 while (s != NtPathName->Buffer)
735 {
736 if (*s == L'\\')
737 {
738 FilePart = s + 1;
739 break;
740 }
741 s--;
742 }
743
744 *NtFileNamePart = FilePart;
745 }
746
747 if (DirectoryInfo != NULL)
748 {
749 DirectoryInfo->RelativeName.Length = 0;
750 DirectoryInfo->RelativeName.MaximumLength = 0;
751 DirectoryInfo->RelativeName.Buffer = NULL;
752 DirectoryInfo->ContainingDirectory = NULL;
753 }
754
755 return TRUE;
756 }
757 }
758
759 Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
760 0,
761 sizeof( fullname ) + MAX_PFX_SIZE);
762 if (Buffer == NULL)
763 {
764 return FALSE;
765 }
766
767 RtlAcquirePebLock ();
768
769 Size = RtlGetFullPathName_U (DosPathName,
770 sizeof(fullname),
771 fullname,
772 (PWSTR*)NtFileNamePart);
773 if (Size == 0 || Size > MAX_PATH * sizeof(WCHAR))
774 {
775 RtlFreeHeap (RtlGetProcessHeap (),
776 0,
777 Buffer);
778 RtlReleasePebLock ();
779 return FALSE;
780 }
781
782 /* Set NT prefix */
783 Offset = 0;
784 memcpy (Buffer, L"\\??\\", 4 * sizeof(WCHAR));
785 tmpLength = 4;
786
787 Type = RtlDetermineDosPathNameType_U (fullname);
788 switch (Type)
789 {
790 case 1:
791 memcpy (Buffer + tmpLength, L"UNC\\", 4 * sizeof(WCHAR));
792 tmpLength += 4;
793 Offset = 2;
794 break; /* \\xxx */
795
796 case 6:
797 Offset = 4;
798 break; /* \\.\xxx */
799 }
800 Length = wcslen(fullname + Offset);
801 memcpy (Buffer + tmpLength, fullname + Offset, (Length + 1) * sizeof(WCHAR));
802 Length += tmpLength;
803 if (Type == RtlPathTypeDriveAbsolute ||
804 Type == RtlPathTypeDriveRelative)
805 {
806 /* make the drive letter to uppercase */
807 Buffer[tmpLength] = towupper(Buffer[tmpLength]);
808 }
809
810 /* set NT filename */
811 NtPathName->Length = Length * sizeof(WCHAR);
812 NtPathName->MaximumLength = sizeof(fullname) + MAX_PFX_SIZE;
813 NtPathName->Buffer = Buffer;
814
815 /* set pointer to file part if possible */
816 if (NtFileNamePart && *NtFileNamePart)
817 *NtFileNamePart = Buffer + Length - wcslen (*NtFileNamePart);
818
819 /* Set name and handle structure if possible */
820 if (DirectoryInfo)
821 {
822 memset (DirectoryInfo, 0, sizeof(RTL_RELATIVE_NAME_U));
823 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
824 if (Type == 5 && cd->Handle)
825 {
826 RtlInitUnicodeString(&us, fullname);
827 if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
828 {
829 Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
830 DirectoryInfo->RelativeName.Buffer = Buffer + Length;
831 DirectoryInfo->RelativeName.Length = NtPathName->Length - (Length * sizeof(WCHAR));
832 DirectoryInfo->RelativeName.MaximumLength = DirectoryInfo->RelativeName.Length;
833 DirectoryInfo->ContainingDirectory = cd->Handle;
834 }
835 }
836 }
837
838 RtlReleasePebLock();
839 return TRUE;
840 }
841
842
843 /*
844 * @implemented
845 */
846 /******************************************************************
847 * RtlDosSearchPath_U
848 *
849 * Searches a file of name 'name' into a ';' separated list of paths
850 * (stored in paths)
851 * Doesn't seem to search elsewhere than the paths list
852 * Stores the result in buffer (file_part will point to the position
853 * of the file name in the buffer)
854 * FIXME:
855 * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
856 */
857 ULONG
858 NTAPI
859 RtlDosSearchPath_U(PCWSTR paths,
860 PCWSTR search,
861 PCWSTR ext,
862 ULONG buffer_size,
863 PWSTR buffer,
864 PWSTR* file_part)
865 {
866 RTL_PATH_TYPE type = RtlDetermineDosPathNameType_U(search);
867 ULONG len = 0;
868
869 if (type == RtlPathTypeRelative)
870 {
871 ULONG allocated = 0, needed, filelen;
872 WCHAR *name = NULL;
873
874 filelen = 1 /* for \ */ + wcslen(search) + 1 /* \0 */;
875
876 /* Windows only checks for '.' without worrying about path components */
877 if (wcschr( search, '.' )) ext = NULL;
878 if (ext != NULL) filelen += wcslen(ext);
879
880 while (*paths)
881 {
882 LPCWSTR ptr;
883
884 for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
885 if (needed + filelen > allocated)
886 {
887 if (!name) name = RtlAllocateHeap(RtlGetProcessHeap(), 0,
888 (needed + filelen) * sizeof(WCHAR));
889 else
890 {
891 WCHAR *newname = RtlReAllocateHeap(RtlGetProcessHeap(), 0, name,
892 (needed + filelen) * sizeof(WCHAR));
893 if (!newname) RtlFreeHeap(RtlGetProcessHeap(), 0, name);
894 name = newname;
895 }
896 if (!name) return 0;
897 allocated = needed + filelen;
898 }
899 memmove(name, paths, needed * sizeof(WCHAR));
900 /* append '\\' if none is present */
901 if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
902 wcscpy(&name[needed], search);
903 if (ext) wcscat(&name[needed], ext);
904 if (RtlDoesFileExists_U(name))
905 {
906 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
907 break;
908 }
909 paths = ptr;
910 }
911 RtlFreeHeap(RtlGetProcessHeap(), 0, name);
912 }
913 else if (RtlDoesFileExists_U(search))
914 {
915 len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
916 }
917
918 return len;
919 }
920
921 /*
922 * @implemented
923 */
924 BOOLEAN
925 NTAPI
926 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
927 IN BOOLEAN SucceedIfBusy)
928 {
929 BOOLEAN Result;
930 RTL_RELATIVE_NAME_U RelativeName;
931 UNICODE_STRING NtPathName;
932 PVOID Buffer;
933 OBJECT_ATTRIBUTES ObjectAttributes;
934 NTSTATUS Status;
935 FILE_BASIC_INFORMATION BasicInformation;
936
937 #if 0
938 /* Get the NT Path */
939 Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
940 &NtPathName,
941 NULL,
942 &RelativeName);
943 #else
944 /* FIXME: Use the old API for now */
945 Result = RtlDosPathNameToNtPathName_U(FileName->Buffer,
946 &NtPathName,
947 NULL,
948 &RelativeName);
949 #endif
950 if (!Result) return FALSE;
951
952 /* Save the buffer */
953 Buffer = NtPathName.Buffer;
954
955 /* Check if we have a relative name */
956 if (RelativeName.RelativeName.Length)
957 {
958 /* Use it */
959 NtPathName = RelativeName.RelativeName;
960 }
961 else
962 {
963 /* Otherwise ignore it */
964 RelativeName.ContainingDirectory = NULL;
965 }
966
967 /* Initialize the object attributes */
968 InitializeObjectAttributes(&ObjectAttributes,
969 &NtPathName,
970 OBJ_CASE_INSENSITIVE,
971 RelativeName.ContainingDirectory,
972 NULL);
973
974 /* Query the attributes and free the buffer now */
975 Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation);
976 RtlReleaseRelativeName(&RelativeName);
977 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
978
979 /* Check if we failed */
980 if (!NT_SUCCESS(Status))
981 {
982 /* Check if we failed because the file is in use */
983 if ((Status == STATUS_SHARING_VIOLATION) ||
984 (Status == STATUS_ACCESS_DENIED))
985 {
986 /* Check if the caller wants this to be considered OK */
987 Result = SucceedIfBusy ? TRUE : FALSE;
988 }
989 else
990 {
991 /* A failure because the file didn't exist */
992 Result = FALSE;
993 }
994 }
995 else
996 {
997 /* The file exists */
998 Result = TRUE;
999 }
1000
1001 /* Return the result */
1002 return Result;
1003 }
1004
1005 BOOLEAN
1006 NTAPI
1007 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName)
1008 {
1009 /* Call the updated API */
1010 return RtlDoesFileExists_UstrEx(FileName, TRUE);
1011 }
1012
1013 /*
1014 * @implemented
1015 */
1016 BOOLEAN
1017 NTAPI
1018 RtlDoesFileExists_UEx(IN PCWSTR FileName,
1019 IN BOOLEAN SucceedIfBusy)
1020 {
1021 UNICODE_STRING NameString;
1022
1023 /* Create the unicode name*/
1024 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName)))
1025 {
1026 /* Call the unicode function */
1027 return NT_SUCCESS(RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy));
1028 }
1029
1030 /* Fail */
1031 return FALSE;
1032 }
1033
1034 /*
1035 * @implemented
1036 */
1037 BOOLEAN
1038 NTAPI
1039 RtlDoesFileExists_U(IN PCWSTR FileName)
1040 {
1041 /* Call the new function */
1042 return RtlDoesFileExists_UEx(FileName, TRUE);
1043 }
1044
1045 /*
1046 * @unimplemented
1047 */
1048 BOOLEAN NTAPI
1049 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1,
1050 PVOID Unknown2,
1051 PVOID Unknown3,
1052 PVOID Unknown4)
1053 {
1054 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
1055 Unknown1, Unknown2, Unknown3, Unknown4);
1056 return FALSE;
1057 }
1058
1059 NTSTATUS NTAPI
1060 RtlpEnsureBufferSize(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3)
1061 {
1062 DPRINT1("RtlpEnsureBufferSize: stub\n");
1063 return STATUS_NOT_IMPLEMENTED;
1064 }
1065
1066 NTSTATUS NTAPI
1067 RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
1068 {
1069 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1070 return STATUS_NOT_IMPLEMENTED;
1071 }
1072
1073 /* EOF */