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/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 );
     }
 }