Make sure the rebar size is above a minimum.
[wine.git] / files / dos_fs.c
blob5b83fb0cfc3fd0171b1ffa52d22b9eb046307e50
1 /*
2 * DOS file system functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
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
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"
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"
65 #include "smb.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
70 WINE_DECLARE_DEBUG_CHANNEL(file);
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
78 long d_ino;
79 long d_off;
80 unsigned short d_reclen;
81 char d_name[256];
82 } KERNEL_DIRENT;
84 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
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
91 #else /* linux */
92 #undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
93 #endif /* linux */
95 /* Chars we don't want to see in DOS file names */
96 #define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
98 /* DOS device descriptor */
99 typedef struct
101 const WCHAR name[5];
102 } DOS_DEVICE;
104 static const DOS_DEVICE DOSFS_Devices[] =
105 /* name, device flags (see Int 21/AX=0x4400) */
107 { {'C','O','N',0} },
108 { {'P','R','N',0} },
109 { {'N','U','L',0} },
110 { {'A','U','X',0} },
111 { {'L','P','T','1',0} },
112 { {'L','P','T','2',0} },
113 { {'L','P','T','3',0} },
114 { {'L','P','T','4',0} },
115 { {'C','O','M','1',0} },
116 { {'C','O','M','2',0} },
117 { {'C','O','M','3',0} },
118 { {'C','O','M','4',0} }
121 static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
122 static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
124 static const WCHAR auxW[] = {'A','U','X',0};
125 static const WCHAR comW[] = {'C','O','M',0};
126 static const WCHAR lptW[] = {'L','P','T',0};
127 static const WCHAR nulW[] = {'N','U','L',0};
129 static const WCHAR nullW[] = {'N','u','l','l',0};
130 static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
131 static const WCHAR serW[] = {'S','e','r','i','a','l',0};
132 static const WCHAR oneW[] = {'1',0};
134 /* at some point we may want to allow Winelib apps to set this */
135 static const BOOL is_case_sensitive = FALSE;
138 * Directory info for DOSFS_ReadDir
139 * contains the names of *all* the files in the directory
141 typedef struct
143 int used;
144 int size;
145 WCHAR names[1];
146 } DOS_DIR;
149 /* return non-zero if c is the end of a directory name */
150 static inline int is_end_of_name(WCHAR c)
152 return !c || (c == '/') || (c == '\\');
155 /***********************************************************************
156 * DOSFS_ValidDOSName
158 * Return 1 if Unix file 'name' is also a valid MS-DOS name
159 * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
160 * File name can be terminated by '\0', '\\' or '/'.
162 static int DOSFS_ValidDOSName( LPCWSTR name )
164 static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
165 const WCHAR *p = name;
166 const char *invalid = !is_case_sensitive ? (invalid_chars + 26) : invalid_chars;
167 int len = 0;
169 if (*p == '.')
171 /* Check for "." and ".." */
172 p++;
173 if (*p == '.') p++;
174 /* All other names beginning with '.' are invalid */
175 return (is_end_of_name(*p));
177 while (!is_end_of_name(*p))
179 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
180 if (*p == '.') break; /* Start of the extension */
181 if (++len > 8) return 0; /* Name too long */
182 p++;
184 if (*p != '.') return 1; /* End of name */
185 p++;
186 if (is_end_of_name(*p)) return 0; /* Empty extension not allowed */
187 len = 0;
188 while (!is_end_of_name(*p))
190 if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
191 if (*p == '.') return 0; /* Second extension not allowed */
192 if (++len > 3) return 0; /* Extension too long */
193 p++;
195 return 1;
199 /***********************************************************************
200 * DOSFS_ToDosFCBFormat
202 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
203 * expanding wild cards and converting to upper-case in the process.
204 * File name can be terminated by '\0', '\\' or '/'.
205 * Return FALSE if the name is not a valid DOS name.
206 * 'buffer' must be at least 12 characters long.
208 static BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
210 static const char invalid_chars[] = INVALID_DOS_CHARS;
211 LPCWSTR p = name;
212 int i;
214 /* Check for "." and ".." */
215 if (*p == '.')
217 p++;
218 buffer[0] = '.';
219 for(i = 1; i < 11; i++) buffer[i] = ' ';
220 buffer[11] = 0;
221 if (*p == '.')
223 buffer[1] = '.';
224 p++;
226 return (!*p || (*p == '/') || (*p == '\\'));
229 for (i = 0; i < 8; i++)
231 switch(*p)
233 case '\0':
234 case '\\':
235 case '/':
236 case '.':
237 buffer[i] = ' ';
238 break;
239 case '?':
240 p++;
241 /* fall through */
242 case '*':
243 buffer[i] = '?';
244 break;
245 default:
246 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
247 buffer[i] = toupperW(*p);
248 p++;
249 break;
253 if (*p == '*')
255 /* Skip all chars after wildcard up to first dot */
256 while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
258 else
260 /* Check if name too long */
261 if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
263 if (*p == '.') p++; /* Skip dot */
265 for (i = 8; i < 11; i++)
267 switch(*p)
269 case '\0':
270 case '\\':
271 case '/':
272 buffer[i] = ' ';
273 break;
274 case '.':
275 return FALSE; /* Second extension not allowed */
276 case '?':
277 p++;
278 /* fall through */
279 case '*':
280 buffer[i] = '?';
281 break;
282 default:
283 if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
284 buffer[i] = toupperW(*p);
285 p++;
286 break;
289 buffer[11] = '\0';
291 /* at most 3 character of the extension are processed
292 * is something behind this ?
294 while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
295 return is_end_of_name(*p);
299 /***********************************************************************
300 * DOSFS_ToDosDTAFormat
302 * Convert a file name from FCB to DTA format (name.ext, null-terminated)
303 * converting to upper-case in the process.
304 * File name can be terminated by '\0', '\\' or '/'.
305 * 'buffer' must be at least 13 characters long.
307 static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
309 LPWSTR p;
311 memcpy( buffer, name, 8 * sizeof(WCHAR) );
312 p = buffer + 8;
313 while ((p > buffer) && (p[-1] == ' ')) p--;
314 *p++ = '.';
315 memcpy( p, name + 8, 3 * sizeof(WCHAR) );
316 p += 3;
317 while (p[-1] == ' ') p--;
318 if (p[-1] == '.') p--;
319 *p = '\0';
323 /***********************************************************************
324 * DOSFS_AddDirEntry
326 * Used to construct an array of filenames in DOSFS_OpenDir
328 static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
330 int extra1 = strlenW(name) + 1;
331 int extra2 = strlenW(dosname) + 1;
333 /* if we need more, at minimum double the size */
334 if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
336 int more = (*dir)->size;
337 DOS_DIR *t;
339 if(more<(extra1+extra2))
340 more = extra1+extra2;
342 t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
343 ((*dir)->size + more)*sizeof(WCHAR) );
344 if(!t)
346 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
347 ERR("Out of memory caching directory structure %d %d %d\n",
348 (*dir)->size, more, (*dir)->used);
349 return FALSE;
351 (*dir) = t;
352 (*dir)->size += more;
355 /* at this point, the dir structure is big enough to hold these names */
356 strcpyW(&(*dir)->names[(*dir)->used], name);
357 (*dir)->used += extra1;
358 strcpyW(&(*dir)->names[(*dir)->used], dosname);
359 (*dir)->used += extra2;
361 return TRUE;
365 /***********************************************************************
366 * DOSFS_OpenDir_VFAT
368 static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
370 #ifdef VFAT_IOCTL_READDIR_BOTH
371 KERNEL_DIRENT de[2];
372 int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
373 BOOL r = TRUE;
375 /* Check if the VFAT ioctl is supported on this directory */
377 if ( fd<0 )
378 return FALSE;
380 while (1)
382 WCHAR long_name[MAX_PATH];
383 WCHAR short_name[12];
385 r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
386 if(!r)
387 break;
388 if (!de[0].d_reclen)
389 break;
390 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
391 if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
392 short_name[0] = '\0';
393 if (de[1].d_name[0])
394 MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
395 else
396 MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
397 r = DOSFS_AddDirEntry(dir, long_name, short_name );
398 if(!r)
399 break;
401 if(r)
403 static const WCHAR empty_strW[] = { 0 };
404 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
406 close(fd);
407 return r;
408 #else
409 return FALSE;
410 #endif /* VFAT_IOCTL_READDIR_BOTH */
414 /***********************************************************************
415 * DOSFS_OpenDir_Normal
417 * Now use the standard opendir/readdir interface
419 static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
421 DIR *unixdir = opendir( unix_path );
422 BOOL r = TRUE;
423 static const WCHAR empty_strW[] = { 0 };
425 if(!unixdir)
426 return FALSE;
427 while(1)
429 WCHAR long_name[MAX_PATH];
430 struct dirent *de = readdir(unixdir);
432 if(!de)
433 break;
434 MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
435 r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
436 if(!r)
437 break;
439 if(r)
440 DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
441 closedir(unixdir);
442 return r;
445 /***********************************************************************
446 * DOSFS_OpenDir
448 static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
450 const int init_size = 0x100;
451 DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
452 BOOL r;
454 TRACE("%s\n",debugstr_a(unix_path));
456 if (!dir)
458 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
459 return NULL;
461 dir->used = 0;
462 dir->size = init_size;
464 /* Treat empty path as root directory. This simplifies path split into
465 directory and mask in several other places */
466 if (!*unix_path) unix_path = "/";
468 r = DOSFS_OpenDir_VFAT( &dir, unix_path);
470 if(!r)
471 r = DOSFS_OpenDir_Normal( &dir, unix_path);
473 if(!r)
475 HeapFree(GetProcessHeap(), 0, dir);
476 return NULL;
478 dir->used = 0;
480 return dir;
484 /***********************************************************************
485 * DOSFS_CloseDir
487 static void DOSFS_CloseDir( DOS_DIR *dir )
489 HeapFree( GetProcessHeap(), 0, dir );
493 /***********************************************************************
494 * DOSFS_ReadDir
496 static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
497 LPCWSTR *short_name )
499 LPCWSTR sn, ln;
501 if (!dir)
502 return FALSE;
504 /* the long pathname is first */
505 ln = &dir->names[dir->used];
506 if(ln[0])
507 *long_name = ln;
508 else
509 return FALSE;
510 dir->used += (strlenW(ln) + 1);
512 /* followed by the short path name */
513 sn = &dir->names[dir->used];
514 if(sn[0])
515 *short_name = sn;
516 else
517 *short_name = NULL;
518 dir->used += (strlenW(sn) + 1);
520 return TRUE;
524 /***********************************************************************
525 * DOSFS_Hash
527 * Transform a Unix file name into a hashed DOS name. If the name is a valid
528 * DOS name, it is converted to upper-case; otherwise it is replaced by a
529 * hashed version that fits in 8.3 format.
530 * File name can be terminated by '\0', '\\' or '/'.
531 * 'buffer' must be at least 13 characters long.
533 static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format )
535 static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
536 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
538 LPCWSTR p, ext;
539 LPWSTR dst;
540 unsigned short hash;
541 int i;
543 if (dir_format)
545 for(i = 0; i < 11; i++) buffer[i] = ' ';
546 buffer[11] = 0;
549 if (DOSFS_ValidDOSName( name ))
551 /* Check for '.' and '..' */
552 if (*name == '.')
554 buffer[0] = '.';
555 if (!dir_format) buffer[1] = buffer[2] = '\0';
556 if (name[1] == '.') buffer[1] = '.';
557 return;
560 /* Simply copy the name, converting to uppercase */
562 for (dst = buffer; !is_end_of_name(*name) && (*name != '.'); name++)
563 *dst++ = toupperW(*name);
564 if (*name == '.')
566 if (dir_format) dst = buffer + 8;
567 else *dst++ = '.';
568 for (name++; !is_end_of_name(*name); name++)
569 *dst++ = toupperW(*name);
571 if (!dir_format) *dst = '\0';
572 return;
575 /* Compute the hash code of the file name */
576 /* If you know something about hash functions, feel free to */
577 /* insert a better algorithm here... */
578 if (!is_case_sensitive)
580 for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
581 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
582 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
584 else
586 for (p = name, hash = 0xbeef; !is_end_of_name(p[1]); p++)
587 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
588 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
591 /* Find last dot for start of the extension */
592 for (p = name+1, ext = NULL; !is_end_of_name(*p); p++)
593 if (*p == '.') ext = p;
594 if (ext && is_end_of_name(ext[1]))
595 ext = NULL; /* Empty extension ignored */
597 /* Copy first 4 chars, replacing invalid chars with '_' */
598 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
600 if (is_end_of_name(*p) || (p == ext)) break;
601 *dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
603 /* Pad to 5 chars with '~' */
604 while (i-- >= 0) *dst++ = '~';
606 /* Insert hash code converted to 3 ASCII chars */
607 *dst++ = hash_chars[(hash >> 10) & 0x1f];
608 *dst++ = hash_chars[(hash >> 5) & 0x1f];
609 *dst++ = hash_chars[hash & 0x1f];
611 /* Copy the first 3 chars of the extension (if any) */
612 if (ext)
614 if (!dir_format) *dst++ = '.';
615 for (i = 3, ext++; (i > 0) && !is_end_of_name(*ext); i--, ext++)
616 *dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
618 if (!dir_format) *dst = '\0';
622 /***********************************************************************
623 * DOSFS_FindUnixName
625 * Find the Unix file name in a given directory that corresponds to
626 * a file name (either in Unix or DOS format).
627 * File name can be terminated by '\0', '\\' or '/'.
628 * Return TRUE if OK, FALSE if no file name matches.
630 * 'long_buf' must be at least 'long_len' characters long. If the long name
631 * turns out to be larger than that, the function returns FALSE.
632 * 'short_buf' must be at least 13 characters long.
634 BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
635 INT long_len, LPWSTR short_buf )
637 DOS_DIR *dir;
638 LPCWSTR long_name, short_name;
639 WCHAR dos_name[12], tmp_buf[13];
640 BOOL ret;
642 LPCWSTR p = strchrW( name, '/' );
643 int len = p ? (int)(p - name) : strlenW(name);
644 if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
645 /* Ignore trailing dots and spaces */
646 while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
647 if (long_len < len + 1) return FALSE;
649 TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
651 if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
653 if (!(dir = DOSFS_OpenDir( path->long_name )))
655 WARN("(%s,%s): can't open dir: %s\n",
656 path->long_name, debugstr_w(name), strerror(errno) );
657 return FALSE;
660 while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
662 /* Check against Unix name */
663 if (len == strlenW(long_name))
665 if (is_case_sensitive)
667 if (!strncmpW( long_name, name, len )) break;
669 else
671 if (!strncmpiW( long_name, name, len )) break;
674 if (dos_name[0])
676 /* Check against hashed DOS name */
677 if (!short_name)
679 DOSFS_Hash( long_name, tmp_buf, TRUE );
680 short_name = tmp_buf;
682 if (!strcmpW( dos_name, short_name )) break;
685 if (ret)
687 if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
688 if (short_buf)
690 if (short_name)
691 DOSFS_ToDosDTAFormat( short_name, short_buf );
692 else
693 DOSFS_Hash( long_name, short_buf, FALSE );
695 TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
696 debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
698 else
699 WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
700 DOSFS_CloseDir( dir );
701 return ret;
705 /**************************************************************************
706 * DOSFS_CreateCommPort
708 static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
710 HANDLE ret;
711 HKEY hkey;
712 DWORD dummy;
713 OBJECT_ATTRIBUTES attr;
714 UNICODE_STRING nameW;
715 WCHAR *devnameW;
716 char tmp[128];
717 char devname[40];
719 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
720 'S','o','f','t','w','a','r','e','\\',
721 'W','i','n','e','\\','W','i','n','e','\\',
722 'C','o','n','f','i','g','\\',
723 'S','e','r','i','a','l','P','o','r','t','s',0};
725 TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
727 attr.Length = sizeof(attr);
728 attr.RootDirectory = 0;
729 attr.ObjectName = &nameW;
730 attr.Attributes = 0;
731 attr.SecurityDescriptor = NULL;
732 attr.SecurityQualityOfService = NULL;
733 RtlInitUnicodeString( &nameW, serialportsW );
735 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
737 RtlInitUnicodeString( &nameW, name );
738 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
739 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
740 else
741 devnameW = NULL;
743 NtClose( hkey );
745 if (!devnameW) return 0;
746 WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
748 TRACE("opening %s as %s\n", devname, debugstr_w(name));
750 ret = FILE_CreateFile( devname, access, FILE_SHARE_READ|FILE_SHARE_WRITE,
751 sa, OPEN_EXISTING, attributes, NULL, FALSE, DRIVE_FIXED );
753 if(!ret)
754 ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
755 else
756 TRACE("return %p\n", ret );
757 return ret;
760 /***********************************************************************
761 * DOSFS_OpenDevice
763 * Open a DOS device. This might not map 1:1 into the UNIX device concept.
764 * Returns 0 on failure.
766 HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
768 unsigned int i;
769 const WCHAR *p;
770 HANDLE handle;
772 if (name[0] && (name[1] == ':')) name += 2;
773 if ((p = strrchrW( name, '/' ))) name = p + 1;
774 if ((p = strrchrW( name, '\\' ))) name = p + 1;
775 for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
777 const WCHAR *dev = DOSFS_Devices[i].name;
778 if (!strncmpiW( dev, name, strlenW(dev) ))
780 p = name + strlenW( dev );
781 if (!*p || (*p == '.') || (*p == ':')) {
782 static const WCHAR nulW[] = {'N','U','L',0};
783 static const WCHAR conW[] = {'C','O','N',0};
784 /* got it */
785 if (!strcmpiW(DOSFS_Devices[i].name, nulW))
786 return FILE_CreateFile( "/dev/null", access,
787 FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
788 OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
789 if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
790 HANDLE to_dup;
791 switch (access & (GENERIC_READ|GENERIC_WRITE)) {
792 case GENERIC_READ:
793 to_dup = GetStdHandle( STD_INPUT_HANDLE );
794 break;
795 case GENERIC_WRITE:
796 to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
797 break;
798 default:
799 FIXME("can't open CON read/write\n");
800 return 0;
802 if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
803 &handle, 0,
804 sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
805 DUPLICATE_SAME_ACCESS ))
806 handle = 0;
807 return handle;
810 if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
811 return handle;
812 FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
813 return 0;
817 return 0;
821 /***********************************************************************
822 * DOSFS_GetPathDrive
824 * Get the drive specified by a given path name (DOS or Unix format).
826 static int DOSFS_GetPathDrive( LPCWSTR *name )
828 int drive;
829 LPCWSTR p = *name;
831 if (*p && (p[1] == ':'))
833 drive = toupperW(*p) - 'A';
834 *name += 2;
836 else if (*p == '/') /* Absolute Unix path? */
838 if ((drive = DRIVE_FindDriveRootW( name )) == -1)
840 MESSAGE("Warning: %s not accessible from a configured DOS drive\n", debugstr_w(*name) );
841 /* Assume it really was a DOS name */
842 drive = DRIVE_GetCurrentDrive();
845 else drive = DRIVE_GetCurrentDrive();
847 if (!DRIVE_IsValid(drive))
849 SetLastError( ERROR_INVALID_DRIVE );
850 return -1;
852 return drive;
856 /***********************************************************************
857 * DOSFS_GetFullName
859 * Convert a file name (DOS or mixed DOS/Unix format) to a valid
860 * Unix name / short DOS name pair.
861 * Return FALSE if one of the path components does not exist. The last path
862 * component is only checked if 'check_last' is non-zero.
863 * The buffers pointed to by 'long_buf' and 'short_buf' must be
864 * at least MAX_PATHNAME_LEN long.
866 BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
868 BOOL found;
869 char *p_l, *root;
870 LPWSTR p_s;
871 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
872 static const WCHAR dos_rootW[] = {'\\',0};
874 TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
876 if ((!*name) || (*name=='\n'))
877 { /* error code for Win98 */
878 SetLastError(ERROR_BAD_PATHNAME);
879 return FALSE;
882 if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
884 lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
885 sizeof(full->long_name) );
886 if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
887 else root = full->long_name; /* root directory */
889 strcpyW( full->short_name, driveA_rootW );
890 full->short_name[0] += full->drive;
892 if ((*name == '\\') || (*name == '/')) /* Absolute path */
894 while ((*name == '\\') || (*name == '/')) name++;
896 else /* Relative path */
898 lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
899 sizeof(full->long_name) - (root - full->long_name) - 1 );
900 if (root[1]) *root = '/';
901 lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
902 sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
905 p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
906 : full->long_name;
907 p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
908 : full->short_name + 2;
909 found = TRUE;
911 while (*name && found)
913 /* Check for '.' and '..' */
915 if (*name == '.')
917 if (is_end_of_name(name[1]))
919 name++;
920 while ((*name == '\\') || (*name == '/')) name++;
921 continue;
923 else if ((name[1] == '.') && is_end_of_name(name[2]))
925 name += 2;
926 while ((*name == '\\') || (*name == '/')) name++;
927 while ((p_l > root) && (*p_l != '/')) p_l--;
928 while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
929 *p_l = *p_s = '\0'; /* Remove trailing separator */
930 continue;
934 /* Make sure buffers are large enough */
936 if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
937 (p_l >= full->long_name + sizeof(full->long_name) - 1))
939 SetLastError( ERROR_PATH_NOT_FOUND );
940 return FALSE;
943 /* Get the long and short name matching the file name */
945 if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
946 sizeof(full->long_name) - (p_l - full->long_name) - 1, p_s + 1 )))
948 *p_l++ = '/';
949 p_l += strlen(p_l);
950 *p_s++ = '\\';
951 p_s += strlenW(p_s);
952 while (!is_end_of_name(*name)) name++;
954 else if (!check_last)
956 *p_l++ = '/';
957 *p_s++ = '\\';
958 while (!is_end_of_name(*name) &&
959 (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
960 (p_l < full->long_name + sizeof(full->long_name) - 1))
962 WCHAR wch;
963 *p_s++ = tolowerW(*name);
964 /* If the drive is case-sensitive we want to create new */
965 /* files in lower-case otherwise we can't reopen them */
966 /* under the same short name. */
967 if (is_case_sensitive) wch = tolowerW(*name);
968 else wch = *name;
969 p_l += WideCharToMultiByte(CP_UNIXCP, 0, &wch, 1, p_l, 2, NULL, NULL);
970 name++;
972 /* Ignore trailing dots and spaces */
973 while(p_l[-1] == '.' || p_l[-1] == ' ') {
974 --p_l;
975 --p_s;
977 *p_l = '\0';
978 *p_s = '\0';
980 while ((*name == '\\') || (*name == '/')) name++;
983 if (!found)
985 if (check_last)
987 SetLastError( ERROR_FILE_NOT_FOUND );
988 return FALSE;
990 if (*name) /* Not last */
992 SetLastError( ERROR_PATH_NOT_FOUND );
993 return FALSE;
996 if (!full->long_name[0]) strcpy( full->long_name, "/" );
997 if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
998 TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
999 return TRUE;
1003 /***********************************************************************
1004 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1006 * Return the full Unix file name for a given path.
1008 BOOL WINAPI wine_get_unix_file_name( LPCWSTR dosW, LPSTR buffer, DWORD len )
1010 BOOL ret;
1011 DOS_FULL_NAME path;
1013 ret = DOSFS_GetFullName( dosW, FALSE, &path );
1014 if (ret && len)
1016 strncpy( buffer, path.long_name, len );
1017 buffer[len - 1] = 0; /* ensure 0 termination */
1019 return ret;
1023 /***********************************************************************
1024 * MulDiv (KERNEL32.@)
1025 * RETURNS
1026 * Result of multiplication and division
1027 * -1: Overflow occurred or Divisor was 0
1029 INT WINAPI MulDiv(
1030 INT nMultiplicand,
1031 INT nMultiplier,
1032 INT nDivisor)
1034 LONGLONG ret;
1036 if (!nDivisor) return -1;
1038 /* We want to deal with a positive divisor to simplify the logic. */
1039 if (nDivisor < 0)
1041 nMultiplicand = - nMultiplicand;
1042 nDivisor = -nDivisor;
1045 /* If the result is positive, we "add" to round. else, we subtract to round. */
1046 if ( ( (nMultiplicand < 0) && (nMultiplier < 0) ) ||
1047 ( (nMultiplicand >= 0) && (nMultiplier >= 0) ) )
1048 ret = (((LONGLONG)nMultiplicand * nMultiplier) + (nDivisor/2)) / nDivisor;
1049 else
1050 ret = (((LONGLONG)nMultiplicand * nMultiplier) - (nDivisor/2)) / nDivisor;
1052 if ((ret > 2147483647) || (ret < -2147483647)) return -1;
1053 return ret;
1057 /***********************************************************************
1058 * DosDateTimeToFileTime (KERNEL32.@)
1060 BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
1062 struct tm newtm;
1063 #ifndef HAVE_TIMEGM
1064 struct tm *gtm;
1065 time_t time1, time2;
1066 #endif
1068 newtm.tm_sec = (fattime & 0x1f) * 2;
1069 newtm.tm_min = (fattime >> 5) & 0x3f;
1070 newtm.tm_hour = (fattime >> 11);
1071 newtm.tm_mday = (fatdate & 0x1f);
1072 newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
1073 newtm.tm_year = (fatdate >> 9) + 80;
1074 #ifdef HAVE_TIMEGM
1075 RtlSecondsSince1970ToTime( timegm(&newtm), (LARGE_INTEGER *)ft );
1076 #else
1077 time1 = mktime(&newtm);
1078 gtm = gmtime(&time1);
1079 time2 = mktime(gtm);
1080 RtlSecondsSince1970ToTime( 2*time1-time2, (LARGE_INTEGER *)ft );
1081 #endif
1082 return TRUE;
1086 /***********************************************************************
1087 * FileTimeToDosDateTime (KERNEL32.@)
1089 BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
1090 LPWORD fattime )
1092 LARGE_INTEGER li;
1093 ULONG t;
1094 time_t unixtime;
1095 struct tm* tm;
1097 li.u.LowPart = ft->dwLowDateTime;
1098 li.u.HighPart = ft->dwHighDateTime;
1099 RtlTimeToSecondsSince1970( &li, &t );
1100 unixtime = t;
1101 tm = gmtime( &unixtime );
1102 if (fattime)
1103 *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
1104 if (fatdate)
1105 *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
1106 + tm->tm_mday;
1107 return TRUE;
1111 /***********************************************************************
1112 * QueryDosDeviceA (KERNEL32.@)
1114 * returns array of strings terminated by \0, terminated by \0
1116 DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
1118 DWORD ret = 0, retW;
1119 LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
1120 bufsize * sizeof(WCHAR));
1121 UNICODE_STRING devnameW;
1123 if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1124 else devnameW.Buffer = NULL;
1126 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1128 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
1129 bufsize, NULL, NULL);
1131 RtlFreeUnicodeString(&devnameW);
1132 if (targetW) HeapFree(GetProcessHeap(),0,targetW);
1133 return ret;
1137 /***********************************************************************
1138 * QueryDosDeviceW (KERNEL32.@)
1140 * returns array of strings terminated by \0, terminated by \0
1142 * FIXME
1143 * - Win9x returns for all calls ERROR_INVALID_PARAMETER
1144 * - the returned devices for devname == NULL is far from complete
1145 * - its not checked that the returned device exist
1147 DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
1149 const WCHAR *pDev, *pName, *pNum = NULL;
1150 int numsiz=0;
1151 DWORD ret;
1153 TRACE("(%s,...)\n", debugstr_w(devname));
1154 if (!devname) {
1155 /* return known MSDOS devices */
1156 DWORD ret = 0;
1157 int i;
1158 static const WCHAR devices[][5] = {{'A','U','X',0},
1159 {'C','O','M','1',0},
1160 {'C','O','M','2',0},
1161 {'L','P','T','1',0},
1162 {'N','U','L',0,}};
1163 for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
1164 DWORD len = strlenW(devices[i]);
1165 if(target && (bufsize >= ret + len + 2)) {
1166 strcpyW(target+ret, devices[i]);
1167 ret += len + 1;
1168 } else {
1169 /* in this case WinXP returns 0 */
1170 FIXME("function return is wrong for WinXP!\n");
1171 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1172 break;
1175 /* append drives here */
1176 if(target && bufsize > 0) target[ret++] = 0;
1177 FIXME("Returned list is not complete\n");
1178 return ret;
1180 /* In theory all that are possible and have been defined.
1181 * Now just those below, since mirc uses it to check for special files.
1183 * (It is more complex, and supports netmounted stuff, and \\.\ stuff,
1184 * but currently we just ignore that.)
1186 if (!strcmpiW(devname, auxW)) {
1187 pDev = dosW;
1188 pName = comW;
1189 numsiz = 1;
1190 pNum = oneW;
1191 } else if (!strcmpiW(devname, nulW)) {
1192 pDev = devW;
1193 pName = nullW;
1194 } else if (!strncmpiW(devname, comW, strlenW(comW))) {
1195 pDev = devW;
1196 pName = serW;
1197 pNum = devname + strlenW(comW);
1198 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1199 if(*(pNum + numsiz)) {
1200 SetLastError(ERROR_FILE_NOT_FOUND);
1201 return 0;
1203 } else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
1204 pDev = devW;
1205 pName = parW;
1206 pNum = devname + strlenW(lptW);
1207 for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
1208 if(*(pNum + numsiz)) {
1209 SetLastError(ERROR_FILE_NOT_FOUND);
1210 return 0;
1212 } else {
1213 /* This might be a DOS device we do not handle yet ... */
1214 FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
1216 /* Win9x set the error ERROR_INVALID_PARAMETER */
1217 SetLastError(ERROR_FILE_NOT_FOUND);
1218 return 0;
1220 FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
1222 ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
1223 if (ret > bufsize) ret = 0;
1224 if (target && ret) {
1225 strcpyW(target,pDev);
1226 strcatW(target,pName);
1227 if (pNum) strcatW(target,pNum);
1228 target[ret-1] = 0;
1230 return ret;