Put network includes in sockets.h
[mono.git] / mono / io-layer / io.c
index f09d4a5bbd7dba7a2687bbc258c10f3820a0b005..17856a8f6fe41bfc1b0a6fcae826bf1dca1635e8 100644 (file)
@@ -1,3 +1,12 @@
+/*
+ * io.c:  File, console and find handles
+ *
+ * Author:
+ *     Dick Porter (dick@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
 #include <config.h>
 #include <glib.h>
 #include <fcntl.h>
 #include <sys/poll.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <glob.h>
+#include <stdio.h>
 #include <utime.h>
 
-#include "mono/io-layer/wapi.h"
-#include "unicode.h"
-#include "wapi-private.h"
+#include <mono/io-layer/wapi.h>
+#include <mono/io-layer/unicode.h>
+#include <mono/io-layer/wapi-private.h>
+#include <mono/io-layer/handles-private.h>
+#include <mono/io-layer/io-private.h>
 
 #undef DEBUG
 #define ACTUALLY_DO_UNICODE
 
-/* Currently used for both FILE and CONSOLE handle types.  This may
- * have to change in future.
- */
-struct _WapiHandle_file
-{
-       WapiHandle handle;
-       int fd;
-       guchar *filename;
-       WapiSecurityAttributes *security_attributes;
-       guint32 fileaccess;
-       guint32 sharemode;
-       guint32 attrs;
-};
-
-static void file_close(WapiHandle *handle);
+static void file_close_shared (gpointer handle);
+static void file_close_private (gpointer handle);
 static WapiFileType file_getfiletype(void);
-static gboolean file_read(WapiHandle *handle, gpointer buffer,
+static gboolean file_read(gpointer handle, gpointer buffer,
                          guint32 numbytes, guint32 *bytesread,
                          WapiOverlapped *overlapped);
-static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
+static gboolean file_write(gpointer handle, gconstpointer buffer,
                           guint32 numbytes, guint32 *byteswritten,
                           WapiOverlapped *overlapped);
-static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
+static gboolean file_flush(gpointer handle);
+static guint32 file_seek(gpointer handle, gint32 movedistance,
                         gint32 *highmovedistance, WapiSeekMethod method);
-static gboolean file_setendoffile(WapiHandle *handle);
-static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize);
-static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
+static gboolean file_setendoffile(gpointer handle);
+static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
+static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
                                 WapiFileTime *last_access,
                                 WapiFileTime *last_write);
-static gboolean file_setfiletime(WapiHandle *handle,
+static gboolean file_setfiletime(gpointer handle,
                                 const WapiFileTime *create_time,
                                 const WapiFileTime *last_access,
                                 const WapiFileTime *last_write);
 
 /* File handle is only signalled for overlapped IO */
-static struct _WapiHandleOps file_ops = {
-       file_close,             /* close */
-       file_getfiletype,       /* getfiletype */
-       file_read,              /* readfile */
-       file_write,             /* writefile */
-       file_seek,              /* seek */
-       file_setendoffile,      /* setendoffile */
-       file_getfilesize,       /* getfilesize */
-       file_getfiletime,       /* getfiletime */
-       file_setfiletime,       /* setfiletime */
-       NULL,                   /* wait */
-       NULL,                   /* wait_multiple */
+struct _WapiHandleOps _wapi_file_ops = {
+       file_close_shared,      /* close_shared */
+       file_close_private,     /* close_private */
        NULL,                   /* signal */
+       NULL,                   /* own */
+       NULL,                   /* is_owned */
 };
 
+static void console_close_shared (gpointer handle);
+static void console_close_private (gpointer handle);
 static WapiFileType console_getfiletype(void);
+static gboolean console_read(gpointer handle, gpointer buffer,
+                            guint32 numbytes, guint32 *bytesread,
+                            WapiOverlapped *overlapped);
+static gboolean console_write(gpointer handle, gconstpointer buffer,
+                             guint32 numbytes, guint32 *byteswritten,
+                             WapiOverlapped *overlapped);
+static gboolean console_flush(gpointer handle);
 
 /* Console is mostly the same as file, except it can block waiting for
  * input or output
  */
-static struct _WapiHandleOps console_ops = {
-       file_close,             /* close */
-       console_getfiletype,    /* getfiletype */
-       file_read,              /* readfile */
-       file_write,             /* writefile */
-       NULL,                   /* seek */
-       NULL,                   /* setendoffile */
-       NULL,                   /* getfilesize */
-       NULL,                   /* getfiletime */
-       NULL,                   /* setfiletime */
-       NULL,                   /* FIXME: wait */
-       NULL,                   /* FIXME: wait_multiple */
+struct _WapiHandleOps _wapi_console_ops = {
+       console_close_shared,   /* close_shared */
+       console_close_private,  /* close_private */
+       NULL,                   /* signal */
+       NULL,                   /* own */
+       NULL,                   /* is_owned */
+};
+
+/* Find handle has no ops.
+ */
+struct _WapiHandleOps _wapi_find_ops = {
+       NULL,                   /* close */
+       NULL,                   /* getfiletype */
        NULL,                   /* signal */
+       NULL,                   /* own */
+       NULL,                   /* is_owned */
+};
+
+static struct {
+       /* File, console and pipe handles */
+       WapiFileType (*getfiletype)(void);
+       
+       /* File and console handles */
+       gboolean (*readfile)(gpointer handle, gpointer buffer,
+                            guint32 numbytes, guint32 *bytesread,
+                            WapiOverlapped *overlapped);
+       gboolean (*writefile)(gpointer handle, gconstpointer buffer,
+                             guint32 numbytes, guint32 *byteswritten,
+                             WapiOverlapped *overlapped);
+       gboolean (*flushfile)(gpointer handle);
+       
+       /* File handles */
+       guint32 (*seek)(gpointer handle, gint32 movedistance,
+                       gint32 *highmovedistance, WapiSeekMethod method);
+       gboolean (*setendoffile)(gpointer handle);
+       guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
+       gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
+                               WapiFileTime *last_access,
+                               WapiFileTime *last_write);
+       gboolean (*setfiletime)(gpointer handle,
+                               const WapiFileTime *create_time,
+                               const WapiFileTime *last_access,
+                               const WapiFileTime *last_write);
+} io_ops[WAPI_HANDLE_COUNT]={
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* file */
+       {file_getfiletype,
+        file_read, file_write,
+        file_flush, file_seek,
+        file_setendoffile,
+        file_getfilesize,
+        file_getfiletime,
+        file_setfiletime},
+       /* console */
+       {console_getfiletype,
+        console_read,
+        console_write,
+        console_flush,
+        NULL, NULL, NULL, NULL, NULL},
+       /* thread */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* sem */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* mutex */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* event */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* socket (will need at least read and write) */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* find */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* process */
+       {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 };
 
-static void file_close(WapiHandle *handle)
+
+static pthread_once_t io_ops_once=PTHREAD_ONCE_INIT;
+
+static void io_ops_init (void)
+{
+/*     _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
+/*                                         WAPI_HANDLE_CAP_WAIT); */
+/*     _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
+/*                                         WAPI_HANDLE_CAP_WAIT); */
+}
+
+/* Some utility functions.
+ */
+static void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime)
+{
+       guint64 ticks;
+       
+       ticks = ((guint64)timeval * 10000000) + 116444736000000000UL;
+       filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
+       filetime->dwHighDateTime = ticks >> 32;
+}
+
+static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
+{
+       guint32 attrs = 0;
+
+       /* FIXME: this could definitely be better */
+
+       if (S_ISDIR (buf->st_mode))
+               attrs |= FILE_ATTRIBUTE_DIRECTORY;
+       else
+               attrs |= FILE_ATTRIBUTE_ARCHIVE;
+       
+       if (!(buf->st_mode & S_IWUSR))
+               attrs |= FILE_ATTRIBUTE_READONLY;
+       
+       return attrs;
+}
+
+static void _wapi_set_last_error_from_errno (void)
+{
+       /* mapping ideas borrowed from wine. they may need some work */
+
+       switch (errno) {
+       case EACCES: case EPERM: case EROFS:
+               SetLastError (ERROR_ACCESS_DENIED);
+               break;
+       
+       case EAGAIN:
+               SetLastError (ERROR_SHARING_VIOLATION);
+               break;
+       
+       case EBUSY:
+               SetLastError (ERROR_LOCK_VIOLATION);
+               break;
+       
+       case EEXIST:
+               SetLastError (ERROR_FILE_EXISTS);
+               break;
+       
+       case EINVAL: case ESPIPE:
+               SetLastError (ERROR_SEEK);
+               break;
+       
+       case EISDIR:
+               SetLastError (ERROR_CANNOT_MAKE);
+               break;
+       
+       case ENFILE: case EMFILE:
+               SetLastError (ERROR_NO_MORE_FILES);
+               break;
+
+       case ENOENT: case ENOTDIR:
+               SetLastError (ERROR_FILE_NOT_FOUND);
+               break;
+       
+       case ENOSPC:
+               SetLastError (ERROR_HANDLE_DISK_FULL);
+               break;
+       
+       case ENOTEMPTY:
+               SetLastError (ERROR_DIR_NOT_EMPTY);
+               break;
+
+       case ENOEXEC:
+               SetLastError (ERROR_BAD_FORMAT);
+               break;
+
+       case ENAMETOOLONG:
+               SetLastError (ERROR_FILENAME_EXCED_RANGE);
+               break;
+       
+       default:
+               g_message ("Unknown errno: %s\n", strerror (errno));
+               SetLastError (ERROR_GEN_FAILURE);
+               break;
+       }
+}
+
+/* Handle ops.
+ */
+static void file_close_shared (gpointer handle)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       gboolean ok;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return;
+       }
        
 #ifdef DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
-                 file_handle, file_handle->fd);
+       g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
 #endif
        
-       close(file_handle->fd);
-       if(file_handle->filename!=NULL) {
-               g_free(file_handle->filename);
-               file_handle->filename=NULL;
+       if(file_handle->filename!=0) {
+               _wapi_handle_scratch_delete (file_handle->filename);
+               file_handle->filename=0;
+       }
+       if(file_handle->security_attributes!=0) {
+               _wapi_handle_scratch_delete (file_handle->security_attributes);
+               file_handle->security_attributes=0;
+       }
+}
+
+static void file_close_private (gpointer handle)
+{
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_UNUSED, NULL,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return;
        }
+       
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
+                 handle, file_private_handle->fd);
+#endif
+       
+       close(file_private_handle->fd);
 }
 
 static WapiFileType file_getfiletype(void)
@@ -107,13 +303,24 @@ static WapiFileType file_getfiletype(void)
        return(FILE_TYPE_DISK);
 }
 
-static gboolean file_read(WapiHandle *handle, gpointer buffer,
+static gboolean file_read(gpointer handle, gpointer buffer,
                          guint32 numbytes, guint32 *bytesread,
                          WapiOverlapped *overlapped G_GNUC_UNUSED)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
        if(bytesread!=NULL) {
                *bytesread=0;
        }
@@ -121,18 +328,18 @@ static gboolean file_read(WapiHandle *handle, gpointer buffer,
        if(!(file_handle->fileaccess&GENERIC_READ) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(FALSE);
        }
        
-       ret=read(file_handle->fd, buffer, numbytes);
+       ret=read(file_private_handle->fd, buffer, numbytes);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": read of handle %p fd %d error: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
@@ -145,13 +352,24 @@ static gboolean file_read(WapiHandle *handle, gpointer buffer,
        return(TRUE);
 }
 
-static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
+static gboolean file_write(gpointer handle, gconstpointer buffer,
                           guint32 numbytes, guint32 *byteswritten,
                           WapiOverlapped *overlapped G_GNUC_UNUSED)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
        if(byteswritten!=NULL) {
                *byteswritten=0;
        }
@@ -159,18 +377,18 @@ static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
        if(!(file_handle->fileaccess&GENERIC_WRITE) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(FALSE);
        }
        
-       ret=write(file_handle->fd, buffer, numbytes);
+       ret=write(file_private_handle->fd, buffer, numbytes);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": write of handle %p fd %d error: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
@@ -182,19 +400,69 @@ static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
        return(TRUE);
 }
 
-static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
+static gboolean file_flush(gpointer handle)
+{
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
+       int ret;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+
+       if(!(file_handle->fileaccess&GENERIC_WRITE) &&
+          !(file_handle->fileaccess&GENERIC_ALL)) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
+#endif
+
+               return(FALSE);
+       }
+
+       ret=fsync(file_private_handle->fd);
+       if (ret==-1) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION
+                         ": write of handle %p fd %d error: %s", handle,
+                         file_private_handle->fd, strerror(errno));
+#endif
+
+               return(FALSE);
+       }
+       
+       return(TRUE);
+}
+
+static guint32 file_seek(gpointer handle, gint32 movedistance,
                         gint32 *highmovedistance, WapiSeekMethod method)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        off_t offset, newpos;
        int whence;
        guint32 ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(INVALID_SET_FILE_POINTER);
+       }
+       
        if(!(file_handle->fileaccess&GENERIC_READ) &&
           !(file_handle->fileaccess&GENERIC_WRITE) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(INVALID_SET_FILE_POINTER);
@@ -242,20 +510,20 @@ static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
 #ifdef HAVE_LARGE_FILE_SUPPORT
        g_message(G_GNUC_PRETTY_FUNCTION
                  ": moving handle %p fd %d by %lld bytes from %d", handle,
-                 file_handle->fd, offset, whence);
+                 file_private_handle->fd, offset, whence);
 #else
        g_message(G_GNUC_PRETTY_FUNCTION
                  ": moving handle %p fd %d by %ld bytes from %d", handle,
-                 file_handle->fd, offset, whence);
+                 file_private_handle->fd, offset, whence);
 #endif
 #endif
 
-       newpos=lseek(file_handle->fd, offset, whence);
+       newpos=lseek(file_private_handle->fd, offset, whence);
        if(newpos==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": lseek on handle %p fd %d returned error %s",
-                         handle, file_handle->fd, strerror(errno));
+                         handle, file_private_handle->fd, strerror(errno));
 #endif
 
                return(INVALID_SET_FILE_POINTER);
@@ -285,24 +553,35 @@ static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION
                  ": move of handle %p fd %d returning %d/%d", handle,
-                 file_handle->fd, ret,
+                 file_private_handle->fd, ret,
                  highmovedistance==NULL?0:*highmovedistance);
 #endif
 
        return(ret);
 }
 
-static gboolean file_setendoffile(WapiHandle *handle)
+static gboolean file_setendoffile(gpointer handle)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        struct stat statbuf;
        off_t size, pos;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
        if(!(file_handle->fileaccess&GENERIC_WRITE) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(FALSE);
@@ -314,24 +593,24 @@ static gboolean file_setendoffile(WapiHandle *handle)
         * than the length, truncate the file.
         */
        
-       ret=fstat(file_handle->fd, &statbuf);
+       ret=fstat(file_private_handle->fd, &statbuf);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d fstat failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
        }
        size=statbuf.st_size;
 
-       pos=lseek(file_handle->fd, (off_t)0, SEEK_CUR);
+       pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR);
        if(pos==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d lseek failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
@@ -339,12 +618,13 @@ static gboolean file_setendoffile(WapiHandle *handle)
        
        if(pos>size) {
                /* extend */
-               ret=write(file_handle->fd, "", 1);
+               ret=write(file_private_handle->fd, "", 1);
                if(ret==-1) {
 #ifdef DEBUG
                        g_message(G_GNUC_PRETTY_FUNCTION
                                  ": handle %p fd %d extend write failed: %s",
-                                 handle, file_handle->fd, strerror(errno));
+                                 handle, file_private_handle->fd,
+                                 strerror(errno));
 #endif
 
                        return(FALSE);
@@ -354,12 +634,12 @@ static gboolean file_setendoffile(WapiHandle *handle)
        /* always truncate, because the extend write() adds an extra
         * byte to the end of the file
         */
-       ret=ftruncate(file_handle->fd, pos);
+       ret=ftruncate(file_private_handle->fd, pos);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d ftruncate failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
                
                return(FALSE);
@@ -368,29 +648,40 @@ static gboolean file_setendoffile(WapiHandle *handle)
        return(TRUE);
 }
 
-static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize)
+static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        struct stat statbuf;
        guint32 size;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(INVALID_FILE_SIZE);
+       }
+       
        if(!(file_handle->fileaccess&GENERIC_READ) &&
           !(file_handle->fileaccess&GENERIC_WRITE) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(INVALID_FILE_SIZE);
        }
 
-       ret=fstat(file_handle->fd, &statbuf);
+       ret=fstat(file_private_handle->fd, &statbuf);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d fstat failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(INVALID_FILE_SIZE);
@@ -417,30 +708,41 @@ static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize)
        return(size);
 }
 
-static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
+static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
                                 WapiFileTime *last_access,
                                 WapiFileTime *last_write)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
        struct stat statbuf;
        guint64 create_ticks, access_ticks, write_ticks;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
        if(!(file_handle->fileaccess&GENERIC_READ) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(FALSE);
        }
        
-       ret=fstat(file_handle->fd, &statbuf);
+       ret=fstat(file_private_handle->fd, &statbuf);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d fstat failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
@@ -494,31 +796,43 @@ static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
        return(TRUE);
 }
 
-static gboolean file_setfiletime(WapiHandle *handle,
+static gboolean file_setfiletime(gpointer handle,
                                 const WapiFileTime *create_time G_GNUC_UNUSED,
                                 const WapiFileTime *last_access,
                                 const WapiFileTime *last_write)
 {
-       struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
+       struct _WapiHandle_file *file_handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
+       guchar *name;
        struct utimbuf utbuf;
        struct stat statbuf;
        guint64 access_ticks, write_ticks;
        int ret;
        
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
        if(!(file_handle->fileaccess&GENERIC_WRITE) &&
           !(file_handle->fileaccess&GENERIC_ALL)) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
 #endif
 
                return(FALSE);
        }
 
-       if(file_handle->filename==NULL) {
+       if(file_handle->filename==0) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d unknown filename", handle,
-                         file_handle->fd);
+                         file_private_handle->fd);
 #endif
 
                return(FALSE);
@@ -527,12 +841,12 @@ static gboolean file_setfiletime(WapiHandle *handle,
        /* Get the current times, so we can put the same times back in
         * the event that one of the FileTime structs is NULL
         */
-       ret=fstat(file_handle->fd, &statbuf);
+       ret=fstat(file_private_handle->fd, &statbuf);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p fd %d fstat failed: %s", handle,
-                         file_handle->fd, strerror(errno));
+                         file_private_handle->fd, strerror(errno));
 #endif
 
                return(FALSE);
@@ -560,26 +874,215 @@ static gboolean file_setfiletime(WapiHandle *handle,
                  utbuf.actime, utbuf.modtime);
 #endif
 
-       ret=utime(file_handle->filename, &utbuf);
+       name=_wapi_handle_scratch_lookup_as_string (file_handle->filename);
+
+       ret=utime(name, &utbuf);
        if(ret==-1) {
 #ifdef DEBUG
                g_message(G_GNUC_PRETTY_FUNCTION
                          ": handle %p [%s] fd %d utime failed: %s", handle,
-                         file_handle->filename, file_handle->fd,
-                         strerror(errno));
-#endif
+                         name, file_private_handle->fd, strerror(errno));
 
+#endif
+               g_free (name);
                return(FALSE);
        }
 
+       g_free (name);
+       
        return(TRUE);
 }
 
+static void console_close_shared (gpointer handle)
+{
+       struct _WapiHandle_file *console_handle;
+       gboolean ok;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
+                               (gpointer *)&console_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               return;
+       }
+       
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
+#endif
+       
+       if(console_handle->filename!=0) {
+               _wapi_handle_scratch_delete (console_handle->filename);
+               console_handle->filename=0;
+       }
+       if(console_handle->security_attributes!=0) {
+               _wapi_handle_scratch_delete (console_handle->security_attributes);
+               console_handle->security_attributes=0;
+       }
+}
+
+static void console_close_private (gpointer handle)
+{
+       struct _WapiHandlePrivate_file *console_private_handle;
+       gboolean ok;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_UNUSED, NULL,
+                               (gpointer *)&console_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               return;
+       }
+       
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": closing console handle %p with fd %d", handle,
+                 console_private_handle->fd);
+#endif
+       
+       close(console_private_handle->fd);
+}
+
 static WapiFileType console_getfiletype(void)
 {
        return(FILE_TYPE_CHAR);
 }
 
+static gboolean console_read(gpointer handle, gpointer buffer,
+                            guint32 numbytes, guint32 *bytesread,
+                            WapiOverlapped *overlapped G_GNUC_UNUSED)
+{
+       struct _WapiHandle_file *console_handle;
+       struct _WapiHandlePrivate_file *console_private_handle;
+       gboolean ok;
+       int ret;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&console_handle,
+                               (gpointer *)&console_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               return(FALSE);
+       }
+       
+       if(bytesread!=NULL) {
+               *bytesread=0;
+       }
+       
+       if(!(console_handle->fileaccess&GENERIC_READ) &&
+          !(console_handle->fileaccess&GENERIC_ALL)) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
+#endif
+
+               return(FALSE);
+       }
+       
+       ret=read(console_private_handle->fd, buffer, numbytes);
+       if(ret==-1) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION
+                         ": read of handle %p fd %d error: %s", handle,
+                         console_private_handle->fd, strerror(errno));
+#endif
+
+               return(FALSE);
+       }
+       
+       if(bytesread!=NULL) {
+               *bytesread=ret;
+       }
+       
+       return(TRUE);
+}
+
+static gboolean console_write(gpointer handle, gconstpointer buffer,
+                             guint32 numbytes, guint32 *byteswritten,
+                             WapiOverlapped *overlapped G_GNUC_UNUSED)
+{
+       struct _WapiHandle_file *console_handle;
+       struct _WapiHandlePrivate_file *console_private_handle;
+       gboolean ok;
+       int ret;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
+                               (gpointer *)&console_handle,
+                               (gpointer *)&console_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               return(FALSE);
+       }
+       
+       if(byteswritten!=NULL) {
+               *byteswritten=0;
+       }
+       
+       if(!(console_handle->fileaccess&GENERIC_WRITE) &&
+          !(console_handle->fileaccess&GENERIC_ALL)) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
+#endif
+
+               return(FALSE);
+       }
+       
+       ret=write(console_private_handle->fd, buffer, numbytes);
+       if(ret==-1) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION
+                         ": write of handle %p fd %d error: %s", handle,
+                         console_private_handle->fd, strerror(errno));
+#endif
+
+               return(FALSE);
+       }
+       if(byteswritten!=NULL) {
+               *byteswritten=ret;
+       }
+       
+       return(TRUE);
+}
+
+static gboolean console_flush(gpointer handle)
+{
+       struct _WapiHandle_file *console_handle;
+       struct _WapiHandlePrivate_file *console_private_handle;
+       gboolean ok;
+       int ret;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
+                               (gpointer *)&console_handle,
+                               (gpointer *)&console_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               return(FALSE);
+       }
+
+       if(!(console_handle->fileaccess&GENERIC_WRITE) &&
+          !(console_handle->fileaccess&GENERIC_ALL)) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
+#endif
+
+               return(FALSE);
+       }
+
+       ret=fsync(console_private_handle->fd);
+       if (ret==-1) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION
+                         ": write of handle %p fd %d error: %s", handle,
+                         console_private_handle->fd, strerror(errno));
+#endif
+
+               return(FALSE);
+       }
+       
+       return(TRUE);
+}
+
 static int convert_flags(guint32 fileaccess, guint32 createmode)
 {
        int flags=0;
@@ -690,17 +1193,21 @@ static mode_t convert_perms(guint32 sharemode)
  *
  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
  */
-WapiHandle *CreateFile(const guchar *name, guint32 fileaccess,
-                      guint32 sharemode, WapiSecurityAttributes *security,
-                      guint32 createmode, guint32 attrs,
-                      WapiHandle *template G_GNUC_UNUSED)
+gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
+                   guint32 sharemode, WapiSecurityAttributes *security,
+                   guint32 createmode, guint32 attrs,
+                   gpointer template G_GNUC_UNUSED)
 {
        struct _WapiHandle_file *file_handle;
-       WapiHandle *handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gpointer handle;
+       gboolean ok;
        int flags=convert_flags(fileaccess, createmode);
        mode_t perms=convert_perms(sharemode);
-       guchar *filename;
+       gchar *filename;
        int ret;
+
+       pthread_once (&io_ops_once, io_ops_init);
        
        if(name==NULL) {
 #ifdef DEBUG
@@ -738,31 +1245,53 @@ WapiHandle *CreateFile(const guchar *name, guint32 fileaccess,
                          filename, strerror(errno));
 #endif
 #endif
+               _wapi_set_last_error_from_errno ();
+               return(INVALID_HANDLE_VALUE);
+       }
+
+       handle=_wapi_handle_new (WAPI_HANDLE_FILE);
+       if(handle==_WAPI_HANDLE_INVALID) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error creating file handle");
                return(INVALID_HANDLE_VALUE);
        }
 
-       file_handle=g_new0(struct _WapiHandle_file, 1);
-       handle=(WapiHandle *)file_handle;
+       _wapi_handle_lock_handle (handle);
        
-       _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_FILE, file_ops);
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               _wapi_handle_unlock_handle (handle);
+               return(INVALID_HANDLE_VALUE);
+       }
 
-       file_handle->fd=ret;
+       file_private_handle->fd=ret;
 #ifdef ACTUALLY_DO_UNICODE
-       file_handle->filename=filename;
+       file_handle->filename=_wapi_handle_scratch_store (filename,
+                                                         strlen (filename));
 #else
-       file_handle->filename=g_strdup(name);
+       file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
 #endif
-       file_handle->security_attributes=security;
+       if(security!=NULL) {
+               file_handle->security_attributes=_wapi_handle_scratch_store (
+                       security, sizeof(WapiSecurityAttributes));
+       }
+       
        file_handle->fileaccess=fileaccess;
        file_handle->sharemode=sharemode;
        file_handle->attrs=attrs;
        
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION
-                 ": returning handle %p [%s] with fd %d",
-                 handle, file_handle->filename, file_handle->fd);
+                 ": returning handle %p with fd %d", handle,
+                 file_private_handle->fd);
 #endif
 
+       _wapi_handle_unlock_handle (handle);
+
        return(handle);
 }
 
@@ -775,9 +1304,9 @@ WapiHandle *CreateFile(const guchar *name, guint32 fileaccess,
  *
  * Return value: %TRUE on success, %FALSE otherwise.
  */
-gboolean DeleteFile(const guchar *name)
+gboolean DeleteFile(const gunichar2 *name)
 {
-       guchar *filename;
+       gchar *filename;
        int ret;
        
        if(name==NULL) {
@@ -816,7 +1345,143 @@ gboolean DeleteFile(const guchar *name)
 }
 
 /**
- * GetStdHandle:
+ * MoveFile:
+ * @name: a pointer to a NULL-terminated unicode string, that names
+ * the file to be moved.
+ * @dest_name: a pointer to a NULL-terminated unicode string, that is the
+ * new name for the file.
+ *
+ * Renames file @name to @dest_name
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
+{
+       gchar *utf8_name, *utf8_dest_name;
+       int result;
+
+       utf8_name = _wapi_unicode_to_utf8 (name);
+       if (utf8_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+               
+               return FALSE;
+       }
+
+       utf8_dest_name = _wapi_unicode_to_utf8 (dest_name);
+       if (utf8_dest_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+
+               g_free (utf8_name);
+               return FALSE;
+       }
+
+       result = rename (utf8_name, utf8_dest_name);
+       g_free (utf8_name);
+       g_free (utf8_dest_name);
+
+       if (result == 0)
+               return TRUE;
+       
+       switch (errno) {
+       case EEXIST:
+               SetLastError (ERROR_ALREADY_EXISTS);
+               break;
+       
+       default:
+               _wapi_set_last_error_from_errno ();
+               break;
+       }
+
+       return FALSE;
+}
+
+/**
+ * CopyFile:
+ * @name: a pointer to a NULL-terminated unicode string, that names
+ * the file to be copied.
+ * @dest_name: a pointer to a NULL-terminated unicode string, that is the
+ * new name for the file.
+ * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
+ *
+ * Copies file @name to @dest_name
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
+                  gboolean fail_if_exists)
+{
+       gpointer src, dest;
+       guint32 attrs;
+       char *buffer;
+       int remain, n;
+
+       attrs = GetFileAttributes (name);
+       if (attrs == -1) {
+               SetLastError (ERROR_FILE_NOT_FOUND);
+               return  FALSE;
+       }
+       
+       src = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                         NULL, OPEN_EXISTING, 0, NULL);
+       if (src == INVALID_HANDLE_VALUE) {
+               _wapi_set_last_error_from_errno ();
+               return FALSE;
+       }
+       
+       dest = CreateFile (dest_name, GENERIC_WRITE, 0, NULL,
+                          fail_if_exists ? CREATE_NEW : CREATE_ALWAYS, attrs, NULL);
+       if (dest == INVALID_HANDLE_VALUE) {
+               _wapi_set_last_error_from_errno ();
+               CloseHandle (src);
+               return FALSE;
+       }
+
+       buffer = g_new (gchar, 2048);
+
+       for (;;) {
+               if (ReadFile (src, buffer,sizeof (buffer), &remain, NULL) == 0) {
+                       _wapi_set_last_error_from_errno ();
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION ": read failed.");
+#endif
+                       
+                       CloseHandle (dest);
+                       CloseHandle (src);
+                       return FALSE;
+               }
+
+               if (remain == 0)
+                       break;
+
+               while (remain > 0) {
+                       if (WriteFile (dest, buffer, remain, &n, NULL) == 0) {
+                               _wapi_set_last_error_from_errno ();
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
+#endif
+                               
+                               CloseHandle (dest);
+                               CloseHandle (src);
+                               return FALSE;
+                       }
+
+                       remain -= n;
+               }
+       }
+
+       g_free (buffer);
+
+       CloseHandle (dest);
+       CloseHandle (src);
+       return TRUE;
+}
+
+/**
+ * GetStdHandle:
  * @stdhandle: specifies the file descriptor
  *
  * Returns a handle for stdin, stdout, or stderr.  Always returns the
@@ -824,23 +1489,32 @@ gboolean DeleteFile(const guchar *name)
  *
  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
  */
-WapiHandle *GetStdHandle(WapiStdHandle stdhandle)
+
+gpointer GetStdHandle(WapiStdHandle stdhandle)
 {
        struct _WapiHandle_file *file_handle;
-       WapiHandle *handle;
+       struct _WapiHandlePrivate_file *file_private_handle;
+       gboolean ok;
+       gpointer handle;
+       const guchar *name;
        int flags, fd;
+
+       pthread_once (&io_ops_once, io_ops_init);
        
        switch(stdhandle) {
        case STD_INPUT_HANDLE:
                fd=0;
+               name="<stdin>";
                break;
 
        case STD_OUTPUT_HANDLE:
                fd=1;
+               name="<stdout>";
                break;
 
        case STD_ERROR_HANDLE:
                fd=2;
+               name="<stderr>";
                break;
 
        default:
@@ -866,26 +1540,40 @@ WapiHandle *GetStdHandle(WapiStdHandle stdhandle)
                return(INVALID_HANDLE_VALUE);
        }
        
-       file_handle=g_new0(struct _WapiHandle_file, 1);
-       handle=(WapiHandle *)file_handle;
-       
-       _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_CONSOLE, console_ops);
+       handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
+       if(handle==_WAPI_HANDLE_INVALID) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error creating file handle");
+               return(NULL);
+       }
 
-       file_handle->fd=fd;
-       /* We might want to set file_handle->filename to something
-        * like "<stdin>" if we ever want to display handle internal
-        * details somehow
-        */
-       file_handle->security_attributes=/*some default*/NULL;
+       _wapi_handle_lock_handle (handle);
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
+                               (gpointer *)&file_handle,
+                               (gpointer *)&file_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up console handle %p", handle);
+               _wapi_handle_unlock_handle (handle);
+               return(NULL);
+       }
+       
+       file_private_handle->fd=fd;
+       file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
+       /* some default security attributes might be needed */
+       file_handle->security_attributes=0;
        file_handle->fileaccess=convert_from_flags(flags);
        file_handle->sharemode=0;
        file_handle->attrs=0;
        
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
-                 handle, file_handle->fd);
+                 handle, file_private_handle->fd);
 #endif
 
+       _wapi_handle_unlock_handle (handle);
+
        return(handle);
 }
 
@@ -916,15 +1604,17 @@ WapiHandle *GetStdHandle(WapiStdHandle stdhandle)
  * read due to an attempt to read past the end of the file), %FALSE on
  * error.
  */
-gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes,
+gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
                  guint32 *bytesread, WapiOverlapped *overlapped)
 {
-       if(handle->ops->readfile==NULL) {
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].readfile==NULL) {
                return(FALSE);
        }
        
-       return(handle->ops->readfile(handle, buffer, numbytes, bytesread,
-                                    overlapped));
+       return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
+                                     overlapped));
 }
 
 /**
@@ -952,15 +1642,38 @@ gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes,
  *
  * Return value: %TRUE if the write succeeds, %FALSE on error.
  */
-gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes,
+gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
                   guint32 *byteswritten, WapiOverlapped *overlapped)
 {
-       if(handle->ops->writefile==NULL) {
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].writefile==NULL) {
                return(FALSE);
        }
        
-       return(handle->ops->writefile(handle, buffer, numbytes, byteswritten,
-                                     overlapped));
+       return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
+                                      overlapped));
+}
+
+/**
+ * FlushFileBuffers:
+ * @handle: Handle to open file.  The handle must have
+ * %GENERIC_WRITE access.
+ *
+ * Flushes buffers of the file and causes all unwritten data to
+ * be written.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean FlushFileBuffers(gpointer handle)
+{
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].flushfile==NULL) {
+               return(FALSE);
+       }
+       
+       return(io_ops[type].flushfile (handle));
 }
 
 /**
@@ -973,13 +1686,15 @@ gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes,
  *
  * Return value: %TRUE on success, %FALSE otherwise.
  */
-gboolean SetEndOfFile(WapiHandle *handle)
+gboolean SetEndOfFile(gpointer handle)
 {
-       if(handle->ops->setendoffile==NULL) {
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].setendoffile==NULL) {
                return(FALSE);
        }
        
-       return(handle->ops->setendoffile(handle));
+       return(io_ops[type].setendoffile (handle));
 }
 
 /**
@@ -1011,15 +1726,17 @@ gboolean SetEndOfFile(WapiHandle *handle)
  * If @highmovedistance is not %NULL, the high 32 bits of the new file
  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
  */
-guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance,
+guint32 SetFilePointer(gpointer handle, gint32 movedistance,
                       gint32 *highmovedistance, WapiSeekMethod method)
 {
-       if(handle->ops->seek==NULL) {
-               return(INVALID_SET_FILE_POINTER);
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].seek==NULL) {
+               return(FALSE);
        }
        
-       return(handle->ops->seek(handle, movedistance, highmovedistance,
-                                method));
+       return(io_ops[type].seek (handle, movedistance, highmovedistance,
+                                 method));
 }
 
 /**
@@ -1033,13 +1750,15 @@ guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance,
  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
  */
-WapiFileType GetFileType(WapiHandle *handle)
+WapiFileType GetFileType(gpointer handle)
 {
-       if(handle->ops->getfiletype==NULL) {
-               return(FILE_TYPE_UNKNOWN);
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].getfiletype==NULL) {
+               return(FALSE);
        }
        
-       return(handle->ops->getfiletype());
+       return(io_ops[type].getfiletype ());
 }
 
 /**
@@ -1058,13 +1777,15 @@ WapiFileType GetFileType(WapiHandle *handle)
  * @highsize is non-%NULL then the high 32 bits of the file size are
  * stored here.  On failure %INVALID_FILE_SIZE is returned.
  */
-guint32 GetFileSize(WapiHandle *handle, guint32 *highsize)
+guint32 GetFileSize(gpointer handle, guint32 *highsize)
 {
-       if(handle->ops->getfilesize==NULL) {
-               return(INVALID_FILE_SIZE);
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].getfilesize==NULL) {
+               return(FALSE);
        }
        
-       return(handle->ops->getfilesize(handle, highsize));
+       return(io_ops[type].getfilesize (handle, highsize));
 }
 
 /**
@@ -1092,15 +1813,17 @@ guint32 GetFileSize(WapiHandle *handle, guint32 *highsize)
  *
  * Return value: %TRUE on success, %FALSE otherwise.
  */
-gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time,
+gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
                     WapiFileTime *last_access, WapiFileTime *last_write)
 {
-       if(handle->ops->getfiletime==NULL) {
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].getfiletime==NULL) {
                return(FALSE);
        }
        
-       return(handle->ops->getfiletime(handle, create_time, last_access,
-                                       last_write));
+       return(io_ops[type].getfiletime (handle, create_time, last_access,
+                                        last_write));
 }
 
 /**
@@ -1126,16 +1849,18 @@ gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time,
  *
  * Return value: %TRUE on success, %FALSE otherwise.
  */
-gboolean SetFileTime(WapiHandle *handle, const WapiFileTime *create_time,
+gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
                     const WapiFileTime *last_access,
                     const WapiFileTime *last_write)
 {
-       if(handle->ops->setfiletime==NULL) {
+       WapiHandleType type=_wapi_handle_type (handle);
+       
+       if(io_ops[type].setfiletime==NULL) {
                return(FALSE);
        }
        
-       return(handle->ops->setfiletime(handle, create_time, last_access,
-                                       last_write));
+       return(io_ops[type].setfiletime (handle, create_time, last_access,
+                                        last_write));
 }
 
 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
@@ -1297,3 +2022,458 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
        
        return(TRUE);
 }
+
+gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
+{
+       struct _WapiHandle_find *find_handle;
+       gpointer handle;
+       gboolean ok;
+       gchar *utf8_pattern = NULL;
+       int result;
+       
+       if (pattern == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
+#endif
+
+               return INVALID_HANDLE_VALUE;
+       }
+
+       utf8_pattern = _wapi_unicode_to_utf8 (pattern);
+       if (utf8_pattern == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+               
+               return INVALID_HANDLE_VALUE;
+       }
+       
+       handle=_wapi_handle_new (WAPI_HANDLE_FIND);
+       if(handle==_WAPI_HANDLE_INVALID) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error creating find handle");
+               return(INVALID_HANDLE_VALUE);
+       }
+
+       _wapi_handle_lock_handle (handle);
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
+                               (gpointer *)&find_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up find handle %p", handle);
+               _wapi_handle_unlock_handle (handle);
+               return(INVALID_HANDLE_VALUE);
+       }
+       
+       result = glob (utf8_pattern, 0, NULL, &find_handle->glob);
+       g_free (utf8_pattern);
+
+       if (result != 0) {
+               globfree (&find_handle->glob);
+               _wapi_handle_unlock_handle (handle);
+               _wapi_handle_unref (handle);
+
+               switch (result) {
+#ifdef GLOB_NOMATCH
+               case GLOB_NOMATCH:
+                       SetLastError (ERROR_NO_MORE_FILES);
+                       break;
+#endif
+
+               default:
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION ": glob failed with code %d.", result);
+#endif
+
+                       break;
+               }
+
+               return INVALID_HANDLE_VALUE;
+       }
+
+       find_handle->count = 0;
+       if (!FindNextFile (handle, find_data)) {
+               FindClose (handle);
+               SetLastError (ERROR_NO_MORE_FILES);
+               return INVALID_HANDLE_VALUE;
+       }
+
+       _wapi_handle_unlock_handle (handle);
+
+       return (handle);
+}
+
+gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
+{
+       struct _WapiHandle_find *find_handle;
+       gboolean ok;
+       struct stat buf;
+       const gchar *filename;
+       
+       gchar *base_filename;
+       gunichar2 *utf16_basename;
+       time_t create_time;
+       int i;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
+                               (gpointer *)&find_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up find handle %p", handle);
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(FALSE);
+       }
+
+       if (find_handle->count >= find_handle->glob.gl_pathc) {
+               SetLastError (ERROR_NO_MORE_FILES);
+               return FALSE;
+       }
+
+       /* stat next glob match */
+
+       filename = find_handle->glob.gl_pathv [find_handle->count ++];
+       if (stat (filename, &buf) != 0) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
+#endif
+
+               SetLastError (ERROR_NO_MORE_FILES);
+               return FALSE;
+       }
+
+       /* fill data block */
+
+       if (buf.st_mtime < buf.st_ctime)
+               create_time = buf.st_mtime;
+       else
+               create_time = buf.st_ctime;
+       
+       find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
+
+       _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
+       _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
+       _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
+
+       if (find_data->dwFileAttributes && FILE_ATTRIBUTE_DIRECTORY) {
+               find_data->nFileSizeHigh = 0;
+               find_data->nFileSizeLow = 0;
+       }
+       else {
+               find_data->nFileSizeHigh = buf.st_size >> 32;
+               find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
+       }
+
+       find_data->dwReserved0 = 0;
+       find_data->dwReserved1 = 0;
+
+       base_filename = g_path_get_basename (filename);
+       utf16_basename = g_utf8_to_utf16 (base_filename, MAX_PATH, NULL, NULL, NULL);
+
+       i = 0;
+       while (utf16_basename [i] != 0) {       /* copy basename */
+               find_data->cFileName [i] = utf16_basename [i];
+               ++ i;
+       }
+
+       find_data->cFileName[i] = 0;            /* null terminate */
+       find_data->cAlternateFileName [0] = 0;  /* not used */
+
+       g_free (base_filename);
+       g_free (utf16_basename);
+       return TRUE;
+}
+
+/**
+ * FindClose:
+ * @wapi_handle: the find handle to close.
+ *
+ * Closes find handle @wapi_handle
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean FindClose (gpointer handle)
+{
+       struct _WapiHandle_find *find_handle;
+       gboolean ok;
+       
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
+                               (gpointer *)&find_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up find handle %p", handle);
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(FALSE);
+       }
+       
+       globfree (&find_handle->glob);
+       _wapi_handle_unref (handle);
+
+       return TRUE;
+}
+
+/**
+ * CreateDirectory:
+ * @name: a pointer to a NULL-terminated unicode string, that names
+ * the directory to be created.
+ * @security: ignored for now
+ *
+ * Creates directory @name
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
+{
+       gchar *utf8_name;
+       int result;
+       
+       utf8_name = _wapi_unicode_to_utf8 (name);
+       if (utf8_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+       
+               return FALSE;
+       }
+
+       result = mkdir (utf8_name, 0777);
+       g_free (utf8_name);
+
+       if (result == 0)
+               return TRUE;
+
+       switch (errno) {
+       case EEXIST:
+               return TRUE;
+       default:
+               _wapi_set_last_error_from_errno ();
+               break;
+       }
+       
+       return FALSE;
+}
+
+/**
+ * RemoveDirectory:
+ * @name: a pointer to a NULL-terminated unicode string, that names
+ * the directory to be removed.
+ *
+ * Removes directory @name
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean RemoveDirectory (const gunichar2 *name)
+{
+       gchar *utf8_name;
+       int result;
+
+       utf8_name = _wapi_unicode_to_utf8 (name);
+       if (utf8_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+               
+               return FALSE;
+       }
+
+       result = rmdir (utf8_name);
+       g_free (utf8_name);
+
+       if (result == 0)
+               return TRUE;
+       
+       _wapi_set_last_error_from_errno ();
+       return FALSE;
+}
+
+/**
+ * GetFileAttributes:
+ * @name: a pointer to a NULL-terminated unicode filename.
+ *
+ * Gets the attributes for @name;
+ *
+ * Return value: -1 on failure
+ */
+guint32 GetFileAttributes (const gunichar2 *name)
+{
+       gchar *utf8_name;
+       struct stat buf;
+       int result;
+       
+       utf8_name = _wapi_unicode_to_utf8 (name);
+       if (utf8_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+
+               SetLastError (ERROR_INVALID_PARAMETER);
+               return -1;
+       }
+
+       result = stat (utf8_name, &buf);
+       g_free (utf8_name);
+
+       if (result != 0) {
+               SetLastError (ERROR_FILE_NOT_FOUND);
+               return -1;
+       }
+       
+       return _wapi_stat_to_file_attributes (&buf);
+}
+
+/**
+ * GetFileAttributesEx:
+ * @name: a pointer to a NULL-terminated unicode filename.
+ * @level: must be GetFileExInfoStandard
+ * @info: pointer to a WapiFileAttributesData structure
+ *
+ * Gets attributes, size and filetimes for @name;
+ *
+ * Return value: %TRUE on success, %FALSE on failure
+ */
+gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
+{
+       gchar *utf8_name;
+       WapiFileAttributesData *data;
+
+       struct stat buf;
+       time_t create_time;
+       int result;
+       
+       if (level != GetFileExInfoStandard) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
+#endif
+
+               return FALSE;
+       }
+
+       utf8_name = _wapi_unicode_to_utf8 (name);
+       if (utf8_name == NULL) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
+#endif
+
+               SetLastError (ERROR_INVALID_PARAMETER);
+               return FALSE;
+       }
+
+       result = stat (utf8_name, &buf);
+       g_free (utf8_name);
+
+       if (result != 0) {
+               SetLastError (ERROR_FILE_NOT_FOUND);
+               return FALSE;
+       }
+
+       /* fill data block */
+
+       data = (WapiFileAttributesData *)info;
+
+       if (buf.st_mtime < buf.st_ctime)
+               create_time = buf.st_mtime;
+       else
+               create_time = buf.st_ctime;
+       
+       data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
+
+       _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
+       _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
+       _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
+
+       if (data->dwFileAttributes && FILE_ATTRIBUTE_DIRECTORY) {
+               data->nFileSizeHigh = 0;
+               data->nFileSizeLow = 0;
+       }
+       else {
+               data->nFileSizeHigh = buf.st_size >> 32;
+               data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
+       }
+
+       return TRUE;
+}
+
+/**
+ * SetFileAttributes
+ * @name: name of file
+ * @attrs: attributes to set
+ *
+ * Changes the attributes on a named file.
+ *
+ * Return value: %TRUE on success, %FALSE on failure.
+ */
+extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
+{
+       /* FIXME: think of something clever to do on unix */
+       
+       SetLastError (ERROR_INVALID_FUNCTION);
+       return FALSE;
+}
+
+/**
+ * GetCurrentDirectory
+ * @length: size of the buffer
+ * @buffer: pointer to buffer that recieves path
+ *
+ * Retrieves the current directory for the current process.
+ *
+ * Return value: number of characters in buffer on success, zero on failure
+ */
+extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
+{
+       gchar *path;
+       gunichar2 *utf16_path, *ptr;
+       glong count = 0;
+       
+       path = g_get_current_dir ();
+       if (path == NULL)
+               return 0;
+       
+       /* if buffer too small, return number of characters required.
+        * this is plain dumb.
+        */
+       
+       count = strlen (path) + 1;
+       if (count > length)
+               return count;
+       
+       utf16_path = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
+       if (utf16_path == NULL)
+               return 0;
+
+       ptr = utf16_path;
+       while (*ptr)
+               *buffer ++ = *ptr ++;
+       
+       *buffer = 0;
+       
+       g_free (utf16_path);
+       g_free (path);
+
+       return count;
+}
+
+/**
+ * SetCurrentDirectory
+ * @path: path to new directory
+ *
+ * Changes the directory path for the current process.
+ *
+ * Return value: %TRUE on success, %FALSE on failure.
+ */
+extern gboolean SetCurrentDirectory (const gunichar2 *path)
+{
+       gchar *utf8_path;
+       gboolean result;
+
+       utf8_path = _wapi_unicode_to_utf8 (path);
+       if (chdir (utf8_path) != 0) {
+               _wapi_set_last_error_from_errno ();
+               result = FALSE;
+       }
+       else
+               result = TRUE;
+
+       g_free (utf8_path);
+       return result;
+}