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