Commit 1de20ae4 authored by Alexandre Julliard's avatar Alexandre Julliard

Rewrote handling of COM and LPT devices to use symlinks in

$WINEPREFIX/dosdevices, with suitable defaults if the symlinks are missing. Rewrote QueryDosDevice and DefineDosDevice to use the new scheme. Added temporary code to create the symlinks based on the contents of the config file.
parent eebc3ef9
......@@ -50,6 +50,7 @@ extern void LOCALE_InitRegistry(void);
extern void COMPUTERNAME_Init(void);
extern int __wine_set_signal_handler(unsigned, int (*)(unsigned));
extern void VOLUME_CreateDevices(void);
/* memory/environ.c */
extern void ENV_CopyStartupInformation(void);
......@@ -126,6 +127,9 @@ static BOOL process_attach(void)
/* Setup computer name */
COMPUTERNAME_Init();
/* Create device symlinks */
VOLUME_CreateDevices();
/* copy process information from ntdll */
ENV_CopyStartupInformation();
......
......@@ -51,6 +51,9 @@ extern HMODULE kernel32_handle;
extern HANDLE dos_handles[DOS_TABLE_SIZE];
void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing );
extern HANDLE VOLUME_OpenDevice( LPCWSTR name, DWORD access, DWORD sharing,
LPSECURITY_ATTRIBUTES sa, DWORD attributes );
extern void PTHREAD_Init(void);
extern BOOL WOWTHUNK_Init(void);
......
......@@ -152,15 +152,6 @@ WINE REGISTRY Version 2
;"dir3" = "/usr/X11R6/lib/X11/fonts/TT"
;"dir4" = "/usr/share/fonts/TT"
[serialports]
"Com1" = "/dev/ttyS0"
"Com2" = "/dev/ttyS1"
"Com3" = "/dev/ttyS2"
"Com4" = "/dev/modem"
[parallelports]
"Lpt1" = "/dev/lp0"
[ppdev]
;; key: io-base of the emulated port
;; value : parport-device{,timeout}
......
......@@ -212,22 +212,6 @@ fiddling with the current defaults and needless to say that you must know
what you are doing.
--debugmsg +loaddll might come in handy for experimenting with that stuff.
.PP
.B [serialports]
.br
.I format: """com[12345678]""=""<devicename>"""
.br
default: none
.br
Used to specify the devices which are used as COM1 - COM8.
.PP
.B [parallelports]
.br
.I format: """lpt[12345678]""=""<devicename>"""
.br
default: none
.br
Used to specify the devices which are used as LPT1 - LPT8.
.PP
.B [Debug]
.br
.I format: """SpyExclude""=""<message names separated by semicolons>"""
......@@ -350,16 +334,22 @@ Make sure to use double backslashes in the section name.
A sample configuration file is distributed as
.B documentation/samples/config
in the Wine source distribution.
.SH FILES
.TP
.I ~/.wine/config
User-specific configuration file
.SH ENVIRONMENT VARIABLES
.TP
.I WINEPREFIX
Specifies the directory that contains the per-user
.I config
file, the registry files, and the wineserver socket. The default is
file, the registry files, and the DOS device mappings. The default is
.I $HOME/.wine.
.SH FILES
.TP
.I $WINEPREFIX/config
User-specific configuration file
.TP
.I $WINEPREFIX/dosdevices
Directory containing the DOS device mappings. Each file in that
directory is a symlink to the Unix device file implementing a given
device. For instance, if COM1 is mapped to /dev/ttyS0 you'd have a
symlink of the form $WINEPREFIX/dosdevices/com1 -> /dev/ttyS0.
.SH "SEE ALSO"
.BR wine (1)
......@@ -95,42 +95,6 @@ typedef struct
/* Chars we don't want to see in DOS file names */
#define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
/* DOS device descriptor */
typedef struct
{
const WCHAR name[5];
} DOS_DEVICE;
static const DOS_DEVICE DOSFS_Devices[] =
/* name, device flags (see Int 21/AX=0x4400) */
{
{ {'C','O','N',0} },
{ {'P','R','N',0} },
{ {'N','U','L',0} },
{ {'A','U','X',0} },
{ {'L','P','T','1',0} },
{ {'L','P','T','2',0} },
{ {'L','P','T','3',0} },
{ {'L','P','T','4',0} },
{ {'C','O','M','1',0} },
{ {'C','O','M','2',0} },
{ {'C','O','M','3',0} },
{ {'C','O','M','4',0} }
};
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};
/* at some point we may want to allow Winelib apps to set this */
static const BOOL is_case_sensitive = FALSE;
......@@ -702,122 +666,6 @@ BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf
}
/**************************************************************************
* 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];
char devname[40];
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));
ret = FILE_CreateFile( devname, access, FILE_SHARE_READ|FILE_SHARE_WRITE,
sa, OPEN_EXISTING, attributes, NULL, FALSE, DRIVE_FIXED );
if(!ret)
ERR("Couldn't open device '%s' ! (check permissions)\n",devname);
else
TRACE("return %p\n", ret );
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;
const WCHAR *p;
HANDLE handle;
if (name[0] && (name[1] == ':')) name += 2;
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) ))
{
p = name + strlenW( dev );
if (!*p || (*p == '.') || (*p == ':')) {
static const WCHAR nulW[] = {'N','U','L',0};
static const WCHAR conW[] = {'C','O','N',0};
/* got it */
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:
to_dup = GetStdHandle( STD_INPUT_HANDLE );
break;
case GENERIC_WRITE:
to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
break;
default:
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,
DUPLICATE_SAME_ACCESS ))
handle = 0;
return handle;
}
if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) )
return handle;
FIXME("device open %s not supported (yet)\n", debugstr_w(DOSFS_Devices[i].name));
return 0;
}
}
}
return 0;
}
/***********************************************************************
* DOSFS_GetPathDrive
*
......@@ -1106,126 +954,3 @@ BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
+ tm->tm_mday;
return TRUE;
}
/***********************************************************************
* QueryDosDeviceA (KERNEL32.@)
*
* returns array of strings terminated by \0, terminated by \0
*/
DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
{
DWORD ret = 0, retW;
LPWSTR targetW = (LPWSTR)HeapAlloc(GetProcessHeap(),0,
bufsize * sizeof(WCHAR));
UNICODE_STRING devnameW;
if(devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
else devnameW.Buffer = NULL;
retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target,
bufsize, NULL, NULL);
RtlFreeUnicodeString(&devnameW);
if (targetW) HeapFree(GetProcessHeap(),0,targetW);
return ret;
}
/***********************************************************************
* QueryDosDeviceW (KERNEL32.@)
*
* returns array of strings terminated by \0, terminated by \0
*
* FIXME
* - Win9x returns for all calls ERROR_INVALID_PARAMETER
* - the returned devices for devname == NULL is far from complete
* - its not checked that the returned device exist
*/
DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
{
const WCHAR *pDev, *pName, *pNum = NULL;
int numsiz=0;
DWORD ret;
TRACE("(%s,...)\n", debugstr_w(devname));
if (!devname) {
/* return known MSDOS devices */
DWORD ret = 0;
int i;
static const WCHAR devices[][5] = {{'A','U','X',0},
{'C','O','M','1',0},
{'C','O','M','2',0},
{'L','P','T','1',0},
{'N','U','L',0,}};
for(i=0; (i< (sizeof(devices)/sizeof(devices[0]))); i++) {
DWORD len = strlenW(devices[i]);
if(target && (bufsize >= ret + len + 2)) {
strcpyW(target+ret, devices[i]);
ret += len + 1;
} else {
/* in this case WinXP returns 0 */
FIXME("function return is wrong for WinXP!\n");
SetLastError(ERROR_INSUFFICIENT_BUFFER);
break;
}
}
/* append drives here */
if(target && bufsize > 0) target[ret++] = 0;
FIXME("Returned list is not complete\n");
return ret;
}
/* In theory all that are possible and have been defined.
* Now just those below, since mirc uses it to check for special files.
*
* (It is more complex, and supports netmounted stuff, and \\.\ stuff,
* but currently we just ignore that.)
*/
if (!strcmpiW(devname, auxW)) {
pDev = dosW;
pName = comW;
numsiz = 1;
pNum = oneW;
} else if (!strcmpiW(devname, nulW)) {
pDev = devW;
pName = nullW;
} else if (!strncmpiW(devname, comW, strlenW(comW))) {
pDev = devW;
pName = serW;
pNum = devname + strlenW(comW);
for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
if(*(pNum + numsiz)) {
SetLastError(ERROR_FILE_NOT_FOUND);
return 0;
}
} else if (!strncmpiW(devname, lptW, strlenW(lptW))) {
pDev = devW;
pName = parW;
pNum = devname + strlenW(lptW);
for(numsiz=0; isdigitW(*(pNum+numsiz)); numsiz++);
if(*(pNum + numsiz)) {
SetLastError(ERROR_FILE_NOT_FOUND);
return 0;
}
} else {
/* This might be a DOS device we do not handle yet ... */
FIXME("(%s) not detected as DOS device!\n",debugstr_w(devname));
/* Win9x set the error ERROR_INVALID_PARAMETER */
SetLastError(ERROR_FILE_NOT_FOUND);
return 0;
}
FIXME("device %s may not exist on this computer\n", debugstr_w(devname));
ret = strlenW(pDev) + strlenW(pName) + numsiz + 2;
if (ret > bufsize) ret = 0;
if (target && ret) {
strcpyW(target,pDev);
strcatW(target,pName);
if (pNum) strcatW(target,pNum);
target[ret-1] = 0;
}
return ret;
}
......@@ -621,87 +621,6 @@ int DRIVE_Chdir( int drive, LPCWSTR path )
/***********************************************************************
* DefineDosDeviceA (KERNEL32.@)
*/
BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
{
UNICODE_STRING d, t;
BOOL ret;
if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
{
RtlFreeUnicodeString(&d);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
RtlFreeUnicodeString(&d);
RtlFreeUnicodeString(&t);
return ret;
}
/***********************************************************************
* DefineDosDeviceA (KERNEL32.@)
*/
BOOL WINAPI DefineDosDeviceW(DWORD flags,LPCWSTR devname,LPCWSTR targetpath)
{
DOSDRIVE *old, *new;
/* this is a temporary hack for int21 support. better implementation has to be done */
if (flags != DDD_RAW_TARGET_PATH ||
!(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
devname[1] != ':' || devname[2] != 0 ||
!(toupperW(targetpath[0]) >= 'A' && toupperW(targetpath[0]) <= 'Z') ||
targetpath[1] != ':' || targetpath[2] != '\\' || targetpath[3] != 0)
{
FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
old = DOSDrives + devname[0] - 'A';
new = DOSDrives + targetpath[0] - 'A';
if (!old->root)
{
SetLastError( ERROR_INVALID_DRIVE );
return 0;
}
if ( new->root )
{
TRACE("Can't map drive %c: to already existing drive %c:\n",
devname[0], targetpath[0] );
/* it is already mapped there, so return success */
if (!strcmp(old->root,new->root))
return 1;
return 0;
}
new->root = heap_strdup( old->root );
new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
strcpyW(new->dos_cwd, old->dos_cwd);
new->unix_cwd = heap_strdup( old->unix_cwd );
new->device = heap_strdup( old->device );
new->type = old->type;
new->flags = old->flags;
new->dev = old->dev;
new->ino = old->ino;
TRACE("Drive %c: is now equal to drive %c:\n",
targetpath[0], devname[0] );
return 1;
}
/***********************************************************************
* DRIVE_GetFreeSpace
*/
static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
......
......@@ -318,6 +318,7 @@ HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
{
DOS_FULL_NAME full_name;
HANDLE ret;
DWORD dosdev;
static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
static const WCHAR bkslashesW[] = {'\\','\\',0};
......@@ -342,6 +343,13 @@ HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
(creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
(creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
/* Open a console for CONIN$ or CONOUT$ */
if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
{
ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
goto done;
}
/* If the name starts with '\\?\', ignore the first 4 chars. */
if (!strncmpW(filename, bkslashes_with_question_markW, 4))
{
......@@ -379,47 +387,61 @@ HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
}
goto done;
}
else if (!RtlIsDosDeviceName_U( filename + 4 ))
else if ((dosdev = RtlIsDosDeviceName_U( filename + 4 )))
{
dosdev += MAKELONG( 0, 4*sizeof(WCHAR) ); /* adjust position to start of filename */
}
else
{
ret = VXD_Open( filename+4, access, sa );
goto done;
}
else
filename+=4; /* fall into DOSFS_Device case below */
}
else dosdev = RtlIsDosDeviceName_U( filename );
/* If the name still starts with '\\', it's a UNC name. */
if (!strncmpW(filename, bkslashesW, 2))
if (dosdev)
{
ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
goto done;
}
static const WCHAR conW[] = {'C','O','N',0};
WCHAR dev[5];
/* If the name contains a DOS wild card (* or ?), do no create a file */
if(strchrW(filename, '*') || strchrW(filename, '?'))
memcpy( dev, filename + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
dev[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
TRACE("opening device %s\n", debugstr_w(dev) );
if (!strcmpiW( dev, conW ))
{
SetLastError(ERROR_BAD_PATHNAME);
switch (access & (GENERIC_READ|GENERIC_WRITE))
{
case GENERIC_READ:
ret = OpenConsoleW(coninW, access, (sa && sa->bInheritHandle), creation);
goto done;
case GENERIC_WRITE:
ret = OpenConsoleW(conoutW, access, (sa && sa->bInheritHandle), creation);
goto done;
default:
FIXME("can't open CON read/write\n");
SetLastError( ERROR_FILE_NOT_FOUND );
return INVALID_HANDLE_VALUE;
}
}
/* Open a console for CONIN$ or CONOUT$ */
if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
{
ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
ret = VOLUME_OpenDevice( dev, access, sharing, sa, attributes );
goto done;
}
if (RtlIsDosDeviceName_U( filename ))
/* If the name still starts with '\\', it's a UNC name. */
if (!strncmpW(filename, bkslashesW, 2))
{
TRACE("opening device %s\n", debugstr_w(filename) );
ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
goto done;
}
if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
/* If the name contains a DOS wild card (* or ?), do no create a file */
if(strchrW(filename, '*') || strchrW(filename, '?'))
{
/* Do not silence this please. It is a critical error. -MM */
ERR("Couldn't open device %s!\n", debugstr_w(filename));
SetLastError( ERROR_FILE_NOT_FOUND );
}
goto done;
SetLastError(ERROR_BAD_PATHNAME);
return INVALID_HANDLE_VALUE;
}
/* check for filename, don't check for last entry if creating */
......
......@@ -50,7 +50,6 @@ extern DWORD DIR_SearchPath( LPCWSTR path, LPCWSTR name, LPCWSTR ext,
DOS_FULL_NAME *full_name, BOOL win32 );
/* files/dos_fs.c */
extern HANDLE DOSFS_OpenDevice( LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa);
extern BOOL DOSFS_FindUnixName( const DOS_FULL_NAME *path, LPCWSTR name, char *long_buf,
INT long_len, LPWSTR short_buf );
extern BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment