Stub implementation for MsiGetFileHashA/W.
[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 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #ifdef HAVE_SYS_STAT_H
29 # include <sys/stat.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 #include "windef.h"
36 #include "winioctl.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
39 #include "wine/library.h"
40 #include "thread.h"
41 #include "ntdll_misc.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(file);
44
45 static const WCHAR DeviceRootW[] = {'\\','\\','.','\\',0};
46 static const WCHAR NTDosPrefixW[] = {'\\','?','?','\\',0};
47 static const WCHAR UncPfxW[] = {'U','N','C','\\',0};
48
49 #define IS_SEPARATOR(ch)  ((ch) == '\\' || (ch) == '/')
50
51 #define MAX_DOS_DRIVES 26
52
53 struct drive_info
54 {
55     dev_t dev;
56     ino_t ino;
57 };
58
59 /***********************************************************************
60  *           get_drives_info
61  *
62  * Retrieve device/inode number for all the drives. Helper for find_drive_root.
63  */
64 static inline int get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
65 {
66     const char *config_dir = wine_get_config_dir();
67     char *buffer, *p;
68     struct stat st;
69     int i, ret;
70
71     buffer = RtlAllocateHeap( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") );
72     if (!buffer) return 0;
73     strcpy( buffer, config_dir );
74     strcat( buffer, "/dosdevices/a:" );
75     p = buffer + strlen(buffer) - 2;
76
77     for (i = ret = 0; i < MAX_DOS_DRIVES; i++)
78     {
79         *p = 'a' + i;
80         if (!stat( buffer, &st ))
81         {
82             info[i].dev = st.st_dev;
83             info[i].ino = st.st_ino;
84             ret++;
85         }
86         else
87         {
88             info[i].dev = 0;
89             info[i].ino = 0;
90         }
91     }
92     RtlFreeHeap( GetProcessHeap(), 0, buffer );
93     return ret;
94 }
95
96
97 /***********************************************************************
98  *           remove_last_componentA
99  *
100  * Remove the last component of the path. Helper for find_drive_rootA.
101  */
102 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
103 {
104     int level = 0;
105
106     while (level < 1)
107     {
108         /* find start of the last path component */
109         unsigned int prev = len;
110         if (prev <= 1) break;  /* reached root */
111         while (prev > 1 && path[prev - 1] != '/') prev--;
112         /* does removing it take us up a level? */
113         if (len - prev != 1 || path[prev] != '.')  /* not '.' */
114         {
115             if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.')  /* is it '..'? */
116                 level--;
117             else
118                 level++;
119         }
120         /* strip off trailing slashes */
121         while (prev > 1 && path[prev - 1] == '/') prev--;
122         len = prev;
123     }
124     return len;
125 }
126
127
128 /***********************************************************************
129  *           find_drive_rootA
130  *
131  * Find a drive for which the root matches the beginning of the given path.
132  * This can be used to translate a Unix path into a drive + DOS path.
133  * Return value is the drive, or -1 on error. On success, ppath is modified
134  * to point to the beginning of the DOS path.
135  */
136 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
137 {
138     /* Starting with the full path, check if the device and inode match any of
139      * the wine 'drives'. If not then remove the last path component and try
140      * again. If the last component was a '..' then skip a normal component
141      * since it's a directory that's ascended back out of.
142      */
143     int drive;
144     char *buffer;
145     const char *path = *ppath;
146     struct stat st;
147     struct drive_info info[MAX_DOS_DRIVES];
148
149     /* get device and inode of all drives */
150     if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
151
152     /* strip off trailing slashes */
153     while (len > 1 && path[len - 1] == '/') len--;
154
155     /* make a copy of the path */
156     if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) return STATUS_NO_MEMORY;
157     memcpy( buffer, path, len );
158     buffer[len] = 0;
159
160     for (;;)
161     {
162         if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
163         {
164             /* Find the drive */
165             for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
166             {
167                 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
168                 {
169                     if (len == 1) len = 0;  /* preserve root slash in returned path */
170                     TRACE( "%s -> drive %c:, root=%s, name=%s\n",
171                            debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
172                     *ppath += len;
173                     *drive_ret = drive;
174                     RtlFreeHeap( GetProcessHeap(), 0, buffer );
175                     return STATUS_SUCCESS;
176                 }
177             }
178         }
179         if (len <= 1) break;  /* reached root */
180         len = remove_last_componentA( buffer, len );
181         buffer[len] = 0;
182     }
183     RtlFreeHeap( GetProcessHeap(), 0, buffer );
184     return STATUS_OBJECT_PATH_NOT_FOUND;
185 }
186
187
188 /***********************************************************************
189  *           remove_last_componentW
190  *
191  * Remove the last component of the path. Helper for find_drive_rootW.
192  */
193 static inline int remove_last_componentW( const WCHAR *path, int len )
194 {
195     int level = 0;
196
197     while (level < 1)
198     {
199         /* find start of the last path component */
200         int prev = len;
201         if (prev <= 1) break;  /* reached root */
202         while (prev > 1 && !IS_SEPARATOR(path[prev - 1])) prev--;
203         /* does removing it take us up a level? */
204         if (len - prev != 1 || path[prev] != '.')  /* not '.' */
205         {
206             if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.')  /* is it '..'? */
207                 level--;
208             else
209                 level++;
210         }
211         /* strip off trailing slashes */
212         while (prev > 1 && IS_SEPARATOR(path[prev - 1])) prev--;
213         len = prev;
214     }
215     return len;
216 }
217
218
219 /***********************************************************************
220  *           find_drive_rootW
221  *
222  * Find a drive for which the root matches the beginning of the given path.
223  * This can be used to translate a Unix path into a drive + DOS path.
224  * Return value is the drive, or -1 on error. On success, ppath is modified
225  * to point to the beginning of the DOS path.
226  */
227 static int find_drive_rootW( LPCWSTR *ppath )
228 {
229     /* Starting with the full path, check if the device and inode match any of
230      * the wine 'drives'. If not then remove the last path component and try
231      * again. If the last component was a '..' then skip a normal component
232      * since it's a directory that's ascended back out of.
233      */
234     int drive, lenA, lenW;
235     char *buffer, *p;
236     const WCHAR *path = *ppath;
237     struct stat st;
238     struct drive_info info[MAX_DOS_DRIVES];
239
240     /* get device and inode of all drives */
241     if (!get_drives_info( info )) return -1;
242
243     /* strip off trailing slashes */
244     lenW = strlenW(path);
245     while (lenW > 1 && IS_SEPARATOR(path[lenW - 1])) lenW--;
246
247     /* convert path to Unix encoding */
248     lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
249     if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, lenA + 1 ))) return -1;
250     lenA = ntdll_wcstoumbs( 0, path, lenW, buffer, lenA, NULL, NULL );
251     buffer[lenA] = 0;
252     for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
253
254     for (;;)
255     {
256         if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
257         {
258             /* Find the drive */
259             for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
260             {
261                 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
262                 {
263                     if (lenW == 1) lenW = 0;  /* preserve root slash in returned path */
264                     TRACE( "%s -> drive %c:, root=%s, name=%s\n",
265                            debugstr_w(path), 'A' + drive, debugstr_a(buffer), debugstr_w(path + lenW));
266                     *ppath += lenW;
267                     RtlFreeHeap( GetProcessHeap(), 0, buffer );
268                     return drive;
269                 }
270             }
271         }
272         if (lenW <= 1) break;  /* reached root */
273         lenW = remove_last_componentW( path, lenW );
274
275         /* we only need the new length, buffer already contains the converted string */
276         lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
277         buffer[lenA] = 0;
278     }
279     RtlFreeHeap( GetProcessHeap(), 0, buffer );
280     return -1;
281 }
282
283
284 /***********************************************************************
285  *             RtlDetermineDosPathNameType_U   (NTDLL.@)
286  */
287 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
288 {
289     if (IS_SEPARATOR(path[0]))
290     {
291         if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH;       /* "/foo" */
292         if (path[2] != '.') return UNC_PATH;                    /* "//foo" */
293         if (IS_SEPARATOR(path[3])) return DEVICE_PATH;          /* "//./foo" */
294         if (path[3]) return UNC_PATH;                           /* "//.foo" */
295         return UNC_DOT_PATH;                                    /* "//." */
296     }
297     else
298     {
299         if (!path[0] || path[1] != ':') return RELATIVE_PATH;   /* "foo" */
300         if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH;  /* "c:/foo" */
301         return RELATIVE_DRIVE_PATH;                             /* "c:foo" */
302     }
303 }
304
305 /***********************************************************************
306  *             RtlIsDosDeviceName_U   (NTDLL.@)
307  *
308  * Check if the given DOS path contains a DOS device name.
309  *
310  * Returns the length of the device name in the low word and its
311  * position in the high word (both in bytes, not WCHARs), or 0 if no
312  * device name is found.
313  */
314 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
315 {
316     static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
317     static const WCHAR auxW[3] = {'A','U','X'};
318     static const WCHAR comW[3] = {'C','O','M'};
319     static const WCHAR conW[3] = {'C','O','N'};
320     static const WCHAR lptW[3] = {'L','P','T'};
321     static const WCHAR nulW[3] = {'N','U','L'};
322     static const WCHAR prnW[3] = {'P','R','N'};
323
324     const WCHAR *start, *end, *p;
325
326     switch(RtlDetermineDosPathNameType_U( dos_name ))
327     {
328     case INVALID_PATH:
329     case UNC_PATH:
330         return 0;
331     case DEVICE_PATH:
332         if (!strcmpiW( dos_name, consoleW ))
333             return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) );  /* 4 is length of \\.\ prefix */
334         return 0;
335     default:
336         break;
337     }
338
339     end = dos_name + strlenW(dos_name) - 1;
340     if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' */
341
342     /* find start of file name */
343     for (start = end; start >= dos_name; start--)
344     {
345         if (IS_SEPARATOR(start[0])) break;
346         /* check for ':' but ignore if before extension (for things like NUL:.txt) */
347         if (start[0] == ':' && start[1] != '.') break;
348     }
349     start++;
350
351     /* remove extension */
352     if ((p = strchrW( start, '.' )))
353     {
354         end = p - 1;
355         if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' before extension */
356     }
357     else
358     {
359         /* no extension, remove trailing spaces */
360         while (end >= dos_name && *end == ' ') end--;
361     }
362
363     /* now we have a potential device name between start and end, check it */
364     switch(end - start + 1)
365     {
366     case 3:
367         if (strncmpiW( start, auxW, 3 ) &&
368             strncmpiW( start, conW, 3 ) &&
369             strncmpiW( start, nulW, 3 ) &&
370             strncmpiW( start, prnW, 3 )) break;
371         return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
372     case 4:
373         if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
374         if (*end <= '0' || *end > '9') break;
375         return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
376     default:  /* can't match anything */
377         break;
378     }
379     return 0;
380 }
381
382
383 /**************************************************************************
384  *                 RtlDosPathNameToNtPathName_U         [NTDLL.@]
385  *
386  * dos_path: a DOS path name (fully qualified or not)
387  * ntpath:   pointer to a UNICODE_STRING to hold the converted
388  *           path name
389  * file_part:will point (in ntpath) to the file part in the path
390  * cd:       directory reference (optional)
391  *
392  * FIXME:
393  *      + fill the cd structure
394  */
395 BOOLEAN  WINAPI RtlDosPathNameToNtPathName_U(PCWSTR dos_path,
396                                              PUNICODE_STRING ntpath,
397                                              PWSTR* file_part,
398                                              CURDIR* cd)
399 {
400     static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
401     ULONG sz, offset;
402     WCHAR local[MAX_PATH];
403     LPWSTR ptr;
404
405     TRACE("(%s,%p,%p,%p)\n",
406           debugstr_w(dos_path), ntpath, file_part, cd);
407
408     if (cd)
409     {
410         FIXME("Unsupported parameter\n");
411         memset(cd, 0, sizeof(*cd));
412     }
413
414     if (!dos_path || !*dos_path) return FALSE;
415
416     if (!strncmpW(dos_path, LongFileNamePfxW, 4))
417     {
418         ntpath->Length = strlenW(dos_path) * sizeof(WCHAR);
419         ntpath->MaximumLength = ntpath->Length + sizeof(WCHAR);
420         ntpath->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, ntpath->MaximumLength);
421         if (!ntpath->Buffer) return FALSE;
422         memcpy( ntpath->Buffer, dos_path, ntpath->MaximumLength );
423         ntpath->Buffer[1] = '?';  /* change \\?\ to \??\ */
424         if (file_part)
425         {
426             if ((ptr = strrchrW( ntpath->Buffer, '\\' )) && ptr[1]) *file_part = ptr + 1;
427             else *file_part = NULL;
428         }
429         return TRUE;
430     }
431
432     ptr = local;
433     sz = RtlGetFullPathName_U(dos_path, sizeof(local), ptr, file_part);
434     if (sz == 0) return FALSE;
435     if (sz > sizeof(local))
436     {
437         if (!(ptr = RtlAllocateHeap(GetProcessHeap(), 0, sz))) return FALSE;
438         sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
439     }
440
441     ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
442     ntpath->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, ntpath->MaximumLength);
443     if (!ntpath->Buffer)
444     {
445         if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
446         return FALSE;
447     }
448
449     strcpyW(ntpath->Buffer, NTDosPrefixW);
450     switch (RtlDetermineDosPathNameType_U(ptr))
451     {
452     case UNC_PATH: /* \\foo */
453         offset = 2;
454         strcatW(ntpath->Buffer, UncPfxW);
455         break;
456     case DEVICE_PATH: /* \\.\foo */
457         offset = 4;
458         break;
459     default:
460         offset = 0;
461         break;
462     }
463
464     strcatW(ntpath->Buffer, ptr + offset);
465     ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
466
467     if (file_part && *file_part)
468         *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - strlenW(*file_part);
469
470     /* FIXME: cd filling */
471
472     if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
473     return TRUE;
474 }
475
476 /******************************************************************
477  *              RtlDosSearchPath_U
478  *
479  * Searchs a file of name 'name' into a ';' separated list of paths
480  * (stored in paths)
481  * Doesn't seem to search elsewhere than the paths list
482  * Stores the result in buffer (file_part will point to the position
483  * of the file name in the buffer)
484  * FIXME:
485  * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
486  */
487 ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext, 
488                                 ULONG buffer_size, LPWSTR buffer, 
489                                 LPWSTR* file_part)
490 {
491     DOS_PATHNAME_TYPE type = RtlDetermineDosPathNameType_U(search);
492     ULONG len = 0;
493
494     if (type == RELATIVE_PATH)
495     {
496         ULONG allocated = 0, needed, filelen;
497         WCHAR *name = NULL;
498
499         filelen = 1 /* for \ */ + strlenW(search) + 1 /* \0 */;
500
501         /* Windows only checks for '.' without worrying about path components */
502         if (strchrW( search, '.' )) ext = NULL;
503         if (ext != NULL) filelen += strlenW(ext);
504
505         while (*paths)
506         {
507             LPCWSTR ptr;
508
509             for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
510             if (needed + filelen > allocated)
511             {
512                 if (!name) name = RtlAllocateHeap(GetProcessHeap(), 0,
513                                                   (needed + filelen) * sizeof(WCHAR));
514                 else
515                 {
516                     WCHAR *newname = RtlReAllocateHeap(GetProcessHeap(), 0, name,
517                                                        (needed + filelen) * sizeof(WCHAR));
518                     if (!newname) RtlFreeHeap(GetProcessHeap(), 0, name);
519                     name = newname;
520                 }
521                 if (!name) return 0;
522                 allocated = needed + filelen;
523             }
524             memmove(name, paths, needed * sizeof(WCHAR));
525             /* append '\\' if none is present */
526             if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
527             strcpyW(&name[needed], search);
528             if (ext) strcatW(&name[needed], ext);
529             if (RtlDoesFileExists_U(name))
530             {
531                 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
532                 break;
533             }
534             paths = ptr;
535         }
536         RtlFreeHeap(GetProcessHeap(), 0, name);
537     }
538     else if (RtlDoesFileExists_U(search))
539     {
540         len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
541     }
542
543     return len;
544 }
545
546
547 /******************************************************************
548  *              collapse_path
549  *
550  * Helper for RtlGetFullPathName_U.
551  * Get rid of . and .. components in the path.
552  */
553 static inline void collapse_path( WCHAR *path, UINT mark )
554 {
555     WCHAR *p, *next;
556
557     /* convert every / into a \ */
558     for (p = path; *p; p++) if (*p == '/') *p = '\\';
559
560     /* collapse duplicate backslashes */
561     next = path + max( 1, mark );
562     for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
563     *next = 0;
564
565     p = path + mark;
566     while (*p)
567     {
568         if (*p == '.')
569         {
570             switch(p[1])
571             {
572             case '\\': /* .\ component */
573                 next = p + 2;
574                 memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
575                 continue;
576             case 0:  /* final . */
577                 if (p > path + mark) p--;
578                 *p = 0;
579                 continue;
580             case '.':
581                 if (p[2] == '\\')  /* ..\ component */
582                 {
583                     next = p + 3;
584                     if (p > path + mark)
585                     {
586                         p--;
587                         while (p > path + mark && p[-1] != '\\') p--;
588                     }
589                     memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
590                     continue;
591                 }
592                 else if (!p[2])  /* final .. */
593                 {
594                     if (p > path + mark)
595                     {
596                         p--;
597                         while (p > path + mark && p[-1] != '\\') p--;
598                         if (p > path + mark) p--;
599                     }
600                     *p = 0;
601                     continue;
602                 }
603                 break;
604             }
605         }
606         /* skip to the next component */
607         while (*p && *p != '\\') p++;
608         if (*p == '\\')
609         {
610             /* remove last dot in previous dir name */
611             if (p > path + mark && p[-1] == '.') memmove( p-1, p, (strlenW(p) + 1) * sizeof(WCHAR) );
612             else p++;
613         }
614     }
615
616     /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
617     while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
618     *p = 0;
619 }
620
621
622 /******************************************************************
623  *              skip_unc_prefix
624  *
625  * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
626  */
627 static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
628 {
629     ptr += 2;
630     while (*ptr && !IS_SEPARATOR(*ptr)) ptr++;  /* share name */
631     while (IS_SEPARATOR(*ptr)) ptr++;
632     while (*ptr && !IS_SEPARATOR(*ptr)) ptr++;  /* dir name */
633     while (IS_SEPARATOR(*ptr)) ptr++;
634     return ptr;
635 }
636
637
638 /******************************************************************
639  *              get_full_path_helper
640  *
641  * Helper for RtlGetFullPathName_U
642  * Note: name and buffer are allowed to point to the same memory spot
643  */
644 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
645 {
646     ULONG                       reqsize = 0, mark = 0, dep = 0, deplen;
647     DOS_PATHNAME_TYPE           type;
648     LPWSTR                      ins_str = NULL;
649     LPCWSTR                     ptr;
650     const UNICODE_STRING*       cd;
651     WCHAR                       tmp[4];
652
653     /* return error if name only consists of spaces */
654     for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
655     if (!*ptr) return 0;
656
657     RtlAcquirePebLock();
658
659     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
660         cd = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath;
661     else
662         cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath;
663
664     switch (type = RtlDetermineDosPathNameType_U(name))
665     {
666     case UNC_PATH:              /* \\foo   */
667         ptr = skip_unc_prefix( name );
668         mark = (ptr - name);
669         break;
670
671     case DEVICE_PATH:           /* \\.\foo */
672         mark = 4;
673         break;
674
675     case ABSOLUTE_DRIVE_PATH:   /* c:\foo  */
676         reqsize = sizeof(WCHAR);
677         tmp[0] = toupperW(name[0]);
678         ins_str = tmp;
679         dep = 1;
680         mark = 3;
681         break;
682
683     case RELATIVE_DRIVE_PATH:   /* c:foo   */
684         dep = 2;
685         if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
686         {
687             UNICODE_STRING      var, val;
688
689             tmp[0] = '=';
690             tmp[1] = name[0];
691             tmp[2] = ':';
692             tmp[3] = '\0';
693             var.Length = 3 * sizeof(WCHAR);
694             var.MaximumLength = 4 * sizeof(WCHAR);
695             var.Buffer = tmp;
696             val.Length = 0;
697             val.MaximumLength = size;
698             val.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, size);
699
700             switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
701             {
702             case STATUS_SUCCESS:
703                 /* FIXME: Win2k seems to check that the environment variable actually points 
704                  * to an existing directory. If not, root of the drive is used
705                  * (this seems also to be the only spot in RtlGetFullPathName that the 
706                  * existence of a part of a path is checked)
707                  */
708                 /* fall thru */
709             case STATUS_BUFFER_TOO_SMALL:
710                 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
711                 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
712                 ins_str = val.Buffer;
713                 break;
714             case STATUS_VARIABLE_NOT_FOUND:
715                 reqsize = 3 * sizeof(WCHAR);
716                 tmp[0] = name[0];
717                 tmp[1] = ':';
718                 tmp[2] = '\\';
719                 ins_str = tmp;
720                 break;
721             default:
722                 ERR("Unsupported status code\n");
723                 break;
724             }
725             mark = 3;
726             break;
727         }
728         /* fall through */
729
730     case RELATIVE_PATH:         /* foo     */
731         reqsize = cd->Length;
732         ins_str = cd->Buffer;
733         if (cd->Buffer[1] != ':')
734         {
735             ptr = skip_unc_prefix( cd->Buffer );
736             mark = ptr - cd->Buffer;
737         }
738         else mark = 3;
739         break;
740
741     case ABSOLUTE_PATH:         /* \xxx    */
742         if (name[0] == '/')  /* may be a Unix path */
743         {
744             const WCHAR *ptr = name;
745             int drive = find_drive_rootW( &ptr );
746             if (drive != -1)
747             {
748                 reqsize = 3 * sizeof(WCHAR);
749                 tmp[0] = 'A' + drive;
750                 tmp[1] = ':';
751                 tmp[2] = '\\';
752                 ins_str = tmp;
753                 mark = 3;
754                 dep = ptr - name;
755                 break;
756             }
757         }
758         if (cd->Buffer[1] == ':')
759         {
760             reqsize = 2 * sizeof(WCHAR);
761             tmp[0] = cd->Buffer[0];
762             tmp[1] = ':';
763             ins_str = tmp;
764             mark = 3;
765         }
766         else
767         {
768             ptr = skip_unc_prefix( cd->Buffer );
769             reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
770             mark = reqsize / sizeof(WCHAR);
771             ins_str = cd->Buffer;
772         }
773         break;
774
775     case UNC_DOT_PATH:         /* \\.     */
776         reqsize = 4 * sizeof(WCHAR);
777         dep = 3;
778         tmp[0] = '\\';
779         tmp[1] = '\\';
780         tmp[2] = '.';
781         tmp[3] = '\\';
782         ins_str = tmp;
783         mark = 4;
784         break;
785
786     case INVALID_PATH:
787         goto done;
788     }
789
790     /* enough space ? */
791     deplen = strlenW(name + dep) * sizeof(WCHAR);
792     if (reqsize + deplen + sizeof(WCHAR) > size)
793     {
794         /* not enough space, return need size (including terminating '\0') */
795         reqsize += deplen + sizeof(WCHAR);
796         goto done;
797     }
798
799     memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
800     if (reqsize) memcpy(buffer, ins_str, reqsize);
801     reqsize += deplen;
802
803     if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
804         RtlFreeHeap(GetProcessHeap(), 0, ins_str);
805
806     collapse_path( buffer, mark );
807     reqsize = strlenW(buffer) * sizeof(WCHAR);
808
809 done:
810     RtlReleasePebLock();
811     return reqsize;
812 }
813
814 /******************************************************************
815  *              RtlGetFullPathName_U  (NTDLL.@)
816  *
817  * Returns the number of bytes written to buffer (not including the
818  * terminating NULL) if the function succeeds, or the required number of bytes
819  * (including the terminating NULL) if the buffer is too small.
820  *
821  * file_part will point to the filename part inside buffer (except if we use
822  * DOS device name, in which case file_in_buf is NULL)
823  *
824  */
825 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
826                                   WCHAR** file_part)
827 {
828     WCHAR*      ptr;
829     DWORD       dosdev;
830     DWORD       reqsize;
831
832     TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
833
834     if (!name || !*name) return 0;
835
836     if (file_part) *file_part = NULL;
837
838     /* check for DOS device name */
839     dosdev = RtlIsDosDeviceName_U(name);
840     if (dosdev)
841     {
842         DWORD   offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
843         DWORD   sz = LOWORD(dosdev); /* in bytes */
844
845         if (8 + sz + 2 > size) return sz + 10;
846         strcpyW(buffer, DeviceRootW);
847         memmove(buffer + 4, name + offset, sz);
848         buffer[4 + sz / sizeof(WCHAR)] = '\0';
849         /* file_part isn't set in this case */
850         return sz + 8;
851     }
852
853     reqsize = get_full_path_helper(name, buffer, size);
854     if (!reqsize) return 0;
855     if (reqsize > size)
856     {
857         LPWSTR tmp = RtlAllocateHeap(GetProcessHeap(), 0, reqsize);
858         reqsize = get_full_path_helper(name, tmp, reqsize);
859         if (reqsize > size)  /* it may have worked the second time */
860         {
861             RtlFreeHeap(GetProcessHeap(), 0, tmp);
862             return reqsize + sizeof(WCHAR);
863         }
864         memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
865         RtlFreeHeap(GetProcessHeap(), 0, tmp);
866     }
867
868     /* find file part */
869     if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
870         *file_part = ptr;
871     return reqsize;
872 }
873
874 /*************************************************************************
875  * RtlGetLongestNtPathLength    [NTDLL.@]
876  *
877  * Get the longest allowed path length
878  *
879  * PARAMS
880  *  None.
881  *
882  * RETURNS
883  *  The longest allowed path length (277 characters under Win2k).
884  */
885 DWORD WINAPI RtlGetLongestNtPathLength(void)
886 {
887     return MAX_NT_PATH_LENGTH;
888 }
889
890 /******************************************************************
891  *             RtlIsNameLegalDOS8Dot3   (NTDLL.@)
892  *
893  * Returns TRUE iff unicode is a valid DOS (8+3) name.
894  * If the name is valid, oem gets filled with the corresponding OEM string
895  * spaces is set to TRUE if unicode contains spaces
896  */
897 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
898                                        OEM_STRING *oem, BOOLEAN *spaces )
899 {
900     static const char* illegal = "*?<>|\"+=,;[]:/\\\345";
901     int dot = -1;
902     int i;
903     char buffer[12];
904     OEM_STRING oem_str;
905     BOOLEAN got_space = FALSE;
906
907     if (!oem)
908     {
909         oem_str.Length = sizeof(buffer);
910         oem_str.MaximumLength = sizeof(buffer);
911         oem_str.Buffer = buffer;
912         oem = &oem_str;
913     }
914     if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
915         return FALSE;
916
917     if (oem->Length > 12) return FALSE;
918
919     /* a starting . is invalid, except for . and .. */
920     if (oem->Buffer[0] == '.')
921     {
922         if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
923         if (spaces) *spaces = FALSE;
924         return TRUE;
925     }
926
927     for (i = 0; i < oem->Length; i++)
928     {
929         switch (oem->Buffer[i])
930         {
931         case ' ':
932             /* leading/trailing spaces not allowed */
933             if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
934             got_space = TRUE;
935             break;
936         case '.':
937             if (dot != -1) return FALSE;
938             dot = i;
939             break;
940         default:
941             if (strchr(illegal, oem->Buffer[i])) return FALSE;
942             break;
943         }
944     }
945     /* check file part is shorter than 8, extension shorter than 3
946      * dot cannot be last in string
947      */
948     if (dot == -1)
949     {
950         if (oem->Length > 8) return FALSE;
951     }
952     else
953     {
954         if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
955     }
956     if (spaces) *spaces = got_space;
957     return TRUE;
958 }
959
960 /******************************************************************
961  *              RtlGetCurrentDirectory_U (NTDLL.@)
962  *
963  */
964 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
965 {
966     UNICODE_STRING*     us;
967     ULONG               len;
968
969     TRACE("(%lu %p)\n", buflen, buf);
970
971     RtlAcquirePebLock();
972
973     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
974         us = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath;
975     else
976         us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath;
977
978     len = us->Length / sizeof(WCHAR);
979     if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
980         len--;
981
982     if (buflen / sizeof(WCHAR) > len)
983     {
984         memcpy(buf, us->Buffer, len * sizeof(WCHAR));
985         buf[len] = '\0';
986     }
987     else
988     {
989         len++;
990     }
991
992     RtlReleasePebLock();
993
994     return len * sizeof(WCHAR);
995 }
996
997 /******************************************************************
998  *              RtlSetCurrentDirectory_U (NTDLL.@)
999  *
1000  */
1001 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
1002 {
1003     FILE_FS_DEVICE_INFORMATION device_info;
1004     OBJECT_ATTRIBUTES attr;
1005     UNICODE_STRING newdir;
1006     IO_STATUS_BLOCK io;
1007     CURDIR *curdir;
1008     HANDLE handle;
1009     NTSTATUS nts;
1010     ULONG size;
1011     PWSTR ptr;
1012
1013     newdir.Buffer = NULL;
1014
1015     RtlAcquirePebLock();
1016
1017     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
1018         curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
1019     else
1020         curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
1021
1022     if (!RtlDosPathNameToNtPathName_U( dir->Buffer, &newdir, NULL, NULL ))
1023     {
1024         nts = STATUS_OBJECT_NAME_INVALID;
1025         goto out;
1026     }
1027
1028     attr.Length = sizeof(attr);
1029     attr.RootDirectory = 0;
1030     attr.Attributes = OBJ_CASE_INSENSITIVE;
1031     attr.ObjectName = &newdir;
1032     attr.SecurityDescriptor = NULL;
1033     attr.SecurityQualityOfService = NULL;
1034
1035     nts = NtOpenFile( &handle, 0, &attr, &io, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1036     if (nts != STATUS_SUCCESS) goto out;
1037
1038     /* don't keep the directory handle open on removable media */
1039     if (!NtQueryVolumeInformationFile( handle, &io, &device_info,
1040                                        sizeof(device_info), FileFsDeviceInformation ) &&
1041         (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
1042     {
1043         NtClose( handle );
1044         handle = 0;
1045     }
1046
1047     if (curdir->Handle) NtClose( curdir->Handle );
1048     curdir->Handle = handle;
1049
1050     /* append trailing \ if missing */
1051     size = newdir.Length / sizeof(WCHAR);
1052     ptr = newdir.Buffer;
1053     ptr += 4;  /* skip \??\ prefix */
1054     size -= 4;
1055     if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
1056
1057     memcpy( curdir->DosPath.Buffer, ptr, size * sizeof(WCHAR));
1058     curdir->DosPath.Buffer[size] = 0;
1059     curdir->DosPath.Length = size * sizeof(WCHAR);
1060
1061     TRACE( "curdir now %s %p\n", debugstr_w(curdir->DosPath.Buffer), curdir->Handle );
1062
1063  out:
1064     RtlFreeUnicodeString( &newdir );
1065     RtlReleasePebLock();
1066     return nts;
1067 }
1068
1069
1070 /******************************************************************
1071  *           wine_unix_to_nt_file_name  (NTDLL.@) Not a Windows API
1072  */
1073 NTSTATUS wine_unix_to_nt_file_name( const ANSI_STRING *name, UNICODE_STRING *nt )
1074 {
1075     static const WCHAR prefixW[] = {'\\','?','?','\\','a',':','\\'};
1076     unsigned int lenW, lenA = name->Length;
1077     const char *path = name->Buffer;
1078     char *cwd;
1079     WCHAR *p;
1080     NTSTATUS status;
1081     int drive;
1082
1083     if (!lenA || path[0] != '/')
1084     {
1085         char *newcwd, *end;
1086         size_t size;
1087
1088         if ((status = DIR_get_unix_cwd( &cwd )) != STATUS_SUCCESS) return status;
1089
1090         size = strlen(cwd) + lenA + 1;
1091         if (!(newcwd = RtlReAllocateHeap( GetProcessHeap(), 0, cwd, size )))
1092         {
1093             status = STATUS_NO_MEMORY;
1094             goto done;
1095         }
1096         cwd = newcwd;
1097         end = cwd + strlen(cwd);
1098         if (end > cwd && end[-1] != '/') *end++ = '/';
1099         memcpy( end, path, lenA );
1100         lenA += end - cwd;
1101         path = cwd;
1102
1103         status = find_drive_rootA( &path, lenA, &drive );
1104         lenA -= (path - cwd);
1105     }
1106     else
1107     {
1108         cwd = NULL;
1109         status = find_drive_rootA( &path, lenA, &drive );
1110         lenA -= (path - name->Buffer);
1111     }
1112
1113     if (status != STATUS_SUCCESS) goto done;
1114     while (lenA && path[0] == '/') { lenA--; path++; }
1115
1116     lenW = ntdll_umbstowcs( 0, path, lenA, NULL, 0 );
1117     if (!(nt->Buffer = RtlAllocateHeap( GetProcessHeap(), 0,
1118                                         (lenW + 1) * sizeof(WCHAR) + sizeof(prefixW) )))
1119     {
1120         status = STATUS_NO_MEMORY;
1121         goto done;
1122     }
1123
1124     memcpy( nt->Buffer, prefixW, sizeof(prefixW) );
1125     nt->Buffer[4] += drive;
1126     ntdll_umbstowcs( 0, path, lenA, nt->Buffer + sizeof(prefixW)/sizeof(WCHAR), lenW );
1127     lenW += sizeof(prefixW)/sizeof(WCHAR);
1128     nt->Buffer[lenW] = 0;
1129     nt->Length = lenW * sizeof(WCHAR);
1130     nt->MaximumLength = nt->Length + sizeof(WCHAR);
1131     for (p = nt->Buffer + sizeof(prefixW)/sizeof(WCHAR); *p; p++) if (*p == '/') *p = '\\';
1132
1133 done:
1134     RtlFreeHeap( GetProcessHeap(), 0, cwd );
1135     return status;
1136 }