Started implementing support for the SubSystemTib field in the TEB of
[wine] / dlls / ntdll / path.c
1 /*
2  * Ntdll path functions
3  *
4  * Copyright 2002, 2003, 2004 Alexandre Julliard
5  * Copyright 2003 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winternl.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "wine/library.h"
35 #include "ntdll_misc.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(file);
38
39 static const WCHAR DeviceRootW[] = {'\\','\\','.','\\',0};
40 static const WCHAR NTDosPrefixW[] = {'\\','?','?','\\',0};
41 static const WCHAR UncPfxW[] = {'U','N','C','\\',0};
42
43 #define IS_SEPARATOR(ch)  ((ch) == '\\' || (ch) == '/')
44
45 #define MAX_DOS_DRIVES 26
46
47 struct drive_info
48 {
49     dev_t dev;
50     ino_t ino;
51 };
52
53 /***********************************************************************
54  *           get_drives_info
55  *
56  * Retrieve device/inode number for all the drives. Helper for find_drive_root.
57  */
58 static inline int get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
59 {
60     const char *config_dir = wine_get_config_dir();
61     char *buffer, *p;
62     struct stat st;
63     int i, ret;
64
65     buffer = RtlAllocateHeap( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") );
66     if (!buffer) return 0;
67     strcpy( buffer, config_dir );
68     strcat( buffer, "/dosdevices/a:" );
69     p = buffer + strlen(buffer) - 2;
70
71     for (i = ret = 0; i < MAX_DOS_DRIVES; i++)
72     {
73         *p = 'a' + i;
74         if (!stat( buffer, &st ))
75         {
76             info[i].dev = st.st_dev;
77             info[i].ino = st.st_ino;
78             ret++;
79         }
80         else
81         {
82             info[i].dev = 0;
83             info[i].ino = 0;
84         }
85     }
86     RtlFreeHeap( GetProcessHeap(), 0, buffer );
87     return ret;
88 }
89
90
91 /***********************************************************************
92  *           remove_last_component
93  *
94  * Remove the last component of the path. Helper for find_drive_root.
95  */
96 static inline int remove_last_component( const WCHAR *path, int len )
97 {
98     int level = 0;
99
100     while (level < 1)
101     {
102         /* find start of the last path component */
103         int prev = len;
104         if (prev <= 1) break;  /* reached root */
105         while (prev > 1 && !IS_SEPARATOR(path[prev - 1])) prev--;
106         /* does removing it take us up a level? */
107         if (len - prev != 1 || path[prev] != '.')  /* not '.' */
108         {
109             if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.')  /* is it '..'? */
110                 level--;
111             else
112                 level++;
113         }
114         /* strip off trailing slashes */
115         while (prev > 1 && IS_SEPARATOR(path[prev - 1])) prev--;
116         len = prev;
117     }
118     return len;
119 }
120
121
122 /***********************************************************************
123  *           find_drive_root
124  *
125  * Find a drive for which the root matches the beginning of the given path.
126  * This can be used to translate a Unix path into a drive + DOS path.
127  * Return value is the drive, or -1 on error. On success, ppath is modified
128  * to point to the beginning of the DOS path.
129  */
130 static int find_drive_root( LPCWSTR *ppath )
131 {
132     /* Starting with the full path, check if the device and inode match any of
133      * the wine 'drives'. If not then remove the last path component and try
134      * again. If the last component was a '..' then skip a normal component
135      * since it's a directory that's ascended back out of.
136      */
137     int drive, lenA, lenW;
138     char *buffer, *p;
139     const WCHAR *path = *ppath;
140     struct stat st;
141     struct drive_info info[MAX_DOS_DRIVES];
142
143     /* get device and inode of all drives */
144     if (!get_drives_info( info )) return -1;
145
146     /* strip off trailing slashes */
147     lenW = strlenW(path);
148     while (lenW > 1 && IS_SEPARATOR(path[lenW - 1])) lenW--;
149
150     /* convert path to Unix encoding */
151     lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
152     if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, lenA + 1 ))) return -1;
153     lenA = ntdll_wcstoumbs( 0, path, lenW, buffer, lenA, NULL, NULL );
154     buffer[lenA] = 0;
155     for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
156
157     for (;;)
158     {
159         if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
160         {
161             /* Find the drive */
162             for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
163             {
164                 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
165                 {
166                     if (lenW == 1) lenW = 0;  /* preserve root slash in returned path */
167                     TRACE( "%s -> drive %c:, root=%s, name=%s\n",
168                            debugstr_w(path), 'A' + drive, debugstr_a(buffer), debugstr_w(path + lenW));
169                     *ppath += lenW;
170                     RtlFreeHeap( GetProcessHeap(), 0, buffer );
171                     return drive;
172                 }
173             }
174         }
175         if (lenW <= 1) break;  /* reached root */
176         lenW = remove_last_component( path, lenW );
177
178         /* we only need the new length, buffer already contains the converted string */
179         lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
180         buffer[lenA] = 0;
181     }
182     RtlFreeHeap( GetProcessHeap(), 0, buffer );
183     return -1;
184 }
185
186
187 /***********************************************************************
188  *             RtlDetermineDosPathNameType_U   (NTDLL.@)
189  */
190 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
191 {
192     if (IS_SEPARATOR(path[0]))
193     {
194         if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH;       /* "/foo" */
195         if (path[2] != '.') return UNC_PATH;                    /* "//foo" */
196         if (IS_SEPARATOR(path[3])) return DEVICE_PATH;          /* "//./foo" */
197         if (path[3]) return UNC_PATH;                           /* "//.foo" */
198         return UNC_DOT_PATH;                                    /* "//." */
199     }
200     else
201     {
202         if (!path[0] || path[1] != ':') return RELATIVE_PATH;   /* "foo" */
203         if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH;  /* "c:/foo" */
204         return RELATIVE_DRIVE_PATH;                             /* "c:foo" */
205     }
206 }
207
208 /***********************************************************************
209  *             RtlIsDosDeviceName_U   (NTDLL.@)
210  *
211  * Check if the given DOS path contains a DOS device name.
212  *
213  * Returns the length of the device name in the low word and its
214  * position in the high word (both in bytes, not WCHARs), or 0 if no
215  * device name is found.
216  */
217 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
218 {
219     static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
220     static const WCHAR auxW[3] = {'A','U','X'};
221     static const WCHAR comW[3] = {'C','O','M'};
222     static const WCHAR conW[3] = {'C','O','N'};
223     static const WCHAR lptW[3] = {'L','P','T'};
224     static const WCHAR nulW[3] = {'N','U','L'};
225     static const WCHAR prnW[3] = {'P','R','N'};
226
227     const WCHAR *start, *end, *p;
228
229     switch(RtlDetermineDosPathNameType_U( dos_name ))
230     {
231     case INVALID_PATH:
232     case UNC_PATH:
233         return 0;
234     case DEVICE_PATH:
235         if (!strcmpiW( dos_name, consoleW ))
236             return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) );  /* 4 is length of \\.\ prefix */
237         return 0;
238     default:
239         break;
240     }
241
242     end = dos_name + strlenW(dos_name) - 1;
243     if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' */
244
245     /* find start of file name */
246     for (start = end; start >= dos_name; start--)
247     {
248         if (IS_SEPARATOR(start[0])) break;
249         /* check for ':' but ignore if before extension (for things like NUL:.txt) */
250         if (start[0] == ':' && start[1] != '.') break;
251     }
252     start++;
253
254     /* remove extension */
255     if ((p = strchrW( start, '.' )))
256     {
257         end = p - 1;
258         if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' before extension */
259     }
260     else
261     {
262         /* no extension, remove trailing spaces */
263         while (end >= dos_name && *end == ' ') end--;
264     }
265
266     /* now we have a potential device name between start and end, check it */
267     switch(end - start + 1)
268     {
269     case 3:
270         if (strncmpiW( start, auxW, 3 ) &&
271             strncmpiW( start, conW, 3 ) &&
272             strncmpiW( start, nulW, 3 ) &&
273             strncmpiW( start, prnW, 3 )) break;
274         return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
275     case 4:
276         if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
277         if (*end <= '0' || *end > '9') break;
278         return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
279     default:  /* can't match anything */
280         break;
281     }
282     return 0;
283 }
284
285
286 /**************************************************************************
287  *                 RtlDosPathNameToNtPathName_U         [NTDLL.@]
288  *
289  * dos_path: a DOS path name (fully qualified or not)
290  * ntpath:   pointer to a UNICODE_STRING to hold the converted
291  *           path name
292  * file_part:will point (in ntpath) to the file part in the path
293  * cd:       directory reference (optional)
294  *
295  * FIXME:
296  *      + fill the cd structure
297  */
298 BOOLEAN  WINAPI RtlDosPathNameToNtPathName_U(PCWSTR dos_path,
299                                              PUNICODE_STRING ntpath,
300                                              PWSTR* file_part,
301                                              CURDIR* cd)
302 {
303     static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
304     ULONG sz, offset;
305     WCHAR local[MAX_PATH];
306     LPWSTR ptr;
307
308     TRACE("(%s,%p,%p,%p)\n",
309           debugstr_w(dos_path), ntpath, file_part, cd);
310
311     if (cd)
312     {
313         FIXME("Unsupported parameter\n");
314         memset(cd, 0, sizeof(*cd));
315     }
316
317     if (!dos_path || !*dos_path) return FALSE;
318
319     if (!strncmpW(dos_path, LongFileNamePfxW, 4))
320     {
321         ntpath->Length = strlenW(dos_path) * sizeof(WCHAR);
322         ntpath->MaximumLength = ntpath->Length + sizeof(WCHAR);
323         ntpath->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, ntpath->MaximumLength);
324         if (!ntpath->Buffer) return FALSE;
325         memcpy( ntpath->Buffer, dos_path, ntpath->MaximumLength );
326         ntpath->Buffer[1] = '?';  /* change \\?\ to \??\ */
327         return TRUE;
328     }
329
330     ptr = local;
331     sz = RtlGetFullPathName_U(dos_path, sizeof(local), ptr, file_part);
332     if (sz == 0) return FALSE;
333     if (sz > sizeof(local))
334     {
335         ptr = RtlAllocateHeap(GetProcessHeap(), 0, sz);
336         sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
337     }
338
339     ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
340     ntpath->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, ntpath->MaximumLength);
341     if (!ntpath->Buffer)
342     {
343         if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
344         return FALSE;
345     }
346
347     strcpyW(ntpath->Buffer, NTDosPrefixW);
348     offset = 0;
349     switch (RtlDetermineDosPathNameType_U(ptr))
350     {
351     case UNC_PATH: /* \\foo */
352         offset = 2;
353         strcatW(ntpath->Buffer, UncPfxW);
354         break;
355     case DEVICE_PATH: /* \\.\foo */
356         offset = 4;
357         break;
358     default:
359         break; /* needed to keep gcc quiet */
360     }
361
362     strcatW(ntpath->Buffer, ptr + offset);
363     ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
364
365     if (file_part && *file_part)
366         *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - strlenW(*file_part);
367
368     /* FIXME: cd filling */
369
370     if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
371     return TRUE;
372 }
373
374 /******************************************************************
375  *              RtlDosSearchPath_U
376  *
377  * Searchs a file of name 'name' into a ';' separated list of paths
378  * (stored in paths)
379  * Doesn't seem to search elsewhere than the paths list
380  * Stores the result in buffer (file_part will point to the position
381  * of the file name in the buffer)
382  * FIXME:
383  * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
384  */
385 ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext, 
386                                 ULONG buffer_size, LPWSTR buffer, 
387                                 LPWSTR* file_part)
388 {
389     DOS_PATHNAME_TYPE type = RtlDetermineDosPathNameType_U(search);
390     ULONG len = 0;
391
392     if (type == RELATIVE_PATH)
393     {
394         ULONG allocated = 0, needed, filelen;
395         WCHAR *name = NULL;
396
397         filelen = 1 /* for \ */ + strlenW(search) + 1 /* \0 */;
398
399         /* Windows only checks for '.' without worrying about path components */
400         if (strchrW( search, '.' )) ext = NULL;
401         if (ext != NULL) filelen += strlenW(ext);
402
403         while (*paths)
404         {
405             LPCWSTR ptr;
406
407             for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
408             if (needed + filelen > allocated)
409             {
410                 if (!name) name = RtlAllocateHeap(GetProcessHeap(), 0,
411                                                   (needed + filelen) * sizeof(WCHAR));
412                 else
413                 {
414                     WCHAR *newname = RtlReAllocateHeap(GetProcessHeap(), 0, name,
415                                                        (needed + filelen) * sizeof(WCHAR));
416                     if (!newname) RtlFreeHeap(GetProcessHeap(), 0, name);
417                     name = newname;
418                 }
419                 if (!name) return 0;
420                 allocated = needed + filelen;
421             }
422             memmove(name, paths, needed * sizeof(WCHAR));
423             /* append '\\' if none is present */
424             if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
425             strcpyW(&name[needed], search);
426             if (ext) strcatW(&name[needed], ext);
427             if (RtlDoesFileExists_U(name))
428             {
429                 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
430                 break;
431             }
432             paths = ptr;
433         }
434         RtlFreeHeap(GetProcessHeap(), 0, name);
435     }
436     else if (RtlDoesFileExists_U(search))
437     {
438         len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
439     }
440
441     return len;
442 }
443
444
445 /******************************************************************
446  *              collapse_path
447  *
448  * Helper for RtlGetFullPathName_U.
449  * Get rid of . and .. components in the path.
450  */
451 static inline void collapse_path( WCHAR *path, UINT mark )
452 {
453     WCHAR *p, *next;
454
455     /* convert every / into a \ */
456     for (p = path; *p; p++) if (*p == '/') *p = '\\';
457
458     /* collapse duplicate backslashes */
459     next = path + max( 1, mark );
460     for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
461     *next = 0;
462
463     p = path + mark;
464     while (*p)
465     {
466         if (*p == '.')
467         {
468             switch(p[1])
469             {
470             case '\\': /* .\ component */
471                 next = p + 2;
472                 memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
473                 continue;
474             case 0:  /* final . */
475                 if (p > path + mark) p--;
476                 *p = 0;
477                 continue;
478             case '.':
479                 if (p[2] == '\\')  /* ..\ component */
480                 {
481                     next = p + 3;
482                     if (p > path + mark)
483                     {
484                         p--;
485                         while (p > path + mark && p[-1] != '\\') p--;
486                     }
487                     memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
488                     continue;
489                 }
490                 else if (!p[2])  /* final .. */
491                 {
492                     if (p > path + mark)
493                     {
494                         p--;
495                         while (p > path + mark && p[-1] != '\\') p--;
496                         if (p > path + mark) p--;
497                     }
498                     *p = 0;
499                     continue;
500                 }
501                 break;
502             }
503         }
504         /* skip to the next component */
505         while (*p && *p != '\\') p++;
506         if (*p == '\\') p++;
507     }
508 }
509
510
511 /******************************************************************
512  *              skip_unc_prefix
513  *
514  * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
515  */
516 static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
517 {
518     ptr += 2;
519     while (*ptr && !IS_SEPARATOR(*ptr)) ptr++;  /* share name */
520     while (IS_SEPARATOR(*ptr)) ptr++;
521     while (*ptr && !IS_SEPARATOR(*ptr)) ptr++;  /* dir name */
522     while (IS_SEPARATOR(*ptr)) ptr++;
523     return ptr;
524 }
525
526
527 /******************************************************************
528  *              get_full_path_helper
529  *
530  * Helper for RtlGetFullPathName_U
531  * Note: name and buffer are allowed to point to the same memory spot
532  */
533 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
534 {
535     ULONG                       reqsize = 0, mark = 0, dep = 0, deplen;
536     DOS_PATHNAME_TYPE           type;
537     LPWSTR                      ins_str = NULL;
538     LPCWSTR                     ptr;
539     const UNICODE_STRING*       cd;
540     WCHAR                       tmp[4];
541
542     RtlAcquirePebLock();
543
544     cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
545
546     switch (type = RtlDetermineDosPathNameType_U(name))
547     {
548     case UNC_PATH:              /* \\foo   */
549         ptr = skip_unc_prefix( name );
550         mark = (ptr - name);
551         break;
552
553     case DEVICE_PATH:           /* \\.\foo */
554         mark = 4;
555         break;
556
557     case ABSOLUTE_DRIVE_PATH:   /* c:\foo  */
558         reqsize = sizeof(WCHAR);
559         tmp[0] = toupperW(name[0]);
560         ins_str = tmp;
561         dep = 1;
562         mark = 3;
563         break;
564
565     case RELATIVE_DRIVE_PATH:   /* c:foo   */
566         dep = 2;
567         if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
568         {
569             UNICODE_STRING      var, val;
570
571             tmp[0] = '=';
572             tmp[1] = name[0];
573             tmp[2] = ':';
574             tmp[3] = '\0';
575             var.Length = 3 * sizeof(WCHAR);
576             var.MaximumLength = 4 * sizeof(WCHAR);
577             var.Buffer = tmp;
578             val.Length = 0;
579             val.MaximumLength = size;
580             val.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, size);
581
582             switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
583             {
584             case STATUS_SUCCESS:
585                 /* FIXME: Win2k seems to check that the environment variable actually points 
586                  * to an existing directory. If not, root of the drive is used
587                  * (this seems also to be the only spot in RtlGetFullPathName that the 
588                  * existence of a part of a path is checked)
589                  */
590                 /* fall thru */
591             case STATUS_BUFFER_TOO_SMALL:
592                 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
593                 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
594                 ins_str = val.Buffer;
595                 break;
596             case STATUS_VARIABLE_NOT_FOUND:
597                 reqsize = 3 * sizeof(WCHAR);
598                 tmp[0] = name[0];
599                 tmp[1] = ':';
600                 tmp[2] = '\\';
601                 ins_str = tmp;
602                 break;
603             default:
604                 ERR("Unsupported status code\n");
605                 break;
606             }
607             mark = 3;
608             break;
609         }
610         /* fall through */
611
612     case RELATIVE_PATH:         /* foo     */
613         reqsize = cd->Length;
614         ins_str = cd->Buffer;
615         if (cd->Buffer[1] != ':')
616         {
617             ptr = skip_unc_prefix( cd->Buffer );
618             mark = ptr - cd->Buffer;
619         }
620         else mark = 3;
621         break;
622
623     case ABSOLUTE_PATH:         /* \xxx    */
624         if (name[0] == '/')  /* may be a Unix path */
625         {
626             const WCHAR *ptr = name;
627             int drive = find_drive_root( &ptr );
628             if (drive != -1)
629             {
630                 reqsize = 3 * sizeof(WCHAR);
631                 tmp[0] = 'A' + drive;
632                 tmp[1] = ':';
633                 tmp[2] = '\\';
634                 ins_str = tmp;
635                 mark = 3;
636                 dep = ptr - name;
637                 break;
638             }
639         }
640         if (cd->Buffer[1] == ':')
641         {
642             reqsize = 2 * sizeof(WCHAR);
643             tmp[0] = cd->Buffer[0];
644             tmp[1] = ':';
645             ins_str = tmp;
646             mark = 3;
647         }
648         else
649         {
650             ptr = skip_unc_prefix( cd->Buffer );
651             reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
652             mark = reqsize / sizeof(WCHAR);
653             ins_str = cd->Buffer;
654         }
655         break;
656
657     case UNC_DOT_PATH:         /* \\.     */
658         reqsize = 4 * sizeof(WCHAR);
659         dep = 3;
660         tmp[0] = '\\';
661         tmp[1] = '\\';
662         tmp[2] = '.';
663         tmp[3] = '\\';
664         ins_str = tmp;
665         mark = 4;
666         break;
667
668     case INVALID_PATH:
669         goto done;
670     }
671
672     /* enough space ? */
673     deplen = strlenW(name + dep) * sizeof(WCHAR);
674     if (reqsize + deplen + sizeof(WCHAR) > size)
675     {
676         /* not enough space, return need size (including terminating '\0') */
677         reqsize += deplen + sizeof(WCHAR);
678         goto done;
679     }
680
681     memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
682     if (reqsize) memcpy(buffer, ins_str, reqsize);
683     reqsize += deplen;
684
685     if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
686         RtlFreeHeap(GetProcessHeap(), 0, ins_str);
687
688     collapse_path( buffer, mark );
689     reqsize = strlenW(buffer) * sizeof(WCHAR);
690
691 done:
692     RtlReleasePebLock();
693     return reqsize;
694 }
695
696 /******************************************************************
697  *              RtlGetFullPathName_U  (NTDLL.@)
698  *
699  * Returns the number of bytes written to buffer (not including the
700  * terminating NULL) if the function succeeds, or the required number of bytes
701  * (including the terminating NULL) if the buffer is too small.
702  *
703  * file_part will point to the filename part inside buffer (except if we use
704  * DOS device name, in which case file_in_buf is NULL)
705  *
706  */
707 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
708                                   WCHAR** file_part)
709 {
710     WCHAR*      ptr;
711     DWORD       dosdev;
712     DWORD       reqsize;
713
714     TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
715
716     if (!name || !*name) return 0;
717
718     if (file_part) *file_part = NULL;
719
720     /* check for DOS device name */
721     dosdev = RtlIsDosDeviceName_U(name);
722     if (dosdev)
723     {
724         DWORD   offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
725         DWORD   sz = LOWORD(dosdev); /* in bytes */
726
727         if (8 + sz + 2 > size) return sz + 10;
728         strcpyW(buffer, DeviceRootW);
729         memmove(buffer + 4, name + offset, sz);
730         buffer[4 + sz / sizeof(WCHAR)] = '\0';
731         /* file_part isn't set in this case */
732         return sz + 8;
733     }
734
735     reqsize = get_full_path_helper(name, buffer, size);
736     if (reqsize > size)
737     {
738         LPWSTR tmp = RtlAllocateHeap(GetProcessHeap(), 0, reqsize);
739         reqsize = get_full_path_helper(name, tmp, reqsize);
740         if (reqsize > size)  /* it may have worked the second time */
741         {
742             RtlFreeHeap(GetProcessHeap(), 0, tmp);
743             return reqsize + sizeof(WCHAR);
744         }
745         memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
746         RtlFreeHeap(GetProcessHeap(), 0, tmp);
747     }
748
749     /* find file part */
750     if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
751         *file_part = ptr;
752     return reqsize;
753 }
754
755 /*************************************************************************
756  * RtlGetLongestNtPathLength    [NTDLL.@]
757  *
758  * Get the longest allowed path length
759  *
760  * PARAMS
761  *  None.
762  *
763  * RETURNS
764  *  The longest allowed path length (277 characters under Win2k).
765  */
766 DWORD WINAPI RtlGetLongestNtPathLength(void)
767 {
768     return 277;
769 }
770
771 /******************************************************************
772  *             RtlIsNameLegalDOS8Dot3   (NTDLL.@)
773  *
774  * Returns TRUE iff unicode is a valid DOS (8+3) name.
775  * If the name is valid, oem gets filled with the corresponding OEM string
776  * spaces is set to TRUE if unicode contains spaces
777  */
778 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
779                                        OEM_STRING *oem, BOOLEAN *spaces )
780 {
781     static const char* illegal = "*?<>|\"+=,;[]:/\\\345";
782     int dot = -1;
783     unsigned int i;
784     char buffer[12];
785     OEM_STRING oem_str;
786     BOOLEAN got_space = FALSE;
787
788     if (!oem)
789     {
790         oem_str.Length = sizeof(buffer);
791         oem_str.MaximumLength = sizeof(buffer);
792         oem_str.Buffer = buffer;
793         oem = &oem_str;
794     }
795     if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
796         return FALSE;
797
798     if (oem->Length > 12) return FALSE;
799
800     /* a starting . is invalid, except for . and .. */
801     if (oem->Buffer[0] == '.')
802     {
803         if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
804         if (spaces) *spaces = FALSE;
805         return TRUE;
806     }
807
808     for (i = 0; i < oem->Length; i++)
809     {
810         switch (oem->Buffer[i])
811         {
812         case ' ':
813             /* leading/trailing spaces not allowed */
814             if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
815             got_space = TRUE;
816             break;
817         case '.':
818             if (dot != -1) return FALSE;
819             dot = i;
820             break;
821         default:
822             if (strchr(illegal, oem->Buffer[i])) return FALSE;
823             break;
824         }
825     }
826     /* check file part is shorter than 8, extension shorter than 3
827      * dot cannot be last in string
828      */
829     if (dot == -1)
830     {
831         if (oem->Length > 8) return FALSE;
832     }
833     else
834     {
835         if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
836     }
837     if (spaces) *spaces = got_space;
838     return TRUE;
839 }
840
841 /******************************************************************
842  *              RtlGetCurrentDirectory_U (NTDLL.@)
843  *
844  */
845 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
846 {
847     UNICODE_STRING*     us;
848     ULONG               len;
849
850     TRACE("(%lu %p)\n", buflen, buf);
851
852     RtlAcquirePebLock();
853
854     us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
855     len = us->Length / sizeof(WCHAR);
856     if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
857         len--;
858
859     if (buflen / sizeof(WCHAR) > len)
860     {
861         memcpy(buf, us->Buffer, len * sizeof(WCHAR));
862         buf[len] = '\0';
863     }
864     else
865     {
866         len++;
867     }
868
869     RtlReleasePebLock();
870
871     return len * sizeof(WCHAR);
872 }
873
874 /******************************************************************
875  *              RtlSetCurrentDirectory_U (NTDLL.@)
876  *
877  */
878 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
879 {
880     UNICODE_STRING*     curdir;
881     NTSTATUS            nts = STATUS_SUCCESS;
882     ULONG               size;
883     PWSTR               buf = NULL;
884
885     TRACE("(%s)\n", debugstr_w(dir->Buffer));
886
887     RtlAcquirePebLock();
888
889     curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
890     size = curdir->MaximumLength;
891
892     buf = RtlAllocateHeap(GetProcessHeap(), 0, size);
893     if (buf == NULL)
894     {
895         nts = STATUS_NO_MEMORY;
896         goto out;
897     }
898
899     size = RtlGetFullPathName_U(dir->Buffer, size, buf, 0);
900     if (!size)
901     {
902         nts = STATUS_OBJECT_NAME_INVALID;
903         goto out;
904     }
905
906     switch (RtlDetermineDosPathNameType_U(buf))
907     {
908     case ABSOLUTE_DRIVE_PATH:
909     case UNC_PATH:
910         break;
911     default:
912         FIXME("Don't support those cases yes\n");
913         nts = STATUS_NOT_IMPLEMENTED;
914         goto out;
915     }
916
917     /* FIXME: should check that the directory actually exists,
918      * and fill CurrentDirectoryHandle accordingly 
919      */
920
921     /* append trailing \ if missing */
922     if (buf[size / sizeof(WCHAR) - 1] != '\\')
923     {
924         buf[size / sizeof(WCHAR)] = '\\';
925         buf[size / sizeof(WCHAR) + 1] = '\0';
926         size += sizeof(WCHAR);
927     }
928
929     memmove(curdir->Buffer, buf, size + sizeof(WCHAR));
930     curdir->Length = size;
931
932 #if 0
933     if (curdir->Buffer[1] == ':')
934     {
935         UNICODE_STRING  env;
936         WCHAR           var[4];
937
938         var[0] = '=';
939         var[1] = curdir->Buffer[0];
940         var[2] = ':';
941         var[3] = 0;
942         env.Length = 3 * sizeof(WCHAR);
943         env.MaximumLength = 4 * sizeof(WCHAR);
944         env.Buffer = var;
945
946         RtlSetEnvironmentVariable(NULL, &env, curdir);
947     }
948 #endif
949
950  out:
951     if (buf) RtlFreeHeap(GetProcessHeap(), 0, buf);
952
953     RtlReleasePebLock();
954
955     return nts;
956 }