Changed fd operations to take a struct fd instead of a struct object.
Removed get_file_info function from object operations.
Added get_device_id request to avoid abusing get_file_info.

diff --git a/files/dos_fs.c b/files/dos_fs.c
index 707ff6b..fce3ff8 100644
--- a/files/dos_fs.c
+++ b/files/dos_fs.c
@@ -832,14 +832,14 @@
 const DOS_DEVICE *DOSFS_GetDeviceByHandle( HANDLE hFile )
 {
     const DOS_DEVICE *ret = NULL;
-    SERVER_START_REQ( get_file_info )
+    SERVER_START_REQ( get_device_id )
     {
         req->handle = hFile;
-        if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
+        if (!wine_server_call( req ))
         {
-            if ((reply->attr >= 0) &&
-                (reply->attr < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
-                ret = &DOSFS_Devices[reply->attr];
+            if ((reply->id >= 0) &&
+                (reply->id < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0])))
+                ret = &DOSFS_Devices[reply->id];
         }
     }
     SERVER_END_REQ;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 07ef51a..260361e 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1512,6 +1512,19 @@
 };
 
 
+
+struct get_device_id_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct get_device_id_reply
+{
+    struct reply_header __header;
+    int          id;
+};
+
+
 #define SNAP_HEAPLIST   0x00000001
 #define SNAP_PROCESS    0x00000002
 #define SNAP_THREAD     0x00000004
@@ -3080,6 +3093,7 @@
     REQ_open_mapping,
     REQ_get_mapping_info,
     REQ_create_device,
+    REQ_get_device_id,
     REQ_create_snapshot,
     REQ_next_process,
     REQ_next_thread,
@@ -3260,6 +3274,7 @@
     struct open_mapping_request open_mapping_request;
     struct get_mapping_info_request get_mapping_info_request;
     struct create_device_request create_device_request;
+    struct get_device_id_request get_device_id_request;
     struct create_snapshot_request create_snapshot_request;
     struct next_process_request next_process_request;
     struct next_thread_request next_thread_request;
@@ -3438,6 +3453,7 @@
     struct open_mapping_reply open_mapping_reply;
     struct get_mapping_info_reply get_mapping_info_reply;
     struct create_device_reply create_device_reply;
+    struct get_device_id_reply get_device_id_reply;
     struct create_snapshot_reply create_snapshot_reply;
     struct next_process_reply next_process_reply;
     struct next_thread_reply next_thread_reply;
@@ -3535,6 +3551,6 @@
     struct get_next_hook_reply get_next_hook_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 96
+#define SERVER_PROTOCOL_VERSION 97
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/atom.c b/server/atom.c
index 40b07f4..78378cf 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -72,7 +72,6 @@
     NULL,                         /* signaled */
     NULL,                         /* satified */
     no_get_fd,                    /* get_fd */
-    no_get_file_info,             /* get_file_info */
     atom_table_destroy            /* destroy */
 };
 
diff --git a/server/change.c b/server/change.c
index e110ead..0edb34b 100644
--- a/server/change.c
+++ b/server/change.c
@@ -47,7 +47,6 @@
     change_signaled,          /* signaled */
     no_satisfied,             /* satisfied */
     no_get_fd,                /* get_fd */
-    no_get_file_info,         /* get_file_info */
     no_destroy                /* destroy */
 };
 
diff --git a/server/console.c b/server/console.c
index 6288a1a..4dbdc37 100644
--- a/server/console.c
+++ b/server/console.c
@@ -40,9 +40,6 @@
 static void console_input_destroy( struct object *obj );
 static int console_input_signaled( struct object *obj, struct thread *thread );
 
-/* common routine */
-static int console_get_file_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
-
 static const struct object_ops console_input_ops =
 {
     sizeof(struct console_input),     /* size */
@@ -52,7 +49,6 @@
     console_input_signaled,           /* signaled */
     no_satisfied,                     /* satisfied */
     no_get_fd,                        /* get_fd */
-    console_get_file_info,            /* get_file_info */
     console_input_destroy             /* destroy */
 };
 
@@ -77,7 +73,6 @@
     console_input_events_signaled,    /* signaled */
     no_satisfied,                     /* satisfied */
     no_get_fd,                        /* get_fd */
-    no_get_file_info,                 /* get_file_info */
     console_input_events_destroy      /* destroy */
 };
 
@@ -114,7 +109,6 @@
     NULL,                             /* signaled */
     NULL,                             /* satisfied */
     no_get_fd,                        /* get_fd */
-    console_get_file_info,            /* get_file_info */
     screen_buffer_destroy             /* destroy */
 };
 
@@ -374,6 +368,11 @@
     }
 }
 
+int is_console_object( struct object *obj )
+{
+    return (obj->ops == &console_input_ops || obj->ops == &screen_buffer_ops);
+}
+
 static struct console_input* console_input_get( obj_handle_t handle, unsigned access )
 {
     struct console_input*	console = 0;
@@ -938,25 +937,6 @@
 	     console->active, console->evt );
 }
 
-static int console_get_file_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
-{
-    if (reply)
-    {
-        reply->type        = FILE_TYPE_CHAR;
-        reply->attr        = 0;
-        reply->access_time = 0;
-        reply->write_time  = 0;
-        reply->size_high   = 0;
-        reply->size_low    = 0;
-        reply->links       = 0;
-        reply->index_high  = 0;
-        reply->index_low   = 0;
-        reply->serial      = 0;
-    }
-    *flags = 0;
-    return FD_TYPE_CONSOLE;
-}
-
 static void console_input_destroy( struct object *obj )
 {
     struct console_input*	console_in = (struct console_input *)obj;
diff --git a/server/console.h b/server/console.h
index 9371d7a..c3b4aae 100644
--- a/server/console.h
+++ b/server/console.h
@@ -48,5 +48,6 @@
 
 extern void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin);
 extern int free_console( struct process *process );
+extern int is_console_object( struct object *obj );
 
 #endif  /* __WINE_SERVER_CONSOLE_H */
diff --git a/server/context_i386.c b/server/context_i386.c
index b27b5f7..b7a7bb0 100644
--- a/server/context_i386.c
+++ b/server/context_i386.c
@@ -36,6 +36,8 @@
 #endif
 
 #include "winbase.h"
+
+#include "file.h"
 #include "thread.h"
 #include "request.h"
 
diff --git a/server/context_powerpc.c b/server/context_powerpc.c
index 3dca874..b1dde4f 100644
--- a/server/context_powerpc.c
+++ b/server/context_powerpc.c
@@ -35,6 +35,7 @@
 
 #include "winbase.h"
 
+#include "file.h"
 #include "thread.h"
 #include "request.h"
 
diff --git a/server/context_sparc.c b/server/context_sparc.c
index 15ef4cf..bc1b3af 100644
--- a/server/context_sparc.c
+++ b/server/context_sparc.c
@@ -35,6 +35,7 @@
 
 #include "winbase.h"
 
+#include "file.h"
 #include "thread.h"
 #include "request.h"
 
diff --git a/server/debugger.c b/server/debugger.c
index 0427646..200c692 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -73,7 +73,6 @@
     debug_event_signaled,          /* signaled */
     no_satisfied,                  /* satisfied */
     no_get_fd,                     /* get_fd */
-    no_get_file_info,              /* get_file_info */
     debug_event_destroy            /* destroy */
 };
 
@@ -90,7 +89,6 @@
     debug_ctx_signaled,            /* signaled */
     no_satisfied,                  /* satisfied */
     no_get_fd,                     /* get_fd */
-    no_get_file_info,              /* get_file_info */
     debug_ctx_destroy              /* destroy */
 };
 
diff --git a/server/device.c b/server/device.c
index 939e6c8..7e06b99 100644
--- a/server/device.c
+++ b/server/device.c
@@ -43,7 +43,6 @@
 };
 
 static void device_dump( struct object *obj, int verbose );
-static int device_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 
 static const struct object_ops device_ops =
 {
@@ -54,7 +53,6 @@
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
     no_get_fd,                /* get_fd */
-    device_get_info,          /* get_file_info */
     no_destroy                /* destroy */
 };
 
@@ -75,28 +73,6 @@
     fprintf( stderr, "Device id=%08x\n", dev->id );
 }
 
-static int device_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
-{
-    struct device *dev = (struct device *)obj;
-    assert( obj->ops == &device_ops );
-
-    if (reply)
-    {
-        reply->type        = FILE_TYPE_UNKNOWN;
-        reply->attr        = dev->id;  /* hack! */
-        reply->access_time = 0;
-        reply->write_time  = 0;
-        reply->size_high   = 0;
-        reply->size_low    = 0;
-        reply->links       = 0;
-        reply->index_high  = 0;
-        reply->index_low   = 0;
-        reply->serial      = 0;
-    }
-    *flags = 0;
-    return FD_TYPE_DEFAULT;
-}
-
 /* create a device */
 DECL_HANDLER(create_device)
 {
@@ -109,3 +85,16 @@
         release_object( dev );
     }
 }
+
+
+/* Retrieve the client private id for a device */
+DECL_HANDLER(get_device_id)
+{
+    struct device *dev;
+
+    if ((dev = (struct device *)get_handle_obj( current->process, req->handle, 0, &device_ops )))
+    {
+        reply->id = dev->id;
+        release_object( dev );
+    }
+}
diff --git a/server/event.c b/server/event.c
index 927e2c7..bc1b5a4 100644
--- a/server/event.c
+++ b/server/event.c
@@ -51,7 +51,6 @@
     event_signaled,            /* signaled */
     event_satisfied,           /* satisfied */
     no_get_fd,                 /* get_fd */
-    no_get_file_info,          /* get_file_info */
     no_destroy                 /* destroy */
 };
 
diff --git a/server/fd.c b/server/fd.c
index 1dff6ef..6c2a49a 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -32,6 +32,7 @@
 #include "handle.h"
 #include "process.h"
 #include "request.h"
+#include "console.h"
 
 struct fd
 {
@@ -54,7 +55,6 @@
     NULL,                     /* signaled */
     NULL,                     /* satisfied */
     default_get_fd,           /* get_fd */
-    no_get_file_info,         /* get_file_info */
     fd_destroy                /* destroy */
 };
 
@@ -95,18 +95,17 @@
     return user;
 }
 
-/* retrieve the unix fd for an object */
-int get_unix_fd( struct object *obj )
+/* retrieve the object that is using an fd */
+void *get_fd_user( struct fd *fd )
 {
-    struct fd *fd = obj->ops->get_fd( obj );
-    int unix_fd = -1;
+    return fd->user;
+}
 
-    if (fd)
-    {
-        unix_fd = fd->unix_fd;
-        release_object( fd );
-    }
-    return unix_fd;
+/* retrieve the unix fd for an object */
+int get_unix_fd( struct fd *fd )
+{
+    if (fd) return fd->unix_fd;
+    return -1;
 }
 
 /* set the unix fd for an object; can only be done once */
@@ -121,17 +120,22 @@
     obj->fd = unix_fd;
 }
 
-/* close a file descriptor */
-void close_fd( struct fd *fd )
-{
-    release_object( fd );
-}
-
 /* callback for event happening in the main poll() loop */
 void fd_poll_event( struct object *obj, int event )
 {
     struct fd *fd = obj->fd_obj;
-    return fd->fd_ops->poll_event( fd->user, event );
+    return fd->fd_ops->poll_event( fd, event );
+}
+
+/* check if events are pending and if yes return which one(s) */
+int check_fd_events( struct fd *fd, int events )
+{
+    struct pollfd pfd;
+
+    pfd.fd     = fd->unix_fd;
+    pfd.events = events;
+    if (poll( &pfd, 1, 0 ) <= 0) return 0;
+    return pfd.revents;
 }
 
 /* default add_queue() routine for objects that poll() on an fd */
@@ -140,7 +144,7 @@
     struct fd *fd = obj->fd_obj;
 
     if (!obj->head)  /* first on the queue */
-        set_select_events( obj, fd->fd_ops->get_poll_events( fd->user ) );
+        set_select_events( obj, fd->fd_ops->get_poll_events( fd ) );
     add_queue( obj, entry );
     return 1;
 }
@@ -159,9 +163,9 @@
 int default_fd_signaled( struct object *obj, struct thread *thread )
 {
     struct fd *fd = obj->fd_obj;
-    int events = fd->fd_ops->get_poll_events( obj );
+    int events = fd->fd_ops->get_poll_events( fd );
 
-    if (check_select_events( fd->unix_fd, events ))
+    if (check_fd_events( fd, events ))
     {
         /* stop waiting on select() if we are signaled */
         set_select_events( obj, 0 );
@@ -172,15 +176,31 @@
     return 0;
 }
 
+/* default handler for poll() events */
+void default_poll_event( struct fd *fd, int event )
+{
+    /* an error occurred, stop polling this fd to avoid busy-looping */
+    if (event & (POLLERR | POLLHUP)) set_select_events( fd->user, -1 );
+    wake_up( fd->user, 0 );
+}
+
 /* default flush() routine */
-int no_flush( struct object *obj )
+int no_flush( struct fd *fd )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
     return 0;
 }
 
+/* default get_file_info() routine */
+int no_get_file_info( struct fd *fd, struct get_file_info_reply *info, int *flags )
+{
+    set_error( STATUS_OBJECT_TYPE_MISMATCH );
+    *flags = 0;
+    return FD_TYPE_INVALID;
+}
+
 /* default queue_async() routine */
-void no_queue_async( struct object *obj, void* ptr, unsigned int status, int type, int count )
+void no_queue_async( struct fd *fd, void* ptr, unsigned int status, int type, int count )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
 }
@@ -192,7 +212,7 @@
     struct fd *fd = NULL;
     struct object *obj;
 
-    if ((obj = get_handle_obj( process, handle, 0, NULL )))
+    if ((obj = get_handle_obj( process, handle, access, NULL )))
     {
         if (obj->fd_obj) fd = (struct fd *)grab_object( obj->fd_obj );
         else set_error( STATUS_OBJECT_TYPE_MISMATCH );
@@ -201,7 +221,6 @@
     return fd;
 }
 
-
 /* flush a file buffers */
 DECL_HANDLER(flush_file)
 {
@@ -209,7 +228,52 @@
 
     if (fd)
     {
-        fd->fd_ops->flush( fd->user );
+        fd->fd_ops->flush( fd );
+        release_object( fd );
+    }
+}
+
+/* get a Unix fd to access a file */
+DECL_HANDLER(get_handle_fd)
+{
+    struct fd *fd;
+
+    reply->fd = -1;
+    reply->type = FD_TYPE_INVALID;
+
+    if ((fd = get_handle_fd_obj( current->process, req->handle, req->access )))
+    {
+        int unix_fd = get_handle_unix_fd( current->process, req->handle, req->access );
+        if (unix_fd != -1) reply->fd = unix_fd;
+        else if (!get_error())
+        {
+            unix_fd = fd->unix_fd;
+            if (unix_fd != -1) send_client_fd( current->process, unix_fd, req->handle );
+        }
+        reply->type = fd->fd_ops->get_file_info( fd, NULL, &reply->flags );
+        release_object( fd );
+    }
+    else  /* check for console handle (FIXME: should be done in the client) */
+    {
+        struct object *obj;
+
+        if ((obj = get_handle_obj( current->process, req->handle, req->access, NULL )))
+        {
+            if (is_console_object( obj )) reply->type = FD_TYPE_CONSOLE;
+            release_object( obj );
+        }
+    }
+}
+
+/* get a file information */
+DECL_HANDLER(get_file_info)
+{
+    struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 );
+
+    if (fd)
+    {
+        int flags;
+        fd->fd_ops->get_file_info( fd, reply, &flags );
         release_object( fd );
     }
 }
@@ -238,7 +302,7 @@
 
     if (fd)
     {
-        fd->fd_ops->queue_async( fd->user, req->overlapped, req->status, req->type, req->count );
+        fd->fd_ops->queue_async( fd, req->overlapped, req->status, req->type, req->count );
         release_object( fd );
     }
 }
diff --git a/server/file.c b/server/file.c
index 60b72a6..7518a09 100644
--- a/server/file.c
+++ b/server/file.c
@@ -66,12 +66,13 @@
 static struct file *file_hash[NAME_HASH_SIZE];
 
 static void file_dump( struct object *obj, int verbose );
-static int file_get_poll_events( struct object *obj );
-static void file_poll_event( struct object *obj, int event );
-static int file_flush( struct object *obj );
-static int file_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static void file_destroy( struct object *obj );
-static void file_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);
+
+static int file_get_poll_events( struct fd *fd );
+static void file_poll_event( struct fd *fd, int event );
+static int file_flush( struct fd *fd );
+static int file_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+static void file_queue_async( struct fd *fd, void *ptr, unsigned int status, int type, int count );
 
 static const struct object_ops file_ops =
 {
@@ -82,7 +83,6 @@
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
     default_get_fd,               /* get_fd */
-    file_get_info,                /* get_file_info */
     file_destroy                  /* destroy */
 };
 
@@ -264,23 +264,23 @@
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->obj.fd, file->flags, file->name );
+    fprintf( stderr, "File fd=%p flags=%08x name='%s'\n", file->obj.fd_obj, file->flags, file->name );
 }
 
-static int file_get_poll_events( struct object *obj )
+static int file_get_poll_events( struct fd *fd )
 {
-    struct file *file = (struct file *)obj;
+    struct file *file = get_fd_user( fd );
     int events = 0;
-    assert( obj->ops == &file_ops );
+    assert( file->obj.ops == &file_ops );
     if (file->access & GENERIC_READ) events |= POLLIN;
     if (file->access & GENERIC_WRITE) events |= POLLOUT;
     return events;
 }
 
-static void file_poll_event( struct object *obj, int event )
+static void file_poll_event( struct fd *fd, int event )
 {
-    struct file *file = (struct file *)obj;
-    assert( obj->ops == &file_ops );
+    struct file *file = get_fd_user( fd );
+    assert( file->obj.ops == &file_ops );
     if ( file->flags & FILE_FLAG_OVERLAPPED )
     {
         if( IS_READY(file->read_q) && (POLLIN & event) )
@@ -294,22 +294,22 @@
             return;
         }
     }
-    default_poll_event( obj, event );
+    default_poll_event( fd, event );
 }
 
 
-static int file_flush( struct object *obj )
+static int file_flush( struct fd *fd )
 {
-    int ret = (fsync( get_unix_fd(obj) ) != -1);
+    int ret = (fsync( get_unix_fd(fd) ) != -1);
     if (!ret) file_set_error();
     return ret;
 }
 
-static int file_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int file_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
     struct stat st;
-    struct file *file = (struct file *)obj;
-    int unix_fd = get_unix_fd( obj );
+    struct file *file = get_fd_user( fd );
+    int unix_fd = get_unix_fd( fd );
 
     if (reply)
     {
@@ -346,13 +346,13 @@
     return FD_TYPE_DEFAULT;
 }
 
-static void file_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
+static void file_queue_async(struct fd *fd, void *ptr, unsigned int status, int type, int count)
 {
-    struct file *file = (struct file *)obj;
+    struct file *file = get_fd_user( fd );
     struct async *async;
     struct async_queue *q;
 
-    assert( obj->ops == &file_ops );
+    assert( file->obj.ops == &file_ops );
 
     if ( !(file->flags & FILE_FLAG_OVERLAPPED) )
     {
@@ -377,10 +377,10 @@
 
     if ( status == STATUS_PENDING )
     {
-        struct pollfd pfd;
+        int events;
 
         if ( !async )
-            async = create_async ( obj, current, ptr );
+            async = create_async ( &file->obj, current, ptr );
         if ( !async )
             return;
 
@@ -389,18 +389,13 @@
             async_insert( q, async );
 
         /* Check if the new pending request can be served immediately */
-        pfd.fd = get_unix_fd( obj );
-        pfd.events = file_get_poll_events ( obj );
-        pfd.revents = 0;
-        poll ( &pfd, 1, 0 );
-
-        if ( pfd.revents )
-            file_poll_event ( obj, pfd.revents );
+        events = check_fd_events( fd, file_get_poll_events( fd ) );
+        if (events) file_poll_event ( fd, events );
     }
     else if ( async ) destroy_async ( async );
     else set_error ( STATUS_INVALID_PARAMETER );
 
-    set_select_events ( obj, file_get_poll_events ( obj ));
+    set_select_events( &file->obj, file_get_poll_events( fd ));
 }
 
 static void file_destroy( struct object *obj )
@@ -456,6 +451,11 @@
     return (struct file *)get_handle_obj( process, handle, access, &file_ops );
 }
 
+int get_file_unix_fd( struct file *file )
+{
+    return get_unix_fd( file->obj.fd_obj );
+}
+
 static int set_file_pointer( obj_handle_t handle, unsigned int *low, int *high, int whence )
 {
     struct file *file;
@@ -464,7 +464,7 @@
     xto = *low+((off_t)*high<<32);
     if (!(file = get_file_obj( current->process, handle, 0 )))
         return 0;
-    if ((result = lseek( get_unix_fd(&file->obj), xto, whence))==-1)
+    if ((result = lseek( get_file_unix_fd(file), xto, whence))==-1)
     {
         /* Check for seek before start of file */
 
@@ -487,7 +487,7 @@
 static int extend_file( struct file *file, off_t size )
 {
     static const char zero;
-    int unix_fd = get_unix_fd( &file->obj );
+    int unix_fd = get_file_unix_fd( file );
 
     /* extend the file one byte beyond the requested size and then truncate it */
     /* this should work around ftruncate implementations that can't extend files */
@@ -505,7 +505,7 @@
 static int truncate_file( struct file *file )
 {
     int ret = 0;
-    int unix_fd = get_unix_fd( &file->obj );
+    int unix_fd = get_file_unix_fd( file );
     off_t pos = lseek( unix_fd, 0, SEEK_CUR );
     off_t eof = lseek( unix_fd, 0, SEEK_END );
 
@@ -524,7 +524,7 @@
 {
     int ret = 0;
     struct stat st;
-    int unix_fd = get_unix_fd( &file->obj );
+    int unix_fd = get_file_unix_fd( file );
     off_t old_pos, size = size_low + (((off_t)size_high)<<32);
 
     if (fstat( unix_fd, &st ) == -1)
@@ -618,27 +618,6 @@
     }
 }
 
-/* get a Unix fd to access a file */
-DECL_HANDLER(get_handle_fd)
-{
-    struct object *obj;
-
-    reply->fd = -1;
-    reply->type = FD_TYPE_INVALID;
-    if ((obj = get_handle_obj( current->process, req->handle, req->access, NULL )))
-    {
-        int fd = get_handle_fd( current->process, req->handle, req->access );
-        if (fd != -1) reply->fd = fd;
-        else if (!get_error())
-        {
-            int unix_fd = get_unix_fd( obj );
-            if (unix_fd != -1) send_client_fd( current->process, unix_fd, req->handle );
-        }
-        reply->type = obj->ops->get_file_info( obj, NULL, &reply->flags );
-        release_object( obj );
-    }
-}
-
 /* set a file current position */
 DECL_HANDLER(set_file_pointer)
 {
@@ -667,19 +646,6 @@
     set_file_time( req->handle, req->access_time, req->write_time );
 }
 
-/* get a file information */
-DECL_HANDLER(get_file_info)
-{
-    struct object *obj;
-
-    if ((obj = get_handle_obj( current->process, req->handle, 0, NULL )))
-    {
-        int flags;
-        obj->ops->get_file_info( obj, reply, &flags );
-        release_object( obj );
-    }
-}
-
 /* lock a region of a file */
 DECL_HANDLER(lock_file)
 {
diff --git a/server/file.h b/server/file.h
index f5c9a32..351c62e 100644
--- a/server/file.h
+++ b/server/file.h
@@ -29,28 +29,47 @@
 struct fd_ops
 {
     /* get the events we want to poll() for on this object */
-    int  (*get_poll_events)(struct object *);
+    int  (*get_poll_events)(struct fd *);
     /* a poll() event occured */
-    void (*poll_event)(struct object *,int event);
+    void (*poll_event)(struct fd *,int event);
     /* flush the object buffers */
-    int  (*flush)(struct object *);
+    int  (*flush)(struct fd *);
     /* get file information */
-    int  (*get_file_info)(struct object *,struct get_file_info_reply *, int *flags);
+    int  (*get_file_info)(struct fd *,struct get_file_info_reply *, int *flags);
     /* queue an async operation - see register_async handler in async.c*/
-    void (*queue_async)(struct object *, void* ptr, unsigned int status, int type, int count);
+    void (*queue_async)(struct fd *, void* ptr, unsigned int status, int type, int count);
 };
 
+/* file descriptor functions */
+
 extern void *alloc_fd_object( const struct object_ops *ops,
                               const struct fd_ops *fd_user_ops, int unix_fd );
-extern int get_unix_fd( struct object *obj );
+extern void *get_fd_user( struct fd *fd );
+extern int get_unix_fd( struct fd *fd );
 extern void set_unix_fd( struct object *obj, int unix_fd );
-extern void close_fd( struct fd *fd );
 extern void fd_poll_event( struct object *obj, int event );
+extern int check_fd_events( struct fd *fd, int events );
 
 extern int default_fd_add_queue( struct object *obj, struct wait_queue_entry *entry );
 extern void default_fd_remove_queue( struct object *obj, struct wait_queue_entry *entry );
 extern int default_fd_signaled( struct object *obj, struct thread *thread );
-extern int no_flush( struct object *obj );
-extern void no_queue_async(struct object *obj, void* ptr, unsigned int status, int type, int count);
+extern void default_poll_event( struct fd *fd, int event );
+extern int no_flush( struct fd *fd );
+extern int no_get_file_info( struct fd *fd, struct get_file_info_reply *info, int *flags );
+extern void no_queue_async( struct fd *fd, void* ptr, unsigned int status, int type, int count );
+
+inline static struct fd *get_obj_fd( struct object *obj ) { return obj->ops->get_fd( obj ); }
+
+/* file functions */
+
+extern struct file *get_file_obj( struct process *process, obj_handle_t handle,
+                                  unsigned int access );
+extern int get_file_unix_fd( struct file *file );
+extern int is_same_file( struct file *file1, struct file *file2 );
+extern int get_file_drive_type( struct file *file );
+extern int grow_file( struct file *file, int size_high, int size_low );
+extern int create_anonymous_file(void);
+extern struct file *create_temp_file( int access );
+extern void file_set_error(void);
 
 #endif  /* __WINE_SERVER_FILE_H */
diff --git a/server/handle.c b/server/handle.c
index 471d5a7..6fa8f71 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -105,7 +105,6 @@
     NULL,                            /* signaled */
     NULL,                            /* satisfied */
     no_get_fd,                       /* get_fd */
-    no_get_file_info,                /* get_file_info */
     handle_table_destroy             /* destroy */
 };
 
@@ -385,7 +384,7 @@
 }
 
 /* retrieve the cached fd for a given handle */
-int get_handle_fd( struct process *process, obj_handle_t handle, unsigned int access )
+int get_handle_unix_fd( struct process *process, obj_handle_t handle, unsigned int access )
 {
     struct handle_entry *entry;
 
diff --git a/server/handle.h b/server/handle.h
index 994ce48..212a0ae 100644
--- a/server/handle.h
+++ b/server/handle.h
@@ -38,7 +38,7 @@
 extern int close_handle( struct process *process, obj_handle_t handle, int *fd );
 extern struct object *get_handle_obj( struct process *process, obj_handle_t handle,
                                       unsigned int access, const struct object_ops *ops );
-extern int get_handle_fd( struct process *process, obj_handle_t handle, unsigned int access );
+extern int get_handle_unix_fd( struct process *process, obj_handle_t handle, unsigned int access );
 extern int set_handle_info( struct process *process, obj_handle_t handle, int mask, int flags, int *fd );
 extern obj_handle_t duplicate_handle( struct process *src, obj_handle_t src_handle, struct process *dst,
                                   unsigned int access, int inherit, int options );
diff --git a/server/hook.c b/server/hook.c
index d10318d..b69b1cc 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -67,7 +67,6 @@
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
     no_get_fd,                    /* get_fd */
-    no_get_file_info,             /* get_file_info */
     hook_table_destroy            /* destroy */
 };
 
diff --git a/server/mapping.c b/server/mapping.c
index 4881bc7..f91edcb 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -24,6 +24,7 @@
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "winbase.h"
@@ -48,8 +49,6 @@
     struct mapping *shared_prev;     /* prev in shared PE mapping list */
 };
 
-static struct fd *mapping_get_fd( struct object *obj );
-static int mapping_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static void mapping_dump( struct object *obj, int verbose );
 static void mapping_destroy( struct object *obj );
 
@@ -61,8 +60,7 @@
     NULL,                        /* remove_queue */
     NULL,                        /* signaled */
     NULL,                        /* satisfied */
-    mapping_get_fd,              /* get_fd */
-    mapping_get_info,            /* get_file_info */
+    default_get_fd,              /* get_fd */
     mapping_destroy              /* destroy */
 };
 
@@ -105,11 +103,6 @@
 #define ROUND_SIZE(addr,size) \
    (((int)(size) + ((int)(addr) & page_mask) + page_mask) & ~page_mask)
 
-/* get the fd to use for mmaping a file */
-inline static int get_mmap_fd( struct file *file )
-{
-    return get_unix_fd( (struct object *)file );
-}
 
 /* find the shared PE mapping for a given mapping */
 static struct file *get_shared_file( struct mapping *mapping )
@@ -152,7 +145,7 @@
 
     if (!(mapping->shared_file = create_temp_file( GENERIC_READ|GENERIC_WRITE ))) return 0;
     if (!grow_file( mapping->shared_file, 0, total_size )) goto error;
-    if ((shared_fd = get_mmap_fd( mapping->shared_file )) == -1) goto error;
+    if ((shared_fd = get_file_unix_fd( mapping->shared_file )) == -1) goto error;
 
     if (!(buffer = malloc( max_size ))) goto error;
 
@@ -192,30 +185,33 @@
     IMAGE_DOS_HEADER dos;
     IMAGE_NT_HEADERS nt;
     IMAGE_SECTION_HEADER *sec = NULL;
-    int fd, filepos, size;
+    struct fd *fd;
+    int unix_fd, filepos, size;
 
     /* load the headers */
 
-    if ((fd = get_mmap_fd( mapping->file )) == -1) return 0;
-    filepos = lseek( fd, 0, SEEK_SET );
-    if (read( fd, &dos, sizeof(dos) ) != sizeof(dos)) goto error;
+    if (!(fd = get_obj_fd( (struct object *)mapping->file ))) return 0;
+    mapping->obj.fd_obj = fd;
+    unix_fd = get_unix_fd( fd );
+    filepos = lseek( unix_fd, 0, SEEK_SET );
+    if (read( unix_fd, &dos, sizeof(dos) ) != sizeof(dos)) goto error;
     if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto error;
-    if (lseek( fd, dos.e_lfanew, SEEK_SET ) == -1) goto error;
+    if (lseek( unix_fd, dos.e_lfanew, SEEK_SET ) == -1) goto error;
 
-    if (read( fd, &nt.Signature, sizeof(nt.Signature) ) != sizeof(nt.Signature)) goto error;
+    if (read( unix_fd, &nt.Signature, sizeof(nt.Signature) ) != sizeof(nt.Signature)) goto error;
     if (nt.Signature != IMAGE_NT_SIGNATURE) goto error;
-    if (read( fd, &nt.FileHeader, sizeof(nt.FileHeader) ) != sizeof(nt.FileHeader)) goto error;
+    if (read( unix_fd, &nt.FileHeader, sizeof(nt.FileHeader) ) != sizeof(nt.FileHeader)) goto error;
     /* zero out Optional header in the case it's not present or partial */
     memset(&nt.OptionalHeader, 0, sizeof(nt.OptionalHeader));
-    if (read( fd, &nt.OptionalHeader, nt.FileHeader.SizeOfOptionalHeader) != nt.FileHeader.SizeOfOptionalHeader) goto error;
+    if (read( unix_fd, &nt.OptionalHeader, nt.FileHeader.SizeOfOptionalHeader) != nt.FileHeader.SizeOfOptionalHeader) goto error;
 
     /* load the section headers */
 
     size = sizeof(*sec) * nt.FileHeader.NumberOfSections;
     if (!(sec = malloc( size ))) goto error;
-    if (read( fd, sec, size ) != size) goto error;
+    if (read( unix_fd, sec, size ) != size) goto error;
 
-    if (!build_shared_mapping( mapping, fd, sec, nt.FileHeader.NumberOfSections )) goto error;
+    if (!build_shared_mapping( mapping, unix_fd, sec, nt.FileHeader.NumberOfSections )) goto error;
 
     if (mapping->shared_file)  /* link it in the list */
     {
@@ -233,17 +229,28 @@
     /* sanity check */
     if (mapping->header_size > mapping->size_low) goto error;
 
-    lseek( fd, filepos, SEEK_SET );
+    lseek( unix_fd, filepos, SEEK_SET );
     free( sec );
     return 1;
 
  error:
-    lseek( fd, filepos, SEEK_SET );
+    lseek( unix_fd, filepos, SEEK_SET );
     if (sec) free( sec );
     set_error( STATUS_INVALID_FILE_FOR_SECTION );
     return 0;
 }
 
+/* get the size of the unix file associated with the mapping */
+inline static int get_file_size( struct mapping *mapping, int *size_high, int *size_low )
+{
+    struct stat st;
+    int unix_fd = get_unix_fd( mapping->obj.fd_obj );
+
+    if (fstat( unix_fd, &st ) == -1) return 0;
+    *size_high = st.st_size >> 32;
+    *size_low  = st.st_size & 0xffffffff;
+    return 1;
+}
 
 static struct object *create_mapping( int size_high, int size_low, int protect,
                                       obj_handle_t handle, const WCHAR *name, size_t len )
@@ -276,14 +283,12 @@
         }
         if (!size_high && !size_low)
         {
-            int flags;
-            struct get_file_info_reply reply;
-            struct object *obj = (struct object *)mapping->file;
-            obj->ops->get_file_info( obj, &reply, &flags );
-            size_high = reply.size_high;
-            size_low  = ROUND_SIZE( 0, reply.size_low );
+            if (!get_file_size( mapping, &size_high, &size_low )) goto error;
         }
-        else if (!grow_file( mapping->file, size_high, size_low )) goto error;
+        else
+        {
+            if (!grow_file( mapping->file, size_high, size_low )) goto error;
+        }
     }
     else  /* Anonymous mapping (no associated file) */
     {
@@ -296,6 +301,7 @@
         if (!(mapping->file = create_temp_file( access ))) goto error;
         if (!grow_file( mapping->file, size_high, size_low )) goto error;
     }
+    mapping->obj.fd_obj = get_obj_fd( (struct object *)mapping->file );
     mapping->size_high = size_high;
     mapping->size_low  = ROUND_SIZE( 0, size_low );
     mapping->protect   = protect;
@@ -318,24 +324,6 @@
     fputc( '\n', stderr );
 }
 
-static struct fd *mapping_get_fd( struct object *obj )
-{
-    struct mapping *mapping = (struct mapping *)obj;
-    assert( obj->ops == &mapping_ops );
-
-    return default_get_fd( (struct object *)mapping->file );
-}
-
-static int mapping_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
-{
-    struct mapping *mapping = (struct mapping *)obj;
-    struct object *file = (struct object *)mapping->file;
-
-    assert( obj->ops == &mapping_ops );
-    assert( file );
-    return file->ops->get_file_info( file, reply, flags );
-}
-
 static void mapping_destroy( struct object *obj )
 {
     struct mapping *mapping = (struct mapping *)obj;
diff --git a/server/mutex.c b/server/mutex.c
index 9ca844e..1bb07eb 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -55,7 +55,6 @@
     mutex_signaled,            /* signaled */
     mutex_satisfied,           /* satisfied */
     no_get_fd,                 /* get_fd */
-    no_get_file_info,          /* get_file_info */
     mutex_destroy              /* destroy */
 };
 
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 5eed612..29c7614 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -94,13 +94,12 @@
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
     no_get_fd,                    /* get_fd */
-    no_get_file_info,             /* get_file_info */
     named_pipe_destroy            /* destroy */
 };
 
 static void pipe_user_dump( struct object *obj, int verbose );
 static void pipe_user_destroy( struct object *obj);
-static int pipe_user_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
+static int pipe_user_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
 
 static const struct object_ops pipe_user_ops =
 {
@@ -111,7 +110,6 @@
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
     default_get_fd,               /* get_fd */
-    pipe_user_get_info,           /* get_file_info */
     pipe_user_destroy             /* destroy */
 };
 
@@ -169,7 +167,7 @@
 
     if(user->other)
     {
-        close_fd( user->other->obj.fd_obj );
+        release_object( user->other->obj.fd_obj );
         user->other->obj.fd_obj = NULL;
         switch(user->other->state)
         {
@@ -195,7 +193,7 @@
     release_object(user->pipe);
 }
 
-static int pipe_user_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int pipe_user_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
     if (reply)
     {
@@ -443,12 +441,12 @@
     if( (user->state == ps_connected_server) &&
         (user->other->state == ps_connected_client) )
     {
-        close_fd( user->other->obj.fd_obj );
+        release_object( user->other->obj.fd_obj );
         user->other->obj.fd_obj = NULL;
         user->other->state = ps_disconnected;
         user->other->other = NULL;
 
-        close_fd( user->obj.fd_obj );
+        release_object( user->obj.fd_obj );
         user->obj.fd_obj = NULL;
         user->state = ps_idle_server;
         user->other = NULL;
diff --git a/server/object.c b/server/object.c
index 77e1162..20fa232 100644
--- a/server/object.c
+++ b/server/object.c
@@ -220,7 +220,7 @@
         assert( !obj->head );
         assert( !obj->tail );
         obj->ops->destroy( obj );
-        if (obj->fd_obj) close_fd( obj->fd_obj );
+        if (obj->fd_obj) release_object( obj->fd_obj );
         if (obj->name) free_name( obj );
         if (obj->select != -1) remove_select_user( obj );
         if (obj->fd != -1) close( obj->fd );
@@ -303,21 +303,6 @@
     return NULL;
 }
 
-int no_get_file_info( struct object *obj, struct get_file_info_reply *info, int *flags )
-{
-    set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    *flags = 0;
-    return FD_TYPE_INVALID;
-}
-
 void no_destroy( struct object *obj )
 {
 }
-
-/* default handler for poll() events */
-void default_poll_event( struct object *obj, int event )
-{
-    /* an error occurred, stop polling this fd to avoid busy-looping */
-    if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1 );
-    wake_up( obj, 0 );
-}
diff --git a/server/object.h b/server/object.h
index 234c972..54d082f 100644
--- a/server/object.h
+++ b/server/object.h
@@ -60,8 +60,6 @@
     int  (*satisfied)(struct object *,struct thread *);
     /* return an fd object that can be used to read/write from the object */
     struct fd *(*get_fd)(struct object *);
-    /* get file information */
-    int  (*get_file_info)(struct object *,struct get_file_info_reply *, int *flags);
     /* destroy on refcount == 0 */
     void (*destroy)(struct object *);
 };
@@ -105,9 +103,7 @@
 extern int no_satisfied( struct object *obj, struct thread *thread );
 extern struct fd *no_get_fd( struct object *obj );
 extern struct fd *default_get_fd( struct object *obj );
-extern int no_get_file_info( struct object *obj, struct get_file_info_reply *info, int *flags );
 extern void no_destroy( struct object *obj );
-extern void default_poll_event( struct object *obj, int event );
 #ifdef DEBUG_OBJECTS
 extern void dump_objects(void);
 #endif
@@ -118,7 +114,6 @@
 extern void remove_select_user( struct object *obj );
 extern void change_select_fd( struct object *obj, int fd, int events );
 extern void set_select_events( struct object *obj, int events );
-extern int check_select_events( int fd, int events );
 extern void select_loop(void);
 
 /* timeout functions */
@@ -153,17 +148,6 @@
 
 extern void abandon_mutexes( struct thread *thread );
 
-/* file functions */
-
-extern struct file *get_file_obj( struct process *process, obj_handle_t handle,
-                                  unsigned int access );
-extern int is_same_file( struct file *file1, struct file *file2 );
-extern int get_file_drive_type( struct file *file );
-extern int grow_file( struct file *file, int size_high, int size_low );
-extern int create_anonymous_file(void);
-extern struct file *create_temp_file( int access );
-extern void file_set_error(void);
-
 /* serial functions */
 
 int get_serial_async_timeout(struct object *obj, int type, int count);
diff --git a/server/pipe.c b/server/pipe.c
index 4f57a2f..79074e5 100644
--- a/server/pipe.c
+++ b/server/pipe.c
@@ -47,11 +47,12 @@
 };
 
 static void pipe_dump( struct object *obj, int verbose );
-static int pipe_get_poll_events( struct object *obj );
 static struct fd *pipe_get_fd( struct object *obj );
-static int pipe_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static void pipe_destroy( struct object *obj );
 
+static int pipe_get_poll_events( struct fd *fd );
+static int pipe_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+
 static const struct object_ops pipe_ops =
 {
     sizeof(struct pipe),          /* size */
@@ -61,7 +62,6 @@
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
     pipe_get_fd,                  /* get_fd */
-    pipe_get_info,                /* get_file_info */
     pipe_destroy                  /* destroy */
 };
 
@@ -118,14 +118,14 @@
 {
     struct pipe *pipe = (struct pipe *)obj;
     assert( obj->ops == &pipe_ops );
-    fprintf( stderr, "Pipe %s-side fd=%d\n",
-             (pipe->side == READ_SIDE) ? "read" : "write", pipe->obj.fd );
+    fprintf( stderr, "Pipe %s-side fd=%p\n",
+             (pipe->side == READ_SIDE) ? "read" : "write", pipe->obj.fd_obj );
 }
 
-static int pipe_get_poll_events( struct object *obj )
+static int pipe_get_poll_events( struct fd *fd )
 {
-    struct pipe *pipe = (struct pipe *)obj;
-    assert( obj->ops == &pipe_ops );
+    struct pipe *pipe = get_fd_user( fd );
+    assert( pipe->obj.ops == &pipe_ops );
     return (pipe->side == READ_SIDE) ? POLLIN : POLLOUT;
 }
 
@@ -142,7 +142,7 @@
     return (struct fd *)grab_object( pipe->obj.fd_obj );
 }
 
-static int pipe_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int pipe_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
     if (reply)
     {
diff --git a/server/process.c b/server/process.c
index ba0e19a..5d808dc 100644
--- a/server/process.c
+++ b/server/process.c
@@ -52,7 +52,7 @@
 
 static void process_dump( struct object *obj, int verbose );
 static int process_signaled( struct object *obj, struct thread *thread );
-static void process_poll_event( struct object *obj, int event );
+static void process_poll_event( struct fd *fd, int event );
 static void process_destroy( struct object *obj );
 
 static const struct object_ops process_ops =
@@ -64,7 +64,6 @@
     process_signaled,            /* signaled */
     no_satisfied,                /* satisfied */
     no_get_fd,                   /* get_fd */
-    no_get_file_info,            /* get_file_info */
     process_destroy              /* destroy */
 };
 
@@ -109,7 +108,6 @@
     startup_info_signaled,         /* signaled */
     no_satisfied,                  /* satisfied */
     no_get_fd,                     /* get_fd */
-    no_get_file_info,              /* get_file_info */
     startup_info_destroy           /* destroy */
 };
 
@@ -434,12 +432,12 @@
 }
 
 
-static void process_poll_event( struct object *obj, int event )
+static void process_poll_event( struct fd *fd, int event )
 {
-    struct process *process = (struct process *)obj;
-    assert( obj->ops == &process_ops );
+    struct process *process = get_fd_user( fd );
+    assert( process->obj.ops == &process_ops );
 
-    if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1 );
+    if (event & (POLLERR | POLLHUP)) set_select_events( &process->obj, -1 );
     else if (event & POLLIN) receive_fd( process );
 }
 
diff --git a/server/protocol.def b/server/protocol.def
index 7a17949..bab120e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1114,6 +1114,14 @@
 @END
 
 
+/* Retrieve the client private id for a device */
+@REQ(get_device_id)
+    obj_handle_t handle;        /* handle to the device */
+@REPLY
+    int          id;            /* client private id */
+@END
+
+
 #define SNAP_HEAPLIST   0x00000001
 #define SNAP_PROCESS    0x00000002
 #define SNAP_THREAD     0x00000004
diff --git a/server/ptrace.c b/server/ptrace.c
index a74dad1..2c0c12b 100644
--- a/server/ptrace.c
+++ b/server/ptrace.c
@@ -33,10 +33,10 @@
 #endif
 #include <unistd.h>
 
+#include "file.h"
 #include "process.h"
 #include "thread.h"
 
-
 #ifndef PTRACE_CONT
 #define PTRACE_CONT PT_CONTINUE
 #endif
diff --git a/server/queue.c b/server/queue.c
index 39c0d0e..32caefe 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -144,7 +144,6 @@
     msg_queue_signaled,        /* signaled */
     msg_queue_satisfied,       /* satisfied */
     no_get_fd,                 /* get_fd */
-    no_get_file_info,          /* get_file_info */
     msg_queue_destroy          /* destroy */
 };
 
@@ -158,7 +157,6 @@
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
     no_get_fd,                    /* get_fd */
-    no_get_file_info,             /* get_file_info */
     thread_input_destroy          /* destroy */
 };
 
diff --git a/server/registry.c b/server/registry.c
index 5b75b50..2262f59 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -169,7 +169,6 @@
     NULL,                    /* signaled */
     NULL,                    /* satisfied */
     no_get_fd,               /* get_fd */
-    no_get_file_info,        /* get_file_info */
     key_destroy              /* destroy */
 };
 
@@ -1446,12 +1445,12 @@
 /* load a part of the registry from a file */
 static void load_registry( struct key *key, obj_handle_t handle )
 {
-    struct object *obj;
+    struct file *file;
     int fd;
 
-    if (!(obj = get_handle_obj( current->process, handle, GENERIC_READ, NULL ))) return;
-    fd = dup( get_unix_fd( obj ) );
-    release_object( obj );
+    if (!(file = get_file_obj( current->process, handle, GENERIC_READ ))) return;
+    fd = dup( get_file_unix_fd( file ) );
+    release_object( file );
     if (fd != -1)
     {
         FILE *f = fdopen( fd, "r" );
@@ -1536,7 +1535,7 @@
 /* save a registry branch to a file handle */
 static void save_registry( struct key *key, obj_handle_t handle )
 {
-    struct object *obj;
+    struct file *file;
     int fd;
 
     if (key->flags & KEY_DELETED)
@@ -1544,9 +1543,9 @@
         set_error( STATUS_KEY_DELETED );
         return;
     }
-    if (!(obj = get_handle_obj( current->process, handle, GENERIC_WRITE, NULL ))) return;
-    fd = dup( get_unix_fd( obj ) );
-    release_object( obj );
+    if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE ))) return;
+    fd = dup( get_file_unix_fd( file ) );
+    release_object( file );
     if (fd != -1)
     {
         FILE *f = fdopen( fd, "w" );
diff --git a/server/request.c b/server/request.c
index 5411313..d13c761 100644
--- a/server/request.c
+++ b/server/request.c
@@ -78,7 +78,7 @@
 };
 
 static void master_socket_dump( struct object *obj, int verbose );
-static void master_socket_poll_event( struct object *obj, int event );
+static void master_socket_poll_event( struct fd *fd, int event );
 
 static const struct object_ops master_socket_ops =
 {
@@ -89,7 +89,6 @@
     NULL,                          /* signaled */
     NULL,                          /* satisfied */
     no_get_fd,                     /* get_fd */
-    no_get_file_info,              /* get_file_info */
     no_destroy                     /* destroy */
 };
 
@@ -446,14 +445,14 @@
 {
     struct master_socket *sock = (struct master_socket *)obj;
     assert( obj->ops == &master_socket_ops );
-    fprintf( stderr, "Master socket fd=%d\n", sock->obj.fd );
+    fprintf( stderr, "Master socket fd=%p\n", sock->obj.fd_obj );
 }
 
 /* handle a socket event */
-static void master_socket_poll_event( struct object *obj, int event )
+static void master_socket_poll_event( struct fd *fd, int event )
 {
-    struct master_socket *sock = (struct master_socket *)obj;
-    assert( obj->ops == &master_socket_ops );
+    struct master_socket *sock = get_fd_user( fd );
+    assert( master_socket->obj.ops == &master_socket_ops );
 
     assert( sock == master_socket );  /* there is only one master socket */
 
@@ -461,7 +460,7 @@
     {
         /* this is not supposed to happen */
         fprintf( stderr, "wineserver: Error on master socket\n" );
-        release_object( obj );
+        release_object( sock );
     }
     else if (event & POLLIN)
     {
@@ -749,7 +748,7 @@
     flush_registry();
 
     /* if a new client is waiting, we keep on running */
-    if (check_select_events( master_socket->obj.fd, POLLIN )) return;
+    if (check_fd_events( master_socket->obj.fd_obj, POLLIN )) return;
 
     if (debug_level) fprintf( stderr, "wineserver: exiting (pid=%ld)\n", (long) getpid() );
 
diff --git a/server/request.h b/server/request.h
index 01bb230..c0983fa 100644
--- a/server/request.h
+++ b/server/request.h
@@ -181,6 +181,7 @@
 DECL_HANDLER(open_mapping);
 DECL_HANDLER(get_mapping_info);
 DECL_HANDLER(create_device);
+DECL_HANDLER(get_device_id);
 DECL_HANDLER(create_snapshot);
 DECL_HANDLER(next_process);
 DECL_HANDLER(next_thread);
@@ -360,6 +361,7 @@
     (req_handler)req_open_mapping,
     (req_handler)req_get_mapping_info,
     (req_handler)req_create_device,
+    (req_handler)req_get_device_id,
     (req_handler)req_create_snapshot,
     (req_handler)req_next_process,
     (req_handler)req_next_thread,
diff --git a/server/select.c b/server/select.c
index 324ec20..f02e917 100644
--- a/server/select.c
+++ b/server/select.c
@@ -138,15 +138,6 @@
     else if (pollfd[user].fd != -1) pollfd[user].events = events;
 }
 
-/* check if events are pending */
-int check_select_events( int fd, int events )
-{
-    struct pollfd pfd;
-    pfd.fd     = fd;
-    pfd.events = events;
-    return poll( &pfd, 1, 0 ) > 0;
-}
-
 /* add a timeout user */
 struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
 {
diff --git a/server/semaphore.c b/server/semaphore.c
index 3f5e953..99f41f3 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -51,7 +51,6 @@
     semaphore_signaled,            /* signaled */
     semaphore_satisfied,           /* satisfied */
     no_get_fd,                     /* get_fd */
-    no_get_file_info,              /* get_file_info */
     no_destroy                     /* destroy */
 };
 
diff --git a/server/serial.c b/server/serial.c
index 998d9b6..06b07cb 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -52,12 +52,13 @@
 #include "async.h"
 
 static void serial_dump( struct object *obj, int verbose );
-static int serial_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
-static int serial_get_poll_events( struct object *obj );
-static void serial_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);
-static void destroy_serial(struct object *obj);
-static void serial_poll_event( struct object *obj, int event );
-static int serial_flush( struct object *obj );
+static void serial_destroy(struct object *obj);
+
+static int serial_get_poll_events( struct fd *fd );
+static void serial_poll_event( struct fd *fd, int event );
+static int serial_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+static int serial_flush( struct fd *fd );
+static void serial_queue_async(struct fd *fd, void *ptr, unsigned int status, int type, int count);
 
 struct serial
 {
@@ -93,8 +94,7 @@
     default_fd_signaled,          /* signaled */
     no_satisfied,                 /* satisfied */
     default_get_fd,               /* get_fd */
-    serial_get_info,              /* get_file_info */
-    destroy_serial                /* destroy */
+    serial_destroy                /* destroy */
 };
 
 static const struct fd_ops serial_fd_ops =
@@ -166,7 +166,7 @@
     return serial;
 }
 
-static void destroy_serial( struct object *obj)
+static void serial_destroy( struct object *obj)
 {
     struct serial *serial = (struct serial *)obj;
 
@@ -179,7 +179,7 @@
 {
     struct serial *serial = (struct serial *)obj;
     assert( obj->ops == &serial_ops );
-    fprintf( stderr, "Port fd=%d mask=%x\n", serial->obj.fd, serial->eventmask );
+    fprintf( stderr, "Port fd=%p mask=%x\n", serial->obj.fd_obj, serial->eventmask );
 }
 
 struct serial *get_serial_obj( struct process *process, obj_handle_t handle, unsigned int access )
@@ -187,11 +187,11 @@
     return (struct serial *)get_handle_obj( process, handle, access, &serial_ops );
 }
 
-static int serial_get_poll_events( struct object *obj )
+static int serial_get_poll_events( struct fd *fd )
 {
-    struct serial *serial = (struct serial *)obj;
+    struct serial *serial = get_fd_user( fd );
     int events = 0;
-    assert( obj->ops == &serial_ops );
+    assert( serial->obj.ops == &serial_ops );
 
     if(IS_READY(serial->read_q))
         events |= POLLIN;
@@ -205,10 +205,10 @@
     return events;
 }
 
-static int serial_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int serial_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
-    struct serial *serial = (struct serial *) obj;
-    assert( obj->ops == &serial_ops );
+    struct serial *serial = get_fd_user( fd );
+    assert( serial->obj.ops == &serial_ops );
 
     if (reply)
     {
@@ -234,9 +234,9 @@
     return FD_TYPE_DEFAULT;
 }
 
-static void serial_poll_event(struct object *obj, int event)
+static void serial_poll_event(struct fd *fd, int event)
 {
-    struct serial *serial = (struct serial *)obj;
+    struct serial *serial = get_fd_user( fd );
 
     /* fprintf(stderr,"Poll event %02x\n",event); */
 
@@ -249,17 +249,17 @@
     if(IS_READY(serial->wait_q) && (POLLIN & event) )
         async_notify(serial->wait_q.head,STATUS_ALERTED);
 
-    set_select_events( obj, serial_get_poll_events(obj) );
+    set_select_events( &serial->obj, serial_get_poll_events(fd) );
 }
 
-static void serial_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
+static void serial_queue_async(struct fd *fd, void *ptr, unsigned int status, int type, int count)
 {
-    struct serial *serial = (struct serial *)obj;
+    struct serial *serial = get_fd_user( fd );
     struct async_queue *q;
     struct async *async;
     int timeout;
 
-    assert(obj->ops == &serial_ops);
+    assert(serial->obj.ops == &serial_ops);
 
     switch(type)
     {
@@ -284,10 +284,10 @@
 
     if ( status == STATUS_PENDING )
     {
-        struct pollfd pfd;
+        int events;
 
         if ( !async )
-            async = create_async ( obj, current, ptr );
+            async = create_async ( &serial->obj, current, ptr );
         if ( !async )
             return;
 
@@ -299,30 +299,26 @@
         }
 
         /* Check if the new pending request can be served immediately */
-        pfd.fd = get_unix_fd( obj );
-        pfd.events = serial_get_poll_events ( obj );
-        pfd.revents = 0;
-        poll ( &pfd, 1, 0 );
-
-        if ( pfd.revents )
+        events = check_fd_events( fd, serial_get_poll_events( fd ) );
+        if (events)
+        {
             /* serial_poll_event() calls set_select_events() */
-            serial_poll_event ( obj, pfd.revents );
-        else
-            set_select_events ( obj, pfd.events );
-        return;
+            serial_poll_event( fd, events );
+            return;
+        }
     }
     else if ( async ) destroy_async ( async );
     else set_error ( STATUS_INVALID_PARAMETER );
 
-    set_select_events ( obj, serial_get_poll_events ( obj ));
+    set_select_events ( &serial->obj, serial_get_poll_events( fd ));
 }
 
-static int serial_flush( struct object *obj )
+static int serial_flush( struct fd *fd )
 {
     /* MSDN says: If hFile is a handle to a communications device,
      * the function only flushes the transmit buffer.
      */
-    int ret = (tcflush( get_unix_fd(obj), TCOFLUSH ) != -1);
+    int ret = (tcflush( get_unix_fd(fd), TCOFLUSH ) != -1);
     if (!ret) file_set_error();
     return ret;
 }
diff --git a/server/smb.c b/server/smb.c
index c3ab30b..4bb7774 100644
--- a/server/smb.c
+++ b/server/smb.c
@@ -52,9 +52,10 @@
 #include "request.h"
 
 static void smb_dump( struct object *obj, int verbose );
-static int smb_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
-static int smb_get_poll_events( struct object *obj );
-static void destroy_smb(struct object *obj);
+static void smb_destroy(struct object *obj);
+
+static int smb_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+static int smb_get_poll_events( struct fd *fd );
 
 struct smb
 {
@@ -75,8 +76,7 @@
     default_fd_signaled,       /* signaled */
     no_satisfied,              /* satisfied */
     default_get_fd,            /* get_fd */
-    smb_get_info,              /* get_file_info */
-    destroy_smb                /* destroy */
+    smb_destroy                /* destroy */
 };
 
 static const struct fd_ops smb_fd_ops =
@@ -88,7 +88,7 @@
     no_queue_async             /* queue_async */
 };
 
-static void destroy_smb( struct object *obj)
+static void smb_destroy( struct object *obj)
 {
     /* struct smb *smb = (struct smb *)obj; */
     assert( obj->ops == &smb_ops );
@@ -98,7 +98,7 @@
 {
     struct smb *smb = (struct smb *)obj;
     assert( obj->ops == &smb_ops );
-    fprintf( stderr, "smb file with socket fd=%d \n", smb->obj.fd );
+    fprintf( stderr, "Smb file fd=%p\n", smb->obj.fd_obj );
 }
 
 struct smb *get_smb_obj( struct process *process, obj_handle_t handle, unsigned int access )
@@ -106,11 +106,11 @@
     return (struct smb *)get_handle_obj( process, handle, access, &smb_ops );
 }
 
-static int smb_get_poll_events( struct object *obj )
+static int smb_get_poll_events( struct fd *fd )
 {
-    /* struct smb *smb = (struct smb *)obj; */
+    struct smb *smb = get_fd_user( fd );
     int events = 0;
-    assert( obj->ops == &smb_ops );
+    assert( smb->obj.ops == &smb_ops );
 
     events |= POLLIN;
 
@@ -119,10 +119,10 @@
     return events;
 }
 
-static int smb_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int smb_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
-    /* struct smb *smb = (struct smb *) obj; */
-    assert( obj->ops == &smb_ops );
+/*    struct smb *smb = get_fd_user( fd ); */
+/*    assert( smb->obj.ops == &smb_ops ); */
 
     if (reply)
     {
diff --git a/server/snapshot.c b/server/snapshot.c
index 90a8bf4..c1284a0 100644
--- a/server/snapshot.c
+++ b/server/snapshot.c
@@ -62,7 +62,6 @@
     NULL,                         /* signaled */
     NULL,                         /* satisfied */
     no_get_fd,                    /* get_fd */
-    no_get_file_info,             /* get_file_info */
     snapshot_destroy              /* destroy */
 };
 
diff --git a/server/sock.c b/server/sock.c
index 3a4e514..44e0088 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -84,13 +84,15 @@
 
 static void sock_dump( struct object *obj, int verbose );
 static int sock_signaled( struct object *obj, struct thread *thread );
-static int sock_get_poll_events( struct object *obj );
-static void sock_poll_event( struct object *obj, int event );
-static int sock_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static void sock_destroy( struct object *obj );
+
+static int sock_get_poll_events( struct fd *fd );
+static void sock_poll_event( struct fd *fd, int event );
+static int sock_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+static void sock_queue_async( struct fd *fd, void *ptr, unsigned int status, int type, int count );
+
 static int sock_get_error( int err );
 static void sock_set_error(void);
-static void sock_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);
 
 static const struct object_ops sock_ops =
 {
@@ -101,7 +103,6 @@
     sock_signaled,                /* signaled */
     no_satisfied,                 /* satisfied */
     default_get_fd,               /* get_fd */
-    sock_get_info,                /* get_file_info */
     sock_destroy                  /* destroy */
 };
 
@@ -192,10 +193,10 @@
 
 static int sock_reselect( struct sock *sock )
 {
-    int ev = sock_get_poll_events( &sock->obj );
+    int ev = sock_get_poll_events( sock->obj.fd_obj );
 
     if (debug_level)
-        fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->obj.fd, ev);
+        fprintf(stderr,"sock_reselect(%p): new mask %x\n", sock, ev);
 
     if (sock->obj.select == -1) {
         /* previously unconnected socket, is this reselect supposed to connect it? */
@@ -212,17 +213,11 @@
    This function is used to signal pending events nevertheless */
 static void sock_try_event ( struct sock *sock, int event )
 {
-    struct pollfd pfd;
-
-    pfd.fd = sock->obj.fd;
-    pfd.events = event;
-    pfd.revents = 0;
-    poll (&pfd, 1, 0);
-
-    if ( pfd.revents )
+    event = check_fd_events( sock->obj.fd_obj, event );
+    if (event)
     {
-        if ( debug_level ) fprintf ( stderr, "sock_try_event: %x\n", pfd.revents );
-        sock_poll_event ( &sock->obj, pfd.revents );
+        if ( debug_level ) fprintf ( stderr, "sock_try_event: %x\n", event );
+        sock_poll_event ( sock->obj.fd_obj, event );
     }
 }
 
@@ -275,23 +270,23 @@
     }
 }
 
-inline static int sock_error(int s)
+inline static int sock_error( struct fd *fd )
 {
     unsigned int optval = 0, optlen;
 
     optlen = sizeof(optval);
-    getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen);
+    getsockopt( get_unix_fd(fd), SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen);
     return optval ? sock_get_error(optval) : 0;
 }
 
-static void sock_poll_event( struct object *obj, int event )
+static void sock_poll_event( struct fd *fd, int event )
 {
-    struct sock *sock = (struct sock *)obj;
+    struct sock *sock = get_fd_user( fd );
     int hangup_seen = 0;
 
     assert( sock->obj.ops == &sock_ops );
     if (debug_level)
-        fprintf(stderr, "socket %d select event: %x\n", sock->obj.fd, event);
+        fprintf(stderr, "socket %p select event: %x\n", sock, event);
     if (sock->state & FD_CONNECT)
     {
         /* connecting */
@@ -303,16 +298,16 @@
             sock->pmask |= FD_CONNECT;
             sock->errors[FD_CONNECT_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d connection success\n", sock->obj.fd);
+                fprintf(stderr, "socket %p connection success\n", sock);
         }
         else if (event & (POLLERR|POLLHUP))
         {
             /* we didn't get connected? */
             sock->state &= ~FD_CONNECT;
             sock->pmask |= FD_CONNECT;
-            sock->errors[FD_CONNECT_BIT] = sock_error( sock->obj.fd );
+            sock->errors[FD_CONNECT_BIT] = sock_error( fd );
             if (debug_level)
-                fprintf(stderr, "socket %d connection failure\n", sock->obj.fd);
+                fprintf(stderr, "socket %p connection failure\n", sock);
         }
     } else
     if (sock->state & FD_WINE_LISTENING)
@@ -329,7 +324,7 @@
         {
             /* failed incoming connection? */
             sock->pmask |= FD_ACCEPT;
-            sock->errors[FD_ACCEPT_BIT] = sock_error( sock->obj.fd );
+            sock->errors[FD_ACCEPT_BIT] = sock_error( fd );
             sock->hmask |= FD_ACCEPT;
         }
     } else
@@ -342,7 +337,7 @@
 
             /* Linux 2.4 doesn't report POLLHUP if only one side of the socket
              * has been closed, so we need to check for it explicitly here */
-            nr  = recv( sock->obj.fd, &dummy, 1, MSG_PEEK );
+            nr  = recv( get_unix_fd( fd ), &dummy, 1, MSG_PEEK );
             if ( nr > 0 )
             {
                 /* incoming data */
@@ -350,7 +345,7 @@
                 sock->hmask |= (FD_READ|FD_CLOSE);
                 sock->errors[FD_READ_BIT] = 0;
                 if (debug_level)
-                    fprintf(stderr, "socket %d is readable\n", sock->obj.fd );
+                    fprintf(stderr, "socket %p is readable\n", sock );
             }
             else if ( nr == 0 )
                 hangup_seen = 1;
@@ -363,7 +358,7 @@
                 else
                 {
                     if ( debug_level )
-                        fprintf ( stderr, "recv error on socket %d: %d\n", sock->obj.fd, errno );
+                        fprintf ( stderr, "recv error on socket %p: %d\n", sock, errno );
                     event = POLLERR;
                 }
             }
@@ -379,7 +374,7 @@
             sock->hmask |= (FD_READ|FD_CLOSE);
             sock->errors[FD_READ_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d is readable\n", sock->obj.fd );
+                fprintf(stderr, "socket %p is readable\n", sock );
 
         }
 
@@ -389,7 +384,7 @@
             sock->hmask |= FD_WRITE;
             sock->errors[FD_WRITE_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d is writable\n", sock->obj.fd);
+                fprintf(stderr, "socket %p is writable\n", sock);
         }
         if (event & POLLPRI)
         {
@@ -397,27 +392,27 @@
             sock->hmask |= FD_OOB;
             sock->errors[FD_OOB_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d got OOB data\n", sock->obj.fd);
+                fprintf(stderr, "socket %p got OOB data\n", sock);
         }
         /* According to WS2 specs, FD_CLOSE is only delivered when there is
            no more data to be read (i.e. hangup_seen = 1) */
         else if ( hangup_seen && (sock->state & (FD_READ|FD_WRITE) ))
         {
-            sock->errors[FD_CLOSE_BIT] = sock_error( sock->obj.fd );
+            sock->errors[FD_CLOSE_BIT] = sock_error( fd );
             if ( (event & POLLERR) || ( sock_shutdown_type == SOCK_SHUTDOWN_EOF && (event & POLLHUP) ))
                 sock->state &= ~FD_WRITE;
             sock->pmask |= FD_CLOSE;
             sock->hmask |= FD_CLOSE;
             if (debug_level)
-                fprintf(stderr, "socket %d aborted by error %d, event: %x - removing from select loop\n",
-                        sock->obj.fd, sock->errors[FD_CLOSE_BIT], event);
+                fprintf(stderr, "socket %p aborted by error %d, event: %x - removing from select loop\n",
+                        sock, sock->errors[FD_CLOSE_BIT], event);
         }
     }
 
     if ( sock->pmask & FD_CLOSE || event & (POLLERR|POLLHUP) )
     {
         if ( debug_level )
-            fprintf ( stderr, "removing socket %d from select loop\n", sock->obj.fd );
+            fprintf ( stderr, "removing socket %p from select loop\n", sock );
         set_select_events( &sock->obj, -1 );
     }
     else
@@ -435,8 +430,8 @@
 {
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
-    printf( "Socket fd=%d, state=%x, mask=%x, pending=%x, held=%x\n",
-            sock->obj.fd, sock->state,
+    printf( "Socket fd=%p, state=%x, mask=%x, pending=%x, held=%x\n",
+            sock->obj.fd_obj, sock->state,
             sock->mask, sock->pmask, sock->hmask );
 }
 
@@ -445,16 +440,16 @@
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
 
-    return check_select_events( get_unix_fd(obj), sock_get_poll_events( &sock->obj ) );
+    return check_fd_events( sock->obj.fd_obj, sock_get_poll_events( sock->obj.fd_obj ) ) != 0;
 }
 
-static int sock_get_poll_events( struct object *obj )
+static int sock_get_poll_events( struct fd *fd )
 {
-    struct sock *sock = (struct sock *)obj;
+    struct sock *sock = get_fd_user( fd );
     unsigned int mask = sock->mask & sock->state & ~sock->hmask;
     int ev = 0;
 
-    assert( obj->ops == &sock_ops );
+    assert( sock->obj.ops == &sock_ops );
 
     if (sock->state & FD_CONNECT)
         /* connecting, wait for writable */
@@ -474,10 +469,10 @@
     return ev;
 }
 
-static int sock_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+static int sock_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
 {
-    struct sock *sock = (struct sock*) obj;
-    assert ( obj->ops == &sock_ops );
+    struct sock *sock = get_fd_user( fd );
+    assert ( sock->obj.ops == &sock_ops );
 
     if (reply)
     {
@@ -502,14 +497,14 @@
     return FD_TYPE_SOCKET;
 }
 
-static void sock_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
+static void sock_queue_async(struct fd *fd, void *ptr, unsigned int status, int type, int count)
 {
-    struct sock *sock = (struct sock *)obj;
+    struct sock *sock = get_fd_user( fd );
     struct async_queue *q;
     struct async *async;
     int pollev;
 
-    assert( obj->ops == &sock_ops );
+    assert( sock->obj.ops == &sock_ops );
 
     if ( !(sock->flags & WSA_FLAG_OVERLAPPED) )
     {
@@ -544,7 +539,7 @@
         else
         {
             if ( !async )
-                async = create_async ( obj, current, ptr );
+                async = create_async ( &sock->obj, current, ptr );
             if ( !async )
                 return;
 
@@ -640,7 +635,7 @@
          * return.
          */
         slen = sizeof(saddr);
-        acceptfd = accept( get_unix_fd(&sock->obj), &saddr, &slen);
+        acceptfd = accept( get_unix_fd(sock->obj.fd_obj), &saddr, &slen);
         if (acceptfd==-1) {
             sock_set_error();
             release_object( sock );
diff --git a/server/thread.c b/server/thread.c
index a649da3..8b3ca12 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -75,7 +75,7 @@
 
 static void dump_thread( struct object *obj, int verbose );
 static int thread_signaled( struct object *obj, struct thread *thread );
-static void thread_poll_event( struct object *obj, int event );
+static void thread_poll_event( struct fd *fd, int event );
 static void destroy_thread( struct object *obj );
 static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only );
 
@@ -88,7 +88,6 @@
     thread_signaled,            /* signaled */
     no_satisfied,               /* satisfied */
     no_get_fd,                  /* get_fd */
-    no_get_file_info,           /* get_file_info */
     destroy_thread              /* destroy */
 };
 
@@ -179,10 +178,10 @@
 }
 
 /* handle a client event */
-static void thread_poll_event( struct object *obj, int event )
+static void thread_poll_event( struct fd *fd, int event )
 {
-    struct thread *thread = (struct thread *)obj;
-    assert( obj->ops == &thread_ops );
+    struct thread *thread = get_fd_user( fd );
+    assert( thread->obj.ops == &thread_ops );
 
     if (event & (POLLERR | POLLHUP)) kill_thread( thread, 0 );
     else if (event & POLLIN) read_request( thread );
diff --git a/server/timer.c b/server/timer.c
index 2d48fc6..22e4e79 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -58,7 +58,6 @@
     timer_signaled,            /* signaled */
     timer_satisfied,           /* satisfied */
     no_get_fd,                 /* get_fd */
-    no_get_file_info,          /* get_file_info */
     timer_destroy              /* destroy */
 };
 
diff --git a/server/trace.c b/server/trace.c
index 5069534..7611f7e 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1289,6 +1289,16 @@
     fprintf( stderr, " handle=%p", req->handle );
 }
 
+static void dump_get_device_id_request( const struct get_device_id_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_get_device_id_reply( const struct get_device_id_reply *req )
+{
+    fprintf( stderr, " id=%d", req->id );
+}
+
 static void dump_create_snapshot_request( const struct create_snapshot_request *req )
 {
     fprintf( stderr, " inherit=%d,", req->inherit );
@@ -2468,6 +2478,7 @@
     (dump_func)dump_open_mapping_request,
     (dump_func)dump_get_mapping_info_request,
     (dump_func)dump_create_device_request,
+    (dump_func)dump_get_device_id_request,
     (dump_func)dump_create_snapshot_request,
     (dump_func)dump_next_process_request,
     (dump_func)dump_next_thread_request,
@@ -2644,6 +2655,7 @@
     (dump_func)dump_open_mapping_reply,
     (dump_func)dump_get_mapping_info_reply,
     (dump_func)dump_create_device_reply,
+    (dump_func)dump_get_device_id_reply,
     (dump_func)dump_create_snapshot_reply,
     (dump_func)dump_next_process_reply,
     (dump_func)dump_next_thread_reply,
@@ -2820,6 +2832,7 @@
     "open_mapping",
     "get_mapping_info",
     "create_device",
+    "get_device_id",
     "create_snapshot",
     "next_process",
     "next_thread",
diff --git a/win32/device.c b/win32/device.c
index 2886dc3..860a87f 100644
--- a/win32/device.c
+++ b/win32/device.c
@@ -359,11 +359,10 @@
 static DWORD DEVICE_GetClientID( HANDLE handle )
 {
     DWORD       ret = 0;
-    SERVER_START_REQ( get_file_info )
+    SERVER_START_REQ( get_device_id )
     {
         req->handle = handle;
-        if (!wine_server_call( req ) && (reply->type == FILE_TYPE_UNKNOWN))
-            ret = reply->attr;
+        if (!wine_server_call( req )) ret = reply->id;
     }
     SERVER_END_REQ;
     return ret;