2004-11-30 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / io.c
1 /*
2  * io.c:  File, console and find handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <dirent.h>
19 #include <fnmatch.h>
20 #include <stdio.h>
21 #include <utime.h>
22
23 #ifndef PLATFORM_WIN32
24 #ifdef HAVE_AIO_H
25 #include <aio.h>
26 #define USE_AIO 1
27 #elif defined(HAVE_SYS_AIO_H)
28 #include <sys/aio.h>
29 #define USE_AIO 1
30 #else
31 #undef USE_AIO
32 #endif
33 #endif
34
35 #include <mono/io-layer/wapi.h>
36 #include <mono/io-layer/wapi-private.h>
37 #include <mono/io-layer/handles-private.h>
38 #include <mono/io-layer/io-private.h>
39 #include <mono/io-layer/timefuncs-private.h>
40 #include <mono/io-layer/thread-private.h>
41 #include <mono/utils/strenc.h>
42
43 #undef DEBUG
44
45 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
46 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
47
48 static void file_close_shared (gpointer handle);
49 static void file_close_private (gpointer handle);
50 static WapiFileType file_getfiletype(void);
51 static gboolean file_read(gpointer handle, gpointer buffer,
52                           guint32 numbytes, guint32 *bytesread,
53                           WapiOverlapped *overlapped);
54 static gboolean file_write(gpointer handle, gconstpointer buffer,
55                            guint32 numbytes, guint32 *byteswritten,
56                            WapiOverlapped *overlapped);
57 static gboolean file_flush(gpointer handle);
58 static guint32 file_seek(gpointer handle, gint32 movedistance,
59                          gint32 *highmovedistance, WapiSeekMethod method);
60 static gboolean file_setendoffile(gpointer handle);
61 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
62 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
63                                  WapiFileTime *last_access,
64                                  WapiFileTime *last_write);
65 static gboolean file_setfiletime(gpointer handle,
66                                  const WapiFileTime *create_time,
67                                  const WapiFileTime *last_access,
68                                  const WapiFileTime *last_write);
69
70 /* File handle is only signalled for overlapped IO */
71 struct _WapiHandleOps _wapi_file_ops = {
72         file_close_shared,      /* close_shared */
73         file_close_private,     /* close_private */
74         NULL,                   /* signal */
75         NULL,                   /* own */
76         NULL,                   /* is_owned */
77 };
78
79 static void console_close_shared (gpointer handle);
80 static void console_close_private (gpointer handle);
81 static WapiFileType console_getfiletype(void);
82 static gboolean console_read(gpointer handle, gpointer buffer,
83                              guint32 numbytes, guint32 *bytesread,
84                              WapiOverlapped *overlapped);
85 static gboolean console_write(gpointer handle, gconstpointer buffer,
86                               guint32 numbytes, guint32 *byteswritten,
87                               WapiOverlapped *overlapped);
88
89 /* Console is mostly the same as file, except it can block waiting for
90  * input or output
91  */
92 struct _WapiHandleOps _wapi_console_ops = {
93         console_close_shared,   /* close_shared */
94         console_close_private,  /* close_private */
95         NULL,                   /* signal */
96         NULL,                   /* own */
97         NULL,                   /* is_owned */
98 };
99
100 /* Find handle has no ops.
101  */
102 struct _WapiHandleOps _wapi_find_ops = {
103         NULL,                   /* close_shared */
104         NULL,                   /* close_private */
105         NULL,                   /* signal */
106         NULL,                   /* own */
107         NULL,                   /* is_owned */
108 };
109
110 static void pipe_close_shared (gpointer handle);
111 static void pipe_close_private (gpointer handle);
112 static WapiFileType pipe_getfiletype (void);
113 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
114                            guint32 *bytesread, WapiOverlapped *overlapped);
115 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
116                             guint32 numbytes, guint32 *byteswritten,
117                             WapiOverlapped *overlapped);
118
119 /* Pipe handles
120  */
121 struct _WapiHandleOps _wapi_pipe_ops = {
122         pipe_close_shared,      /* close_shared */
123         pipe_close_private,     /* close_private */
124         NULL,                   /* signal */
125         NULL,                   /* own */
126         NULL,                   /* is_owned */
127 };
128
129 static struct {
130         /* File, console and pipe handles */
131         WapiFileType (*getfiletype)(void);
132         
133         /* File, console and pipe handles */
134         gboolean (*readfile)(gpointer handle, gpointer buffer,
135                              guint32 numbytes, guint32 *bytesread,
136                              WapiOverlapped *overlapped);
137         gboolean (*writefile)(gpointer handle, gconstpointer buffer,
138                               guint32 numbytes, guint32 *byteswritten,
139                               WapiOverlapped *overlapped);
140         gboolean (*flushfile)(gpointer handle);
141         
142         /* File handles */
143         guint32 (*seek)(gpointer handle, gint32 movedistance,
144                         gint32 *highmovedistance, WapiSeekMethod method);
145         gboolean (*setendoffile)(gpointer handle);
146         guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
147         gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
148                                 WapiFileTime *last_access,
149                                 WapiFileTime *last_write);
150         gboolean (*setfiletime)(gpointer handle,
151                                 const WapiFileTime *create_time,
152                                 const WapiFileTime *last_access,
153                                 const WapiFileTime *last_write);
154 } io_ops[WAPI_HANDLE_COUNT]={
155         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
156         /* file */
157         {file_getfiletype,
158          file_read, file_write,
159          file_flush, file_seek,
160          file_setendoffile,
161          file_getfilesize,
162          file_getfiletime,
163          file_setfiletime},
164         /* console */
165         {console_getfiletype,
166          console_read,
167          console_write,
168          NULL, NULL, NULL, NULL, NULL, NULL},
169         /* thread */
170         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
171         /* sem */
172         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
173         /* mutex */
174         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
175         /* event */
176         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
177         /* socket (will need at least read and write) */
178         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
179         /* find */
180         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
181         /* process */
182         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
183         /* pipe */
184         {pipe_getfiletype,
185          pipe_read,
186          pipe_write,
187          NULL, NULL, NULL, NULL, NULL, NULL},
188 };
189
190
191 static mono_once_t io_ops_once=MONO_ONCE_INIT;
192
193 static void io_ops_init (void)
194 {
195 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
196 /*                                          WAPI_HANDLE_CAP_WAIT); */
197 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
198 /*                                          WAPI_HANDLE_CAP_WAIT); */
199 }
200
201 /* Some utility functions.
202  */
203
204 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
205 {
206         guint32 attrs = 0;
207
208         /* FIXME: this could definitely be better */
209
210         if (S_ISDIR (buf->st_mode))
211                 attrs |= FILE_ATTRIBUTE_DIRECTORY;
212         else
213                 attrs |= FILE_ATTRIBUTE_ARCHIVE;
214         
215         if (!(buf->st_mode & S_IWUSR))
216                 attrs |= FILE_ATTRIBUTE_READONLY;
217         
218         return attrs;
219 }
220
221 static void
222 _wapi_set_last_error_from_errno (void)
223 {
224         SetLastError (_wapi_get_win32_file_error (errno));
225 }
226
227 /* Handle ops.
228  */
229 static void file_close_shared (gpointer handle)
230 {
231         struct _WapiHandle_file *file_handle;
232         gboolean ok;
233         
234         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
235                                 (gpointer *)&file_handle, NULL);
236         if(ok==FALSE) {
237                 g_warning (G_GNUC_PRETTY_FUNCTION
238                            ": error looking up file handle %p", handle);
239                 SetLastError (ERROR_INVALID_HANDLE);
240                 return;
241         }
242         
243 #ifdef DEBUG
244         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
245 #endif
246         
247         if(file_handle->filename!=0) {
248                 _wapi_handle_scratch_delete (file_handle->filename);
249                 file_handle->filename=0;
250         }
251         if(file_handle->security_attributes!=0) {
252                 _wapi_handle_scratch_delete (file_handle->security_attributes);
253                 file_handle->security_attributes=0;
254         }
255 }
256
257 static void file_close_private (gpointer handle)
258 {
259         struct _WapiHandlePrivate_file *file_private_handle;
260         gboolean ok;
261         
262         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
263                                 (gpointer *)&file_private_handle);
264         if(ok==FALSE) {
265                 g_warning (G_GNUC_PRETTY_FUNCTION
266                            ": error looking up file handle %p", handle);
267                 SetLastError (ERROR_INVALID_HANDLE);
268                 return;
269         }
270         
271         if (file_private_handle->fd_mapped.assigned) {
272 #ifdef DEBUG
273                 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
274                           handle, file_private_handle->fd_mapped.fd);
275 #endif
276         
277                 /* Blank out the mapping, to make catching errors easier */
278                 _wapi_handle_fd_offset_store (file_private_handle->fd_mapped.fd, NULL);
279         
280                 close(file_private_handle->fd_mapped.fd);
281         }
282 }
283
284 static WapiFileType file_getfiletype(void)
285 {
286         return(FILE_TYPE_DISK);
287 }
288
289 #ifdef USE_AIO
290 typedef struct {
291         struct aiocb *aio;
292         WapiOverlapped *overlapped;
293         WapiOverlappedCB callback;
294 } notifier_data_t;
295
296 #define SIGPTR(a) a.SIGVAL_PTR
297
298 static void
299 async_notifier (union sigval sig)
300 {
301         notifier_data_t *ndata = SIGPTR (sig);
302         guint32 error;
303         guint32 numbytes;
304
305         error = aio_return (ndata->aio);
306         if (error < 0) {
307                 error = _wapi_get_win32_file_error (error);
308                 numbytes = 0;
309         } else {
310                 numbytes = error;
311                 error = 0;
312         }
313
314         ndata->callback (error, numbytes, ndata->overlapped);
315         g_free (ndata->aio);
316         g_free (ndata);
317 }
318
319 #endif /* USE_AIO */
320
321 static gboolean file_read(gpointer handle, gpointer buffer,
322                           guint32 numbytes, guint32 *bytesread,
323                           WapiOverlapped *overlapped)
324 {
325         struct _WapiHandle_file *file_handle;
326         struct _WapiHandlePrivate_file *file_private_handle;
327         gboolean ok;
328         int ret;
329         
330         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
331                                 (gpointer *)&file_handle,
332                                 (gpointer *)&file_private_handle);
333         if(ok==FALSE) {
334                 g_warning (G_GNUC_PRETTY_FUNCTION
335                            ": error looking up file handle %p", handle);
336                 SetLastError (ERROR_INVALID_HANDLE);
337                 return(FALSE);
338         }
339
340         if (file_private_handle->fd_mapped.assigned == FALSE) {
341                 SetLastError (ERROR_INVALID_HANDLE);
342                 return(FALSE);
343         }
344         
345         if(bytesread!=NULL) {
346                 *bytesread=0;
347         }
348         
349         if(!(file_handle->fileaccess&GENERIC_READ) &&
350            !(file_handle->fileaccess&GENERIC_ALL)) {
351 #ifdef DEBUG
352                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
353 #endif
354
355                 SetLastError (ERROR_ACCESS_DENIED);
356                 return(FALSE);
357         }
358
359         if (file_private_handle->async == FALSE) {
360                 do {
361                         ret=read(file_private_handle->fd_mapped.fd, buffer,
362                                  numbytes);
363                 }
364                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
365                         
366                 if(ret==-1) {
367                         gint err = errno;
368
369 #ifdef DEBUG
370                         g_message(G_GNUC_PRETTY_FUNCTION
371                                   ": read of handle %p fd %d error: %s", handle,
372                                   file_private_handle->fd_mapped.fd,
373                                   strerror(err));
374 #endif
375                         SetLastError (_wapi_get_win32_file_error (err));
376                         return(FALSE);
377                 }
378                 
379                 if(bytesread!=NULL) {
380                         *bytesread=ret;
381                 }
382                 
383                 return(TRUE);
384         }
385
386 #ifndef USE_AIO
387         SetLastError (ERROR_NOT_SUPPORTED);
388         return FALSE;
389 #else
390         if (overlapped == NULL || file_private_handle->callback == NULL) {
391                 SetLastError (ERROR_INVALID_PARAMETER);
392                 return FALSE;
393         }
394
395         {
396         int fd = file_private_handle->fd_mapped.fd;
397         struct aiocb *aio;
398         int result;
399         notifier_data_t *ndata;
400
401         ndata = g_new0 (notifier_data_t, 1);
402         aio = g_new0 (struct aiocb, 1);
403         ndata->overlapped = overlapped;
404         ndata->aio = aio;
405         ndata->callback = file_private_handle->callback;
406
407         aio->aio_fildes = fd;
408         aio->aio_lio_opcode = LIO_READ;
409         aio->aio_nbytes = numbytes;
410         aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
411         aio->aio_buf = buffer;
412         aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
413         aio->aio_sigevent.sigev_notify_function = async_notifier;
414         SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
415
416         result = aio_read (aio);
417         if (result == -1) {
418                 _wapi_set_last_error_from_errno ();
419                 return FALSE;
420         }
421
422         result = aio_error (aio);
423 #ifdef DEBUG
424         g_print ("aio_error (read) returned %d for %d\n", result, fd);
425 #endif
426         if (result == 0) {
427                 numbytes = aio_return (aio);
428 #ifdef DEBUG
429                 g_print ("numbytes %d for %d\n", numbytes, fd);
430 #endif
431         } else {
432                 errno = result;
433                 _wapi_set_last_error_from_errno ();
434                 return FALSE;
435         }
436
437         if (bytesread)
438                 *bytesread = numbytes;
439
440         return TRUE;
441         }
442 #endif
443 }
444
445 static gboolean file_write(gpointer handle, gconstpointer buffer,
446                            guint32 numbytes, guint32 *byteswritten,
447                            WapiOverlapped *overlapped G_GNUC_UNUSED)
448 {
449         struct _WapiHandle_file *file_handle;
450         struct _WapiHandlePrivate_file *file_private_handle;
451         gboolean ok;
452         int ret;
453         
454         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
455                                 (gpointer *)&file_handle,
456                                 (gpointer *)&file_private_handle);
457         if(ok==FALSE) {
458                 g_warning (G_GNUC_PRETTY_FUNCTION
459                            ": error looking up file handle %p", handle);
460                 SetLastError (ERROR_INVALID_HANDLE);
461                 return(FALSE);
462         }
463
464         if (file_private_handle->fd_mapped.assigned == FALSE) {
465                 SetLastError (ERROR_INVALID_HANDLE);
466                 return(FALSE);
467         }
468         
469         if(byteswritten!=NULL) {
470                 *byteswritten=0;
471         }
472         
473         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
474            !(file_handle->fileaccess&GENERIC_ALL)) {
475 #ifdef DEBUG
476                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
477 #endif
478
479                 SetLastError (ERROR_ACCESS_DENIED);
480                 return(FALSE);
481         }
482         
483         if (file_private_handle->async == FALSE) {
484                 off_t current_pos;
485                 
486                 /* Need to lock the region we're about to write to,
487                  * because we only do advisory locking on POSIX
488                  * systems
489                  */
490                 current_pos = lseek (file_private_handle->fd_mapped.fd,
491                                      (off_t)0, SEEK_CUR);
492                 if (current_pos == -1) {
493 #ifdef DEBUG
494                         g_message (G_GNUC_PRETTY_FUNCTION ": handle %p fd %d lseek failed: %s", handle, file_private_handle->fd_mapped.fd, strerror (errno));
495 #endif
496                         _wapi_set_last_error_from_errno ();
497                         return(FALSE);
498                 }
499                 
500                 if (_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
501                                             current_pos, numbytes) == FALSE) {
502                         /* The error has already been set */
503                         return(FALSE);
504                 }
505                 
506                 do {
507                         ret=write(file_private_handle->fd_mapped.fd, buffer,
508                                   numbytes);
509                 }
510                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
511
512                 _wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
513                                           current_pos, numbytes);
514
515                 if(ret==-1) {
516 #ifdef DEBUG
517                         g_message(G_GNUC_PRETTY_FUNCTION
518                                   ": write of handle %p fd %d error: %s", handle,
519                                   file_private_handle->fd_mapped.fd,
520                                   strerror(errno));
521 #endif
522
523                         _wapi_set_last_error_from_errno ();
524                         return(FALSE);
525                 }
526                 if(byteswritten!=NULL) {
527                         *byteswritten=ret;
528                 }
529                 return(TRUE);
530         }
531
532 #ifndef USE_AIO
533         SetLastError (ERROR_NOT_SUPPORTED);
534         return FALSE;
535 #else
536         if (overlapped == NULL || file_private_handle->callback == NULL) {
537                 SetLastError (ERROR_INVALID_PARAMETER);
538                 return FALSE;
539         }
540
541         {
542         int fd = file_private_handle->fd_mapped.fd;
543         struct aiocb *aio;
544         int result;
545         notifier_data_t *ndata;
546
547         ndata = g_new0 (notifier_data_t, 1);
548         aio = g_new0 (struct aiocb, 1);
549         ndata->overlapped = overlapped;
550         ndata->aio = aio;
551         ndata->callback = file_private_handle->callback;
552
553         aio->aio_fildes = fd;
554         aio->aio_lio_opcode = LIO_WRITE;
555         aio->aio_nbytes = numbytes;
556         aio->aio_offset = overlapped->Offset + (((gint64) overlapped->OffsetHigh) << 32);
557         aio->aio_buf = (gpointer) buffer;
558         aio->aio_sigevent.sigev_notify = SIGEV_THREAD;
559         aio->aio_sigevent.sigev_notify_function = async_notifier;
560         SIGPTR (aio->aio_sigevent.sigev_value) = ndata;
561
562         result = aio_write (aio);
563         if (result == -1) {
564                 _wapi_set_last_error_from_errno ();
565                 return FALSE;
566         }
567
568         result = aio_error (aio);
569 #ifdef DEBUG
570         g_print ("aio_error (write) returned %d for %d\n", result, fd);
571 #endif
572         if (result == 0) {
573                 numbytes = aio_return (aio);
574 #ifdef DEBUG
575         g_print ("numbytes %d for %d\n", numbytes, fd);
576 #endif
577         } else {
578                 errno = result;
579                 _wapi_set_last_error_from_errno ();
580                 return FALSE;
581         }
582
583         if (byteswritten)
584                 *byteswritten = numbytes;
585
586         return TRUE;
587         }
588 #endif
589 }
590
591 static gboolean file_flush(gpointer handle)
592 {
593         struct _WapiHandle_file *file_handle;
594         struct _WapiHandlePrivate_file *file_private_handle;
595         gboolean ok;
596         int ret;
597         
598         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
599                                 (gpointer *)&file_handle,
600                                 (gpointer *)&file_private_handle);
601         if(ok==FALSE) {
602                 g_warning (G_GNUC_PRETTY_FUNCTION
603                            ": error looking up file handle %p", handle);
604                 SetLastError (ERROR_INVALID_HANDLE);
605                 return(FALSE);
606         }
607
608         if (file_private_handle->fd_mapped.assigned == FALSE) {
609                 SetLastError (ERROR_INVALID_HANDLE);
610                 return(FALSE);
611         }
612
613         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
614            !(file_handle->fileaccess&GENERIC_ALL)) {
615 #ifdef DEBUG
616                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
617 #endif
618
619                 SetLastError (ERROR_ACCESS_DENIED);
620                 return(FALSE);
621         }
622
623         ret=fsync(file_private_handle->fd_mapped.fd);
624         if (ret==-1) {
625 #ifdef DEBUG
626                 g_message(G_GNUC_PRETTY_FUNCTION
627                           ": fsync of handle %p fd %d error: %s", handle,
628                           file_private_handle->fd_mapped.fd, strerror(errno));
629 #endif
630
631                 _wapi_set_last_error_from_errno ();
632                 return(FALSE);
633         }
634         
635         return(TRUE);
636 }
637
638 static guint32 file_seek(gpointer handle, gint32 movedistance,
639                          gint32 *highmovedistance, WapiSeekMethod method)
640 {
641         struct _WapiHandle_file *file_handle;
642         struct _WapiHandlePrivate_file *file_private_handle;
643         gboolean ok;
644         off_t offset, newpos;
645         int whence;
646         guint32 ret;
647         
648         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
649                                 (gpointer *)&file_handle,
650                                 (gpointer *)&file_private_handle);
651         if(ok==FALSE) {
652                 g_warning (G_GNUC_PRETTY_FUNCTION
653                            ": error looking up file handle %p", handle);
654                 SetLastError (ERROR_INVALID_HANDLE);
655                 return(INVALID_SET_FILE_POINTER);
656         }
657
658         if (file_private_handle->fd_mapped.assigned == FALSE) {
659                 SetLastError (ERROR_INVALID_HANDLE);
660                 return(FALSE);
661         }
662         
663         if(!(file_handle->fileaccess&GENERIC_READ) &&
664            !(file_handle->fileaccess&GENERIC_WRITE) &&
665            !(file_handle->fileaccess&GENERIC_ALL)) {
666 #ifdef DEBUG
667                 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_mapped.fd, file_handle->fileaccess);
668 #endif
669
670                 SetLastError (ERROR_ACCESS_DENIED);
671                 return(INVALID_SET_FILE_POINTER);
672         }
673
674         switch(method) {
675         case FILE_BEGIN:
676                 whence=SEEK_SET;
677                 break;
678         case FILE_CURRENT:
679                 whence=SEEK_CUR;
680                 break;
681         case FILE_END:
682                 whence=SEEK_END;
683                 break;
684         default:
685 #ifdef DEBUG
686                 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
687                           method);
688 #endif
689
690                 SetLastError (ERROR_INVALID_PARAMETER);
691                 return(INVALID_SET_FILE_POINTER);
692         }
693
694 #ifdef HAVE_LARGE_FILE_SUPPORT
695         if(highmovedistance==NULL) {
696                 offset=movedistance;
697 #ifdef DEBUG
698                 g_message(G_GNUC_PRETTY_FUNCTION
699                           ": setting offset to %lld (low %d)", offset,
700                           movedistance);
701 #endif
702         } else {
703                 offset=((gint64) *highmovedistance << 32) | (unsigned long)movedistance;
704                 
705 #ifdef DEBUG
706                 g_message(G_GNUC_PRETTY_FUNCTION ": setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
707 #endif
708         }
709 #else
710         offset=movedistance;
711 #endif
712
713 #ifdef DEBUG
714 #ifdef HAVE_LARGE_FILE_SUPPORT
715         g_message(G_GNUC_PRETTY_FUNCTION
716                   ": moving handle %p fd %d by %lld bytes from %d", handle,
717                   file_private_handle->fd_mapped.fd, offset, whence);
718 #else
719         g_message(G_GNUC_PRETTY_FUNCTION
720                   ": moving handle %p fd %d by %ld bytes from %d", handle,
721                   file_private_handle->fd_mapped.fd, offset, whence);
722 #endif
723 #endif
724
725         newpos=lseek(file_private_handle->fd_mapped.fd, offset, whence);
726         if(newpos==-1) {
727 #ifdef DEBUG
728                 g_message(G_GNUC_PRETTY_FUNCTION
729                           ": lseek on handle %p fd %d returned error %s",
730                           handle, file_private_handle->fd_mapped.fd,
731                           strerror(errno));
732 #endif
733
734                 _wapi_set_last_error_from_errno ();
735                 return(INVALID_SET_FILE_POINTER);
736         }
737
738 #ifdef DEBUG
739 #ifdef HAVE_LARGE_FILE_SUPPORT
740         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
741 #else
742         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
743 #endif
744 #endif
745
746 #ifdef HAVE_LARGE_FILE_SUPPORT
747         ret=newpos & 0xFFFFFFFF;
748         if(highmovedistance!=NULL) {
749                 *highmovedistance=newpos>>32;
750         }
751 #else
752         ret=newpos;
753         if(highmovedistance!=NULL) {
754                 /* Accurate, but potentially dodgy :-) */
755                 *highmovedistance=0;
756         }
757 #endif
758
759 #ifdef DEBUG
760         g_message(G_GNUC_PRETTY_FUNCTION
761                   ": move of handle %p fd %d returning %d/%d", handle,
762                   file_private_handle->fd_mapped.fd, ret,
763                   highmovedistance==NULL?0:*highmovedistance);
764 #endif
765
766         return(ret);
767 }
768
769 static gboolean file_setendoffile(gpointer handle)
770 {
771         struct _WapiHandle_file *file_handle;
772         struct _WapiHandlePrivate_file *file_private_handle;
773         gboolean ok;
774         struct stat statbuf;
775         off_t size, pos;
776         int ret;
777         
778         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
779                                 (gpointer *)&file_handle,
780                                 (gpointer *)&file_private_handle);
781         if(ok==FALSE) {
782                 g_warning (G_GNUC_PRETTY_FUNCTION
783                            ": error looking up file handle %p", handle);
784                 SetLastError (ERROR_INVALID_HANDLE);
785                 return(FALSE);
786         }
787
788         if (file_private_handle->fd_mapped.assigned == FALSE) {
789                 SetLastError (ERROR_INVALID_HANDLE);
790                 return(FALSE);
791         }
792         
793         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
794            !(file_handle->fileaccess&GENERIC_ALL)) {
795 #ifdef DEBUG
796                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
797 #endif
798
799                 SetLastError (ERROR_ACCESS_DENIED);
800                 return(FALSE);
801         }
802
803         /* Find the current file position, and the file length.  If
804          * the file position is greater than the length, write to
805          * extend the file with a hole.  If the file position is less
806          * than the length, truncate the file.
807          */
808         
809         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
810         if(ret==-1) {
811 #ifdef DEBUG
812                 g_message(G_GNUC_PRETTY_FUNCTION
813                           ": handle %p fd %d fstat failed: %s", handle,
814                           file_private_handle->fd_mapped.fd, strerror(errno));
815 #endif
816
817                 _wapi_set_last_error_from_errno ();
818                 return(FALSE);
819         }
820         size=statbuf.st_size;
821
822         pos=lseek(file_private_handle->fd_mapped.fd, (off_t)0, SEEK_CUR);
823         if(pos==-1) {
824 #ifdef DEBUG
825                 g_message(G_GNUC_PRETTY_FUNCTION
826                           ": handle %p fd %d lseek failed: %s", handle,
827                           file_private_handle->fd_mapped.fd, strerror(errno));
828 #endif
829
830                 _wapi_set_last_error_from_errno ();
831                 return(FALSE);
832         }
833         
834         if(pos>size) {
835                 /* extend */
836                 do {
837                         ret=write(file_private_handle->fd_mapped.fd, "", 1);
838                 }
839                 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
840
841                 if(ret==-1) {
842 #ifdef DEBUG
843                         g_message(G_GNUC_PRETTY_FUNCTION
844                                   ": handle %p fd %d extend write failed: %s",
845                                   handle, file_private_handle->fd_mapped.fd,
846                                   strerror(errno));
847 #endif
848
849                         _wapi_set_last_error_from_errno ();
850                         return(FALSE);
851                 }
852         }
853
854         /* always truncate, because the extend write() adds an extra
855          * byte to the end of the file
856          */
857         do {
858                 ret=ftruncate(file_private_handle->fd_mapped.fd, pos);
859         }
860         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
861                         
862         if(ret==-1) {
863 #ifdef DEBUG
864                 g_message(G_GNUC_PRETTY_FUNCTION
865                           ": handle %p fd %d ftruncate failed: %s", handle,
866                           file_private_handle->fd_mapped.fd, strerror(errno));
867 #endif
868                 
869                 _wapi_set_last_error_from_errno ();
870                 return(FALSE);
871         }
872                 
873         return(TRUE);
874 }
875
876 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
877 {
878         struct _WapiHandle_file *file_handle;
879         struct _WapiHandlePrivate_file *file_private_handle;
880         gboolean ok;
881         struct stat statbuf;
882         guint32 size;
883         int ret;
884         
885         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
886                                 (gpointer *)&file_handle,
887                                 (gpointer *)&file_private_handle);
888         if(ok==FALSE) {
889                 g_warning (G_GNUC_PRETTY_FUNCTION
890                            ": error looking up file handle %p", handle);
891                 SetLastError (ERROR_INVALID_HANDLE);
892                 return(INVALID_FILE_SIZE);
893         }
894
895         if (file_private_handle->fd_mapped.assigned == FALSE) {
896                 SetLastError (ERROR_INVALID_HANDLE);
897                 return(FALSE);
898         }
899         
900         if(!(file_handle->fileaccess&GENERIC_READ) &&
901            !(file_handle->fileaccess&GENERIC_WRITE) &&
902            !(file_handle->fileaccess&GENERIC_ALL)) {
903 #ifdef DEBUG
904                 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_mapped.fd, file_handle->fileaccess);
905 #endif
906
907                 SetLastError (ERROR_ACCESS_DENIED);
908                 return(INVALID_FILE_SIZE);
909         }
910
911         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
912         if(ret==-1) {
913 #ifdef DEBUG
914                 g_message(G_GNUC_PRETTY_FUNCTION
915                           ": handle %p fd %d fstat failed: %s", handle,
916                           file_private_handle->fd_mapped.fd, strerror(errno));
917 #endif
918
919                 _wapi_set_last_error_from_errno ();
920                 return(INVALID_FILE_SIZE);
921         }
922         
923 #ifdef HAVE_LARGE_FILE_SUPPORT
924         size=statbuf.st_size & 0xFFFFFFFF;
925         if(highsize!=NULL) {
926                 *highsize=statbuf.st_size>>32;
927         }
928 #else
929         if(highsize!=NULL) {
930                 /* Accurate, but potentially dodgy :-) */
931                 *highsize=0;
932         }
933         size=statbuf.st_size;
934 #endif
935
936 #ifdef DEBUG
937         g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
938                   *highsize);
939 #endif
940         
941         return(size);
942 }
943
944 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
945                                  WapiFileTime *last_access,
946                                  WapiFileTime *last_write)
947 {
948         struct _WapiHandle_file *file_handle;
949         struct _WapiHandlePrivate_file *file_private_handle;
950         gboolean ok;
951         struct stat statbuf;
952         guint64 create_ticks, access_ticks, write_ticks;
953         int ret;
954         
955         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
956                                 (gpointer *)&file_handle,
957                                 (gpointer *)&file_private_handle);
958         if(ok==FALSE) {
959                 g_warning (G_GNUC_PRETTY_FUNCTION
960                            ": error looking up file handle %p", handle);
961                 SetLastError (ERROR_INVALID_HANDLE);
962                 return(FALSE);
963         }
964
965         if (file_private_handle->fd_mapped.assigned == FALSE) {
966                 SetLastError (ERROR_INVALID_HANDLE);
967                 return(FALSE);
968         }
969         
970         if(!(file_handle->fileaccess&GENERIC_READ) &&
971            !(file_handle->fileaccess&GENERIC_ALL)) {
972 #ifdef DEBUG
973                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
974 #endif
975
976                 SetLastError (ERROR_ACCESS_DENIED);
977                 return(FALSE);
978         }
979         
980         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
981         if(ret==-1) {
982 #ifdef DEBUG
983                 g_message(G_GNUC_PRETTY_FUNCTION
984                           ": handle %p fd %d fstat failed: %s", handle,
985                           file_private_handle->fd_mapped.fd, strerror(errno));
986 #endif
987
988                 _wapi_set_last_error_from_errno ();
989                 return(FALSE);
990         }
991
992 #ifdef DEBUG
993         g_message(G_GNUC_PRETTY_FUNCTION
994                   ": atime: %ld ctime: %ld mtime: %ld",
995                   statbuf.st_atime, statbuf.st_ctime,
996                   statbuf.st_mtime);
997 #endif
998
999         /* Try and guess a meaningful create time by using the older
1000          * of atime or ctime
1001          */
1002         /* The magic constant comes from msdn documentation
1003          * "Converting a time_t Value to a File Time"
1004          */
1005         if(statbuf.st_atime < statbuf.st_ctime) {
1006                 create_ticks=((guint64)statbuf.st_atime*10000000)
1007                         + 116444736000000000ULL;
1008         } else {
1009                 create_ticks=((guint64)statbuf.st_ctime*10000000)
1010                         + 116444736000000000ULL;
1011         }
1012         
1013         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1014         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1015         
1016 #ifdef DEBUG
1017                 g_message(G_GNUC_PRETTY_FUNCTION
1018                           ": aticks: %llu cticks: %llu wticks: %llu",
1019                           access_ticks, create_ticks, write_ticks);
1020 #endif
1021
1022         if(create_time!=NULL) {
1023                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1024                 create_time->dwHighDateTime = create_ticks >> 32;
1025         }
1026         
1027         if(last_access!=NULL) {
1028                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1029                 last_access->dwHighDateTime = access_ticks >> 32;
1030         }
1031         
1032         if(last_write!=NULL) {
1033                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1034                 last_write->dwHighDateTime = write_ticks >> 32;
1035         }
1036
1037         return(TRUE);
1038 }
1039
1040 static gboolean file_setfiletime(gpointer handle,
1041                                  const WapiFileTime *create_time G_GNUC_UNUSED,
1042                                  const WapiFileTime *last_access,
1043                                  const WapiFileTime *last_write)
1044 {
1045         struct _WapiHandle_file *file_handle;
1046         struct _WapiHandlePrivate_file *file_private_handle;
1047         gboolean ok;
1048         guchar *name;
1049         struct utimbuf utbuf;
1050         struct stat statbuf;
1051         guint64 access_ticks, write_ticks;
1052         int ret;
1053         
1054         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1055                                 (gpointer *)&file_handle,
1056                                 (gpointer *)&file_private_handle);
1057         if(ok==FALSE) {
1058                 g_warning (G_GNUC_PRETTY_FUNCTION
1059                            ": error looking up file handle %p", handle);
1060                 SetLastError (ERROR_INVALID_HANDLE);
1061                 return(FALSE);
1062         }
1063
1064         if (file_private_handle->fd_mapped.assigned == FALSE) {
1065                 SetLastError (ERROR_INVALID_HANDLE);
1066                 return(FALSE);
1067         }
1068         
1069         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
1070            !(file_handle->fileaccess&GENERIC_ALL)) {
1071 #ifdef DEBUG
1072                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd_mapped.fd, file_handle->fileaccess);
1073 #endif
1074
1075                 SetLastError (ERROR_ACCESS_DENIED);
1076                 return(FALSE);
1077         }
1078
1079         if(file_handle->filename==0) {
1080 #ifdef DEBUG
1081                 g_message(G_GNUC_PRETTY_FUNCTION
1082                           ": handle %p fd %d unknown filename", handle,
1083                           file_private_handle->fd_mapped.fd);
1084 #endif
1085
1086                 SetLastError (ERROR_INVALID_HANDLE);
1087                 return(FALSE);
1088         }
1089         
1090         /* Get the current times, so we can put the same times back in
1091          * the event that one of the FileTime structs is NULL
1092          */
1093         ret=fstat(file_private_handle->fd_mapped.fd, &statbuf);
1094         if(ret==-1) {
1095 #ifdef DEBUG
1096                 g_message(G_GNUC_PRETTY_FUNCTION
1097                           ": handle %p fd %d fstat failed: %s", handle,
1098                           file_private_handle->fd_mapped.fd, strerror(errno));
1099 #endif
1100
1101                 SetLastError (ERROR_INVALID_PARAMETER);
1102                 return(FALSE);
1103         }
1104
1105         if(last_access!=NULL) {
1106                 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
1107                         last_access->dwLowDateTime;
1108                 /* This is (time_t)0.  We can actually go to INT_MIN,
1109                  * but this will do for now.
1110                  */
1111                 if (access_ticks < 116444736000000000ULL) {
1112 #ifdef DEBUG
1113                         g_message (G_GNUC_PRETTY_FUNCTION
1114                                    ": attempt to set access time too early");
1115 #endif
1116                         SetLastError (ERROR_INVALID_PARAMETER);
1117                         return(FALSE);
1118                 }
1119                 
1120                 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1121         } else {
1122                 utbuf.actime=statbuf.st_atime;
1123         }
1124
1125         if(last_write!=NULL) {
1126                 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
1127                         last_write->dwLowDateTime;
1128                 /* This is (time_t)0.  We can actually go to INT_MIN,
1129                  * but this will do for now.
1130                  */
1131                 if (write_ticks < 116444736000000000ULL) {
1132 #ifdef DEBUG
1133                         g_message (G_GNUC_PRETTY_FUNCTION
1134                                    ": attempt to set write time too early");
1135 #endif
1136                         SetLastError (ERROR_INVALID_PARAMETER);
1137                         return(FALSE);
1138                 }
1139                 
1140                 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1141         } else {
1142                 utbuf.modtime=statbuf.st_mtime;
1143         }
1144
1145 #ifdef DEBUG
1146         g_message(G_GNUC_PRETTY_FUNCTION
1147                   ": setting handle %p access %ld write %ld", handle,
1148                   utbuf.actime, utbuf.modtime);
1149 #endif
1150
1151         name=_wapi_handle_scratch_lookup (file_handle->filename);
1152
1153         ret=utime(name, &utbuf);
1154         if(ret==-1) {
1155 #ifdef DEBUG
1156                 g_message(G_GNUC_PRETTY_FUNCTION
1157                           ": handle %p [%s] fd %d utime failed: %s", handle,
1158                           name, file_private_handle->fd_mapped.fd,
1159                           strerror(errno));
1160
1161 #endif
1162                 g_free (name);
1163                 SetLastError (ERROR_INVALID_PARAMETER);
1164                 return(FALSE);
1165         }
1166
1167         g_free (name);
1168         
1169         return(TRUE);
1170 }
1171
1172 static void console_close_shared (gpointer handle)
1173 {
1174         struct _WapiHandle_file *console_handle;
1175         gboolean ok;
1176         
1177         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1178                                 (gpointer *)&console_handle, NULL);
1179         if(ok==FALSE) {
1180                 g_warning (G_GNUC_PRETTY_FUNCTION
1181                            ": error looking up console handle %p", handle);
1182                 SetLastError (ERROR_INVALID_HANDLE);
1183                 return;
1184         }
1185         
1186 #ifdef DEBUG
1187         g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
1188 #endif
1189         
1190         if(console_handle->filename!=0) {
1191                 _wapi_handle_scratch_delete (console_handle->filename);
1192                 console_handle->filename=0;
1193         }
1194         if(console_handle->security_attributes!=0) {
1195                 _wapi_handle_scratch_delete (console_handle->security_attributes);
1196                 console_handle->security_attributes=0;
1197         }
1198 }
1199
1200 static void console_close_private (gpointer handle)
1201 {
1202         struct _WapiHandlePrivate_file *console_private_handle;
1203         gboolean ok;
1204         
1205         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
1206                                 (gpointer *)&console_private_handle);
1207         if(ok==FALSE) {
1208                 g_warning (G_GNUC_PRETTY_FUNCTION
1209                            ": error looking up console handle %p", handle);
1210                 SetLastError (ERROR_INVALID_HANDLE);
1211                 return;
1212         }
1213         
1214         if (console_private_handle->fd_mapped.assigned == TRUE) {
1215 #ifdef DEBUG
1216                 g_message(G_GNUC_PRETTY_FUNCTION
1217                           ": closing console handle %p with fd %d", handle,
1218                           console_private_handle->fd_mapped.fd);
1219 #endif
1220         
1221                 /* Blank out the mapping, to make catching errors easier */
1222                 _wapi_handle_fd_offset_store (console_private_handle->fd_mapped.fd, NULL);
1223         
1224                 close(console_private_handle->fd_mapped.fd);
1225         }
1226 }
1227
1228 static WapiFileType console_getfiletype(void)
1229 {
1230         return(FILE_TYPE_CHAR);
1231 }
1232
1233 static gboolean console_read(gpointer handle, gpointer buffer,
1234                              guint32 numbytes, guint32 *bytesread,
1235                              WapiOverlapped *overlapped G_GNUC_UNUSED)
1236 {
1237         struct _WapiHandle_file *console_handle;
1238         struct _WapiHandlePrivate_file *console_private_handle;
1239         gboolean ok;
1240         int ret;
1241         
1242         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1243                                 (gpointer *)&console_handle,
1244                                 (gpointer *)&console_private_handle);
1245         if(ok==FALSE) {
1246                 g_warning (G_GNUC_PRETTY_FUNCTION
1247                            ": error looking up console handle %p", handle);
1248                 SetLastError (ERROR_INVALID_HANDLE);
1249                 return(FALSE);
1250         }
1251
1252         if (console_private_handle->fd_mapped.assigned == FALSE) {
1253                 SetLastError (ERROR_INVALID_HANDLE);
1254                 return(FALSE);
1255         }
1256         
1257         if(bytesread!=NULL) {
1258                 *bytesread=0;
1259         }
1260         
1261         if(!(console_handle->fileaccess&GENERIC_READ) &&
1262            !(console_handle->fileaccess&GENERIC_ALL)) {
1263 #ifdef DEBUG
1264                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1265 #endif
1266
1267                 SetLastError (ERROR_ACCESS_DENIED);
1268                 return(FALSE);
1269         }
1270         
1271         do {
1272                 ret=read(console_private_handle->fd_mapped.fd, buffer,
1273                          numbytes);
1274         }
1275         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1276
1277         if(ret==-1) {
1278 #ifdef DEBUG
1279                 g_message(G_GNUC_PRETTY_FUNCTION
1280                           ": read of handle %p fd %d error: %s", handle,
1281                           console_private_handle->fd_mapped.fd,
1282                           strerror(errno));
1283 #endif
1284
1285                 _wapi_set_last_error_from_errno ();
1286                 return(FALSE);
1287         }
1288         
1289         if(bytesread!=NULL) {
1290                 *bytesread=ret;
1291         }
1292         
1293         return(TRUE);
1294 }
1295
1296 static gboolean console_write(gpointer handle, gconstpointer buffer,
1297                               guint32 numbytes, guint32 *byteswritten,
1298                               WapiOverlapped *overlapped G_GNUC_UNUSED)
1299 {
1300         struct _WapiHandle_file *console_handle;
1301         struct _WapiHandlePrivate_file *console_private_handle;
1302         gboolean ok;
1303         int ret;
1304         
1305         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1306                                 (gpointer *)&console_handle,
1307                                 (gpointer *)&console_private_handle);
1308         if(ok==FALSE) {
1309                 g_warning (G_GNUC_PRETTY_FUNCTION
1310                            ": error looking up console handle %p", handle);
1311                 SetLastError (ERROR_INVALID_HANDLE);
1312                 return(FALSE);
1313         }
1314
1315         if (console_private_handle->fd_mapped.assigned == FALSE) {
1316                 SetLastError (ERROR_INVALID_HANDLE);
1317                 return(FALSE);
1318         }
1319         
1320         if(byteswritten!=NULL) {
1321                 *byteswritten=0;
1322         }
1323         
1324         if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1325            !(console_handle->fileaccess&GENERIC_ALL)) {
1326 #ifdef DEBUG
1327                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd_mapped.fd, console_handle->fileaccess);
1328 #endif
1329
1330                 SetLastError (ERROR_ACCESS_DENIED);
1331                 return(FALSE);
1332         }
1333         
1334         do {
1335                 ret=write(console_private_handle->fd_mapped.fd, buffer,
1336                           numbytes);
1337         }
1338         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1339
1340         if(ret==-1) {
1341 #ifdef DEBUG
1342                 g_message(G_GNUC_PRETTY_FUNCTION
1343                           ": write of handle %p fd %d error: %s", handle,
1344                           console_private_handle->fd_mapped.fd,
1345                           strerror(errno));
1346 #endif
1347
1348                 _wapi_set_last_error_from_errno ();
1349                 return(FALSE);
1350         }
1351         if(byteswritten!=NULL) {
1352                 *byteswritten=ret;
1353         }
1354         
1355         return(TRUE);
1356 }
1357
1358 static void pipe_close_shared (gpointer handle)
1359 {
1360         struct _WapiHandle_file *pipe_handle;
1361         gboolean ok;
1362         
1363         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1364                                 (gpointer *)&pipe_handle, NULL);
1365         if(ok==FALSE) {
1366                 g_warning (G_GNUC_PRETTY_FUNCTION
1367                            ": error looking up pipe handle %p", handle);
1368                 SetLastError (ERROR_INVALID_HANDLE);
1369                 return;
1370         }
1371         
1372 #ifdef DEBUG
1373         g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1374 #endif
1375         
1376         if(pipe_handle->filename!=0) {
1377                 _wapi_handle_scratch_delete (pipe_handle->filename);
1378                 pipe_handle->filename=0;
1379         }
1380         if(pipe_handle->security_attributes!=0) {
1381                 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1382                 pipe_handle->security_attributes=0;
1383         }
1384 }
1385
1386 static void pipe_close_private (gpointer handle)
1387 {
1388         struct _WapiHandlePrivate_file *pipe_private_handle;
1389         gboolean ok;
1390         
1391         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1392                                 (gpointer *)&pipe_private_handle);
1393         if(ok==FALSE) {
1394                 g_warning (G_GNUC_PRETTY_FUNCTION
1395                            ": error looking up pipe handle %p", handle);
1396                 SetLastError (ERROR_INVALID_HANDLE);
1397                 return;
1398         }
1399         
1400         if (pipe_private_handle->fd_mapped.assigned == TRUE) {
1401 #ifdef DEBUG
1402                 g_message(G_GNUC_PRETTY_FUNCTION
1403                           ": closing pipe handle %p with fd %d", handle,
1404                           pipe_private_handle->fd_mapped.fd);
1405 #endif
1406         
1407                 /* Blank out the mapping, to make catching errors easier */
1408                 _wapi_handle_fd_offset_store (pipe_private_handle->fd_mapped.fd, NULL);
1409         
1410                 close(pipe_private_handle->fd_mapped.fd);
1411         }
1412 }
1413
1414 static WapiFileType pipe_getfiletype(void)
1415 {
1416         return(FILE_TYPE_PIPE);
1417 }
1418
1419 static gboolean pipe_read (gpointer handle, gpointer buffer,
1420                            guint32 numbytes, guint32 *bytesread,
1421                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1422 {
1423         struct _WapiHandle_file *pipe_handle;
1424         struct _WapiHandlePrivate_file *pipe_private_handle;
1425         gboolean ok;
1426         int ret;
1427         
1428         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1429                                 (gpointer *)&pipe_handle,
1430                                 (gpointer *)&pipe_private_handle);
1431         if(ok==FALSE) {
1432                 g_warning (G_GNUC_PRETTY_FUNCTION
1433                            ": error looking up pipe handle %p", handle);
1434                 SetLastError (ERROR_INVALID_HANDLE);
1435                 return(FALSE);
1436         }
1437
1438         if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1439                 SetLastError (ERROR_INVALID_HANDLE);
1440                 return(FALSE);
1441         }
1442
1443         if(bytesread!=NULL) {
1444                 *bytesread=0;
1445         }
1446         
1447         if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1448            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1449 #ifdef DEBUG
1450                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1451 #endif
1452
1453                 SetLastError (ERROR_ACCESS_DENIED);
1454                 return(FALSE);
1455         }
1456         
1457 #ifdef DEBUG
1458         g_message (G_GNUC_PRETTY_FUNCTION
1459                    ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1460                    handle, pipe_private_handle->fd_mapped.fd);
1461 #endif
1462
1463         do {
1464                 ret=read(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1465         }
1466         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1467                 
1468         if(ret==-1) {
1469 #ifdef DEBUG
1470                 g_message(G_GNUC_PRETTY_FUNCTION
1471                           ": read of handle %p fd %d error: %s", handle,
1472                           pipe_private_handle->fd_mapped.fd, strerror(errno));
1473 #endif
1474
1475                 _wapi_set_last_error_from_errno ();
1476                 return(FALSE);
1477         }
1478         
1479 #ifdef DEBUG
1480         g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1481 #endif
1482
1483         if(bytesread!=NULL) {
1484                 *bytesread=ret;
1485         }
1486         
1487         return(TRUE);
1488 }
1489
1490 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1491                            guint32 numbytes, guint32 *byteswritten,
1492                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1493 {
1494         struct _WapiHandle_file *pipe_handle;
1495         struct _WapiHandlePrivate_file *pipe_private_handle;
1496         gboolean ok;
1497         int ret;
1498         
1499         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1500                                 (gpointer *)&pipe_handle,
1501                                 (gpointer *)&pipe_private_handle);
1502         if(ok==FALSE) {
1503                 g_warning (G_GNUC_PRETTY_FUNCTION
1504                            ": error looking up pipe handle %p", handle);
1505                 SetLastError (ERROR_INVALID_HANDLE);
1506                 return(FALSE);
1507         }
1508
1509         if (pipe_private_handle->fd_mapped.assigned == FALSE) {
1510                 SetLastError (ERROR_INVALID_HANDLE);
1511                 return(FALSE);
1512         }
1513         
1514         if(byteswritten!=NULL) {
1515                 *byteswritten=0;
1516         }
1517         
1518         if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1519            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1520 #ifdef DEBUG
1521                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, pipe_private_handle->fd_mapped.fd, pipe_handle->fileaccess);
1522 #endif
1523
1524                 SetLastError (ERROR_ACCESS_DENIED);
1525                 return(FALSE);
1526         }
1527         
1528 #ifdef DEBUG
1529         g_message (G_GNUC_PRETTY_FUNCTION
1530                    ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1531                    handle, pipe_private_handle->fd_mapped.fd);
1532 #endif
1533
1534         do {
1535                 ret=write(pipe_private_handle->fd_mapped.fd, buffer, numbytes);
1536         }
1537         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1538
1539         if(ret==-1) {
1540 #ifdef DEBUG
1541                 g_message(G_GNUC_PRETTY_FUNCTION
1542                           ": write of handle %p fd %d error: %s", handle,
1543                           pipe_private_handle->fd_mapped.fd, strerror(errno));
1544 #endif
1545
1546                 _wapi_set_last_error_from_errno ();
1547                 return(FALSE);
1548         }
1549         if(byteswritten!=NULL) {
1550                 *byteswritten=ret;
1551         }
1552         
1553         return(TRUE);
1554 }
1555
1556 static int convert_flags(guint32 fileaccess, guint32 createmode)
1557 {
1558         int flags=0;
1559         
1560         switch(fileaccess) {
1561         case GENERIC_READ:
1562                 flags=O_RDONLY;
1563                 break;
1564         case GENERIC_WRITE:
1565                 flags=O_WRONLY;
1566                 break;
1567         case GENERIC_READ|GENERIC_WRITE:
1568                 flags=O_RDWR;
1569                 break;
1570         default:
1571 #ifdef DEBUG
1572                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1573                           fileaccess);
1574 #endif
1575                 break;
1576         }
1577
1578         switch(createmode) {
1579         case CREATE_NEW:
1580                 flags|=O_CREAT|O_EXCL;
1581                 break;
1582         case CREATE_ALWAYS:
1583                 flags|=O_CREAT|O_TRUNC;
1584                 break;
1585         case OPEN_EXISTING:
1586                 break;
1587         case OPEN_ALWAYS:
1588                 flags|=O_CREAT;
1589                 break;
1590         case TRUNCATE_EXISTING:
1591                 flags|=O_TRUNC;
1592                 break;
1593         default:
1594 #ifdef DEBUG
1595                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1596                           createmode);
1597 #endif
1598                 break;
1599         }
1600         
1601         return(flags);
1602 }
1603
1604 static guint32 convert_from_flags(int flags)
1605 {
1606         guint32 fileaccess=0;
1607         
1608 #ifndef O_ACCMODE
1609 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1610 #endif
1611
1612         if((flags & O_ACCMODE) == O_RDONLY) {
1613                 fileaccess=GENERIC_READ;
1614         } else if ((flags & O_ACCMODE) == O_WRONLY) {
1615                 fileaccess=GENERIC_WRITE;
1616         } else if ((flags & O_ACCMODE) == O_RDWR) {
1617                 fileaccess=GENERIC_READ|GENERIC_WRITE;
1618         } else {
1619 #ifdef DEBUG
1620                 g_message(G_GNUC_PRETTY_FUNCTION
1621                           ": Can't figure out flags 0x%x", flags);
1622 #endif
1623         }
1624
1625         /* Maybe sort out create mode too */
1626
1627         return(fileaccess);
1628 }
1629
1630 static mode_t convert_perms(guint32 sharemode)
1631 {
1632         mode_t perms=0600;
1633         
1634         if(sharemode&FILE_SHARE_READ) {
1635                 perms|=044;
1636         }
1637         if(sharemode&FILE_SHARE_WRITE) {
1638                 perms|=022;
1639         }
1640
1641         return(perms);
1642 }
1643
1644
1645 /**
1646  * CreateFile:
1647  * @name: a pointer to a NULL-terminated unicode string, that names
1648  * the file or other object to create.
1649  * @fileaccess: specifies the file access mode
1650  * @sharemode: whether the file should be shared.  This parameter is
1651  * currently ignored.
1652  * @security: Ignored for now.
1653  * @createmode: specifies whether to create a new file, whether to
1654  * overwrite an existing file, whether to truncate the file, etc.
1655  * @attrs: specifies file attributes and flags.  On win32 attributes
1656  * are characteristics of the file, not the handle, and are ignored
1657  * when an existing file is opened.  Flags give the library hints on
1658  * how to process a file to optimise performance.
1659  * @template: the handle of an open %GENERIC_READ file that specifies
1660  * attributes to apply to a newly created file, ignoring @attrs.
1661  * Normally this parameter is NULL.  This parameter is ignored when an
1662  * existing file is opened.
1663  *
1664  * Creates a new file handle.  This only applies to normal files:
1665  * pipes are handled by CreatePipe(), and console handles are created
1666  * with GetStdHandle().
1667  *
1668  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1669  */
1670 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1671                     guint32 sharemode, WapiSecurityAttributes *security,
1672                     guint32 createmode, guint32 attrs,
1673                     gpointer template G_GNUC_UNUSED)
1674 {
1675         struct _WapiHandle_file *file_handle;
1676         struct _WapiHandlePrivate_file *file_private_handle;
1677         gpointer handle;
1678         gboolean ok;
1679         int flags=convert_flags(fileaccess, createmode);
1680         /*mode_t perms=convert_perms(sharemode);*/
1681         /* we don't use sharemode, because that relates to sharing of the file
1682          * when the file is open and is already handled by other code, perms instead
1683          * are the on-disk permissions and this is a sane default.
1684          */
1685         mode_t perms=0644;
1686         gchar *filename;
1687         int fd, ret;
1688         int thr_ret;
1689         gpointer cf_ret = INVALID_HANDLE_VALUE;
1690         struct stat statbuf;
1691         gboolean file_already_shared;
1692         guint32 file_existing_share, file_existing_access;
1693         
1694         mono_once (&io_ops_once, io_ops_init);
1695
1696         if(name==NULL) {
1697 #ifdef DEBUG
1698                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1699 #endif
1700
1701                 SetLastError (ERROR_INVALID_NAME);
1702                 return(INVALID_HANDLE_VALUE);
1703         }
1704
1705         filename=mono_unicode_to_external (name);
1706         if(filename==NULL) {
1707 #ifdef DEBUG
1708                 g_message(G_GNUC_PRETTY_FUNCTION
1709                           ": unicode conversion returned NULL");
1710 #endif
1711
1712                 SetLastError (ERROR_INVALID_NAME);
1713                 return(INVALID_HANDLE_VALUE);
1714         }
1715         
1716 #ifdef DEBUG
1717         g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess);
1718 #endif
1719         
1720         fd = open(filename, flags, perms);
1721     
1722         /* If we were trying to open a directory with write permissions
1723          * (e.g. O_WRONLY or O_RDWR), this call will fail with
1724          * EISDIR. However, this is a bit bogus because calls to
1725          * manipulate the directory (e.g. SetFileTime) will still work on
1726          * the directory because they use other API calls
1727          * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1728          * to open the directory again without write permission.
1729          */
1730         if (fd == -1 && errno == EISDIR)
1731         {
1732                 /* Try again but don't try to make it writable */
1733                 fd = open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
1734         }
1735         
1736         if (fd == -1) {
1737 #ifdef DEBUG
1738                 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1739                           filename, strerror(errno));
1740 #endif
1741                 _wapi_set_last_error_from_errno ();
1742                 g_free (filename);
1743
1744                 return(INVALID_HANDLE_VALUE);
1745         }
1746
1747         if (fd >= _wapi_fd_offset_table_size) {
1748 #ifdef DEBUG
1749                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
1750 #endif
1751
1752                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1753                 
1754                 close (fd);
1755                 g_free (filename);
1756                 
1757                 return(INVALID_HANDLE_VALUE);
1758         }
1759
1760         ret = fstat (fd, &statbuf);
1761         if (ret == -1) {
1762 #ifdef DEBUG
1763                 g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno));
1764 #endif
1765                 _wapi_set_last_error_from_errno ();
1766                 g_free (filename);
1767                 close (fd);
1768                 
1769                 return(INVALID_HANDLE_VALUE);
1770         }
1771
1772         file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access);
1773         
1774         if (file_already_shared) {
1775                 if (file_existing_share == 0) {
1776                         /* Quick and easy, no possibility to share */
1777 #ifdef DEBUG
1778                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess);
1779 #endif
1780                         SetLastError (ERROR_SHARING_VIOLATION);
1781                         g_free (filename);
1782                         close (fd);
1783                 
1784                         return(INVALID_HANDLE_VALUE);
1785                 }
1786
1787                 if (((file_existing_share == FILE_SHARE_READ) &&
1788                      (fileaccess != GENERIC_READ)) ||
1789                     ((file_existing_share == FILE_SHARE_WRITE) &&
1790                      (fileaccess != GENERIC_WRITE))) {
1791                         /* New access mode doesn't match up */
1792 #ifdef DEBUG
1793                         g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share);
1794 #endif
1795                         SetLastError (ERROR_SHARING_VIOLATION);
1796                         g_free (filename);
1797                         close (fd);
1798                 
1799                         return(INVALID_HANDLE_VALUE);
1800                 }
1801
1802                 if (((file_existing_access & GENERIC_READ) &&
1803                      !(sharemode & FILE_SHARE_READ)) ||
1804                     ((file_existing_access & GENERIC_WRITE) &&
1805                      !(sharemode & FILE_SHARE_WRITE))) {
1806                         /* New share mode doesn't match up */
1807 #ifdef DEBUG
1808                         g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access);
1809 #endif
1810                         SetLastError (ERROR_SHARING_VIOLATION);
1811                         g_free (filename);
1812                         close (fd);
1813                 
1814                         return(INVALID_HANDLE_VALUE);
1815                 }
1816         } else {
1817 #ifdef DEBUG
1818                 g_message (G_GNUC_PRETTY_FUNCTION ": New file!");
1819 #endif
1820         }
1821         
1822         handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1823         if(handle==_WAPI_HANDLE_INVALID) {
1824                 g_warning (G_GNUC_PRETTY_FUNCTION
1825                            ": error creating file handle");
1826                 g_free (filename);
1827                 close (fd);
1828                 
1829                 SetLastError (ERROR_GEN_FAILURE);
1830                 return(INVALID_HANDLE_VALUE);
1831         }
1832
1833         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
1834                               handle);
1835         thr_ret = _wapi_handle_lock_handle (handle);
1836         g_assert (thr_ret == 0);
1837         
1838         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1839                                 (gpointer *)&file_handle,
1840                                 (gpointer *)&file_private_handle);
1841         if(ok==FALSE) {
1842                 g_warning (G_GNUC_PRETTY_FUNCTION
1843                            ": error looking up file handle %p", handle);
1844                 SetLastError (ERROR_INVALID_HANDLE);
1845                 close (fd);
1846                 goto cleanup;
1847         }
1848
1849         _wapi_handle_fd_offset_store (fd, handle);
1850         cf_ret = GINT_TO_POINTER (fd);
1851
1852         file_private_handle->fd_mapped.fd=fd;
1853         file_private_handle->fd_mapped.assigned=TRUE;
1854         file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0);
1855         file_handle->filename=_wapi_handle_scratch_store (filename,
1856                                                           strlen (filename));
1857         if(security!=NULL) {
1858                 file_handle->security_attributes=_wapi_handle_scratch_store (
1859                         security, sizeof(WapiSecurityAttributes));
1860         }
1861         
1862         file_handle->fileaccess=fileaccess;
1863         file_handle->sharemode=sharemode;
1864         file_handle->attrs=attrs;
1865         file_handle->device = statbuf.st_dev;
1866         file_handle->inode = statbuf.st_ino;
1867         
1868 #ifdef DEBUG
1869         g_message(G_GNUC_PRETTY_FUNCTION
1870                   ": returning handle %p with fd %d", handle,
1871                   file_private_handle->fd_mapped.fd);
1872 #endif
1873
1874 cleanup:
1875         thr_ret = _wapi_handle_unlock_handle (handle);
1876         g_assert (thr_ret == 0);
1877         pthread_cleanup_pop (0);
1878         
1879         g_free (filename);
1880         
1881         return(cf_ret);
1882 }
1883
1884 /**
1885  * DeleteFile:
1886  * @name: a pointer to a NULL-terminated unicode string, that names
1887  * the file to be deleted.
1888  *
1889  * Deletes file @name.
1890  *
1891  * Return value: %TRUE on success, %FALSE otherwise.
1892  */
1893 gboolean DeleteFile(const gunichar2 *name)
1894 {
1895         gchar *filename;
1896         int ret;
1897         
1898         if(name==NULL) {
1899 #ifdef DEBUG
1900                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1901 #endif
1902
1903                 SetLastError (ERROR_INVALID_NAME);
1904                 return(FALSE);
1905         }
1906
1907         filename=mono_unicode_to_external(name);
1908         if(filename==NULL) {
1909 #ifdef DEBUG
1910                 g_message(G_GNUC_PRETTY_FUNCTION
1911                           ": unicode conversion returned NULL");
1912 #endif
1913
1914                 SetLastError (ERROR_INVALID_NAME);
1915                 return(FALSE);
1916         }
1917         
1918         ret=unlink(filename);
1919         
1920         g_free(filename);
1921
1922         if(ret==0) {
1923                 return(TRUE);
1924         }
1925
1926         _wapi_set_last_error_from_errno ();
1927         return(FALSE);
1928 }
1929
1930 /**
1931  * MoveFile:
1932  * @name: a pointer to a NULL-terminated unicode string, that names
1933  * the file to be moved.
1934  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1935  * new name for the file.
1936  *
1937  * Renames file @name to @dest_name
1938  *
1939  * Return value: %TRUE on success, %FALSE otherwise.
1940  */
1941 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1942 {
1943         gchar *utf8_name, *utf8_dest_name;
1944         int result;
1945         
1946         if(name==NULL) {
1947 #ifdef DEBUG
1948                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1949 #endif
1950
1951                 SetLastError (ERROR_INVALID_NAME);
1952                 return(FALSE);
1953         }
1954
1955         utf8_name = mono_unicode_to_external (name);
1956         if (utf8_name == NULL) {
1957 #ifdef DEBUG
1958                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1959 #endif
1960                 
1961                 SetLastError (ERROR_INVALID_NAME);
1962                 return FALSE;
1963         }
1964         
1965         if(dest_name==NULL) {
1966 #ifdef DEBUG
1967                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1968 #endif
1969
1970                 g_free (utf8_name);
1971                 SetLastError (ERROR_INVALID_NAME);
1972                 return(FALSE);
1973         }
1974
1975         utf8_dest_name = mono_unicode_to_external (dest_name);
1976         if (utf8_dest_name == NULL) {
1977 #ifdef DEBUG
1978                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1979 #endif
1980
1981                 g_free (utf8_name);
1982                 SetLastError (ERROR_INVALID_NAME);
1983                 return FALSE;
1984         }
1985
1986         result = rename (utf8_name, utf8_dest_name);
1987         g_free (utf8_name);
1988         g_free (utf8_dest_name);
1989
1990         if (result != 0 && errno == EXDEV) {
1991                 /* Try a copy to the new location, and delete the source */
1992                 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1993                         /* CopyFile will set the error */
1994                         return(FALSE);
1995                 }
1996                 
1997                 return(DeleteFile (name));
1998         }
1999
2000         if (result == 0) {
2001                 return TRUE;
2002         }
2003         
2004         switch (errno) {
2005         case EEXIST:
2006                 SetLastError (ERROR_ALREADY_EXISTS);
2007                 break;
2008         
2009         default:
2010                 _wapi_set_last_error_from_errno ();
2011                 break;
2012         }
2013
2014         return FALSE;
2015 }
2016
2017 /**
2018  * CopyFile:
2019  * @name: a pointer to a NULL-terminated unicode string, that names
2020  * the file to be copied.
2021  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
2022  * new name for the file.
2023  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
2024  *
2025  * Copies file @name to @dest_name
2026  *
2027  * Return value: %TRUE on success, %FALSE otherwise.
2028  */
2029 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
2030                    gboolean fail_if_exists)
2031 {
2032         gchar *utf8_src, *utf8_dest;
2033         int src_fd, dest_fd;
2034         int buf_size;
2035         char *buf;
2036         int remain, n;
2037         struct stat st;
2038         
2039         if(name==NULL) {
2040 #ifdef DEBUG
2041                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2042 #endif
2043
2044                 SetLastError (ERROR_INVALID_NAME);
2045                 return(FALSE);
2046         }
2047         
2048         utf8_src = mono_unicode_to_external (name);
2049         if (utf8_src == NULL) {
2050 #ifdef DEBUG
2051                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of source returned NULL");
2052 #endif
2053
2054                 SetLastError (ERROR_INVALID_PARAMETER);
2055                 return(FALSE);
2056         }
2057         
2058         if(dest_name==NULL) {
2059 #ifdef DEBUG
2060                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
2061 #endif
2062
2063                 g_free (utf8_src);
2064                 SetLastError (ERROR_INVALID_NAME);
2065                 return(FALSE);
2066         }
2067         
2068         utf8_dest = mono_unicode_to_external (dest_name);
2069         if (utf8_dest == NULL) {
2070 #ifdef DEBUG
2071                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion of dest returned NULL");
2072 #endif
2073
2074                 SetLastError (ERROR_INVALID_PARAMETER);
2075
2076                 g_free (utf8_src);
2077                 
2078                 return(FALSE);
2079         }
2080         
2081         src_fd = open (utf8_src, O_RDONLY);
2082         if (src_fd < 0) {
2083                 _wapi_set_last_error_from_errno ();
2084
2085                 g_free (utf8_src);
2086                 g_free (utf8_dest);
2087                 
2088                 return(FALSE);
2089         }
2090
2091         if (fstat (src_fd, &st) < 0) {
2092                 _wapi_set_last_error_from_errno ();
2093
2094                 g_free (utf8_src);
2095                 g_free (utf8_dest);
2096                 close (src_fd);
2097                 
2098                 return(FALSE);
2099         }
2100         
2101         if (fail_if_exists) {
2102                 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
2103         } else {
2104                 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2105                 if (dest_fd < 0) {
2106                         /* O_TRUNC might cause a fail if the file
2107                          * doesn't exist
2108                          */
2109                         dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2110                                         st.st_mode);
2111                 }
2112         }
2113         if (dest_fd < 0) {
2114                 _wapi_set_last_error_from_errno ();
2115
2116                 g_free (utf8_src);
2117                 g_free (utf8_dest);
2118                 close (src_fd);
2119                 
2120                 return(FALSE);
2121         }
2122         
2123         buf_size = st.st_blksize;
2124         buf = (char *) alloca (buf_size);
2125         
2126         for (;;) {
2127                 remain = read (src_fd, buf, buf_size);
2128                 
2129                 if (remain < 0) {
2130                         if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
2131                                 continue;
2132                         }
2133                         
2134                         _wapi_set_last_error_from_errno ();
2135
2136                         g_free (utf8_src);
2137                         g_free (utf8_dest);
2138                         close (src_fd);
2139                         close (dest_fd);
2140                         
2141                         return(FALSE);
2142                 }
2143                 
2144                 if (remain == 0) {
2145                         break;
2146                 }
2147
2148                 while (remain > 0) {
2149                         if ((n = write (dest_fd, buf, remain)) < 0) {
2150                                 if (errno == EINTR && !_wapi_thread_cur_apc_pending())
2151                                         continue;
2152
2153                                 _wapi_set_last_error_from_errno ();
2154 #ifdef DEBUG
2155                                 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
2156 #endif
2157
2158                                 g_free (utf8_src);
2159                                 g_free (utf8_dest);
2160                                 close (src_fd);
2161                                 close (dest_fd);
2162
2163                                 return (FALSE);
2164                         }
2165
2166                         remain -= n;
2167                 }
2168         }
2169
2170         g_free (utf8_src);
2171         g_free (utf8_dest);
2172         close (src_fd);
2173         close (dest_fd);
2174
2175         return(TRUE);
2176 }
2177
2178 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
2179 static gpointer stdin_handle=INVALID_HANDLE_VALUE;
2180 static gpointer stdout_handle=INVALID_HANDLE_VALUE;
2181 static gpointer stderr_handle=INVALID_HANDLE_VALUE;
2182
2183 static gpointer stdhandle_create (int fd, const guchar *name)
2184 {
2185         struct _WapiHandle_file *file_handle;
2186         struct _WapiHandlePrivate_file *file_private_handle;
2187         gboolean ok;
2188         gpointer handle, ret = INVALID_HANDLE_VALUE;
2189         int flags;
2190         int thr_ret;
2191         
2192 #ifdef DEBUG
2193         g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
2194                   name);
2195 #endif
2196         
2197         /* Check if fd is valid */
2198         do {
2199                 flags=fcntl(fd, F_GETFL);
2200         }
2201         while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
2202
2203         if(flags==-1) {
2204                 /* Invalid fd.  Not really much point checking for EBADF
2205                  * specifically
2206                  */
2207 #ifdef DEBUG
2208                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
2209                           fd, strerror(errno));
2210 #endif
2211
2212                 _wapi_set_last_error_from_errno ();
2213                 return(INVALID_HANDLE_VALUE);
2214         }
2215
2216         handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
2217         if(handle==_WAPI_HANDLE_INVALID) {
2218                 g_warning (G_GNUC_PRETTY_FUNCTION
2219                            ": error creating file handle");
2220                 SetLastError (ERROR_GEN_FAILURE);
2221                 return(INVALID_HANDLE_VALUE);
2222         }
2223
2224         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2225                               handle);
2226         thr_ret = _wapi_handle_lock_handle (handle);
2227         g_assert (thr_ret == 0);
2228         
2229         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2230                                 (gpointer *)&file_handle,
2231                                 (gpointer *)&file_private_handle);
2232         if(ok==FALSE) {
2233                 g_warning (G_GNUC_PRETTY_FUNCTION
2234                            ": error looking up console handle %p", handle);
2235                 SetLastError (ERROR_INVALID_HANDLE);
2236                 goto cleanup;
2237         }
2238
2239         /* We know this is fd 0, 1 or 2 */
2240         _wapi_handle_fd_offset_store (fd, handle);
2241         ret = GINT_TO_POINTER (fd);
2242         
2243         file_private_handle->fd_mapped.fd=fd;
2244         file_private_handle->fd_mapped.assigned=TRUE;
2245         file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
2246         /* some default security attributes might be needed */
2247         file_handle->security_attributes=0;
2248         file_handle->fileaccess=convert_from_flags(flags);
2249         file_handle->sharemode=0;
2250         file_handle->attrs=0;
2251         
2252 #ifdef DEBUG
2253         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
2254                   handle, file_private_handle->fd_mapped.fd);
2255 #endif
2256
2257 cleanup:
2258         thr_ret = _wapi_handle_unlock_handle (handle);
2259         g_assert (thr_ret == 0);
2260         pthread_cleanup_pop (0);
2261
2262         return(ret);
2263 }
2264
2265 static void stdhandle_init (void)
2266 {
2267         stdin_handle=stdhandle_create (0, "<stdin>");
2268         stdout_handle=stdhandle_create (1, "<stdout>");
2269         stderr_handle=stdhandle_create (2, "<stderr>");
2270 }
2271
2272 /**
2273  * GetStdHandle:
2274  * @stdhandle: specifies the file descriptor
2275  *
2276  * Returns a handle for stdin, stdout, or stderr.  Always returns the
2277  * same handle for the same @stdhandle.
2278  *
2279  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2280  */
2281
2282 gpointer GetStdHandle(WapiStdHandle stdhandle)
2283 {
2284         gpointer handle;
2285         
2286         mono_once (&io_ops_once, io_ops_init);
2287         mono_once (&stdhandle_once, stdhandle_init);
2288         
2289         switch(stdhandle) {
2290         case STD_INPUT_HANDLE:
2291                 handle=stdin_handle;
2292                 break;
2293
2294         case STD_OUTPUT_HANDLE:
2295                 handle=stdout_handle;
2296                 break;
2297
2298         case STD_ERROR_HANDLE:
2299                 handle=stderr_handle;
2300                 break;
2301
2302         default:
2303 #ifdef DEBUG
2304                 g_message(G_GNUC_PRETTY_FUNCTION
2305                           ": unknown standard handle type");
2306 #endif
2307
2308                 SetLastError (ERROR_INVALID_PARAMETER);
2309                 return(INVALID_HANDLE_VALUE);
2310         }
2311
2312         if (handle == INVALID_HANDLE_VALUE) {
2313                 SetLastError (ERROR_NO_MORE_FILES);
2314                 return(INVALID_HANDLE_VALUE);
2315         }
2316         
2317         /* Add a reference to this handle */
2318         _wapi_handle_ref (_wapi_handle_fd_offset_to_handle (handle));
2319         
2320         return(handle);
2321 }
2322
2323 /**
2324  * ReadFile:
2325  * @handle: The file handle to read from.  The handle must have
2326  * %GENERIC_READ access.
2327  * @buffer: The buffer to store read data in
2328  * @numbytes: The maximum number of bytes to read
2329  * @bytesread: The actual number of bytes read is stored here.  This
2330  * value can be zero if the handle is positioned at the end of the
2331  * file.
2332  * @overlapped: points to a required %WapiOverlapped structure if
2333  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2334  * otherwise.
2335  *
2336  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2337  * function reads up to @numbytes bytes from the file from the current
2338  * file position, and stores them in @buffer.  If there are not enough
2339  * bytes left in the file, just the amount available will be read.
2340  * The actual number of bytes read is stored in @bytesread.
2341
2342  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2343  * file position is ignored and the read position is taken from data
2344  * in the @overlapped structure.
2345  *
2346  * Return value: %TRUE if the read succeeds (even if no bytes were
2347  * read due to an attempt to read past the end of the file), %FALSE on
2348  * error.
2349  */
2350 gboolean ReadFile(gpointer fd_handle, gpointer buffer, guint32 numbytes,
2351                   guint32 *bytesread, WapiOverlapped *overlapped)
2352 {
2353         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2354         WapiHandleType type;
2355
2356         if (handle == NULL) {
2357                 SetLastError (ERROR_INVALID_HANDLE);
2358                 return(FALSE);
2359         }
2360         
2361         type = _wapi_handle_type (handle);
2362         
2363         if(io_ops[type].readfile==NULL) {
2364                 SetLastError (ERROR_INVALID_HANDLE);
2365                 return(FALSE);
2366         }
2367         
2368         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2369                                       overlapped));
2370 }
2371
2372 /**
2373  * WriteFile:
2374  * @handle: The file handle to write to.  The handle must have
2375  * %GENERIC_WRITE access.
2376  * @buffer: The buffer to read data from.
2377  * @numbytes: The maximum number of bytes to write.
2378  * @byteswritten: The actual number of bytes written is stored here.
2379  * If the handle is positioned at the file end, the length of the file
2380  * is extended.  This parameter may be %NULL.
2381  * @overlapped: points to a required %WapiOverlapped structure if
2382  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2383  * otherwise.
2384  *
2385  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2386  * function writes up to @numbytes bytes from @buffer to the file at
2387  * the current file position.  If @handle is positioned at the end of
2388  * the file, the file is extended.  The actual number of bytes written
2389  * is stored in @byteswritten.
2390  *
2391  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2392  * file position is ignored and the write position is taken from data
2393  * in the @overlapped structure.
2394  *
2395  * Return value: %TRUE if the write succeeds, %FALSE on error.
2396  */
2397 gboolean WriteFile(gpointer fd_handle, gconstpointer buffer, guint32 numbytes,
2398                    guint32 *byteswritten, WapiOverlapped *overlapped)
2399 {
2400         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2401         WapiHandleType type;
2402
2403         if (handle == NULL) {
2404                 SetLastError (ERROR_INVALID_HANDLE);
2405                 return(FALSE);
2406         }
2407         
2408         type = _wapi_handle_type (handle);
2409         
2410         if(io_ops[type].writefile==NULL) {
2411                 SetLastError (ERROR_INVALID_HANDLE);
2412                 return(FALSE);
2413         }
2414         
2415         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2416                                        overlapped));
2417 }
2418
2419 /**
2420  * FlushFileBuffers:
2421  * @handle: Handle to open file.  The handle must have
2422  * %GENERIC_WRITE access.
2423  *
2424  * Flushes buffers of the file and causes all unwritten data to
2425  * be written.
2426  *
2427  * Return value: %TRUE on success, %FALSE otherwise.
2428  */
2429 gboolean FlushFileBuffers(gpointer fd_handle)
2430 {
2431         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2432         WapiHandleType type;
2433
2434         if (handle == NULL) {
2435                 SetLastError (ERROR_INVALID_HANDLE);
2436                 return(FALSE);
2437         }
2438         
2439         type = _wapi_handle_type (handle);
2440         
2441         if(io_ops[type].flushfile==NULL) {
2442                 SetLastError (ERROR_INVALID_HANDLE);
2443                 return(FALSE);
2444         }
2445         
2446         return(io_ops[type].flushfile (handle));
2447 }
2448
2449 /**
2450  * SetEndOfFile:
2451  * @handle: The file handle to set.  The handle must have
2452  * %GENERIC_WRITE access.
2453  *
2454  * Moves the end-of-file position to the current position of the file
2455  * pointer.  This function is used to truncate or extend a file.
2456  *
2457  * Return value: %TRUE on success, %FALSE otherwise.
2458  */
2459 gboolean SetEndOfFile(gpointer fd_handle)
2460 {
2461         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2462         WapiHandleType type;
2463
2464         if (handle == NULL) {
2465                 SetLastError (ERROR_INVALID_HANDLE);
2466                 return(FALSE);
2467         }
2468         
2469         type = _wapi_handle_type (handle);
2470         
2471         if(io_ops[type].setendoffile==NULL) {
2472                 SetLastError (ERROR_INVALID_HANDLE);
2473                 return(FALSE);
2474         }
2475         
2476         return(io_ops[type].setendoffile (handle));
2477 }
2478
2479 /**
2480  * SetFilePointer:
2481  * @handle: The file handle to set.  The handle must have
2482  * %GENERIC_READ or %GENERIC_WRITE access.
2483  * @movedistance: Low 32 bits of a signed value that specifies the
2484  * number of bytes to move the file pointer.
2485  * @highmovedistance: Pointer to the high 32 bits of a signed value
2486  * that specifies the number of bytes to move the file pointer, or
2487  * %NULL.
2488  * @method: The starting point for the file pointer move.
2489  *
2490  * Sets the file pointer of an open file.
2491  *
2492  * The distance to move the file pointer is calculated from
2493  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2494  * @movedistance is the 32-bit signed value; otherwise, @movedistance
2495  * is the low 32 bits and @highmovedistance a pointer to the high 32
2496  * bits of a 64 bit signed value.  A positive distance moves the file
2497  * pointer forward from the position specified by @method; a negative
2498  * distance moves the file pointer backward.
2499  *
2500  * If the library is compiled without large file support,
2501  * @highmovedistance is ignored and its value is set to zero on a
2502  * successful return.
2503  *
2504  * Return value: On success, the low 32 bits of the new file pointer.
2505  * If @highmovedistance is not %NULL, the high 32 bits of the new file
2506  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
2507  */
2508 guint32 SetFilePointer(gpointer fd_handle, gint32 movedistance,
2509                        gint32 *highmovedistance, WapiSeekMethod method)
2510 {
2511         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2512         WapiHandleType type;
2513
2514         if (handle == NULL) {
2515                 SetLastError (ERROR_INVALID_HANDLE);
2516                 return(INVALID_SET_FILE_POINTER);
2517         }
2518         
2519         type = _wapi_handle_type (handle);
2520         
2521         if(io_ops[type].seek==NULL) {
2522                 SetLastError (ERROR_INVALID_HANDLE);
2523                 return(INVALID_SET_FILE_POINTER);
2524         }
2525         
2526         return(io_ops[type].seek (handle, movedistance, highmovedistance,
2527                                   method));
2528 }
2529
2530 /**
2531  * GetFileType:
2532  * @handle: The file handle to test.
2533  *
2534  * Finds the type of file @handle.
2535  *
2536  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2537  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
2538  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2539  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2540  */
2541 WapiFileType GetFileType(gpointer fd_handle)
2542 {
2543         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2544         WapiHandleType type;
2545
2546         if (handle == NULL) {
2547                 SetLastError (ERROR_INVALID_HANDLE);
2548                 return(FILE_TYPE_UNKNOWN);
2549         }
2550         
2551         type = _wapi_handle_type (handle);
2552         
2553         if(io_ops[type].getfiletype==NULL) {
2554                 SetLastError (ERROR_INVALID_HANDLE);
2555                 return(FILE_TYPE_UNKNOWN);
2556         }
2557         
2558         return(io_ops[type].getfiletype ());
2559 }
2560
2561 /**
2562  * GetFileSize:
2563  * @handle: The file handle to query.  The handle must have
2564  * %GENERIC_READ or %GENERIC_WRITE access.
2565  * @highsize: If non-%NULL, the high 32 bits of the file size are
2566  * stored here.
2567  *
2568  * Retrieves the size of the file @handle.
2569  *
2570  * If the library is compiled without large file support, @highsize
2571  * has its value set to zero on a successful return.
2572  *
2573  * Return value: On success, the low 32 bits of the file size.  If
2574  * @highsize is non-%NULL then the high 32 bits of the file size are
2575  * stored here.  On failure %INVALID_FILE_SIZE is returned.
2576  */
2577 guint32 GetFileSize(gpointer fd_handle, guint32 *highsize)
2578 {
2579         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2580         WapiHandleType type;
2581
2582         if (handle == NULL) {
2583                 SetLastError (ERROR_INVALID_HANDLE);
2584                 return(INVALID_FILE_SIZE);
2585         }
2586         
2587         type = _wapi_handle_type (handle);
2588         
2589         if(io_ops[type].getfilesize==NULL) {
2590                 SetLastError (ERROR_INVALID_HANDLE);
2591                 return(INVALID_FILE_SIZE);
2592         }
2593         
2594         return(io_ops[type].getfilesize (handle, highsize));
2595 }
2596
2597 /**
2598  * GetFileTime:
2599  * @handle: The file handle to query.  The handle must have
2600  * %GENERIC_READ access.
2601  * @create_time: Points to a %WapiFileTime structure to receive the
2602  * number of ticks since the epoch that file was created.  May be
2603  * %NULL.
2604  * @last_access: Points to a %WapiFileTime structure to receive the
2605  * number of ticks since the epoch when file was last accessed.  May be
2606  * %NULL.
2607  * @last_write: Points to a %WapiFileTime structure to receive the
2608  * number of ticks since the epoch when file was last written to.  May
2609  * be %NULL.
2610  *
2611  * Finds the number of ticks since the epoch that the file referenced
2612  * by @handle was created, last accessed and last modified.  A tick is
2613  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2614  * GMT.
2615  *
2616  * Create time isn't recorded on POSIX file systems or reported by
2617  * stat(2), so that time is guessed by returning the oldest of the
2618  * other times.
2619  *
2620  * Return value: %TRUE on success, %FALSE otherwise.
2621  */
2622 gboolean GetFileTime(gpointer fd_handle, WapiFileTime *create_time,
2623                      WapiFileTime *last_access, WapiFileTime *last_write)
2624 {
2625         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2626         WapiHandleType type;
2627
2628         if (handle == NULL) {
2629                 SetLastError (ERROR_INVALID_HANDLE);
2630                 return(FALSE);
2631         }
2632         
2633         type = _wapi_handle_type (handle);
2634         
2635         if(io_ops[type].getfiletime==NULL) {
2636                 SetLastError (ERROR_INVALID_HANDLE);
2637                 return(FALSE);
2638         }
2639         
2640         return(io_ops[type].getfiletime (handle, create_time, last_access,
2641                                          last_write));
2642 }
2643
2644 /**
2645  * SetFileTime:
2646  * @handle: The file handle to set.  The handle must have
2647  * %GENERIC_WRITE access.
2648  * @create_time: Points to a %WapiFileTime structure that contains the
2649  * number of ticks since the epoch that the file was created.  May be
2650  * %NULL.
2651  * @last_access: Points to a %WapiFileTime structure that contains the
2652  * number of ticks since the epoch when the file was last accessed.
2653  * May be %NULL.
2654  * @last_write: Points to a %WapiFileTime structure that contains the
2655  * number of ticks since the epoch when the file was last written to.
2656  * May be %NULL.
2657  *
2658  * Sets the number of ticks since the epoch that the file referenced
2659  * by @handle was created, last accessed or last modified.  A tick is
2660  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2661  * GMT.
2662  *
2663  * Create time isn't recorded on POSIX file systems, and is ignored.
2664  *
2665  * Return value: %TRUE on success, %FALSE otherwise.
2666  */
2667 gboolean SetFileTime(gpointer fd_handle, const WapiFileTime *create_time,
2668                      const WapiFileTime *last_access,
2669                      const WapiFileTime *last_write)
2670 {
2671         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
2672         WapiHandleType type;
2673
2674         if (handle == NULL) {
2675                 SetLastError (ERROR_INVALID_HANDLE);
2676                 return(FALSE);
2677         }
2678         
2679         type = _wapi_handle_type (handle);
2680         
2681         if(io_ops[type].setfiletime==NULL) {
2682                 SetLastError (ERROR_INVALID_HANDLE);
2683                 return(FALSE);
2684         }
2685         
2686         return(io_ops[type].setfiletime (handle, create_time, last_access,
2687                                          last_write));
2688 }
2689
2690 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2691  * January 1 1601 GMT
2692  */
2693
2694 #define TICKS_PER_MILLISECOND 10000L
2695 #define TICKS_PER_SECOND 10000000L
2696 #define TICKS_PER_MINUTE 600000000L
2697 #define TICKS_PER_HOUR 36000000000LL
2698 #define TICKS_PER_DAY 864000000000LL
2699
2700 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2701
2702 static const guint16 mon_yday[2][13]={
2703         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2704         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2705 };
2706
2707 /**
2708  * FileTimeToSystemTime:
2709  * @file_time: Points to a %WapiFileTime structure that contains the
2710  * number of ticks to convert.
2711  * @system_time: Points to a %WapiSystemTime structure to receive the
2712  * broken-out time.
2713  *
2714  * Converts a tick count into broken-out time values.
2715  *
2716  * Return value: %TRUE on success, %FALSE otherwise.
2717  */
2718 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2719                               WapiSystemTime *system_time)
2720 {
2721         gint64 file_ticks, totaldays, rem, y;
2722         const guint16 *ip;
2723         
2724         if(system_time==NULL) {
2725 #ifdef DEBUG
2726                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2727 #endif
2728
2729                 SetLastError (ERROR_INVALID_PARAMETER);
2730                 return(FALSE);
2731         }
2732         
2733         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2734                 file_time->dwLowDateTime;
2735         
2736         /* Really compares if file_ticks>=0x8000000000000000
2737          * (LLONG_MAX+1) but we're working with a signed value for the
2738          * year and day calculation to work later
2739          */
2740         if(file_ticks<0) {
2741 #ifdef DEBUG
2742                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2743 #endif
2744
2745                 SetLastError (ERROR_INVALID_PARAMETER);
2746                 return(FALSE);
2747         }
2748
2749         totaldays=(file_ticks / TICKS_PER_DAY);
2750         rem = file_ticks % TICKS_PER_DAY;
2751 #ifdef DEBUG
2752         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2753                   totaldays, rem);
2754 #endif
2755
2756         system_time->wHour=rem/TICKS_PER_HOUR;
2757         rem %= TICKS_PER_HOUR;
2758 #ifdef DEBUG
2759         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2760                   system_time->wHour, rem);
2761 #endif
2762         
2763         system_time->wMinute = rem / TICKS_PER_MINUTE;
2764         rem %= TICKS_PER_MINUTE;
2765 #ifdef DEBUG
2766         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2767                   system_time->wMinute, rem);
2768 #endif
2769         
2770         system_time->wSecond = rem / TICKS_PER_SECOND;
2771         rem %= TICKS_PER_SECOND;
2772 #ifdef DEBUG
2773         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2774                   system_time->wSecond, rem);
2775 #endif
2776         
2777         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2778 #ifdef DEBUG
2779         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2780                   system_time->wMilliseconds);
2781 #endif
2782
2783         /* January 1, 1601 was a Monday, according to Emacs calendar */
2784         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2785 #ifdef DEBUG
2786         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2787                   system_time->wDayOfWeek);
2788 #endif
2789         
2790         /* This algorithm to find year and month given days from epoch
2791          * from glibc
2792          */
2793         y=1601;
2794         
2795 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2796 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2797
2798         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2799                 /* Guess a corrected year, assuming 365 days per year */
2800                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2801 #ifdef DEBUG
2802                 g_message(G_GNUC_PRETTY_FUNCTION
2803                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2804                           y);
2805                 g_message(G_GNUC_PRETTY_FUNCTION
2806                           ": LEAPS(yg): %lld LEAPS(y): %lld",
2807                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2808 #endif
2809                 
2810                 /* Adjust days and y to match the guessed year. */
2811                 totaldays -= ((yg - y) * 365
2812                               + LEAPS_THRU_END_OF (yg - 1)
2813                               - LEAPS_THRU_END_OF (y - 1));
2814 #ifdef DEBUG
2815                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2816                           totaldays);
2817 #endif
2818                 y = yg;
2819 #ifdef DEBUG
2820                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2821 #endif
2822         }
2823         
2824         system_time->wYear = y;
2825 #ifdef DEBUG
2826         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2827 #endif
2828
2829         ip = mon_yday[isleap(y)];
2830         
2831         for(y=11; totaldays < ip[y]; --y) {
2832                 continue;
2833         }
2834         totaldays-=ip[y];
2835 #ifdef DEBUG
2836         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2837 #endif
2838         
2839         system_time->wMonth = y + 1;
2840 #ifdef DEBUG
2841         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2842 #endif
2843
2844         system_time->wDay = totaldays + 1;
2845 #ifdef DEBUG
2846         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2847 #endif
2848         
2849         return(TRUE);
2850 }
2851
2852 static gint
2853 file_compare (gconstpointer a, gconstpointer b)
2854 {
2855         gchar *astr = *(gchar **) a;
2856         gchar *bstr = *(gchar **) b;
2857
2858         return strcmp (astr, bstr);
2859 }
2860
2861 static gint
2862 get_errno_from_g_file_error (gint error)
2863 {
2864         switch (error) {
2865 #ifdef EACCESS
2866         case G_FILE_ERROR_ACCES:
2867                 error = EACCES;
2868                 break;
2869 #endif
2870 #ifdef ENAMETOOLONG
2871         case G_FILE_ERROR_NAMETOOLONG:
2872                 error = ENAMETOOLONG;
2873                 break;
2874 #endif
2875 #ifdef ENOENT
2876         case G_FILE_ERROR_NOENT:
2877                 error = ENOENT;
2878                 break;
2879 #endif
2880 #ifdef ENOTDIR
2881         case G_FILE_ERROR_NOTDIR:
2882                 error = ENOTDIR;
2883                 break;
2884 #endif
2885 #ifdef ENXIO
2886         case G_FILE_ERROR_NXIO:
2887                 error = ENXIO;
2888                 break;
2889 #endif
2890 #ifdef ENODEV
2891         case G_FILE_ERROR_NODEV:
2892                 error = ENODEV;
2893                 break;
2894 #endif
2895 #ifdef EROFS
2896         case G_FILE_ERROR_ROFS:
2897                 error = EROFS;
2898                 break;
2899 #endif
2900 #ifdef ETXTBSY
2901         case G_FILE_ERROR_TXTBSY:
2902                 error = ETXTBSY;
2903                 break;
2904 #endif
2905 #ifdef EFAULT
2906         case G_FILE_ERROR_FAULT:
2907                 error = EFAULT;
2908                 break;
2909 #endif
2910 #ifdef ELOOP
2911         case G_FILE_ERROR_LOOP:
2912                 error = ELOOP;
2913                 break;
2914 #endif
2915 #ifdef ENOSPC
2916         case G_FILE_ERROR_NOSPC:
2917                 error = ENOSPC;
2918                 break;
2919 #endif
2920 #ifdef ENOMEM
2921         case G_FILE_ERROR_NOMEM:
2922                 error = ENOMEM;
2923                 break;
2924 #endif
2925 #ifdef EMFILE
2926         case G_FILE_ERROR_MFILE:
2927                 error = EMFILE;
2928                 break;
2929 #endif
2930 #ifdef ENFILE
2931         case G_FILE_ERROR_NFILE:
2932                 error = ENFILE;
2933                 break;
2934 #endif
2935 #ifdef EBADF
2936         case G_FILE_ERROR_BADF:
2937                 error = EBADF;
2938                 break;
2939 #endif
2940 #ifdef EINVAL
2941         case G_FILE_ERROR_INVAL:
2942                 error = EINVAL;
2943                 break;
2944 #endif
2945 #ifdef EPIPE
2946         case G_FILE_ERROR_PIPE:
2947                 error = EPIPE;
2948                 break;
2949 #endif
2950 #ifdef EAGAIN
2951         case G_FILE_ERROR_AGAIN:
2952                 error = EAGAIN;
2953                 break;
2954 #endif
2955 #ifdef EINTR
2956         case G_FILE_ERROR_INTR:
2957                 error = EINTR;
2958                 break;
2959 #endif
2960 #ifdef EWIO
2961         case G_FILE_ERROR_IO:
2962                 error = EIO;
2963                 break;
2964 #endif
2965 #ifdef EPERM
2966         case G_FILE_ERROR_PERM:
2967                 error = EPERM;
2968                 break;
2969 #endif
2970         case G_FILE_ERROR_FAILED:
2971                 error = ERROR_INVALID_PARAMETER;
2972                 break;
2973         }
2974
2975         return error;
2976 }
2977
2978 /* scandir using glib */
2979 static gint
2980 mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
2981 {
2982         GError *error = NULL;
2983         GDir *dir;
2984         GPtrArray *names;
2985         const gchar *name;
2986         gint result;
2987         GPatternSpec *patspec;
2988
2989         dir = g_dir_open (dirname, 0, &error);
2990         if (dir == NULL) {
2991                 /* g_dir_open returns ENOENT on directories on which we don't
2992                  * have read/x permission */
2993                 gint errnum = get_errno_from_g_file_error (error->code);
2994                 g_error_free (error);
2995                 if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
2996                         errnum = EACCES;
2997
2998                 errno = errnum;
2999                 return -1;
3000         }
3001
3002         patspec = g_pattern_spec_new (pattern);
3003         names = g_ptr_array_new ();
3004         while ((name = g_dir_read_name (dir)) != NULL) {
3005                 if (g_pattern_match_string (patspec, name))
3006                         g_ptr_array_add (names, g_strdup (name));
3007         }
3008         
3009         g_pattern_spec_free (patspec);
3010         g_dir_close (dir);
3011         result = names->len;
3012         if (result > 0) {
3013                 g_ptr_array_sort (names, file_compare);
3014                 g_ptr_array_set_size (names, result + 1);
3015
3016                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
3017         } else {
3018                 g_ptr_array_free (names, TRUE);
3019         }
3020
3021         return result;
3022 }
3023
3024 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
3025 {
3026         struct _WapiHandlePrivate_find *find_handle;
3027         gpointer handle, find_ret = INVALID_HANDLE_VALUE;
3028         gboolean ok;
3029         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3030         int result;
3031         int thr_ret;
3032         gboolean unref = FALSE;
3033         
3034         if (pattern == NULL) {
3035 #ifdef DEBUG
3036                 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
3037 #endif
3038
3039                 SetLastError (ERROR_PATH_NOT_FOUND);
3040                 return(INVALID_HANDLE_VALUE);
3041         }
3042
3043         utf8_pattern = mono_unicode_to_external (pattern);
3044         if (utf8_pattern == NULL) {
3045 #ifdef DEBUG
3046                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3047 #endif
3048                 
3049                 SetLastError (ERROR_INVALID_NAME);
3050                 return(INVALID_HANDLE_VALUE);
3051         }
3052
3053 #ifdef DEBUG
3054         g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
3055                 utf8_pattern);
3056 #endif
3057         
3058         /* Figure out which bit of the pattern is the directory */
3059         dir_part=g_path_get_dirname (utf8_pattern);
3060         entry_part=g_path_get_basename (utf8_pattern);
3061
3062 #if 0
3063         /* Don't do this check for now, it breaks if directories
3064          * really do have metachars in their names (see bug 58116).
3065          * FIXME: Figure out a better solution to keep some checks...
3066          */
3067         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3068                 SetLastError (ERROR_INVALID_NAME);
3069                 g_free (dir_part);
3070                 g_free (entry_part);
3071                 g_free (utf8_pattern);
3072                 return(INVALID_HANDLE_VALUE);
3073         }
3074 #endif
3075         
3076         handle=_wapi_handle_new (WAPI_HANDLE_FIND);
3077         if(handle==_WAPI_HANDLE_INVALID) {
3078                 g_warning (G_GNUC_PRETTY_FUNCTION ": error creating find handle");
3079                 g_free (dir_part);
3080                 g_free (entry_part);
3081                 g_free (utf8_pattern);
3082                 SetLastError (ERROR_GEN_FAILURE);
3083                 
3084                 return(INVALID_HANDLE_VALUE);
3085         }
3086
3087         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3088                               handle);
3089         thr_ret = _wapi_handle_lock_handle (handle);
3090         g_assert (thr_ret == 0);
3091         
3092         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3093                                 (gpointer *)&find_handle);
3094         if(ok==FALSE) {
3095                 g_warning (G_GNUC_PRETTY_FUNCTION
3096                            ": error looking up find handle %p", handle);
3097                 g_free (dir_part);
3098                 dir_part = NULL;
3099                 g_free (entry_part);
3100                 entry_part = NULL;
3101                 g_free (utf8_pattern);
3102                 utf8_pattern = NULL;
3103                 SetLastError (ERROR_INVALID_HANDLE);
3104                 goto cleanup;
3105         }
3106
3107         /* The pattern can specify a directory or a set of files.
3108          *
3109          * The pattern can have wildcard characters ? and *, but only
3110          * in the section after the last directory delimiter.  (Return
3111          * ERROR_INVALID_NAME if there are wildcards in earlier path
3112          * sections.)  "*" has the usual 0-or-more chars meaning.  "?" 
3113          * means "match one character", "??" seems to mean "match one
3114          * or two characters", "???" seems to mean "match one, two or
3115          * three characters", etc.  Windows will also try and match
3116          * the mangled "short name" of files, so 8 character patterns
3117          * with wildcards will show some surprising results.
3118          *
3119          * All the written documentation I can find says that '?' 
3120          * should only match one character, and doesn't mention '??',
3121          * '???' etc.  I'm going to assume that the strict behaviour
3122          * (ie '???' means three and only three characters) is the
3123          * correct one, because that lets me use fnmatch(3) rather
3124          * than mess around with regexes.
3125          */
3126
3127         find_handle->namelist = NULL;
3128         result = mono_io_scandir (dir_part, entry_part, &find_handle->namelist);
3129         
3130         if (result < 0) {
3131 #ifdef DEBUG
3132                 gint errnum = errno;
3133 #endif
3134                 _wapi_set_last_error_from_errno ();
3135 #ifdef DEBUG
3136                 g_message (G_GNUC_PRETTY_FUNCTION ": scandir error: %s", g_strerror (errnum));
3137 #endif
3138                 g_free (utf8_pattern);
3139                 g_free (entry_part);
3140                 g_free (dir_part);
3141                 unref = TRUE;
3142                 goto cleanup;
3143         }
3144
3145         g_free (utf8_pattern);
3146         g_free (entry_part);
3147         
3148 #ifdef DEBUG
3149         g_message (G_GNUC_PRETTY_FUNCTION ": Got %d matches", result);
3150 #endif
3151
3152         find_handle->dir_part = dir_part;
3153         find_handle->num = result;
3154         find_handle->count = 0;
3155
3156         find_ret = handle;
3157
3158 cleanup:
3159         thr_ret = _wapi_handle_unlock_handle (handle);
3160         g_assert (thr_ret == 0);
3161         pthread_cleanup_pop (0);
3162
3163         /* FindNextFile has to be called after unlocking the handle,
3164          * because it wants to lock the handle itself
3165          */
3166         if (find_ret != INVALID_HANDLE_VALUE &&
3167             !FindNextFile (handle, find_data)) {
3168                 FindClose (handle);
3169                 SetLastError (ERROR_NO_MORE_FILES);
3170                 find_ret = INVALID_HANDLE_VALUE;
3171         }
3172
3173         /* Must not call _wapi_handle_unref() with the handle already
3174          * locked
3175          */
3176         if (unref) {
3177                 _wapi_handle_unref (handle);
3178         }
3179         
3180         return (find_ret);
3181 }
3182
3183 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
3184 {
3185         struct _WapiHandlePrivate_find *find_handle;
3186         gboolean ok;
3187         struct stat buf;
3188         gchar *filename;
3189         gchar *utf8_filename, *utf8_basename;
3190         gunichar2 *utf16_basename;
3191         time_t create_time;
3192         glong bytes;
3193         int thr_ret;
3194         gboolean ret = FALSE;
3195         
3196         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3197                                 (gpointer *)&find_handle);
3198         if(ok==FALSE) {
3199                 g_warning (G_GNUC_PRETTY_FUNCTION
3200                            ": error looking up find handle %p", handle);
3201                 SetLastError (ERROR_INVALID_HANDLE);
3202                 return(FALSE);
3203         }
3204
3205         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3206                               handle);
3207         thr_ret = _wapi_handle_lock_handle (handle);
3208         g_assert (thr_ret == 0);
3209         
3210 retry:
3211         if (find_handle->count >= find_handle->num) {
3212                 SetLastError (ERROR_NO_MORE_FILES);
3213                 goto cleanup;
3214         }
3215
3216         /* stat next match */
3217
3218         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3219         if (lstat (filename, &buf) != 0) {
3220 #ifdef DEBUG
3221                 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
3222 #endif
3223
3224                 g_free (filename);
3225                 goto retry;
3226         }
3227
3228         /* Check for dangling symlinks, and ignore them (principle of
3229          * least surprise, avoiding confusion where we report the file
3230          * exists, but when someone tries to open it we would report
3231          * it isn't there.)
3232          */
3233         if(S_ISLNK (buf.st_mode)) {
3234                 if(stat (filename, &buf) != 0) {
3235                         g_free (filename);
3236                         goto retry;
3237                 }
3238         }
3239         
3240         utf8_filename=mono_utf8_from_external (filename);
3241         if(utf8_filename==NULL) {
3242                 /* We couldn't turn this filename into utf8 (eg the
3243                  * encoding of the name wasn't convertible), so just
3244                  * ignore it.
3245                  */
3246                 g_free (filename);
3247                 goto retry;
3248         }
3249         g_free (filename);
3250         
3251 #ifdef DEBUG
3252         g_message (G_GNUC_PRETTY_FUNCTION ": Found [%s]", utf8_filename);
3253 #endif
3254         
3255         /* fill data block */
3256
3257         if (buf.st_mtime < buf.st_ctime)
3258                 create_time = buf.st_mtime;
3259         else
3260                 create_time = buf.st_ctime;
3261         
3262         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3263
3264         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3265         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3266         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3267
3268         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3269                 find_data->nFileSizeHigh = 0;
3270                 find_data->nFileSizeLow = 0;
3271         }
3272         else {
3273                 find_data->nFileSizeHigh = buf.st_size >> 32;
3274                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3275         }
3276
3277         find_data->dwReserved0 = 0;
3278         find_data->dwReserved1 = 0;
3279
3280         utf8_basename = g_path_get_basename (utf8_filename);
3281         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3282                                           NULL);
3283         if(utf16_basename==NULL) {
3284                 g_free (utf8_basename);
3285                 g_free (utf8_filename);
3286                 goto retry;
3287         }
3288         ret = TRUE;
3289         
3290         /* utf16 is 2 * utf8 */
3291         bytes *= 2;
3292
3293         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3294
3295         /* Truncating a utf16 string like this might leave the last
3296          * char incomplete
3297          */
3298         memcpy (find_data->cFileName, utf16_basename,
3299                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3300
3301         find_data->cAlternateFileName [0] = 0;  /* not used */
3302
3303         g_free (utf8_basename);
3304         g_free (utf8_filename);
3305         g_free (utf16_basename);
3306
3307 cleanup:
3308         thr_ret = _wapi_handle_unlock_handle (handle);
3309         g_assert (thr_ret == 0);
3310         pthread_cleanup_pop (0);
3311         
3312         return(ret);
3313 }
3314
3315 /**
3316  * FindClose:
3317  * @wapi_handle: the find handle to close.
3318  *
3319  * Closes find handle @wapi_handle
3320  *
3321  * Return value: %TRUE on success, %FALSE otherwise.
3322  */
3323 gboolean FindClose (gpointer handle)
3324 {
3325         struct _WapiHandlePrivate_find *find_handle;
3326         gboolean ok;
3327         int thr_ret;
3328
3329         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND, NULL,
3330                                 (gpointer *)&find_handle);
3331         if(ok==FALSE) {
3332                 g_warning (G_GNUC_PRETTY_FUNCTION
3333                            ": error looking up find handle %p", handle);
3334                 SetLastError (ERROR_INVALID_HANDLE);
3335                 return(FALSE);
3336         }
3337
3338         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3339                               handle);
3340         thr_ret = _wapi_handle_lock_handle (handle);
3341         g_assert (thr_ret == 0);
3342         
3343         g_strfreev (find_handle->namelist);
3344         g_free (find_handle->dir_part);
3345
3346         thr_ret = _wapi_handle_unlock_handle (handle);
3347         g_assert (thr_ret == 0);
3348         pthread_cleanup_pop (0);
3349         
3350         _wapi_handle_unref (handle);
3351         
3352         return(TRUE);
3353 }
3354
3355 /**
3356  * CreateDirectory:
3357  * @name: a pointer to a NULL-terminated unicode string, that names
3358  * the directory to be created.
3359  * @security: ignored for now
3360  *
3361  * Creates directory @name
3362  *
3363  * Return value: %TRUE on success, %FALSE otherwise.
3364  */
3365 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
3366 {
3367         gchar *utf8_name;
3368         int result;
3369         struct stat buf;
3370         guint32 attrs;
3371         
3372         if (name == NULL) {
3373 #ifdef DEBUG
3374                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3375 #endif
3376
3377                 SetLastError (ERROR_INVALID_NAME);
3378                 return(FALSE);
3379         }
3380         
3381         utf8_name = mono_unicode_to_external (name);
3382         if (utf8_name == NULL) {
3383 #ifdef DEBUG
3384                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3385 #endif
3386         
3387                 SetLastError (ERROR_INVALID_NAME);
3388                 return FALSE;
3389         }
3390
3391         result = mkdir (utf8_name, 0777);
3392
3393         if (result == 0) {
3394                 g_free (utf8_name);
3395                 return TRUE;
3396         }
3397
3398         if (errno == EEXIST) {
3399                 result = stat (utf8_name, &buf);
3400                 if (result == -1) {
3401                         _wapi_set_last_error_from_errno ();
3402                         g_free (utf8_name);
3403                         return FALSE;
3404                 }
3405
3406                 g_free (utf8_name);
3407                 attrs = _wapi_stat_to_file_attributes (&buf);
3408                 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
3409                         return TRUE;
3410
3411                 errno = EEXIST;
3412                 _wapi_set_last_error_from_errno ();
3413                 return FALSE;
3414         }
3415
3416         _wapi_set_last_error_from_errno ();
3417         g_free (utf8_name);
3418         return FALSE;
3419 }
3420
3421 /**
3422  * RemoveDirectory:
3423  * @name: a pointer to a NULL-terminated unicode string, that names
3424  * the directory to be removed.
3425  *
3426  * Removes directory @name
3427  *
3428  * Return value: %TRUE on success, %FALSE otherwise.
3429  */
3430 gboolean RemoveDirectory (const gunichar2 *name)
3431 {
3432         gchar *utf8_name;
3433         int result;
3434         
3435         if (name == NULL) {
3436 #ifdef DEBUG
3437                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3438 #endif
3439
3440                 SetLastError (ERROR_INVALID_NAME);
3441                 return(FALSE);
3442         }
3443
3444         utf8_name = mono_unicode_to_external (name);
3445         if (utf8_name == NULL) {
3446 #ifdef DEBUG
3447                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3448 #endif
3449                 
3450                 SetLastError (ERROR_INVALID_NAME);
3451                 return FALSE;
3452         }
3453
3454         result = rmdir (utf8_name);
3455         g_free (utf8_name);
3456
3457         if (result == 0)
3458                 return TRUE;
3459         
3460         _wapi_set_last_error_from_errno ();
3461         return FALSE;
3462 }
3463
3464 /**
3465  * GetFileAttributes:
3466  * @name: a pointer to a NULL-terminated unicode filename.
3467  *
3468  * Gets the attributes for @name;
3469  *
3470  * Return value: %INVALID_FILE_ATTRIBUTES on failure
3471  */
3472 guint32 GetFileAttributes (const gunichar2 *name)
3473 {
3474         gchar *utf8_name;
3475         struct stat buf;
3476         int result;
3477         
3478         if (name == NULL) {
3479 #ifdef DEBUG
3480                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3481 #endif
3482
3483                 SetLastError (ERROR_INVALID_NAME);
3484                 return(FALSE);
3485         }
3486         
3487         utf8_name = mono_unicode_to_external (name);
3488         if (utf8_name == NULL) {
3489 #ifdef DEBUG
3490                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3491 #endif
3492
3493                 SetLastError (ERROR_INVALID_PARAMETER);
3494                 return (INVALID_FILE_ATTRIBUTES);
3495         }
3496
3497         result = stat (utf8_name, &buf);
3498
3499         if (result != 0) {
3500                 _wapi_set_last_error_from_errno ();
3501                 g_free (utf8_name);
3502                 return (INVALID_FILE_ATTRIBUTES);
3503         }
3504         
3505         g_free (utf8_name);
3506         return _wapi_stat_to_file_attributes (&buf);
3507 }
3508
3509 /**
3510  * GetFileAttributesEx:
3511  * @name: a pointer to a NULL-terminated unicode filename.
3512  * @level: must be GetFileExInfoStandard
3513  * @info: pointer to a WapiFileAttributesData structure
3514  *
3515  * Gets attributes, size and filetimes for @name;
3516  *
3517  * Return value: %TRUE on success, %FALSE on failure
3518  */
3519 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3520 {
3521         gchar *utf8_name;
3522         WapiFileAttributesData *data;
3523
3524         struct stat buf;
3525         time_t create_time;
3526         int result;
3527         
3528         if (level != GetFileExInfoStandard) {
3529 #ifdef DEBUG
3530                 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
3531 #endif
3532
3533                 SetLastError (ERROR_INVALID_PARAMETER);
3534                 return FALSE;
3535         }
3536         
3537         if (name == NULL) {
3538 #ifdef DEBUG
3539                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3540 #endif
3541
3542                 SetLastError (ERROR_INVALID_NAME);
3543                 return(FALSE);
3544         }
3545
3546         utf8_name = mono_unicode_to_external (name);
3547         if (utf8_name == NULL) {
3548 #ifdef DEBUG
3549                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3550 #endif
3551
3552                 SetLastError (ERROR_INVALID_PARAMETER);
3553                 return FALSE;
3554         }
3555
3556         result = stat (utf8_name, &buf);
3557         g_free (utf8_name);
3558
3559         if (result != 0) {
3560                 SetLastError (ERROR_FILE_NOT_FOUND);
3561                 return FALSE;
3562         }
3563
3564         /* fill data block */
3565
3566         data = (WapiFileAttributesData *)info;
3567
3568         if (buf.st_mtime < buf.st_ctime)
3569                 create_time = buf.st_mtime;
3570         else
3571                 create_time = buf.st_ctime;
3572         
3573         data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
3574
3575         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3576         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3577         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3578
3579         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3580                 data->nFileSizeHigh = 0;
3581                 data->nFileSizeLow = 0;
3582         }
3583         else {
3584                 data->nFileSizeHigh = buf.st_size >> 32;
3585                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3586         }
3587
3588         return TRUE;
3589 }
3590
3591 /**
3592  * SetFileAttributes
3593  * @name: name of file
3594  * @attrs: attributes to set
3595  *
3596  * Changes the attributes on a named file.
3597  *
3598  * Return value: %TRUE on success, %FALSE on failure.
3599  */
3600 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3601 {
3602         /* FIXME: think of something clever to do on unix */
3603         gchar *utf8_name;
3604         struct stat buf;
3605         int result;
3606
3607         /*
3608          * Currently we only handle one *internal* case, with a value that is
3609          * not standard: 0x80000000, which means `set executable bit'
3610          */
3611         
3612         if (name == NULL) {
3613 #ifdef DEBUG
3614                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
3615 #endif
3616
3617                 SetLastError (ERROR_INVALID_NAME);
3618                 return(FALSE);
3619         }
3620
3621         utf8_name = mono_unicode_to_external (name);
3622         if (utf8_name == NULL) {
3623 #ifdef DEBUG
3624                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
3625 #endif
3626
3627                 SetLastError (ERROR_INVALID_NAME);
3628                 return FALSE;
3629         }
3630
3631         result = stat (utf8_name, &buf);
3632         if (result != 0) {
3633                 g_free (utf8_name);
3634                 SetLastError (ERROR_FILE_NOT_FOUND);
3635                 return FALSE;
3636         }
3637
3638         /* Contrary to the documentation, ms allows NORMAL to be
3639          * specified along with other attributes, so dont bother to
3640          * catch that case here.
3641          */
3642         if (attrs & FILE_ATTRIBUTE_READONLY) {
3643                 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3644         } else {
3645                 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
3646         }
3647
3648         /* Ignore the other attributes for now */
3649
3650         if (attrs & 0x80000000){
3651                 mode_t exec_mask = 0;
3652
3653                 if ((buf.st_mode & S_IRUSR) != 0)
3654                         exec_mask |= S_IXUSR;
3655
3656                 if ((buf.st_mode & S_IRGRP) != 0)
3657                         exec_mask |= S_IXGRP;
3658
3659                 if ((buf.st_mode & S_IROTH) != 0)
3660                         exec_mask |= S_IXOTH;
3661
3662                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3663         }
3664         /* Don't bother to reset executable (might need to change this
3665          * policy)
3666          */
3667         
3668         g_free (utf8_name);
3669
3670         return(TRUE);
3671 }
3672
3673 /**
3674  * GetCurrentDirectory
3675  * @length: size of the buffer
3676  * @buffer: pointer to buffer that recieves path
3677  *
3678  * Retrieves the current directory for the current process.
3679  *
3680  * Return value: number of characters in buffer on success, zero on failure
3681  */
3682 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3683 {
3684         gchar *path;
3685         gunichar2 *utf16_path;
3686         glong count;
3687         gsize bytes;
3688         
3689         path = g_get_current_dir ();
3690         if (path == NULL)
3691                 return 0;
3692
3693         utf16_path=mono_unicode_from_external (path, &bytes);
3694         
3695         /* if buffer too small, return number of characters required.
3696          * this is plain dumb.
3697          */
3698         
3699         count = (bytes/2)+1;
3700         if (count > length) {
3701                 g_free(path);
3702                 g_free (utf16_path);
3703                 
3704                 return (count);
3705         }
3706
3707         /* Add the terminator */
3708         memset (buffer, '\0', bytes+2);
3709         memcpy (buffer, utf16_path, bytes);
3710         
3711         g_free (utf16_path);
3712         g_free (path);
3713
3714         return count;
3715 }
3716
3717 /**
3718  * SetCurrentDirectory
3719  * @path: path to new directory
3720  *
3721  * Changes the directory path for the current process.
3722  *
3723  * Return value: %TRUE on success, %FALSE on failure.
3724  */
3725 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3726 {
3727         gchar *utf8_path;
3728         gboolean result;
3729
3730         utf8_path = mono_unicode_to_external (path);
3731         if (chdir (utf8_path) != 0) {
3732                 _wapi_set_last_error_from_errno ();
3733                 result = FALSE;
3734         }
3735         else
3736                 result = TRUE;
3737
3738         g_free (utf8_path);
3739         return result;
3740 }
3741
3742 /* When we're confident there are no more bugs in the fd->handle
3743  * mapping, this can be replaced as a no-op: GPOINTER_TO_INT(fd_handle) == fd
3744  */
3745 int _wapi_file_handle_to_fd (gpointer fd_handle)
3746 {
3747         struct _WapiHandlePrivate_file *file_private_handle;
3748         gboolean ok;
3749         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3750         
3751 #ifdef DEBUG
3752         g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
3753 #endif
3754
3755         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
3756                                 (gpointer *)&file_private_handle);
3757         if(ok==FALSE) {
3758                 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
3759                                         (gpointer *)&file_private_handle);
3760                 if(ok==FALSE) {
3761                         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
3762                                                 (gpointer *)&file_private_handle);
3763                         if(ok==FALSE) {
3764 #ifdef DEBUG
3765                                 g_message (G_GNUC_PRETTY_FUNCTION
3766                                            ": returning -1");
3767 #endif
3768                                 SetLastError (ERROR_INVALID_HANDLE);
3769                                 return(-1);
3770                         }
3771                 }
3772         }
3773         
3774         if (file_private_handle->fd_mapped.assigned == FALSE) {
3775                 SetLastError (ERROR_INVALID_HANDLE);
3776                 return(-1);
3777         }
3778         
3779 #ifdef DEBUG
3780         g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
3781                    file_private_handle->fd_mapped.fd);
3782 #endif
3783         
3784         g_assert (file_private_handle->fd_mapped.fd == GPOINTER_TO_INT (fd_handle));
3785         
3786         return(file_private_handle->fd_mapped.fd);
3787 }
3788
3789 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3790                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3791 {
3792         struct _WapiHandle_file *pipe_read_handle;
3793         struct _WapiHandle_file *pipe_write_handle;
3794         struct _WapiHandlePrivate_file *pipe_read_private_handle;
3795         struct _WapiHandlePrivate_file *pipe_write_private_handle;
3796         gpointer read_handle;
3797         gpointer write_handle;
3798         gboolean ok;
3799         int filedes[2];
3800         int ret;
3801         int thr_ret;
3802         gboolean unref_read = FALSE, unref_write = FALSE;
3803         gboolean cp_ret = FALSE;
3804         
3805         mono_once (&io_ops_once, io_ops_init);
3806         
3807 #ifdef DEBUG
3808         g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
3809 #endif
3810
3811         ret=pipe (filedes);
3812         if(ret==-1) {
3813 #ifdef DEBUG
3814                 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
3815                            strerror (errno));
3816 #endif
3817                 
3818                 _wapi_set_last_error_from_errno ();
3819                 return(FALSE);
3820         }
3821
3822         if (filedes[0] >= _wapi_fd_offset_table_size ||
3823             filedes[1] >= _wapi_fd_offset_table_size) {
3824 #ifdef DEBUG
3825                 g_message (G_GNUC_PRETTY_FUNCTION ": File descriptor is too big");
3826 #endif
3827
3828                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3829                 
3830                 close (filedes[0]);
3831                 close (filedes[1]);
3832                 
3833                 return(FALSE);
3834         }
3835         
3836         /* filedes[0] is open for reading, filedes[1] for writing */
3837
3838         read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3839         if(read_handle==_WAPI_HANDLE_INVALID) {
3840                 g_warning (G_GNUC_PRETTY_FUNCTION
3841                            ": error creating pipe read handle");
3842                 close (filedes[0]);
3843                 close (filedes[1]);
3844                 SetLastError (ERROR_GEN_FAILURE);
3845                 
3846                 return(FALSE);
3847         }
3848         
3849         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3850                               read_handle);
3851         thr_ret = _wapi_handle_lock_handle (read_handle);
3852         g_assert (thr_ret == 0);
3853
3854         ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
3855                                 (gpointer *)&pipe_read_handle,
3856                                 (gpointer *)&pipe_read_private_handle);
3857         if(ok==FALSE) {
3858                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3859                 close (filedes[0]);
3860                 close (filedes[1]);
3861                 SetLastError (ERROR_INVALID_HANDLE);
3862                 goto cleanup;
3863         }
3864         
3865         write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
3866         if(write_handle==_WAPI_HANDLE_INVALID) {
3867                 g_warning (G_GNUC_PRETTY_FUNCTION
3868                            ": error creating pipe write handle");
3869                 unref_read = TRUE;
3870                 
3871                 close (filedes[0]);
3872                 close (filedes[1]);
3873                 SetLastError (ERROR_GEN_FAILURE);
3874                 
3875                 goto cleanup;
3876         }
3877         
3878         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3879                               write_handle);
3880         thr_ret = _wapi_handle_lock_handle (write_handle);
3881         g_assert (thr_ret == 0);
3882
3883         ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
3884                                 (gpointer *)&pipe_write_handle,
3885                                 (gpointer *)&pipe_write_private_handle);
3886         if(ok==FALSE) {
3887                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
3888                 unref_read = TRUE;
3889                 unref_write = TRUE;
3890                 
3891                 close (filedes[0]);
3892                 close (filedes[1]);
3893                 SetLastError (ERROR_INVALID_HANDLE);
3894                 goto write_cleanup;
3895         }
3896         cp_ret = TRUE;
3897         
3898         pipe_read_private_handle->fd_mapped.fd=filedes[0];
3899         pipe_read_private_handle->fd_mapped.assigned=TRUE;
3900         pipe_read_handle->fileaccess=GENERIC_READ;
3901         
3902         _wapi_handle_fd_offset_store (filedes[0], read_handle);
3903         *readpipe=GINT_TO_POINTER (filedes[0]);
3904
3905         pipe_write_private_handle->fd_mapped.fd=filedes[1];
3906         pipe_write_private_handle->fd_mapped.assigned=TRUE;
3907         pipe_write_handle->fileaccess=GENERIC_WRITE;
3908         
3909         _wapi_handle_fd_offset_store (filedes[1], write_handle);
3910         *writepipe=GINT_TO_POINTER (filedes[1]);
3911
3912 #ifdef DEBUG
3913         g_message (G_GNUC_PRETTY_FUNCTION ": Returning pipe: read handle %p (fd %d), write handle %p (fd %d)", read_handle, filedes[0], write_handle, filedes[1]);
3914 #endif
3915
3916 write_cleanup:
3917         thr_ret =_wapi_handle_unlock_handle (write_handle);
3918         g_assert (thr_ret == 0);
3919         pthread_cleanup_pop (0);
3920
3921         if (unref_write) {
3922                 _wapi_handle_unref (write_handle);
3923         }
3924
3925 cleanup:
3926         thr_ret =_wapi_handle_unlock_handle (read_handle);
3927         g_assert (thr_ret == 0);
3928         pthread_cleanup_pop (0);
3929
3930         /* Must not call _wapi_handle_unref() with the handle already
3931          * locked
3932          */
3933         if (unref_read) {
3934                 _wapi_handle_unref (read_handle);
3935         }
3936         
3937         return(cp_ret);
3938 }
3939
3940 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3941 {
3942         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3943         gunichar2 *tmpdir16=NULL;
3944         glong dirlen;
3945         gsize bytes;
3946         guint32 ret;
3947         
3948         if(tmpdir[strlen (tmpdir)]!='/') {
3949                 g_free (tmpdir);
3950                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3951         }
3952         
3953         tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3954         if(tmpdir16==NULL) {
3955                 g_free (tmpdir);
3956                 return(0);
3957         } else {
3958                 dirlen=(bytes/2);
3959                 
3960                 if(dirlen+1>len) {
3961 #ifdef DEBUG
3962                         g_message (G_GNUC_PRETTY_FUNCTION
3963                                    ": Size %d smaller than needed (%ld)", len,
3964                                    dirlen+1);
3965 #endif
3966                 
3967                         ret=dirlen+1;
3968                 } else {
3969                         /* Add the terminator */
3970                         memset (buf, '\0', bytes+2);
3971                         memcpy (buf, tmpdir16, bytes);
3972                 
3973                         ret=dirlen;
3974                 }
3975         }
3976
3977         if(tmpdir16!=NULL) {
3978                 g_free (tmpdir16);
3979         }
3980         g_free (tmpdir);
3981         
3982         return(ret);
3983 }
3984
3985 gboolean
3986 _wapi_io_add_callback (gpointer fd_handle,
3987                        WapiOverlappedCB callback,
3988                        guint64 flags G_GNUC_UNUSED)
3989 {
3990         struct _WapiHandle_file *file_handle;
3991         struct _WapiHandlePrivate_file *file_private_handle;
3992         gboolean ok;
3993         int thr_ret;
3994         gboolean ret = FALSE;
3995         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
3996         
3997         if (handle == NULL) {
3998                 SetLastError (ERROR_INVALID_HANDLE);
3999                 return(FALSE);
4000         }
4001         
4002         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4003                                   (gpointer *) &file_handle,
4004                                   (gpointer *) &file_private_handle);
4005
4006         if (ok == FALSE) {
4007                 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
4008                                           (gpointer *) &file_handle,
4009                                           (gpointer *) &file_private_handle);
4010
4011         }
4012
4013         if (ok == FALSE || file_private_handle->async == FALSE) {
4014                 SetLastError (ERROR_INVALID_HANDLE);
4015                 return FALSE;
4016         }
4017
4018         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
4019                               handle);
4020         thr_ret = _wapi_handle_lock_handle (handle);
4021         g_assert (thr_ret == 0);
4022         
4023         if (file_private_handle->callback != NULL) {
4024                 SetLastError (ERROR_INVALID_PARAMETER);
4025                 goto cleanup;
4026         }
4027         ret = TRUE;
4028         
4029         file_private_handle->callback = callback;
4030
4031 cleanup:
4032         thr_ret = _wapi_handle_unlock_handle (handle);
4033         g_assert (thr_ret == 0);
4034         pthread_cleanup_pop (0);
4035
4036         return(ret);
4037 }
4038
4039 gint32
4040 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
4041 {
4042         FILE *fp;
4043         gunichar2 *ptr, *dir;
4044         glong length, total = 0;
4045         gchar buffer [512];
4046         gchar **splitted;
4047
4048         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
4049         buf [0] = '/';
4050         buf [1] = 0;
4051         buf [2] = 0;
4052
4053         /* Sigh, mntent and friends don't work well.
4054          * It stops on the first line that doesn't begin with a '/'.
4055          * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4056         fp = fopen ("/etc/mtab", "rt");
4057         if (fp == NULL) {
4058                 fp = fopen ("/etc/mnttab", "rt");
4059                 if (fp == NULL)
4060                         return 1;
4061         }
4062
4063         ptr = buf;
4064         while (fgets (buffer, 512, fp) != NULL) {
4065                 if (*buffer != '/')
4066                         continue;
4067
4068                 splitted = g_strsplit (buffer, " ", 0);
4069                 if (!*splitted || !*(splitted + 1))
4070                         continue;
4071
4072                 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
4073                 g_strfreev (splitted);
4074                 if (total + length + 1 > len) {
4075                         return len * 2; /* guess */
4076                 }
4077
4078                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4079                 g_free (dir);
4080                 total += length + 1;
4081         }
4082
4083         fclose (fp);
4084         return total;
4085 /* Commented out, does not work with my mtab!!! - Gonz */
4086 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4087 {
4088         FILE *fp;
4089         struct mntent *mnt;
4090         gunichar2 *ptr, *dir;
4091         glong len, total = 0;
4092         
4093
4094         fp = setmntent ("/etc/mtab", "rt");
4095         if (fp == NULL) {
4096                 fp = setmntent ("/etc/mnttab", "rt");
4097                 if (fp == NULL)
4098                         return;
4099         }
4100
4101         ptr = buf;
4102         while ((mnt = getmntent (fp)) != NULL) {
4103                 g_print ("GOT %s\n", mnt->mnt_dir);
4104                 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4105                 if (total + len + 1 > len) {
4106                         return len * 2; /* guess */
4107                 }
4108
4109                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4110                 g_free (dir);
4111                 total += len + 1;
4112         }
4113
4114         endmntent (fp);
4115         return total;
4116 }
4117 #endif
4118 }
4119
4120 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
4121 {
4122         struct flock lock_data;
4123         int ret;
4124
4125         lock_data.l_type = F_WRLCK;
4126         lock_data.l_whence = SEEK_SET;
4127         lock_data.l_start = offset;
4128         lock_data.l_len = length;
4129         
4130         do {
4131                 ret = fcntl (fd, F_SETLK, &lock_data);
4132         }
4133         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4134         
4135 #ifdef DEBUG
4136         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4137 #endif
4138
4139         if (ret == -1) {
4140                 SetLastError (ERROR_LOCK_VIOLATION);
4141                 return(FALSE);
4142         }
4143
4144         return(TRUE);
4145 }
4146
4147 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
4148 {
4149         struct flock lock_data;
4150         int ret;
4151
4152         lock_data.l_type = F_UNLCK;
4153         lock_data.l_whence = SEEK_SET;
4154         lock_data.l_start = offset;
4155         lock_data.l_len = length;
4156         
4157         do {
4158                 ret = fcntl (fd, F_SETLK, &lock_data);
4159         }
4160         while(ret == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
4161         
4162 #ifdef DEBUG
4163         g_message (G_GNUC_PRETTY_FUNCTION ": fcntl returns %d", ret);
4164 #endif
4165         
4166         if (ret == -1) {
4167                 SetLastError (ERROR_LOCK_VIOLATION);
4168                 return(FALSE);
4169         }
4170
4171         return(TRUE);
4172 }
4173
4174 gboolean LockFile (gpointer fd_handle, guint32 offset_low, guint32 offset_high,
4175                    guint32 length_low, guint32 length_high)
4176 {
4177         struct _WapiHandle_file *file_handle;
4178         struct _WapiHandlePrivate_file *file_private_handle;
4179         gboolean ok;
4180         off_t offset, length;
4181         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4182
4183         if (handle == NULL) {
4184                 SetLastError (ERROR_INVALID_HANDLE);
4185                 return(FALSE);
4186         }
4187         
4188         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4189                                   (gpointer *)&file_handle,
4190                                   (gpointer *)&file_private_handle);
4191         if (ok == FALSE) {
4192                 g_warning (G_GNUC_PRETTY_FUNCTION
4193                            ": error looking up file handle %p", handle);
4194                 SetLastError (ERROR_INVALID_HANDLE);
4195                 return(FALSE);
4196         }
4197
4198         if (file_private_handle->fd_mapped.assigned == FALSE) {
4199                 SetLastError (ERROR_INVALID_HANDLE);
4200                 return(FALSE);
4201         }
4202
4203         if (!(file_handle->fileaccess & GENERIC_READ) &&
4204             !(file_handle->fileaccess & GENERIC_WRITE) &&
4205             !(file_handle->fileaccess & GENERIC_ALL)) {
4206 #ifdef DEBUG
4207                 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_mapped.fd, file_handle->fileaccess);
4208 #endif
4209                 SetLastError (ERROR_ACCESS_DENIED);
4210                 return(FALSE);
4211         }
4212
4213 #ifdef HAVE_LARGE_FILE_SUPPORT
4214         offset = ((gint64)offset_high << 32) | offset_low;
4215         length = ((gint64)length_high << 32) | length_low;
4216
4217 #ifdef DEBUG
4218         g_message (G_GNUC_PRETTY_FUNCTION
4219                    ": Locking handle %p fd %d, offset %lld, length %lld",
4220                    handle, file_private_handle->fd_mapped.fd, offset, length);
4221 #endif
4222 #else
4223         offset = offset_low;
4224         length = length_low;
4225
4226 #ifdef DEBUG
4227         g_message (G_GNUC_PRETTY_FUNCTION
4228                    ": Locking handle %p fd %d, offset %ld, length %ld",
4229                    handle, file_private_handle->fd_mapped.fd, offset, length);
4230 #endif
4231 #endif
4232
4233         return(_wapi_lock_file_region (file_private_handle->fd_mapped.fd,
4234                                        offset, length));
4235 }
4236
4237 gboolean UnlockFile (gpointer fd_handle, guint32 offset_low,
4238                      guint32 offset_high, guint32 length_low,
4239                      guint32 length_high)
4240 {
4241         struct _WapiHandle_file *file_handle;
4242         struct _WapiHandlePrivate_file *file_private_handle;
4243         gboolean ok;
4244         off_t offset, length;
4245         gpointer handle = _wapi_handle_fd_offset_to_handle (fd_handle);
4246
4247         if (handle == NULL) {
4248                 SetLastError (ERROR_INVALID_HANDLE);
4249                 return(FALSE);
4250         }
4251         
4252         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4253                                   (gpointer *)&file_handle,
4254                                   (gpointer *)&file_private_handle);
4255         if (ok == FALSE) {
4256                 g_warning (G_GNUC_PRETTY_FUNCTION
4257                            ": error looking up file handle %p", handle);
4258                 SetLastError (ERROR_INVALID_HANDLE);
4259                 return(FALSE);
4260         }
4261
4262         if (file_private_handle->fd_mapped.assigned == FALSE) {
4263                 SetLastError (ERROR_INVALID_HANDLE);
4264                 return(FALSE);
4265         }
4266         
4267         if (!(file_handle->fileaccess & GENERIC_READ) &&
4268             !(file_handle->fileaccess & GENERIC_WRITE) &&
4269             !(file_handle->fileaccess & GENERIC_ALL)) {
4270 #ifdef DEBUG
4271                 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_mapped.fd, file_handle->fileaccess);
4272 #endif
4273                 SetLastError (ERROR_ACCESS_DENIED);
4274                 return(FALSE);
4275         }
4276
4277 #ifdef HAVE_LARGE_FILE_SUPPORT
4278         offset = ((gint64)offset_high << 32) | offset_low;
4279         length = ((gint64)length_high << 32) | length_low;
4280
4281 #ifdef DEBUG
4282         g_message (G_GNUC_PRETTY_FUNCTION
4283                    ": Unlocking handle %p fd %d, offset %lld, length %lld",
4284                    handle, file_private_handle->fd_mapped.fd, offset, length);
4285 #endif
4286 #else
4287         offset = offset_low;
4288         length = length_low;
4289
4290 #ifdef DEBUG
4291         g_message (G_GNUC_PRETTY_FUNCTION
4292                    ": Unlocking handle %p fd %d, offset %ld, length %ld",
4293                    handle, file_private_handle->fd_mapped.fd, offset, length);
4294 #endif
4295 #endif
4296
4297         return(_wapi_unlock_file_region (file_private_handle->fd_mapped.fd,
4298                                          offset, length));
4299 }