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