server: Support for opening devices.
Added support for opening devices directly with the server when they
don't correspond to a Unix file.
diff --git a/dlls/kernel/file.c b/dlls/kernel/file.c
index 5f8d128..a9edd35 100644
--- a/dlls/kernel/file.c
+++ b/dlls/kernel/file.c
@@ -1296,6 +1296,10 @@
         {
             dosdev += MAKELONG( 0, 4*sizeof(WCHAR) );  /* adjust position to start of filename */
         }
+        else if (!(GetVersion() & 0x80000000))
+        {
+            dosdev = 0;
+        }
         else if (filename[4])
         {
             ret = VXD_Open( filename+4, access, sa );
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index a50de84..023674d 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -1337,7 +1337,7 @@
 
     /* make sure the device name is ASCII */
     for (i = 0; i < name_len; i++)
-        if (name[i] <= 32 || name[i] >= 127) return STATUS_OBJECT_NAME_NOT_FOUND;
+        if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
 
     unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
 
@@ -1405,7 +1405,7 @@
         dev = NULL; /* last try */
     }
     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
-    return STATUS_OBJECT_NAME_NOT_FOUND;
+    return STATUS_BAD_DEVICE_TYPE;
 }
 
 
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 2e70296..f9363fa 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -202,6 +202,22 @@
     io->u.Status = wine_nt_to_unix_file_name( attr->ObjectName, &unix_name, disposition,
                                               !(attr->Attributes & OBJ_CASE_INSENSITIVE) );
 
+    if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
+    {
+        SERVER_START_REQ( open_file_object )
+        {
+            req->access     = access;
+            req->attributes = attr->Attributes;
+            req->rootdir    = attr->RootDirectory;
+            req->sharing    = sharing;
+            wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
+            io->u.Status = wine_server_call( req );
+            *handle = reply->handle;
+        }
+        SERVER_END_REQ;
+        return io->u.Status;
+    }
+
     if (io->u.Status == STATUS_NO_SUCH_FILE &&
         disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
     {
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 46af45f..1391b08 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -772,6 +772,23 @@
 
 
 
+struct open_file_object_request
+{
+    struct request_header __header;
+    unsigned int access;
+    unsigned int attributes;
+    obj_handle_t rootdir;
+    unsigned int sharing;
+    /* VARARG(filename,unicode_str); */
+};
+struct open_file_object_reply
+{
+    struct reply_header __header;
+    obj_handle_t handle;
+};
+
+
+
 struct alloc_file_handle_request
 {
     struct request_header __header;
@@ -3716,6 +3733,7 @@
     REQ_release_semaphore,
     REQ_open_semaphore,
     REQ_create_file,
+    REQ_open_file_object,
     REQ_alloc_file_handle,
     REQ_get_handle_fd,
     REQ_set_handle_fd,
@@ -3933,6 +3951,7 @@
     struct release_semaphore_request release_semaphore_request;
     struct open_semaphore_request open_semaphore_request;
     struct create_file_request create_file_request;
+    struct open_file_object_request open_file_object_request;
     struct alloc_file_handle_request alloc_file_handle_request;
     struct get_handle_fd_request get_handle_fd_request;
     struct set_handle_fd_request set_handle_fd_request;
@@ -4148,6 +4167,7 @@
     struct release_semaphore_reply release_semaphore_reply;
     struct open_semaphore_reply open_semaphore_reply;
     struct create_file_reply create_file_reply;
+    struct open_file_object_reply open_file_object_reply;
     struct alloc_file_handle_reply alloc_file_handle_reply;
     struct get_handle_fd_reply get_handle_fd_reply;
     struct set_handle_fd_reply set_handle_fd_reply;
@@ -4325,6 +4345,6 @@
     struct query_symlink_reply query_symlink_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 215
+#define SERVER_PROTOCOL_VERSION 216
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/fd.c b/server/fd.c
index 4ab7cbc..07ae40e 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -145,7 +145,8 @@
     unsigned int         access;      /* file access (GENERIC_READ/WRITE) */
     unsigned int         sharing;     /* file sharing mode */
     int                  unix_fd;     /* unix file descriptor */
-    int                  fs_locks;    /* can we use filesystem locks for this fd? */
+    int                  fs_locks :1; /* can we use filesystem locks for this fd? */
+    int                  unmounted :1;/* has the device been unmounted? */
     int                  poll_index;  /* index of fd in poll array */
     struct list          read_q;      /* async readers of this fd */
     struct list          write_q;     /* async writers of this fd */
@@ -1210,6 +1211,7 @@
     if (fd->unix_fd != -1) close( fd->unix_fd );
 
     fd->unix_fd = -1;
+    fd->unmounted = 1;
     fd->closed->unix_fd = -1;
     fd->closed->unlink[0] = 0;
 
@@ -1232,6 +1234,7 @@
     fd->sharing    = 0;
     fd->unix_fd    = -1;
     fd->fs_locks   = 1;
+    fd->unmounted  = 0;
     fd->poll_index = -1;
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
@@ -1246,6 +1249,30 @@
     return fd;
 }
 
+/* allocate a pseudo fd object, for objects that need to behave like files but don't have a unix fd */
+struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *user )
+{
+    struct fd *fd = alloc_object( &fd_ops );
+
+    if (!fd) return NULL;
+
+    fd->fd_ops     = fd_user_ops;
+    fd->user       = user;
+    fd->inode      = NULL;
+    fd->closed     = NULL;
+    fd->access     = 0;
+    fd->sharing    = 0;
+    fd->unix_fd    = -1;
+    fd->fs_locks   = 0;
+    fd->unmounted  = 0;
+    fd->poll_index = -1;
+    list_init( &fd->inode_entry );
+    list_init( &fd->locks );
+    list_init( &fd->read_q );
+    list_init( &fd->write_q );
+    return fd;
+}
+
 /* check if the desired access is possible without violating */
 /* the sharing mode of other opens of the same file */
 static int check_sharing( struct fd *fd, unsigned int access, unsigned int sharing )
@@ -1409,7 +1436,11 @@
 /* retrieve the unix fd for an object */
 int get_unix_fd( struct fd *fd )
 {
-    if (fd->unix_fd == -1) set_error( STATUS_VOLUME_DISMOUNTED );
+    if (fd->unix_fd == -1)
+    {
+        if (fd->unmounted) set_error( STATUS_VOLUME_DISMOUNTED );
+        else set_error( STATUS_BAD_DEVICE_TYPE );
+    }
     return fd->unix_fd;
 }
 
@@ -1595,8 +1626,11 @@
     struct device *device;
     struct inode *inode;
     struct fd *fd;
+    int unix_fd = get_unix_fd( device_fd );
 
-    if (device_fd->unix_fd == -1 || fstat( device_fd->unix_fd, &st ) == -1 || !S_ISBLK( st.st_mode ))
+    if (unix_fd == -1) return;
+
+    if (fstat( unix_fd, &st ) == -1 || !S_ISBLK( st.st_mode ))
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
@@ -1653,6 +1687,32 @@
     }
 }
 
+/* open a file object */
+DECL_HANDLER(open_file_object)
+{
+    struct unicode_str name;
+    struct directory *root = NULL;
+    struct object *obj;
+
+    get_req_unicode_str( &name );
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+        return;
+
+    if ((obj = open_object_dir( root, &name, req->attributes, NULL )))
+    {
+        /* make sure this is a valid file object */
+        struct fd *fd = get_obj_fd( obj );
+        if (fd)
+        {
+            reply->handle = alloc_handle( current->process, obj, req->access, req->attributes );
+            release_object( fd );
+        }
+        release_object( obj );
+    }
+
+    if (root) release_object( root );
+}
+
 /* get a Unix fd to access a file */
 DECL_HANDLER(get_handle_fd)
 {
diff --git a/server/file.h b/server/file.h
index f89940b..4419998 100644
--- a/server/file.h
+++ b/server/file.h
@@ -47,6 +47,7 @@
 /* file descriptor functions */
 
 extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
+extern struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *user );
 extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
                            unsigned int access, unsigned int sharing, unsigned int options );
 extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
diff --git a/server/protocol.def b/server/protocol.def
index 3d055cb..2aa4f0e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -604,6 +604,18 @@
 @END
 
 
+/* Open a file object */
+@REQ(open_file_object)
+    unsigned int access;        /* wanted access rights */
+    unsigned int attributes;    /* open attributes */
+    obj_handle_t rootdir;       /* root directory */
+    unsigned int sharing;       /* sharing flags */
+    VARARG(filename,unicode_str); /* file name */
+@REPLY
+    obj_handle_t handle;        /* handle to the file */
+@END
+
+
 /* Allocate a file handle for a Unix fd */
 @REQ(alloc_file_handle)
     unsigned int access;        /* wanted access rights */
diff --git a/server/request.h b/server/request.h
index 016e390..1fecb0f 100644
--- a/server/request.h
+++ b/server/request.h
@@ -145,6 +145,7 @@
 DECL_HANDLER(release_semaphore);
 DECL_HANDLER(open_semaphore);
 DECL_HANDLER(create_file);
+DECL_HANDLER(open_file_object);
 DECL_HANDLER(alloc_file_handle);
 DECL_HANDLER(get_handle_fd);
 DECL_HANDLER(set_handle_fd);
@@ -361,6 +362,7 @@
     (req_handler)req_release_semaphore,
     (req_handler)req_open_semaphore,
     (req_handler)req_create_file,
+    (req_handler)req_open_file_object,
     (req_handler)req_alloc_file_handle,
     (req_handler)req_get_handle_fd,
     (req_handler)req_set_handle_fd,
diff --git a/server/trace.c b/server/trace.c
index fb1be68..016b007 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1025,6 +1025,21 @@
     fprintf( stderr, " handle=%p", req->handle );
 }
 
+static void dump_open_file_object_request( const struct open_file_object_request *req )
+{
+    fprintf( stderr, " access=%08x,", req->access );
+    fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
+    fprintf( stderr, " sharing=%08x,", req->sharing );
+    fprintf( stderr, " filename=" );
+    dump_varargs_unicode_str( cur_size );
+}
+
+static void dump_open_file_object_reply( const struct open_file_object_reply *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
 static void dump_alloc_file_handle_request( const struct alloc_file_handle_request *req )
 {
     fprintf( stderr, " access=%08x,", req->access );
@@ -3223,6 +3238,7 @@
     (dump_func)dump_release_semaphore_request,
     (dump_func)dump_open_semaphore_request,
     (dump_func)dump_create_file_request,
+    (dump_func)dump_open_file_object_request,
     (dump_func)dump_alloc_file_handle_request,
     (dump_func)dump_get_handle_fd_request,
     (dump_func)dump_set_handle_fd_request,
@@ -3436,6 +3452,7 @@
     (dump_func)dump_release_semaphore_reply,
     (dump_func)dump_open_semaphore_reply,
     (dump_func)dump_create_file_reply,
+    (dump_func)dump_open_file_object_reply,
     (dump_func)dump_alloc_file_handle_reply,
     (dump_func)dump_get_handle_fd_reply,
     (dump_func)dump_set_handle_fd_reply,
@@ -3649,6 +3666,7 @@
     "release_semaphore",
     "open_semaphore",
     "create_file",
+    "open_file_object",
     "alloc_file_handle",
     "get_handle_fd",
     "set_handle_fd",
@@ -3835,6 +3853,7 @@
     { "ACCESS_DENIED",               STATUS_ACCESS_DENIED },
     { "ACCESS_VIOLATION",            STATUS_ACCESS_VIOLATION },
     { "ALIAS_EXISTS",                STATUS_ALIAS_EXISTS },
+    { "BAD_DEVICE_TYPE",             STATUS_BAD_DEVICE_TYPE },
     { "BUFFER_OVERFLOW",             STATUS_BUFFER_OVERFLOW },
     { "BUFFER_TOO_SMALL",            STATUS_BUFFER_TOO_SMALL },
     { "CHILD_MUST_BE_VOLATILE",      STATUS_CHILD_MUST_BE_VOLATILE },