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