- NDK 0.98, now with versionned headers. Too many changes to list, see the TinyKRNL...
[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:
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <rtl.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* DEFINITONS and MACROS ******************************************************/
17
18 #define MAX_PFX_SIZE 16
19
20 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
21
22
23 /* GLOBALS ********************************************************************/
24
25 static const WCHAR DeviceRootW[] = L"\\\\.\\";
26
27 static const UNICODE_STRING _condev = RTL_CONSTANT_STRING(L"\\\\.\\CON");
28
29 static const UNICODE_STRING _lpt = RTL_CONSTANT_STRING(L"LPT");
30
31 static const UNICODE_STRING _com = RTL_CONSTANT_STRING(L"COM");
32
33 static const UNICODE_STRING _prn = RTL_CONSTANT_STRING(L"PRN");
34
35 static const UNICODE_STRING _aux = RTL_CONSTANT_STRING(L"AUX");
36
37 static const UNICODE_STRING _con = RTL_CONSTANT_STRING(L"CON");
38
39 static const UNICODE_STRING _nul = RTL_CONSTANT_STRING(L"NUL");
40
41 /* FUNCTIONS *****************************************************************/
42
43
44 /*
45 * @implemented
46 */
47 ULONG NTAPI RtlGetLongestNtPathLength (VOID)
48 {
49 return (MAX_PATH + 9);
50 }
51
52
53 /*
54 * @implemented
55 *
56 */
57 ULONG NTAPI
58 RtlDetermineDosPathNameType_U(PCWSTR Path)
59 {
60 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
61
62 if (Path == NULL)
63 {
64 return RtlPathTypeUnknown;
65 }
66
67 if (IS_PATH_SEPARATOR(Path[0]))
68 {
69 if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \xxx */
70 if (Path[2] != L'.') return RtlPathTypeUncAbsolute; /* \\xxx */
71 if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\xxx */
72 if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.xxxx */
73
74 return RtlPathTypeRootLocalDevice; /* \\. */
75 }
76 else
77 {
78 /* FIXME: the Wine version of this line reads:
79 * if (!Path[1] || Path[1] != L':') return RtlPathTypeRelative
80 * Should we do this too?
81 * -Gunnar
82 */
83 if (Path[1] != L':') return RtlPathTypeRelative; /* xxx */
84 if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\xxx */
85
86 return RtlPathTypeDriveRelative; /* x:xxx */
87 }
88 }
89
90
91 /* returns 0 if name is not valid DOS device name, or DWORD with
92 * offset in bytes to DOS device name from beginning of buffer in high word
93 * and size in bytes of DOS device name in low word */
94
95 /*
96 * @implemented
97 */
98 ULONG NTAPI
99 RtlIsDosDeviceName_U(PWSTR DeviceName)
100 {
101 ULONG Type;
102 ULONG Length = 0;
103 ULONG Offset;
104 PWCHAR wc;
105 UNICODE_STRING DeviceNameU;
106
107 if (DeviceName == NULL)
108 {
109 return 0;
110 }
111
112 while (DeviceName[Length])
113 {
114 Length++;
115 }
116
117 Type = RtlDetermineDosPathNameType_U(DeviceName);
118 if (Type <= 1)
119 {
120 return 0;
121 }
122
123 if (Type == 6)
124 {
125 DeviceNameU.Length = DeviceNameU.MaximumLength = Length * sizeof(WCHAR);
126 DeviceNameU.Buffer = DeviceName;
127 if (Length == 7 &&
128 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_condev, TRUE))
129 return 0x00080006;
130 return 0;
131 }
132
133 /* name can end with ':' */
134 if (Length && DeviceName[Length - 1 ] == L':')
135 {
136 Length--;
137 }
138
139 /* there can be spaces or points at the end of name */
140 wc = DeviceName + Length - 1;
141 while (Length && (*wc == L'.' || *wc == L' '))
142 {
143 Length--;
144 wc--;
145 }
146
147 /* let's find a beginning of name */
148 wc = DeviceName + Length - 1;
149 while (wc > DeviceName && !IS_PATH_SEPARATOR(*(wc - 1)))
150 {
151 wc--;
152 }
153 Offset = wc - DeviceName;
154 Length -= Offset;
155 DeviceNameU.Length = DeviceNameU.MaximumLength = 3 * sizeof(WCHAR);
156 DeviceNameU.Buffer = wc;
157
158 /* check for LPTx or COMx */
159 if (Length == 4 && wc[3] >= L'0' && wc[3] <= L'9')
160 {
161 if (wc[3] == L'0')
162 {
163 return 0;
164 }
165
166 if (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_lpt, TRUE) ||
167 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_com, TRUE))
168 {
169 return ((Offset * 2) << 16 ) | 8;
170 }
171 return 0;
172 }
173
174 /* check for PRN,AUX,NUL or CON */
175 if (Length == 3 &&
176 (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_prn, TRUE) ||
177 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_aux, TRUE) ||
178 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_nul, TRUE) ||
179 RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_con, TRUE)))
180 {
181 return ((Offset * 2) << 16) | 6;
182 }
183
184 return 0;
185 }
186
187
188 /*
189 * @implemented
190 */
191 ULONG NTAPI
192 RtlGetCurrentDirectory_U(ULONG MaximumLength,
193 PWSTR Buffer)
194 {
195 ULONG Length;
196 PCURDIR cd;
197
198 DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
199
200 RtlAcquirePebLock();
201
202 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
203 Length = cd->DosPath.Length / sizeof(WCHAR);
204 if (cd->DosPath.Buffer[Length - 1] == L'\\' &&
205 cd->DosPath.Buffer[Length - 2] != L':')
206 Length--;
207
208 DPRINT ("cd->DosPath.Buffer %S Length %lu\n",
209 cd->DosPath.Buffer, Length);
210
211 if (MaximumLength / sizeof(WCHAR) > Length)
212 {
213 memcpy (Buffer,
214 cd->DosPath.Buffer,
215 Length * sizeof(WCHAR));
216 Buffer[Length] = 0;
217 }
218 else
219 {
220 Length++;
221 }
222
223 RtlReleasePebLock ();
224
225 DPRINT ("CurrentDirectory %S\n", Buffer);
226
227 return (Length * sizeof(WCHAR));
228 }
229
230
231 /*
232 * @implemented
233 */
234 NTSTATUS NTAPI
235 RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
236 {
237 UNICODE_STRING full;
238 UNICODE_STRING envvar;
239 FILE_FS_DEVICE_INFORMATION device_info;
240 OBJECT_ATTRIBUTES Attr;
241 IO_STATUS_BLOCK iosb;
242 PCURDIR cd;
243 NTSTATUS Status;
244 ULONG size;
245 HANDLE handle = NULL;
246 WCHAR var[4];
247 PWSTR ptr;
248
249 DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
250
251 RtlAcquirePebLock ();
252
253 cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
254
255 if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
256 {
257 RtlReleasePebLock ();
258 return STATUS_OBJECT_NAME_INVALID;
259 }
260
261 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
262
263 InitializeObjectAttributes (&Attr,
264 &full,
265 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
266 NULL,
267 NULL);
268
269 Status = ZwOpenFile (&handle,
270 SYNCHRONIZE | FILE_TRAVERSE,
271 &Attr,
272 &iosb,
273 FILE_SHARE_READ | FILE_SHARE_WRITE,
274 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
275
276 if (!NT_SUCCESS(Status))
277 {
278 RtlFreeUnicodeString( &full);
279 RtlReleasePebLock ();
280 return Status;
281 }
282
283 /* don't keep the directory handle open on removable media */
284 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
285 sizeof(device_info), FileFsDeviceInformation )) &&
286 (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
287 {
288 DPRINT1("don't keep the directory handle open on removable media\n");
289 ZwClose( handle );
290 handle = 0;
291 }
292
293 if (cd->Handle)
294 ZwClose(cd->Handle);
295 cd->Handle = handle;
296
297 /* append trailing \ if missing */
298 size = full.Length / sizeof(WCHAR);
299 ptr = full.Buffer;
300 ptr += 4; /* skip \??\ prefix */
301 size -= 4;
302
303 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
304 * So the nullterm is replaced with \
305 * -Gunnar
306 */
307 if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
308
309 memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
310 cd->DosPath.Buffer[size] = 0;
311 cd->DosPath.Length = size * sizeof(WCHAR);
312
313
314 /* FIXME: whats this all about??? Wine doesnt have this. -Gunnar */
315 if (cd->DosPath.Buffer[1]==':')
316 {
317 envvar.Length = 2 * swprintf (var, L"=%c:", cd->DosPath.Buffer[0]);
318 envvar.MaximumLength = 8;
319 envvar.Buffer = var;
320
321 RtlSetEnvironmentVariable(NULL,
322 &envvar,
323 &cd->DosPath);
324 }
325
326 RtlFreeUnicodeString( &full);
327 RtlReleasePebLock();
328
329 return STATUS_SUCCESS;
330 }
331
332
333
334 /******************************************************************
335 * collapse_path
336 *
337 * Helper for RtlGetFullPathName_U.
338 * 1) Convert slashes into backslashes
339 * 2) Get rid of duplicate backslashes
340 * 3) Get rid of . and .. components in the path.
341 */
342 static __inline void 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 == '\\') p++;
398 }
399
400 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
401 while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
402 *p = 0;
403 }
404
405
406
407 /******************************************************************
408 * skip_unc_prefix
409 *
410 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
411 */
412 static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
413 {
414 ptr += 2;
415 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */
416 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
417 while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */
418 while (IS_PATH_SEPARATOR(*ptr)) ptr++;
419 return ptr;
420 }
421
422
423 /******************************************************************
424 * get_full_path_helper
425 *
426 * Helper for RtlGetFullPathName_U
427 * Note: name and buffer are allowed to point to the same memory spot
428 */
429 static ULONG get_full_path_helper(
430 LPCWSTR name,
431 LPWSTR buffer,
432 ULONG size)
433 {
434 ULONG reqsize = 0, mark = 0, dep = 0, deplen;
435 RTL_PATH_TYPE type;
436 LPWSTR ins_str = NULL;
437 LPCWSTR ptr;
438 const UNICODE_STRING* cd;
439 WCHAR tmp[4];
440
441 /* return error if name only consists of spaces */
442 for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
443 if (!*ptr) return 0;
444
445 RtlAcquirePebLock();
446
447 cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
448
449 switch (type = RtlDetermineDosPathNameType_U(name))
450 {
451 case RtlPathTypeUncAbsolute: /* \\foo */
452 ptr = skip_unc_prefix( name );
453 mark = (ptr - name);
454 break;
455
456 case RtlPathTypeLocalDevice: /* \\.\foo */
457 mark = 4;
458 break;
459
460 case RtlPathTypeDriveAbsolute: /* c:\foo */
461 reqsize = sizeof(WCHAR);
462 tmp[0] = towupper(name[0]);
463 ins_str = tmp;
464 dep = 1;
465 mark = 3;
466 break;
467
468 case RtlPathTypeDriveRelative: /* c:foo */
469 dep = 2;
470 if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
471 {
472 UNICODE_STRING var, val;
473
474 tmp[0] = '=';
475 tmp[1] = name[0];
476 tmp[2] = ':';
477 tmp[3] = '\0';
478 var.Length = 3 * sizeof(WCHAR);
479 var.MaximumLength = 4 * sizeof(WCHAR);
480 var.Buffer = tmp;
481 val.Length = 0;
482 val.MaximumLength = size;
483 val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
484 if (val.Buffer == NULL)
485 {
486 reqsize = 0;
487 goto done;
488 }
489
490 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
491 {
492 case STATUS_SUCCESS:
493 /* FIXME: Win2k seems to check that the environment variable actually points
494 * to an existing directory. If not, root of the drive is used
495 * (this seems also to be the only spot in RtlGetFullPathName that the
496 * existence of a part of a path is checked)
497 */
498 /* fall thru */
499 case STATUS_BUFFER_TOO_SMALL:
500 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
501 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
502 ins_str = val.Buffer;
503 break;
504 case STATUS_VARIABLE_NOT_FOUND:
505 reqsize = 3 * sizeof(WCHAR);
506 tmp[0] = name[0];
507 tmp[1] = ':';
508 tmp[2] = '\\';
509 ins_str = tmp;
510 break;
511 default:
512 DPRINT1("Unsupported status code\n");
513 break;
514 }
515 mark = 3;
516 break;
517 }
518 /* fall through */
519
520 case RtlPathTypeRelative: /* foo */
521 reqsize = cd->Length;
522 ins_str = cd->Buffer;
523 if (cd->Buffer[1] != ':')
524 {
525 ptr = skip_unc_prefix( cd->Buffer );
526 mark = ptr - cd->Buffer;
527 }
528 else mark = 3;
529 break;
530
531 case RtlPathTypeRooted: /* \xxx */
532 #ifdef __WINE__
533 if (name[0] == '/') /* may be a Unix path */
534 {
535 const WCHAR *ptr = name;
536 int drive = find_drive_root( &ptr );
537 if (drive != -1)
538 {
539 reqsize = 3 * sizeof(WCHAR);
540 tmp[0] = 'A' + drive;
541 tmp[1] = ':';
542 tmp[2] = '\\';
543 ins_str = tmp;
544 mark = 3;
545 dep = ptr - name;
546 break;
547 }
548 }
549 #endif
550 if (cd->Buffer[1] == ':')
551 {
552 reqsize = 2 * sizeof(WCHAR);
553 tmp[0] = cd->Buffer[0];
554 tmp[1] = ':';
555 ins_str = tmp;
556 mark = 3;
557 }
558 else
559 {
560 ptr = skip_unc_prefix( cd->Buffer );
561 reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
562 mark = reqsize / sizeof(WCHAR);
563 ins_str = cd->Buffer;
564 }
565 break;
566
567 case RtlPathTypeRootLocalDevice: /* \\. */
568 reqsize = 4 * sizeof(WCHAR);
569 dep = 3;
570 tmp[0] = '\\';
571 tmp[1] = '\\';
572 tmp[2] = '.';
573 tmp[3] = '\\';
574 ins_str = tmp;
575 mark = 4;
576 break;
577
578 case RtlPathTypeUnknown:
579 goto done;
580 }
581
582 /* enough space ? */
583 deplen = wcslen(name + dep) * sizeof(WCHAR);
584 if (reqsize + deplen + sizeof(WCHAR) > size)
585 {
586 /* not enough space, return need size (including terminating '\0') */
587 reqsize += deplen + sizeof(WCHAR);
588 goto done;
589 }
590
591 memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
592 if (reqsize) memcpy(buffer, ins_str, reqsize);
593 reqsize += deplen;
594
595 if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
596 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
597
598 collapse_path( buffer, mark );
599 reqsize = wcslen(buffer) * sizeof(WCHAR);
600
601 done:
602 RtlReleasePebLock();
603 return reqsize;
604 }
605
606
607 /******************************************************************
608 * RtlGetFullPathName_U (NTDLL.@)
609 *
610 * Returns the number of bytes written to buffer (not including the
611 * terminating NULL) if the function succeeds, or the required number of bytes
612 * (including the terminating NULL) if the buffer is too small.
613 *
614 * file_part will point to the filename part inside buffer (except if we use
615 * DOS device name, in which case file_in_buf is NULL)
616 *
617 * @implemented
618 */
619 DWORD NTAPI RtlGetFullPathName_U(
620 const WCHAR* name,
621 ULONG size,
622 WCHAR* buffer,
623 WCHAR** file_part)
624 {
625 WCHAR* ptr;
626 DWORD dosdev;
627 DWORD reqsize;
628
629 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
630
631 if (!name || !*name) return 0;
632
633 if (file_part) *file_part = NULL;
634
635 /* check for DOS device name */
636 dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
637 if (dosdev)
638 {
639 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
640 DWORD sz = LOWORD(dosdev); /* in bytes */
641
642 if (8 + sz + 2 > size) return sz + 10;
643 wcscpy(buffer, DeviceRootW);
644 memmove(buffer + 4, name + offset, sz);
645 buffer[4 + sz / sizeof(WCHAR)] = '\0';
646 /* file_part isn't set in this case */
647 return sz + 8;
648 }
649
650 reqsize = get_full_path_helper(name, buffer, size);
651 if (!reqsize) return 0;
652 if (reqsize > size)
653 {
654 LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
655 if (tmp == NULL)
656 return 0;
657 reqsize = get_full_path_helper(name, tmp, reqsize);
658 if (reqsize > size) /* it may have worked the second time */
659 {
660 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
661 return reqsize + sizeof(WCHAR);
662 }
663 memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
664 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
665 }
666
667 /* find file part */
668 if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
669 *file_part = ptr;
670 return reqsize;
671 }
672
673
674 /*
675 * @implemented
676 */
677 BOOLEAN NTAPI
678 RtlDosPathNameToNtPathName_U(IN PCWSTR DosPathName,
679 OUT PUNICODE_STRING NtPathName,
680 OUT PCWSTR *NtFileNamePart,
681 OUT CURDIR *DirectoryInfo)
682 {
683 UNICODE_STRING us;
684 PCURDIR cd;
685 ULONG Type;
686 ULONG Size;
687 ULONG Length;
688 ULONG tmpLength;
689 ULONG Offset;
690 WCHAR fullname[MAX_PATH + 1];
691 PWSTR Buffer = NULL;
692
693 RtlInitUnicodeString (&us, DosPathName);
694 if (us.Length > 8)
695 {
696 Buffer = us.Buffer;
697 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
698 if (Buffer[0] == L'\\' && Buffer[1] == L'\\' &&
699 Buffer[2] == L'?' && Buffer[3] == L'\\')
700 {
701 /* allocate the new string and simply copy it */
702 NtPathName->Length = us.Length;
703 NtPathName->MaximumLength = us.Length + sizeof(WCHAR);
704 NtPathName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
705 0,
706 NtPathName->MaximumLength);
707 if (NtPathName->Buffer == NULL)
708 {
709 return FALSE;
710 }
711
712 /* copy the string */
713 RtlCopyMemory(NtPathName->Buffer,
714 us.Buffer,
715 NtPathName->Length);
716 NtPathName->Buffer[us.Length / sizeof(WCHAR)] = L'\0';
717
718 /* change the \\?\ prefix to \??\ */
719 NtPathName->Buffer[1] = L'?';
720
721 if (NtFileNamePart != NULL)
722 {
723 PWSTR FilePart = NULL;
724 PWSTR s;
725
726 /* try to find the last separator */
727 s = NtPathName->Buffer + (NtPathName->Length / sizeof(WCHAR));
728 while (s != NtPathName->Buffer)
729 {
730 if (*s == L'\\')
731 {
732 FilePart = s + 1;
733 break;
734 }
735 s--;
736 }
737
738 *NtFileNamePart = FilePart;
739 }
740
741 if (DirectoryInfo != NULL)
742 {
743 DirectoryInfo->DosPath.Length = 0;
744 DirectoryInfo->DosPath.MaximumLength = 0;
745 DirectoryInfo->DosPath.Buffer = NULL;
746 DirectoryInfo->Handle = NULL;
747 }
748
749 return TRUE;
750 }
751 }
752
753 Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
754 0,
755 sizeof( fullname ) + MAX_PFX_SIZE);
756 if (Buffer == NULL)
757 {
758 return FALSE;
759 }
760
761 RtlAcquirePebLock ();
762
763 Size = RtlGetFullPathName_U (DosPathName,
764 sizeof(fullname),
765 fullname,
766 (PWSTR*)NtFileNamePart);
767 if (Size == 0 || Size > MAX_PATH * sizeof(WCHAR))
768 {
769 RtlFreeHeap (RtlGetProcessHeap (),
770 0,
771 Buffer);
772 RtlReleasePebLock ();
773 return FALSE;
774 }
775
776 /* Set NT prefix */
777 Offset = 0;
778 memcpy (Buffer, L"\\??\\", 4 * sizeof(WCHAR));
779 tmpLength = 4;
780
781 Type = RtlDetermineDosPathNameType_U (fullname);
782 switch (Type)
783 {
784 case 1:
785 memcpy (Buffer + tmpLength, L"UNC\\", 4 * sizeof(WCHAR));
786 tmpLength += 4;
787 Offset = 2;
788 break; /* \\xxx */
789
790 case 6:
791 Offset = 4;
792 break; /* \\.\xxx */
793 }
794 Length = wcslen(fullname + Offset);
795 memcpy (Buffer + tmpLength, fullname + Offset, (Length + 1) * sizeof(WCHAR));
796 Length += tmpLength;
797 if (Type == RtlPathTypeDriveAbsolute ||
798 Type == RtlPathTypeDriveRelative)
799 {
800 /* make the drive letter to uppercase */
801 Buffer[tmpLength] = towupper(Buffer[tmpLength]);
802 }
803
804 /* set NT filename */
805 NtPathName->Length = Length * sizeof(WCHAR);
806 NtPathName->MaximumLength = sizeof(fullname) + MAX_PFX_SIZE;
807 NtPathName->Buffer = Buffer;
808
809 /* set pointer to file part if possible */
810 if (NtFileNamePart && *NtFileNamePart)
811 *NtFileNamePart = Buffer + Length - wcslen (*NtFileNamePart);
812
813 /* Set name and handle structure if possible */
814 if (DirectoryInfo)
815 {
816 memset (DirectoryInfo, 0, sizeof(CURDIR));
817 cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
818 if (Type == 5 && cd->Handle)
819 {
820 RtlInitUnicodeString(&us, fullname);
821 if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
822 {
823 Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
824 DirectoryInfo->DosPath.Buffer = Buffer + Length;
825 DirectoryInfo->DosPath.Length = NtPathName->Length - (Length * sizeof(WCHAR));
826 DirectoryInfo->DosPath.MaximumLength = DirectoryInfo->DosPath.Length;
827 DirectoryInfo->Handle = cd->Handle;
828 }
829 }
830 }
831
832 RtlReleasePebLock();
833 return TRUE;
834 }
835
836
837 /*
838 * @implemented
839 */
840 ULONG
841 NTAPI
842 RtlDosSearchPath_U (
843 PCWSTR sp,
844 PCWSTR name,
845 PCWSTR ext,
846 ULONG buf_sz,
847 WCHAR *buffer,
848 PWSTR *FilePart
849 )
850 {
851 ULONG Type;
852 ULONG Length = 0;
853 PWSTR full_name;
854 PWSTR wcs;
855 PCWSTR path;
856
857 Type = RtlDetermineDosPathNameType_U (name);
858
859 if (Type == 5)
860 {
861 Length = wcslen (sp);
862 Length += wcslen (name);
863 if (wcschr (name, L'.'))
864 ext = NULL;
865 if (ext != NULL)
866 Length += wcslen (ext);
867
868 full_name = (WCHAR*)RtlAllocateHeap (RtlGetProcessHeap (),
869 0,
870 (Length + 1) * sizeof(WCHAR));
871 Length = 0;
872 if (full_name != NULL)
873 {
874 path = sp;
875 while (*path)
876 {
877 wcs = full_name;
878 while (*path && *path != L';')
879 *wcs++ = *path++;
880 if (*path)
881 path++;
882 if (wcs != full_name && *(wcs - 1) != L'\\')
883 *wcs++ = L'\\';
884 wcscpy (wcs, name);
885 if (ext)
886 wcscat (wcs, ext);
887 if (RtlDoesFileExists_U (full_name))
888 {
889 Length = RtlGetFullPathName_U (full_name,
890 buf_sz,
891 buffer,
892 FilePart);
893 break;
894 }
895 }
896
897 RtlFreeHeap (RtlGetProcessHeap (),
898 0,
899 full_name);
900 }
901 }
902 else if (RtlDoesFileExists_U (name))
903 {
904 Length = RtlGetFullPathName_U (name,
905 buf_sz,
906 buffer,
907 FilePart);
908 }
909
910 return Length;
911 }
912
913
914 /*
915 * @implemented
916 */
917 BOOLEAN NTAPI
918 RtlDoesFileExists_U(IN PCWSTR FileName)
919 {
920 UNICODE_STRING NtFileName;
921 OBJECT_ATTRIBUTES Attr;
922 FILE_BASIC_INFORMATION Info;
923 NTSTATUS Status;
924 CURDIR CurDir;
925
926
927 if (!RtlDosPathNameToNtPathName_U (FileName,
928 &NtFileName,
929 NULL,
930 &CurDir))
931 return FALSE;
932
933 if (CurDir.DosPath.Length)
934 NtFileName = CurDir.DosPath;
935 else
936 CurDir.Handle = 0;
937
938 InitializeObjectAttributes (&Attr,
939 &NtFileName,
940 OBJ_CASE_INSENSITIVE,
941 CurDir.Handle,
942 NULL);
943
944 Status = ZwQueryAttributesFile (&Attr, &Info);
945
946 RtlFreeUnicodeString(&NtFileName);
947
948
949 if (NT_SUCCESS(Status) ||
950 Status == STATUS_SHARING_VIOLATION ||
951 Status == STATUS_ACCESS_DENIED)
952 return TRUE;
953
954 return FALSE;
955 }
956
957
958 /*
959 * @unimplemented
960 */
961 BOOLEAN NTAPI
962 RtlDosPathNameToRelativeNtPathName_U(PVOID Unknown1,
963 PVOID Unknown2,
964 PVOID Unknown3,
965 PVOID Unknown4)
966 {
967 DPRINT1("RtlDosPathNameToRelativeNtPathName_U(0x%p, 0x%p, 0x%p, 0x%p) UNIMPLEMENTED!\n",
968 Unknown1, Unknown2, Unknown3, Unknown4);
969 return FALSE;
970 }
971
972
973 /*
974 * @unimplemented
975 */
976 VOID NTAPI
977 RtlReleaseRelativeName(PVOID Unknown)
978 {
979 DPRINT1("RtlReleaseRelativeName(0x%p) UNIMPLEMENTED\n", Unknown);
980 }
981
982 NTSTATUS NTAPI
983 RtlpEnsureBufferSize(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3)
984 {
985 DPRINT1("RtlpEnsureBufferSize: stub\n");
986 return STATUS_NOT_IMPLEMENTED;
987 }
988
989 NTSTATUS NTAPI
990 RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
991 {
992 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
993 return STATUS_NOT_IMPLEMENTED;
994 }
995
996 /* EOF */