- Handle "? :" conditionals.
[wine] / files / dos_fs.c
1 /*
2  * DOS file system functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
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 <sys/types.h>
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #ifdef HAVE_LINUX_IOCTL_H
41 #include <linux/ioctl.h>
42 #endif
43 #include <time.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
50 #include "ntstatus.h"
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winerror.h"
54 #include "wingdi.h"
55
56 #include "wine/unicode.h"
57 #include "wine/winbase16.h"
58 #include "file.h"
59 #include "winreg.h"
60 #include "winternl.h"
61 #include "wine/server.h"
62 #include "wine/exception.h"
63 #include "excpt.h"
64
65 #include "smb.h"
66
67 #include "wine/debug.h"
68
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
70 WINE_DECLARE_DEBUG_CHANNEL(file);
71
72 /* Define the VFAT ioctl to get both short and long file names */
73 /* FIXME: is it possible to get this to work on other systems? */
74 #ifdef linux
75 /* We want the real kernel dirent structure, not the libc one */
76 typedef struct
77 {
78     long d_ino;
79     long d_off;
80     unsigned short d_reclen;
81     char d_name[256];
82 } KERNEL_DIRENT;
83
84 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )
85
86 /* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
87 #ifndef O_DIRECTORY
88 # define O_DIRECTORY    0200000 /* must be directory */
89 #endif
90
91 #else   /* linux */
92 #undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
93 #endif  /* linux */
94
95 /* Chars we don't want to see in DOS file names */
96 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
97
98 /* at some point we may want to allow Winelib apps to set this */
99 static const BOOL is_case_sensitive = FALSE;
100
101 /*
102  * Directory info for DOSFS_ReadDir
103  * contains the names of *all* the files in the directory
104  */
105 typedef struct
106 {
107     int used;
108     int size;
109     WCHAR names[1];
110 } DOS_DIR;
111
112
113 /* return non-zero if c is the end of a directory name */
114 static inline int is_end_of_name(WCHAR c)
115 {
116     return !c || (c == '/') || (c == '\\');
117 }
118
119 /***********************************************************************
120  *           DOSFS_ValidDOSName
121  *
122  * Return 1 if Unix file 'name' is also a valid MS-DOS name
123  * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
124  * File name can be terminated by '\0', '\\' or '/'.
125  */
126 static int DOSFS_ValidDOSName( LPCWSTR name )
127 {
128     static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
129     const WCHAR *p = name;
130     const char *invalid = !is_case_sensitive ? (invalid_chars + 26) : invalid_chars;
131     int len = 0;
132
133     if (*p == '.')
134     {
135         /* Check for "." and ".." */
136         p++;
137         if (*p == '.') p++;
138         /* All other names beginning with '.' are invalid */
139         return (is_end_of_name(*p));
140     }
141     while (!is_end_of_name(*p))
142     {
143         if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
144         if (*p == '.') break;  /* Start of the extension */
145         if (++len > 8) return 0;  /* Name too long */
146         p++;
147     }
148     if (*p != '.') return 1;  /* End of name */
149     p++;
150     if (is_end_of_name(*p)) return 0;  /* Empty extension not allowed */
151     len = 0;
152     while (!is_end_of_name(*p))
153     {
154         if (*p < 256 && strchr( invalid, (char)*p )) return 0;  /* Invalid char */
155         if (*p == '.') return 0;  /* Second extension not allowed */
156         if (++len > 3) return 0;  /* Extension too long */
157         p++;
158     }
159     return 1;
160 }
161
162
163 /***********************************************************************
164  *           DOSFS_ToDosFCBFormat
165  *
166  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
167  * expanding wild cards and converting to upper-case in the process.
168  * File name can be terminated by '\0', '\\' or '/'.
169  * Return FALSE if the name is not a valid DOS name.
170  * 'buffer' must be at least 12 characters long.
171  */
172 static BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
173 {
174     static const char invalid_chars[] = INVALID_DOS_CHARS;
175     LPCWSTR p = name;
176     int i;
177
178     /* Check for "." and ".." */
179     if (*p == '.')
180     {
181         p++;
182         buffer[0] = '.';
183         for(i = 1; i < 11; i++) buffer[i] = ' ';
184         buffer[11] = 0;
185         if (*p == '.')
186         {
187             buffer[1] = '.';
188             p++;
189         }
190         return (!*p || (*p == '/') || (*p == '\\'));
191     }
192
193     for (i = 0; i < 8; i++)
194     {
195         switch(*p)
196         {
197         case '\0':
198         case '\\':
199         case '/':
200         case '.':
201             buffer[i] = ' ';
202             break;
203         case '?':
204             p++;
205             /* fall through */
206         case '*':
207             buffer[i] = '?';
208             break;
209         default:
210             if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
211             buffer[i] = toupperW(*p);
212             p++;
213             break;
214         }
215     }
216
217     if (*p == '*')
218     {
219         /* Skip all chars after wildcard up to first dot */
220         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
221     }
222     else
223     {
224         /* Check if name too long */
225         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
226     }
227     if (*p == '.') p++;  /* Skip dot */
228
229     for (i = 8; i < 11; i++)
230     {
231         switch(*p)
232         {
233         case '\0':
234         case '\\':
235         case '/':
236             buffer[i] = ' ';
237             break;
238         case '.':
239             return FALSE;  /* Second extension not allowed */
240         case '?':
241             p++;
242             /* fall through */
243         case '*':
244             buffer[i] = '?';
245             break;
246         default:
247             if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
248             buffer[i] = toupperW(*p);
249             p++;
250             break;
251         }
252     }
253     buffer[11] = '\0';
254
255     /* at most 3 character of the extension are processed
256      * is something behind this ?
257      */
258     while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
259     return is_end_of_name(*p);
260 }
261
262
263 /***********************************************************************
264  *           DOSFS_ToDosDTAFormat
265  *
266  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
267  * converting to upper-case in the process.
268  * File name can be terminated by '\0', '\\' or '/'.
269  * 'buffer' must be at least 13 characters long.
270  */
271 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
272 {
273     LPWSTR p;
274
275     memcpy( buffer, name, 8 * sizeof(WCHAR) );
276     p = buffer + 8;
277     while ((p > buffer) && (p[-1] == ' ')) p--;
278     *p++ = '.';
279     memcpy( p, name + 8, 3 * sizeof(WCHAR) );
280     p += 3;
281     while (p[-1] == ' ') p--;
282     if (p[-1] == '.') p--;
283     *p = '\0';
284 }
285
286
287 /***********************************************************************
288  *           DOSFS_AddDirEntry
289  *
290  *  Used to construct an array of filenames in DOSFS_OpenDir
291  */
292 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
293 {
294     int extra1 = strlenW(name) + 1;
295     int extra2 = strlenW(dosname) + 1;
296
297     /* if we need more, at minimum double the size */
298     if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
299     {
300         int more = (*dir)->size;
301         DOS_DIR *t;
302
303         if(more<(extra1+extra2))
304             more = extra1+extra2;
305
306         t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) + 
307                         ((*dir)->size + more)*sizeof(WCHAR) );
308         if(!t)
309         {
310             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
311             ERR("Out of memory caching directory structure %d %d %d\n",
312                  (*dir)->size, more, (*dir)->used);
313             return FALSE;
314         }
315         (*dir) = t;
316         (*dir)->size += more;
317     }
318
319     /* at this point, the dir structure is big enough to hold these names */
320     strcpyW(&(*dir)->names[(*dir)->used], name);
321     (*dir)->used += extra1;
322     strcpyW(&(*dir)->names[(*dir)->used], dosname);
323     (*dir)->used += extra2;
324
325     return TRUE;
326 }
327
328
329 /***********************************************************************
330  *           DOSFS_OpenDir_VFAT
331  */
332 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
333 {
334 #ifdef VFAT_IOCTL_READDIR_BOTH
335     KERNEL_DIRENT de[2];
336     int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
337     BOOL r = TRUE;
338
339     /* Check if the VFAT ioctl is supported on this directory */
340
341     if ( fd<0 )
342         return FALSE;
343
344     while (1)
345     {
346         WCHAR long_name[MAX_PATH];
347         WCHAR short_name[12];
348
349         r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
350         if(!r)
351             break;
352         if (!de[0].d_reclen)
353             break;
354         MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
355         if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
356             short_name[0] = '\0';
357         if (de[1].d_name[0])
358             MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
359         else
360             MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
361         r = DOSFS_AddDirEntry(dir, long_name, short_name );
362         if(!r)
363             break;
364     }
365     if(r)
366     {
367         static const WCHAR empty_strW[] = { 0 };
368         DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
369     }
370     close(fd);
371     return r;
372 #else
373     return FALSE;
374 #endif  /* VFAT_IOCTL_READDIR_BOTH */
375 }
376
377
378 /***********************************************************************
379  *           DOSFS_OpenDir_Normal
380  *
381  * Now use the standard opendir/readdir interface
382  */
383 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
384 {
385     DIR *unixdir = opendir( unix_path );
386     BOOL r = TRUE;
387     static const WCHAR empty_strW[] = { 0 };
388
389     if(!unixdir)
390         return FALSE;
391     while(1)
392     {
393         WCHAR long_name[MAX_PATH];
394         struct dirent *de = readdir(unixdir);
395
396         if(!de)
397             break;
398         MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
399         r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
400         if(!r)
401             break;
402     }
403     if(r)
404         DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
405     closedir(unixdir);
406     return r;
407 }
408
409 /***********************************************************************
410  *           DOSFS_OpenDir
411  */
412 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
413 {
414     const int init_size = 0x100;
415     DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
416     BOOL r;
417
418     TRACE("%s\n",debugstr_a(unix_path));
419
420     if (!dir)
421     {
422         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
423         return NULL;
424     }
425     dir->used = 0;
426     dir->size = init_size;
427
428     /* Treat empty path as root directory. This simplifies path split into
429        directory and mask in several other places */
430     if (!*unix_path) unix_path = "/";
431
432     r = DOSFS_OpenDir_VFAT( &dir, unix_path);
433
434     if(!r)
435         r = DOSFS_OpenDir_Normal( &dir, unix_path);
436
437     if(!r)
438     {
439         HeapFree(GetProcessHeap(), 0, dir);
440         return NULL;
441     }
442     dir->used = 0;
443
444     return dir;
445 }
446
447
448 /***********************************************************************
449  *           DOSFS_CloseDir
450  */
451 static void DOSFS_CloseDir( DOS_DIR *dir )
452 {
453     HeapFree( GetProcessHeap(), 0, dir );
454 }
455
456
457 /***********************************************************************
458  *           DOSFS_ReadDir
459  */
460 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
461                              LPCWSTR *short_name )
462 {
463     LPCWSTR sn, ln;
464
465     if (!dir)
466        return FALSE;
467
468     /* the long pathname is first */
469     ln = &dir->names[dir->used];
470     if(ln[0])
471         *long_name  = ln;
472     else
473         return FALSE;
474     dir->used += (strlenW(ln) + 1);
475
476     /* followed by the short path name */
477     sn = &dir->names[dir->used];
478     if(sn[0])
479         *short_name = sn;
480     else
481         *short_name = NULL;
482     dir->used += (strlenW(sn) + 1);
483
484     return TRUE;
485 }
486
487
488 /***********************************************************************
489  *           DOSFS_Hash
490  *
491  * Transform a Unix file name into a hashed DOS name. If the name is a valid
492  * DOS name, it is converted to upper-case; otherwise it is replaced by a
493  * hashed version that fits in 8.3 format.
494  * File name can be terminated by '\0', '\\' or '/'.
495  * 'buffer' must be at least 13 characters long.
496  */
497 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format )
498 {
499     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
500     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
501
502     LPCWSTR p, ext;
503     LPWSTR dst;
504     unsigned short hash;
505     int i;
506
507     if (dir_format)
508     {
509         for(i = 0; i < 11; i++) buffer[i] = ' ';
510         buffer[11] = 0;
511     }
512
513     if (DOSFS_ValidDOSName( name ))
514     {
515         /* Check for '.' and '..' */
516         if (*name == '.')
517         {
518             buffer[0] = '.';
519             if (!dir_format) buffer[1] = buffer[2] = '\0';
520             if (name[1] == '.') buffer[1] = '.';
521             return;
522         }
523
524         /* Simply copy the name, converting to uppercase */
525
526         for (dst = buffer; !is_end_of_name(*name) && (*name != '.'); name++)
527             *dst++ = toupperW(*name);
528         if (*name == '.')
529         {
530             if (dir_format) dst = buffer + 8;
531             else *dst++ = '.';
532             for (name++; !is_end_of_name(*name); name++)
533                 *dst++ = toupperW(*name);
534         }
535         if (!dir_format) *dst = '\0';
536         return;
537     }
538
539     /* Compute the hash code of the file name */
540     /* If you know something about hash functions, feel free to */
541     /* insert a better algorithm here... */
542     if (!is_case_sensitive)
543     {
544         for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
545             hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
546         hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
547     }
548     else
549     {
550         for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
551             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
552         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
553     }
554
555     /* Find last dot for start of the extension */
556     for (p = name+1, ext = NULL; !is_end_of_name(*p); p++)
557         if (*p == '.') ext = p;
558     if (ext && is_end_of_name(ext[1]))
559         ext = NULL;  /* Empty extension ignored */
560
561     /* Copy first 4 chars, replacing invalid chars with '_' */
562     for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
563     {
564         if (is_end_of_name(*p) || (p == ext)) break;
565         *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
566     }
567     /* Pad to 5 chars with '~' */
568     while (i-- >= 0) *dst++ = '~';
569
570     /* Insert hash code converted to 3 ASCII chars */
571     *dst++ = hash_chars[(hash >> 10) & 0x1f];
572     *dst++ = hash_chars[(hash >> 5) & 0x1f];
573     *dst++ = hash_chars[hash & 0x1f];
574
575     /* Copy the first 3 chars of the extension (if any) */
576     if (ext)
577     {
578         if (!dir_format) *dst++ = '.';
579         for (i = 3, ext++; (i > 0) && !is_end_of_name(*ext); i--, ext++)
580             *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
581     }
582     if (!dir_format) *dst = '\0';
583 }
584
585
586 /***********************************************************************
587  *           DOSFS_FindUnixName
588  *
589  * Find the Unix file name in a given directory that corresponds to
590  * a file name (either in Unix or DOS format).
591  * File name can be terminated by '\0', '\\' or '/'.
592  * Return TRUE if OK, FALSE if no file name matches.
593  *
594  * 'long_buf' must be at least 'long_len' characters long. If the long name
595  * turns out to be larger than that, the function returns FALSE.
596  * 'short_buf' must be at least 13 characters long.
597  */
598 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
599                          INT long_len, LPWSTR short_buf )
600 {
601     DOS_DIR *dir;
602     LPCWSTR long_name, short_name;
603     WCHAR dos_name[12], tmp_buf[13];
604     BOOL ret;
605
606     LPCWSTR p = strchrW( name, '/' );
607     int len = p ? (int)(p - name) : strlenW(name);
608     if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
609     /* Ignore trailing dots and spaces */
610     while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
611     if (long_len < len + 1) return FALSE;
612
613     TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
614
615     if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
616
617     if (!(dir = DOSFS_OpenDir( path->long_name )))
618     {
619         WARN("(%s,%s): can't open dir: %s\n",
620              path->long_name, debugstr_w(name), strerror(errno) );
621         return FALSE;
622     }
623
624     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
625     {
626         /* Check against Unix name */
627         if (len == strlenW(long_name))
628         {
629             if (is_case_sensitive)
630             {
631                 if (!strncmpW( long_name, name, len )) break;
632             }
633             else
634             {
635                 if (!strncmpiW( long_name, name, len )) break;
636             }
637         }
638         if (dos_name[0])
639         {
640             /* Check against hashed DOS name */
641             if (!short_name)
642             {
643                 DOSFS_Hash( long_name, tmp_buf, TRUE );
644                 short_name = tmp_buf;
645             }
646             if (!strcmpW( dos_name, short_name )) break;
647         }
648     }
649     if (ret)
650     {
651         if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
652         if (short_buf)
653         {
654             if (short_name)
655                 DOSFS_ToDosDTAFormat( short_name, short_buf );
656             else
657                 DOSFS_Hash( long_name, short_buf, FALSE );
658         }
659         TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
660               debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
661     }
662     else
663         WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
664     DOSFS_CloseDir( dir );
665     return ret;
666 }
667
668
669 /***********************************************************************
670  *           DOSFS_GetPathDrive
671  *
672  * Get the drive specified by a given path name (DOS or Unix format).
673  */
674 static int DOSFS_GetPathDrive( LPCWSTR *name )
675 {
676     int drive;
677     LPCWSTR p = *name;
678
679     if (*p && (p[1] == ':'))
680     {
681         drive = toupperW(*p) - 'A';
682         *name += 2;
683     }
684     else if (*p == '/') /* Absolute Unix path? */
685     {
686         if ((drive = DRIVE_FindDriveRootW( name )) == -1)
687         {
688             MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
689             /* Assume it really was a DOS name */
690             drive = DRIVE_GetCurrentDrive();
691         }
692     }
693     else drive = DRIVE_GetCurrentDrive();
694
695     if (!DRIVE_IsValid(drive))
696     {
697         SetLastError( ERROR_INVALID_DRIVE );
698         return -1;
699     }
700     return drive;
701 }
702
703
704 /***********************************************************************
705  *           DOSFS_GetFullName
706  *
707  * Convert a file name (DOS or mixed DOS/Unix format) to a valid
708  * Unix name / short DOS name pair.
709  * Return FALSE if one of the path components does not exist. The last path
710  * component is only checked if 'check_last' is non-zero.
711  * The buffers pointed to by 'long_buf' and 'short_buf' must be
712  * at least MAX_PATHNAME_LEN long.
713  */
714 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
715 {
716     BOOL found;
717     char *p_l, *root;
718     LPWSTR p_s;
719     static const WCHAR driveA_rootW[] = {'A',':','\\',0};
720     static const WCHAR dos_rootW[] = {'\\',0};
721
722     TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
723
724     if ((!*name) || (*name=='\n'))
725     { /* error code for Win98 */
726         SetLastError(ERROR_BAD_PATHNAME);
727         return FALSE;
728     }
729
730     if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
731
732     lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
733                  sizeof(full->long_name) );
734     if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
735     else root = full->long_name;  /* root directory */
736
737     strcpyW( full->short_name, driveA_rootW );
738     full->short_name[0] += full->drive;
739
740     if ((*name == '\\') || (*name == '/'))  /* Absolute path */
741     {
742         while ((*name == '\\') || (*name == '/')) name++;
743     }
744     else  /* Relative path */
745     {
746         lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
747                      sizeof(full->long_name) - (root - full->long_name) - 1 );
748         if (root[1]) *root = '/';
749         lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
750                    sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
751     }
752
753     p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
754                              : full->long_name;
755     p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
756                               : full->short_name + 2;
757     found = TRUE;
758
759     while (*name && found)
760     {
761         /* Check for '.' and '..' */
762
763         if (*name == '.')
764         {
765             if (is_end_of_name(name[1]))
766             {
767                 name++;
768                 while ((*name == '\\') || (*name == '/')) name++;
769                 continue;
770             }
771             else if ((name[1] == '.') && is_end_of_name(name[2]))
772             {
773                 name += 2;
774                 while ((*name == '\\') || (*name == '/')) name++;
775                 while ((p_l > root) && (*p_l != '/')) p_l--;
776                 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
777                 *p_l = *p_s = '\0';  /* Remove trailing separator */
778                 continue;
779             }
780         }
781
782         /* Make sure buffers are large enough */
783
784         if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
785             (p_l >= full->long_name + sizeof(full->long_name) - 1))
786         {
787             SetLastError( ERROR_PATH_NOT_FOUND );
788             return FALSE;
789         }
790
791         /* Get the long and short name matching the file name */
792
793         if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
794                          sizeof(full->long_name) - (p_l - full->long_name) - 1, p_s + 1 )))
795         {
796             *p_l++ = '/';
797             p_l   += strlen(p_l);
798             *p_s++ = '\\';
799             p_s   += strlenW(p_s);
800             while (!is_end_of_name(*name)) name++;
801         }
802         else if (!check_last)
803         {
804             *p_l++ = '/';
805             *p_s++ = '\\';
806             while (!is_end_of_name(*name) &&
807                    (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
808                    (p_l < full->long_name + sizeof(full->long_name) - 1))
809             {
810                 WCHAR wch;
811                 *p_s++ = tolowerW(*name);
812                 /* If the drive is case-sensitive we want to create new */
813                 /* files in lower-case otherwise we can't reopen them   */
814                 /* under the same short name. */
815                 if (is_case_sensitive) wch = tolowerW(*name);
816                 else wch = *name;
817                 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
818                 name++;
819             }
820             /* Ignore trailing dots and spaces */
821             while(p_l[-1] == '.' || p_l[-1] == ' ') {
822                 --p_l;
823                 --p_s;
824             }
825             *p_l = '\0';
826             *p_s = '\0';
827         }
828         while ((*name == '\\') || (*name == '/')) name++;
829     }
830
831     if (!found)
832     {
833         if (check_last)
834         {
835             SetLastError( ERROR_FILE_NOT_FOUND );
836             return FALSE;
837         }
838         if (*name)  /* Not last */
839         {
840             SetLastError( ERROR_PATH_NOT_FOUND );
841             return FALSE;
842         }
843     }
844     if (!full->long_name[0]) strcpy( full->long_name, "/" );
845     if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
846     TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
847     return TRUE;
848 }
849
850
851 /***********************************************************************
852  *           MulDiv   (KERNEL32.@)
853  * RETURNS
854  *      Result of multiplication and division
855  *      -1: Overflow occurred or Divisor was 0
856  */
857 INT WINAPI MulDiv(
858              INT nMultiplicand,
859              INT nMultiplier,
860              INT nDivisor)
861 {
862     LONGLONG ret;
863
864     if (!nDivisor) return -1;
865
866     /* We want to deal with a positive divisor to simplify the logic. */
867     if (nDivisor < 0)
868     {
869       nMultiplicand = - nMultiplicand;
870       nDivisor = -nDivisor;
871     }
872
873     /* If the result is positive, we "add" to round. else, we subtract to round. */
874     if ( ( (nMultiplicand <  0) && (nMultiplier <  0) ) ||
875          ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
876       ret = (((LONGLONG)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
877     else
878       ret = (((LONGLONG)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
879
880     if ((ret > 2147483647) || (ret < -2147483647)) return -1;
881     return ret;
882 }
883
884
885 /***********************************************************************
886  *           DosDateTimeToFileTime   (KERNEL32.@)
887  */
888 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
889 {
890     struct tm newtm;
891 #ifndef HAVE_TIMEGM
892     struct tm *gtm;
893     time_t time1, time2;
894 #endif
895
896     newtm.tm_sec  = (fattime & 0x1f) * 2;
897     newtm.tm_min  = (fattime >> 5) & 0x3f;
898     newtm.tm_hour = (fattime >> 11);
899     newtm.tm_mday = (fatdate & 0x1f);
900     newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
901     newtm.tm_year = (fatdate >> 9) + 80;
902 #ifdef HAVE_TIMEGM
903     RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
904 #else
905     time1 = mktime(&newtm);
906     gtm = gmtime(&time1);
907     time2 = mktime(gtm);
908     RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
909 #endif
910     return TRUE;
911 }
912
913
914 /***********************************************************************
915  *           FileTimeToDosDateTime   (KERNEL32.@)
916  */
917 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
918                                      LPWORD fattime )
919 {
920     LARGE_INTEGER       li;
921     ULONG               t;
922     time_t              unixtime;
923     struct tm*          tm;
924
925     li.u.LowPart = ft->dwLowDateTime;
926     li.u.HighPart = ft->dwHighDateTime;
927     RtlTimeToSecondsSince1970( &li, &t );
928     unixtime = t;
929     tm = gmtime( &unixtime );
930     if (fattime)
931         *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
932     if (fatdate)
933         *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
934                    + tm->tm_mday;
935     return TRUE;
936 }