Newer
Older
/*
* DOS file system functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#ifdef HAVE_SYS_ERRNO_H
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "ntstatus.h"
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "wine/unicode.h"
#include "wine/winbase16.h"
#include "winternl.h"
#include "excpt.h"
#include "smb.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
WINE_DECLARE_DEBUG_CHANNEL(file);
/* Define the VFAT ioctl to get both short and long file names */
/* FIXME: is it possible to get this to work on other systems? */
/* We want the real kernel dirent structure, not the libc one */
typedef struct
{
long d_ino;
long d_off;
unsigned short d_reclen;
char d_name[256];
} KERNEL_DIRENT;
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
Michal Janusz Miroslaw
committed
/* To avoid blocking on non-directories in DOSFS_OpenDir_VFAT*/
#ifndef O_DIRECTORY
# define O_DIRECTORY 0200000 /* must be directory */
#endif
#else /* linux */
#undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
#endif /* linux */
#define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
static const DOS_DEVICE DOSFS_Devices[] =
/* name, device flags (see Int 21/AX=0x4400) */
{
{ {'C','O','N',0}, 0xc0d3 },
{ {'P','R','N',0}, 0xa0c0 },
{ {'N','U','L',0}, 0x80c4 },
{ {'A','U','X',0}, 0x80c0 },
{ {'L','P','T','1',0}, 0xa0c0 },
{ {'L','P','T','2',0}, 0xa0c0 },
{ {'L','P','T','3',0}, 0xa0c0 },
{ {'L','P','T','4',0}, 0xc0d3 },
{ {'C','O','M','1',0}, 0x80c0 },
{ {'C','O','M','2',0}, 0x80c0 },
{ {'C','O','M','3',0}, 0x80c0 },
{ {'C','O','M','4',0}, 0x80c0 },
{ {'S','C','S','I','M','G','R','$',0}, 0xc0c0 },
{ {'H','P','S','C','A','N',0}, 0xc0c0 },
{ {'E','M','M','X','X','X','X','0',0}, 0x0000 }
static const WCHAR devW[] = {'\\','D','e','v','i','c','e','\\',0};
static const WCHAR dosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
static const WCHAR auxW[] = {'A','U','X',0};
static const WCHAR comW[] = {'C','O','M',0};
static const WCHAR lptW[] = {'L','P','T',0};
static const WCHAR nulW[] = {'N','U','L',0};
static const WCHAR nullW[] = {'N','u','l','l',0};
static const WCHAR parW[] = {'P','a','r','a','l','l','e','l',0};
static const WCHAR serW[] = {'S','e','r','i','a','l',0};
static const WCHAR oneW[] = {'1',0};
/*
* Directory info for DOSFS_ReadDir
* contains the names of *all* the files in the directory
*/
/* Info structure for FindFirstFile handle */
typedef struct
{
char *path; /* unix path */
LPWSTR long_mask;
LPWSTR short_mask;
CRITICAL_SECTION cs;
union
{
DOS_DIR *dos_dir;
SMB_DIR *smb_dir;
} u;
static WINE_EXCEPTION_FILTER(page_fault)
{
if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_EXECUTE_HANDLER;
return EXCEPTION_CONTINUE_SEARCH;
}
/***********************************************************************
* DOSFS_ValidDOSName
*
* Return 1 if Unix file 'name' is also a valid MS-DOS name
* (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
* File name can be terminated by '\0', '\\' or '/'.
*/
static int DOSFS_ValidDOSName( LPCWSTR name, int ignore_case )
static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
int len = 0;
if (*p == '.')
{
/* Check for "." and ".." */
p++;
if (*p == '.') p++;
/* All other names beginning with '.' are invalid */
return (IS_END_OF_NAME(*p));
}
while (!IS_END_OF_NAME(*p))
{
if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
if (*p == '.') break; /* Start of the extension */
if (++len > 8) return 0; /* Name too long */
p++;
}
if (*p != '.') return 1; /* End of name */
p++;
if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
len = 0;
while (!IS_END_OF_NAME(*p))
{
if (*p < 256 && strchr( invalid, (char)*p )) return 0; /* Invalid char */
if (*p == '.') return 0; /* Second extension not allowed */
if (++len > 3) return 0; /* Extension too long */
p++;
}
return 1;
}
/***********************************************************************
* DOSFS_ToDosFCBFormat
*
* Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
* expanding wild cards and converting to upper-case in the process.
* File name can be terminated by '\0', '\\' or '/'.
* Return FALSE if the name is not a valid DOS name.
* 'buffer' must be at least 12 characters long.
BOOL DOSFS_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
{
static const char invalid_chars[] = INVALID_DOS_CHARS;
int i;
/* Check for "." and ".." */
if (*p == '.')
{
p++;
buffer[0] = '.';
for(i = 1; i < 11; i++) buffer[i] = ' ';
buffer[11] = 0;
if (*p == '.')
{
buffer[1] = '.';
p++;
}
}
for (i = 0; i < 8; i++)
{
switch(*p)
{
case '\0':
case '\\':
case '/':
case '.':
buffer[i] = ' ';
break;
case '?':
p++;
/* fall through */
case '*':
buffer[i] = '?';
break;
default:
if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
buffer[i] = toupperW(*p);
p++;
break;
}
}
if (*p == '*')
{
/* Skip all chars after wildcard up to first dot */
while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
}
else
{
/* Check if name too long */
if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
}
if (*p == '.') p++; /* Skip dot */
for (i = 8; i < 11; i++)
{
switch(*p)
{
case '\0':
case '\\':
case '/':
buffer[i] = ' ';
break;
case '.':
case '?':
p++;
/* fall through */
case '*':
buffer[i] = '?';
break;
default:
if (*p < 256 && strchr( invalid_chars, (char)*p )) return FALSE;
buffer[i] = toupperW(*p);
/* at most 3 character of the extension are processed
while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
return IS_END_OF_NAME(*p);
}
/***********************************************************************
* DOSFS_ToDosDTAFormat
*
* Convert a file name from FCB to DTA format (name.ext, null-terminated)
* converting to upper-case in the process.
* File name can be terminated by '\0', '\\' or '/'.
static void DOSFS_ToDosDTAFormat( LPCWSTR name, LPWSTR buffer )
memcpy( buffer, name, 8 * sizeof(WCHAR) );
p = buffer + 8;
while ((p > buffer) && (p[-1] == ' ')) p--;
memcpy( p, name + 8, 3 * sizeof(WCHAR) );
p += 3;
while (p[-1] == ' ') p--;
if (p[-1] == '.') p--;
*p = '\0';
}
/***********************************************************************
*
* Check a DOS file name against a mask (both in FCB format).
*/
static int DOSFS_MatchShort( LPCWSTR mask, LPCWSTR name )
{
int i;
for (i = 11; i > 0; i--, mask++, name++)
if ((*mask != '?') && (*mask != *name)) return 0;
return 1;
}
/***********************************************************************
* DOSFS_MatchLong
*
* Check a long file name against a mask.
*
* Tests (done in W95 DOS shell - case insensitive):
* *.txt test1.test.txt *
* *st1* test1.txt *
* *.t??????.t* test1.ta.tornado.txt *
* *tornado* test1.ta.tornado.txt *
* t*t test1.ta.tornado.txt *
* ?est* test1.txt *
* ?est??? test1.txt -
* h?l?o*t.dat hellothisisatest.dat *
static int DOSFS_MatchLong( LPCWSTR mask, LPCWSTR name, int case_sensitive )
LPCWSTR lastjoker = NULL;
LPCWSTR next_to_retry = NULL;
static const WCHAR asterisk_dot_asterisk[] = {'*','.','*',0};
TRACE("(%s, %s, %x)\n", debugstr_w(mask), debugstr_w(name), case_sensitive);
if (!strcmpW( mask, asterisk_dot_asterisk )) return 1;
while (*name && *mask)
{
if (*mask == '*')
{
mask++;
while (*mask == '*') mask++; /* Skip consecutive '*' */
lastjoker = mask;
if (!*mask) return 1; /* end of mask is all '*', so match */
/* skip to the next match after the joker(s) */
if (case_sensitive) while (*name && (*name != *mask)) name++;
else while (*name && (toupperW(*name) != toupperW(*mask))) name++;
if (!*name) break;
next_to_retry = name;
int mismatch = 0;
if (*mask != *name) mismatch = 1;
}
else
{
if (toupperW(*mask) != toupperW(*name)) mismatch = 1;
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
}
if (!mismatch)
{
mask++;
name++;
if (*mask == '\0')
{
if (*name == '\0')
return 1;
if (lastjoker)
mask = lastjoker;
}
}
else /* mismatch ! */
{
if (lastjoker) /* we had an '*', so we can try unlimitedly */
{
mask = lastjoker;
/* this scan sequence was a mismatch, so restart
* 1 char after the first char we checked last time */
next_to_retry++;
name = next_to_retry;
}
else
return 0; /* bad luck */
else /* '?' */
{
mask++;
name++;
}
while ((*mask == '.') || (*mask == '*'))
mask++; /* Ignore trailing '.' or '*' in mask */
/***********************************************************************
* DOSFS_AddDirEntry
*
* Used to construct an array of filenames in DOSFS_OpenDir
static BOOL DOSFS_AddDirEntry(DOS_DIR **dir, LPCWSTR name, LPCWSTR dosname)
int extra1 = strlenW(name) + 1;
int extra2 = strlenW(dosname) + 1;
/* if we need more, at minimum double the size */
if( (extra1 + extra2 + (*dir)->used) > (*dir)->size)
int more = (*dir)->size;
DOS_DIR *t;
if(more<(extra1+extra2))
more = extra1+extra2;
t = HeapReAlloc(GetProcessHeap(), 0, *dir, sizeof(**dir) +
((*dir)->size + more)*sizeof(WCHAR) );
if(!t)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
ERR("Out of memory caching directory structure %d %d %d\n",
(*dir)->size, more, (*dir)->used);
return FALSE;
}
(*dir) = t;
(*dir)->size += more;
/* at this point, the dir structure is big enough to hold these names */
strcpyW(&(*dir)->names[(*dir)->used], name);
strcpyW(&(*dir)->names[(*dir)->used], dosname);
(*dir)->used += extra2;
return TRUE;
}
/***********************************************************************
* DOSFS_OpenDir_VFAT
*/
static BOOL DOSFS_OpenDir_VFAT(DOS_DIR **dir, const char *unix_path)
Michal Janusz Miroslaw
committed
int fd = open( unix_path, O_RDONLY|O_DIRECTORY );
/* Check if the VFAT ioctl is supported on this directory */
if ( fd<0 )
return FALSE;
while (1)
WCHAR long_name[MAX_PATH];
WCHAR short_name[12];
r = (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1);
if(!r)
break;
if (!de[0].d_reclen)
break;
MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
if (!DOSFS_ToDosFCBFormat( long_name, short_name ))
short_name[0] = '\0';
if (de[1].d_name[0])
MultiByteToWideChar(CP_UNIXCP, 0, de[1].d_name, -1, long_name, MAX_PATH);
MultiByteToWideChar(CP_UNIXCP, 0, de[0].d_name, -1, long_name, MAX_PATH);
r = DOSFS_AddDirEntry(dir, long_name, short_name );
if(!r)
break;
if(r)
{
static const WCHAR empty_strW[] = { 0 };
DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
}
close(fd);
return r;
#else
return FALSE;
}
/***********************************************************************
* DOSFS_OpenDir_Normal
*
* Now use the standard opendir/readdir interface
*/
static BOOL DOSFS_OpenDir_Normal( DOS_DIR **dir, const char *unix_path )
{
DIR *unixdir = opendir( unix_path );
BOOL r = TRUE;
static const WCHAR empty_strW[] = { 0 };
if(!unixdir)
return FALSE;
while(1)
{
WCHAR long_name[MAX_PATH];
struct dirent *de = readdir(unixdir);
if(!de)
break;
MultiByteToWideChar(CP_UNIXCP, 0, de->d_name, -1, long_name, MAX_PATH);
r = DOSFS_AddDirEntry(dir, long_name, empty_strW);
if(!r)
break;
}
if(r)
DOSFS_AddDirEntry(dir, empty_strW, empty_strW);
closedir(unixdir);
return r;
}
/***********************************************************************
* DOSFS_OpenDir
*/
static DOS_DIR *DOSFS_OpenDir( const char *unix_path )
{
const int init_size = 0x100;
DOS_DIR *dir = HeapAlloc( GetProcessHeap(), 0, sizeof(*dir) + init_size*sizeof (WCHAR));
BOOL r;
TRACE("%s\n",debugstr_a(unix_path));
if (!dir)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return NULL;
}
dir->used = 0;
dir->size = init_size;
/* Treat empty path as root directory. This simplifies path split into
directory and mask in several other places */
if (!*unix_path) unix_path = "/";
r = DOSFS_OpenDir_VFAT( &dir, unix_path);
r = DOSFS_OpenDir_Normal( &dir, unix_path);
HeapFree(GetProcessHeap(), 0, dir);
return dir;
}
/***********************************************************************
* DOSFS_CloseDir
*/
static void DOSFS_CloseDir( DOS_DIR *dir )
{
HeapFree( GetProcessHeap(), 0, dir );
}
/***********************************************************************
* DOSFS_ReadDir
*/
static BOOL DOSFS_ReadDir( DOS_DIR *dir, LPCWSTR *long_name,
LPCWSTR *short_name )
if (!dir)
return FALSE;
/* the long pathname is first */
if(ln[0])
*long_name = ln;
else
return FALSE;
/* followed by the short path name */
if(sn[0])
*short_name = sn;
else
*short_name = NULL;
/***********************************************************************
* DOSFS_Hash
*
* Transform a Unix file name into a hashed DOS name. If the name is a valid
* DOS name, it is converted to upper-case; otherwise it is replaced by a
* hashed version that fits in 8.3 format.
* File name can be terminated by '\0', '\\' or '/'.
static void DOSFS_Hash( LPCWSTR name, LPWSTR buffer, BOOL dir_format,
BOOL ignore_case )
{
static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
LPCWSTR p, ext;
LPWSTR dst;
if (dir_format)
{
for(i = 0; i < 11; i++) buffer[i] = ' ';
buffer[11] = 0;
}
{
/* Check for '.' and '..' */
if (*name == '.')
{
buffer[0] = '.';
if (!dir_format) buffer[1] = buffer[2] = '\0';
if (name[1] == '.') buffer[1] = '.';
}
/* Simply copy the name, converting to uppercase */
for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
if (*name == '.')
{
if (dir_format) dst = buffer + 8;
else *dst++ = '.';
for (name++; !IS_END_OF_NAME(*name); name++)
return;
}
/* Compute the hash code of the file name */
/* If you know something about hash functions, feel free to */
/* insert a better algorithm here... */
if (ignore_case)
{
for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
}
/* Find last dot for start of the extension */
for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
if (*p == '.') ext = p;
if (ext && IS_END_OF_NAME(ext[1]))
ext = NULL; /* Empty extension ignored */
/* Copy first 4 chars, replacing invalid chars with '_' */
for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
{
if (IS_END_OF_NAME(*p) || (p == ext)) break;
*dst++ = (*p < 256 && strchr( invalid_chars, (char)*p )) ? '_' : toupperW(*p);
}
/* Pad to 5 chars with '~' */
while (i-- >= 0) *dst++ = '~';
/* Insert hash code converted to 3 ASCII chars */
*dst++ = hash_chars[(hash >> 10) & 0x1f];
*dst++ = hash_chars[(hash >> 5) & 0x1f];
*dst++ = hash_chars[hash & 0x1f];
/* Copy the first 3 chars of the extension (if any) */
if (ext)
{
if (!dir_format) *dst++ = '.';
for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
*dst++ = (*ext < 256 && strchr( invalid_chars, (char)*ext )) ? '_' : toupperW(*ext);
}
/***********************************************************************
* DOSFS_FindUnixName
*
* Find the Unix file name in a given directory that corresponds to
* a file name (either in Unix or DOS format).
* File name can be terminated by '\0', '\\' or '/'.
* Return TRUE if OK, FALSE if no file name matches.
*
* 'long_buf' must be at least 'long_len' characters long. If the long name
* turns out to be larger than that, the function returns FALSE.
* 'short_buf' must be at least 13 characters long.
BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
INT long_len, LPWSTR short_buf, BOOL ignore_case)
LPCWSTR long_name, short_name;
WCHAR dos_name[12], tmp_buf[13];
BOOL ret;
LPCWSTR p = strchrW( name, '/' );
int len = p ? (int)(p - name) : strlenW(name);
if ((p = strchrW( name, '\\' ))) len = min( (int)(p - name), len );
/* Ignore trailing dots and spaces */
while (len > 1 && (name[len-1] == '.' || name[len-1] == ' ')) len--;
TRACE("%s,%s\n", path->long_name, debugstr_w(name) );
if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
if (!(dir = DOSFS_OpenDir( path->long_name )))
WARN("(%s,%s): can't open dir: %s\n",
path->long_name, debugstr_w(name), strerror(errno) );
while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
if (len == strlenW(long_name))
if (!strncmpW( long_name, name, len )) break;
if (!strncmpiW( long_name, name, len )) break;
{
DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
short_name = tmp_buf;
}
if (!strcmpW( dos_name, short_name )) break;
if (long_buf) WideCharToMultiByte(CP_UNIXCP, 0, long_name, -1, long_buf, long_len, NULL, NULL);
if (short_buf)
{
if (short_name)
DOSFS_ToDosDTAFormat( short_name, short_buf );
else
DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
}
TRACE("(%s,%s) -> %s (%s)\n", path->long_name, debugstr_w(name),
debugstr_w(long_name), short_buf ? debugstr_w(short_buf) : "***");
WARN("%s not found in '%s'\n", debugstr_w(name), path->long_name);
}
/***********************************************************************
* Check if a DOS file name represents a DOS device and return the device.
const DOS_DEVICE *DOSFS_GetDevice( LPCWSTR name )
unsigned int i;
if (!name) return NULL; /* if wine_server_handle_to_fd was used */
if ((p = strrchrW( name, '/' ))) name = p + 1;
if ((p = strrchrW( name, '\\' ))) name = p + 1;
for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
{
const WCHAR *dev = DOSFS_Devices[i].name;
if (!strncmpiW( dev, name, strlenW(dev) ))
if (!*p || (*p == '.') || (*p == ':')) return &DOSFS_Devices[i];
/***********************************************************************
* DOSFS_GetDeviceByHandle
*/
const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
const DOS_DEVICE *ret = NULL;
SERVER_START_REQ( get_device_id )
req->handle = hFile;
if (!wine_server_call( req ))
if ((reply->id >= 0) &&
(reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
ret = &DOSFS_Devices[reply->id];
SERVER_END_REQ;
return ret;
/**************************************************************************
* DOSFS_CreateCommPort
*/
static HANDLE DOSFS_CreateCommPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa)
HANDLE ret;
HKEY hkey;
DWORD dummy;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING nameW;
WCHAR *devnameW;
char tmp[128];
static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','W','i','n','e','\\',
'C','o','n','f','i','g','\\',
'S','e','r','i','a','l','P','o','r','t','s',0};
TRACE_(file)("%s %lx %lx\n", debugstr_w(name), access, attributes);
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.ObjectName = &nameW;
attr.Attributes = 0;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
RtlInitUnicodeString( &nameW, serialportsW );
if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0;
RtlInitUnicodeString( &nameW, name );
if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
else
devnameW = NULL;
NtClose( hkey );
if (!devnameW) return 0;
WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL);
TRACE("opening %s as %s\n", devname, debugstr_w(name));
SERVER_START_REQ( create_serial )
{
req->access = access;
req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
req->attributes = attributes;
req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
wine_server_add_data( req, devname, strlen(devname) );
SetLastError(0);
wine_server_call_err( req );
ret = reply->handle;
SERVER_END_REQ;
ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
return ret;
/***********************************************************************
* DOSFS_OpenDevice
*
* Open a DOS device. This might not map 1:1 into the UNIX device concept.
* Returns 0 on failure.
HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa )
unsigned int i;
HANDLE handle;
if ((p = strrchrW( name, '/' ))) name = p + 1;
if ((p = strrchrW( name, '\\' ))) name = p + 1;
for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
{
const WCHAR *dev = DOSFS_Devices[i].name;
if (!strncmpiW( dev, name, strlenW(dev) ))
if (!*p || (*p == '.') || (*p == ':')) {
static const WCHAR nulW[] = {'N','U','L',0};
static const WCHAR conW[] = {'C','O','N',0};
static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0};
static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
if (!strcmpiW(DOSFS_Devices[i].name, nulW))
return FILE_CreateFile( "/dev/null", access,
FILE_SHARE_READ|FILE_SHARE_WRITE, sa,
OPEN_EXISTING, 0, 0, TRUE, DRIVE_UNKNOWN );
if (!strcmpiW(DOSFS_Devices[i].name, conW)) {
HANDLE to_dup;
switch (access & (GENERIC_READ|GENERIC_WRITE)) {
case GENERIC_READ:
FIXME("can't open CON read/write\n");
return 0;
if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
&handle, 0,
sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle,
handle = 0;
if (!strcmpiW(DOSFS_Devices[i].name, scsimgrW) ||
!strcmpiW(DOSFS_Devices[i].name, hpscanW) ||
!strcmpiW(DOSFS_Devices[i].name, emmxxxx0W))
return FILE_CreateDevice( i, access, sa );
if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
return 0;