Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[mono.git] / mono / metadata / w32file-unix.c
1
2 #include <config.h>
3 #include <glib.h>
4
5 #include <stdlib.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #ifdef HAVE_SYS_STATVFS_H
12 #include <sys/statvfs.h>
13 #endif
14 #if defined(HAVE_SYS_STATFS_H)
15 #include <sys/statfs.h>
16 #endif
17 #if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
18 #include <sys/param.h>
19 #include <sys/mount.h>
20 #endif
21 #include <sys/types.h>
22 #include <stdio.h>
23 #include <utime.h>
24 #ifdef __linux__
25 #include <sys/ioctl.h>
26 #include <linux/fs.h>
27 #include <mono/utils/linux_magic.h>
28 #endif
29 #include <sys/time.h>
30 #ifdef HAVE_DIRENT_H
31 # include <dirent.h>
32 #endif
33
34 #include "w32file.h"
35 #include "w32file-internals.h"
36
37 #include "w32file-unix-glob.h"
38 #include "w32handle.h"
39 #include "w32error.h"
40 #include "utils/mono-io-portability.h"
41 #include "utils/mono-logger-internals.h"
42 #include "utils/mono-os-mutex.h"
43 #include "utils/mono-threads.h"
44 #include "utils/mono-threads-api.h"
45 #include "utils/strenc.h"
46
47 typedef struct {
48         guint64 device;
49         guint64 inode;
50         guint32 sharemode;
51         guint32 access;
52         guint32 handle_refs;
53         guint32 timestamp;
54 } FileShare;
55
56 /* Currently used for both FILE, CONSOLE and PIPE handle types.
57  * This may have to change in future. */
58 typedef struct {
59         gchar *filename;
60         FileShare *share_info;  /* Pointer into shared mem */
61         gint fd;
62         guint32 security_attributes;
63         guint32 fileaccess;
64         guint32 sharemode;
65         guint32 attrs;
66 } MonoW32HandleFile;
67
68 typedef struct {
69         gchar **namelist;
70         gchar *dir_part;
71         gint num;
72         gsize count;
73 } MonoW32HandleFind;
74
75 /*
76  * If SHM is disabled, this will point to a hash of FileShare structures, otherwise
77  * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
78  * 4MB array.
79  */
80 static GHashTable *file_share_table;
81 static mono_mutex_t file_share_mutex;
82
83 static void
84 time_t_to_filetime (time_t timeval, FILETIME *filetime)
85 {
86         guint64 ticks;
87         
88         ticks = ((guint64)timeval * 10000000) + 116444736000000000ULL;
89         filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
90         filetime->dwHighDateTime = ticks >> 32;
91 }
92
93 static void
94 file_share_release (FileShare *share_info)
95 {
96         /* Prevent new entries racing with us */
97         mono_os_mutex_lock (&file_share_mutex);
98
99         g_assert (share_info->handle_refs > 0);
100         share_info->handle_refs -= 1;
101
102         if (share_info->handle_refs == 0)
103                 g_hash_table_remove (file_share_table, share_info);
104
105         mono_os_mutex_unlock (&file_share_mutex);
106 }
107
108 static gint
109 file_share_equal (gconstpointer ka, gconstpointer kb)
110 {
111         const FileShare *s1 = (const FileShare *)ka;
112         const FileShare *s2 = (const FileShare *)kb;
113
114         return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
115 }
116
117 static guint
118 file_share_hash (gconstpointer data)
119 {
120         const FileShare *s = (const FileShare *)data;
121
122         return s->inode;
123 }
124
125 static gboolean
126 file_share_get (guint64 device, guint64 inode, guint32 new_sharemode, guint32 new_access,
127         guint32 *old_sharemode, guint32 *old_access, FileShare **share_info)
128 {
129         FileShare *file_share;
130         gboolean exists = FALSE;
131
132         /* Prevent new entries racing with us */
133         mono_os_mutex_lock (&file_share_mutex);
134
135         FileShare tmp;
136
137         /*
138          * Instead of allocating a 4MB array, we use a hash table to keep track of this
139          * info. This is needed even if SHM is disabled, to track sharing inside
140          * the current process.
141          */
142         if (!file_share_table)
143                 file_share_table = g_hash_table_new_full (file_share_hash, file_share_equal, NULL, g_free);
144
145         tmp.device = device;
146         tmp.inode = inode;
147
148         file_share = (FileShare *)g_hash_table_lookup (file_share_table, &tmp);
149         if (file_share) {
150                 *old_sharemode = file_share->sharemode;
151                 *old_access = file_share->access;
152                 *share_info = file_share;
153
154                 g_assert (file_share->handle_refs > 0);
155                 file_share->handle_refs += 1;
156
157                 exists = TRUE;
158         } else {
159                 file_share = g_new0 (FileShare, 1);
160
161                 file_share->device = device;
162                 file_share->inode = inode;
163                 file_share->sharemode = new_sharemode;
164                 file_share->access = new_access;
165                 file_share->handle_refs = 1;
166                 *share_info = file_share;
167
168                 g_hash_table_insert (file_share_table, file_share, file_share);
169         }
170
171         mono_os_mutex_unlock (&file_share_mutex);
172
173         return(exists);
174 }
175
176 static gint
177 _wapi_open (const gchar *pathname, gint flags, mode_t mode)
178 {
179         gint fd;
180         gchar *located_filename;
181
182         if (flags & O_CREAT) {
183                 located_filename = mono_portability_find_file (pathname, FALSE);
184                 if (located_filename == NULL) {
185                         fd = open (pathname, flags, mode);
186                 } else {
187                         fd = open (located_filename, flags, mode);
188                         g_free (located_filename);
189                 }
190         } else {
191                 fd = open (pathname, flags, mode);
192                 if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
193                         gint saved_errno = errno;
194                         located_filename = mono_portability_find_file (pathname, TRUE);
195
196                         if (located_filename == NULL) {
197                                 errno = saved_errno;
198                                 return -1;
199                         }
200
201                         fd = open (located_filename, flags, mode);
202                         g_free (located_filename);
203                 }
204         }
205
206         return(fd);
207 }
208
209 static gint
210 _wapi_access (const gchar *pathname, gint mode)
211 {
212         gint ret;
213
214         ret = access (pathname, mode);
215         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
216                 gint saved_errno = errno;
217                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
218
219                 if (located_filename == NULL) {
220                         errno = saved_errno;
221                         return -1;
222                 }
223
224                 ret = access (located_filename, mode);
225                 g_free (located_filename);
226         }
227
228         return ret;
229 }
230
231 static gint
232 _wapi_chmod (const gchar *pathname, mode_t mode)
233 {
234         gint ret;
235
236         ret = chmod (pathname, mode);
237         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
238                 gint saved_errno = errno;
239                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
240
241                 if (located_filename == NULL) {
242                         errno = saved_errno;
243                         return -1;
244                 }
245
246                 ret = chmod (located_filename, mode);
247                 g_free (located_filename);
248         }
249
250         return ret;
251 }
252
253 static gint
254 _wapi_utime (const gchar *filename, const struct utimbuf *buf)
255 {
256         gint ret;
257
258         ret = utime (filename, buf);
259         if (ret == -1 && errno == ENOENT && IS_PORTABILITY_SET) {
260                 gint saved_errno = errno;
261                 gchar *located_filename = mono_portability_find_file (filename, TRUE);
262
263                 if (located_filename == NULL) {
264                         errno = saved_errno;
265                         return -1;
266                 }
267
268                 ret = utime (located_filename, buf);
269                 g_free (located_filename);
270         }
271
272         return ret;
273 }
274
275 static gint
276 _wapi_unlink (const gchar *pathname)
277 {
278         gint ret;
279
280         ret = unlink (pathname);
281         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && IS_PORTABILITY_SET) {
282                 gint saved_errno = errno;
283                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
284
285                 if (located_filename == NULL) {
286                         errno = saved_errno;
287                         return -1;
288                 }
289
290                 ret = unlink (located_filename);
291                 g_free (located_filename);
292         }
293
294         return ret;
295 }
296
297 static gint
298 _wapi_rename (const gchar *oldpath, const gchar *newpath)
299 {
300         gint ret;
301         gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
302
303         if (located_newpath == NULL) {
304                 ret = rename (oldpath, newpath);
305         } else {
306                 ret = rename (oldpath, located_newpath);
307
308                 if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && IS_PORTABILITY_SET) {
309                         gint saved_errno = errno;
310                         gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
311
312                         if (located_oldpath == NULL) {
313                                 g_free (located_oldpath);
314                                 g_free (located_newpath);
315
316                                 errno = saved_errno;
317                                 return -1;
318                         }
319
320                         ret = rename (located_oldpath, located_newpath);
321                         g_free (located_oldpath);
322                 }
323                 g_free (located_newpath);
324         }
325
326         return ret;
327 }
328
329 static gint
330 _wapi_stat (const gchar *path, struct stat *buf)
331 {
332         gint ret;
333
334         ret = stat (path, buf);
335         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
336                 gint saved_errno = errno;
337                 gchar *located_filename = mono_portability_find_file (path, TRUE);
338
339                 if (located_filename == NULL) {
340                         errno = saved_errno;
341                         return -1;
342                 }
343
344                 ret = stat (located_filename, buf);
345                 g_free (located_filename);
346         }
347
348         return ret;
349 }
350
351 static gint
352 _wapi_lstat (const gchar *path, struct stat *buf)
353 {
354         gint ret;
355
356         ret = lstat (path, buf);
357         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
358                 gint saved_errno = errno;
359                 gchar *located_filename = mono_portability_find_file (path, TRUE);
360
361                 if (located_filename == NULL) {
362                         errno = saved_errno;
363                         return -1;
364                 }
365
366                 ret = lstat (located_filename, buf);
367                 g_free (located_filename);
368         }
369
370         return ret;
371 }
372
373 static gint
374 _wapi_mkdir (const gchar *pathname, mode_t mode)
375 {
376         gint ret;
377         gchar *located_filename = mono_portability_find_file (pathname, FALSE);
378
379         if (located_filename == NULL) {
380                 ret = mkdir (pathname, mode);
381         } else {
382                 ret = mkdir (located_filename, mode);
383                 g_free (located_filename);
384         }
385
386         return ret;
387 }
388
389 static gint
390 _wapi_rmdir (const gchar *pathname)
391 {
392         gint ret;
393
394         ret = rmdir (pathname);
395         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
396                 gint saved_errno = errno;
397                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
398
399                 if (located_filename == NULL) {
400                         errno = saved_errno;
401                         return -1;
402                 }
403
404                 ret = rmdir (located_filename);
405                 g_free (located_filename);
406         }
407
408         return ret;
409 }
410
411 static gint
412 _wapi_chdir (const gchar *path)
413 {
414         gint ret;
415
416         ret = chdir (path);
417         if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && IS_PORTABILITY_SET) {
418                 gint saved_errno = errno;
419                 gchar *located_filename = mono_portability_find_file (path, TRUE);
420
421                 if (located_filename == NULL) {
422                         errno = saved_errno;
423                         return -1;
424                 }
425
426                 ret = chdir (located_filename);
427                 g_free (located_filename);
428         }
429
430         return ret;
431 }
432
433 static gchar*
434 _wapi_basename (const gchar *filename)
435 {
436         gchar *new_filename = g_strdup (filename), *ret;
437
438         if (IS_PORTABILITY_SET) {
439                 g_strdelimit (new_filename, "\\", '/');
440         }
441
442         if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
443                 gint len = strlen (new_filename);
444
445                 g_memmove (new_filename, new_filename + 2, len - 2);
446                 new_filename[len - 2] = '\0';
447         }
448
449         ret = g_path_get_basename (new_filename);
450         g_free (new_filename);
451
452         return ret;
453 }
454
455 static gchar*
456 _wapi_dirname (const gchar *filename)
457 {
458         gchar *new_filename = g_strdup (filename), *ret;
459
460         if (IS_PORTABILITY_SET) {
461                 g_strdelimit (new_filename, "\\", '/');
462         }
463
464         if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) {
465                 gint len = strlen (new_filename);
466
467                 g_memmove (new_filename, new_filename + 2, len - 2);
468                 new_filename[len - 2] = '\0';
469         }
470
471         ret = g_path_get_dirname (new_filename);
472         g_free (new_filename);
473
474         return ret;
475 }
476
477 static GDir*
478 _wapi_g_dir_open (const gchar *path, guint flags, GError **error)
479 {
480         GDir *ret;
481
482         ret = g_dir_open (path, flags, error);
483         if (ret == NULL && ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && IS_PORTABILITY_SET) {
484                 gchar *located_filename = mono_portability_find_file (path, TRUE);
485                 GError *tmp_error = NULL;
486
487                 if (located_filename == NULL) {
488                         return(NULL);
489                 }
490
491                 ret = g_dir_open (located_filename, flags, &tmp_error);
492                 g_free (located_filename);
493                 if (tmp_error == NULL) {
494                         g_clear_error (error);
495                 }
496         }
497
498         return ret;
499 }
500
501 static gint
502 get_errno_from_g_file_error (gint error)
503 {
504         switch (error) {
505 #ifdef EACCESS
506         case G_FILE_ERROR_ACCES: return EACCES;
507 #endif
508 #ifdef ENAMETOOLONG
509         case G_FILE_ERROR_NAMETOOLONG: return ENAMETOOLONG;
510 #endif
511 #ifdef ENOENT
512         case G_FILE_ERROR_NOENT: return ENOENT;
513 #endif
514 #ifdef ENOTDIR
515         case G_FILE_ERROR_NOTDIR: return ENOTDIR;
516 #endif
517 #ifdef ENXIO
518         case G_FILE_ERROR_NXIO: return ENXIO;
519 #endif
520 #ifdef ENODEV
521         case G_FILE_ERROR_NODEV: return ENODEV;
522 #endif
523 #ifdef EROFS
524         case G_FILE_ERROR_ROFS: return EROFS;
525 #endif
526 #ifdef ETXTBSY
527         case G_FILE_ERROR_TXTBSY: return ETXTBSY;
528 #endif
529 #ifdef EFAULT
530         case G_FILE_ERROR_FAULT: return EFAULT;
531 #endif
532 #ifdef ELOOP
533         case G_FILE_ERROR_LOOP: return ELOOP;
534 #endif
535 #ifdef ENOSPC
536         case G_FILE_ERROR_NOSPC: return ENOSPC;
537 #endif
538 #ifdef ENOMEM
539         case G_FILE_ERROR_NOMEM: return ENOMEM;
540 #endif
541 #ifdef EMFILE
542         case G_FILE_ERROR_MFILE: return EMFILE;
543 #endif
544 #ifdef ENFILE
545         case G_FILE_ERROR_NFILE: return ENFILE;
546 #endif
547 #ifdef EBADF
548         case G_FILE_ERROR_BADF: return EBADF;
549 #endif
550 #ifdef EINVAL
551         case G_FILE_ERROR_INVAL: return EINVAL;
552 #endif
553 #ifdef EPIPE
554         case G_FILE_ERROR_PIPE: return EPIPE;
555 #endif
556 #ifdef EAGAIN
557         case G_FILE_ERROR_AGAIN: return EAGAIN;
558 #endif
559 #ifdef EINTR
560         case G_FILE_ERROR_INTR: return EINTR;
561 #endif
562 #ifdef EWIO
563         case G_FILE_ERROR_IO: return EIO;
564 #endif
565 #ifdef EPERM
566         case G_FILE_ERROR_PERM: return EPERM;
567 #endif
568         case G_FILE_ERROR_FAILED: return ERROR_INVALID_PARAMETER;
569         default:
570                 g_assert_not_reached ();
571         }
572 }
573
574 static gint
575 file_compare (gconstpointer a, gconstpointer b)
576 {
577         gchar *astr = *(gchar **) a;
578         gchar *bstr = *(gchar **) b;
579
580         return strcmp (astr, bstr);
581 }
582
583 /* scandir using glib */
584 static gint
585 _wapi_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
586 {
587         GError *error = NULL;
588         GDir *dir;
589         GPtrArray *names;
590         gint result;
591         mono_w32file_unix_glob_t glob_buf;
592         gint flags = 0, i;
593
594         dir = _wapi_g_dir_open (dirname, 0, &error);
595         if (dir == NULL) {
596                 /* g_dir_open returns ENOENT on directories on which we don't
597                  * have read/x permission */
598                 gint errnum = get_errno_from_g_file_error (error->code);
599                 g_error_free (error);
600                 if (errnum == ENOENT &&
601                     !_wapi_access (dirname, F_OK) &&
602                     _wapi_access (dirname, R_OK|X_OK)) {
603                         errnum = EACCES;
604                 }
605
606                 errno = errnum;
607                 return -1;
608         }
609
610         if (IS_PORTABILITY_CASE) {
611                 flags = W32FILE_UNIX_GLOB_IGNORECASE;
612         }
613
614         result = mono_w32file_unix_glob (dir, pattern, flags, &glob_buf);
615         if (g_str_has_suffix (pattern, ".*")) {
616                 /* Special-case the patterns ending in '.*', as
617                  * windows also matches entries with no extension with
618                  * this pattern.
619                  *
620                  * TODO: should this be a MONO_IOMAP option?
621                  */
622                 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
623                 gint result2;
624
625                 g_dir_rewind (dir);
626                 result2 = mono_w32file_unix_glob (dir, pattern2, flags | W32FILE_UNIX_GLOB_APPEND | W32FILE_UNIX_GLOB_UNIQUE, &glob_buf);
627
628                 g_free (pattern2);
629
630                 if (result != 0) {
631                         result = result2;
632                 }
633         }
634
635         g_dir_close (dir);
636         if (glob_buf.gl_pathc == 0) {
637                 return(0);
638         } else if (result != 0) {
639                 return -1;
640         }
641
642         names = g_ptr_array_new ();
643         for (i = 0; i < glob_buf.gl_pathc; i++) {
644                 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
645         }
646
647         mono_w32file_unix_globfree (&glob_buf);
648
649         result = names->len;
650         if (result > 0) {
651                 g_ptr_array_sort (names, file_compare);
652                 g_ptr_array_set_size (names, result + 1);
653
654                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
655         } else {
656                 g_ptr_array_free (names, TRUE);
657         }
658
659         return result;
660 }
661
662 static gboolean
663 _wapi_lock_file_region (gint fd, off_t offset, off_t length)
664 {
665 #if defined(__native_client__)
666         printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__);
667         // behave as below -- locks are not available
668         return TRUE;
669 #else
670         struct flock lock_data;
671         gint ret;
672
673         if (offset < 0 || length < 0) {
674                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
675                 return FALSE;
676         }
677
678         lock_data.l_type = F_WRLCK;
679         lock_data.l_whence = SEEK_SET;
680         lock_data.l_start = offset;
681         lock_data.l_len = length;
682
683         do {
684                 ret = fcntl (fd, F_SETLK, &lock_data);
685         } while(ret == -1 && errno == EINTR);
686
687         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
688
689         if (ret == -1) {
690                 /*
691                  * if locks are not available (NFS for example),
692                  * ignore the error
693                  */
694                 if (errno == ENOLCK
695 #ifdef EOPNOTSUPP
696                     || errno == EOPNOTSUPP
697 #endif
698 #ifdef ENOTSUP
699                     || errno == ENOTSUP
700 #endif
701                    ) {
702                         return TRUE;
703                 }
704
705                 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
706                 return FALSE;
707         }
708
709         return TRUE;
710 #endif /* __native_client__ */
711 }
712
713 static gboolean
714 _wapi_unlock_file_region (gint fd, off_t offset, off_t length)
715 {
716 #if defined(__native_client__)
717         printf("WARNING: %s: fcntl() not available on Native Client!\n", __func__);
718         return TRUE;
719 #else
720         struct flock lock_data;
721         gint ret;
722
723         lock_data.l_type = F_UNLCK;
724         lock_data.l_whence = SEEK_SET;
725         lock_data.l_start = offset;
726         lock_data.l_len = length;
727
728         do {
729                 ret = fcntl (fd, F_SETLK, &lock_data);
730         } while(ret == -1 && errno == EINTR);
731
732         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl returns %d", __func__, ret);
733
734         if (ret == -1) {
735                 /*
736                  * if locks are not available (NFS for example),
737                  * ignore the error
738                  */
739                 if (errno == ENOLCK
740 #ifdef EOPNOTSUPP
741                     || errno == EOPNOTSUPP
742 #endif
743 #ifdef ENOTSUP
744                     || errno == ENOTSUP
745 #endif
746                    ) {
747                         return TRUE;
748                 }
749
750                 mono_w32error_set_last (ERROR_LOCK_VIOLATION);
751                 return FALSE;
752         }
753
754         return TRUE;
755 #endif /* __native_client__ */
756 }
757
758 static void file_close (gpointer handle, gpointer data);
759 static void file_details (gpointer data);
760 static const gchar* file_typename (void);
761 static gsize file_typesize (void);
762 static gint file_getfiletype(void);
763 static gboolean file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
764 static gboolean file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
765 static gboolean file_flush(gpointer handle);
766 static guint32 file_seek(gpointer handle, gint32 movedistance,
767                          gint32 *highmovedistance, gint method);
768 static gboolean file_setendoffile(gpointer handle);
769 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
770 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
771                                  FILETIME *access_time,
772                                  FILETIME *write_time);
773 static gboolean file_setfiletime(gpointer handle,
774                                  const FILETIME *create_time,
775                                  const FILETIME *access_time,
776                                  const FILETIME *write_time);
777 static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name);
778
779 /* File handle is only signalled for overlapped IO */
780 static MonoW32HandleOps _wapi_file_ops = {
781         file_close,             /* close */
782         NULL,                   /* signal */
783         NULL,                   /* own */
784         NULL,                   /* is_owned */
785         NULL,                   /* special_wait */
786         NULL,                   /* prewait */
787         file_details,   /* details */
788         file_typename,  /* typename */
789         file_typesize,  /* typesize */
790 };
791
792 static void console_close (gpointer handle, gpointer data);
793 static void console_details (gpointer data);
794 static const gchar* console_typename (void);
795 static gsize console_typesize (void);
796 static gint console_getfiletype(void);
797 static gboolean console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
798 static gboolean console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
799
800 /* Console is mostly the same as file, except it can block waiting for
801  * input or output
802  */
803 static MonoW32HandleOps _wapi_console_ops = {
804         console_close,          /* close */
805         NULL,                   /* signal */
806         NULL,                   /* own */
807         NULL,                   /* is_owned */
808         NULL,                   /* special_wait */
809         NULL,                   /* prewait */
810         console_details,        /* details */
811         console_typename,       /* typename */
812         console_typesize,       /* typesize */
813 };
814
815 static const gchar* find_typename (void);
816 static gsize find_typesize (void);
817
818 static MonoW32HandleOps _wapi_find_ops = {
819         NULL,                   /* close */
820         NULL,                   /* signal */
821         NULL,                   /* own */
822         NULL,                   /* is_owned */
823         NULL,                   /* special_wait */
824         NULL,                   /* prewait */
825         NULL,                   /* details */
826         find_typename,  /* typename */
827         find_typesize,  /* typesize */
828 };
829
830 static void pipe_close (gpointer handle, gpointer data);
831 static void pipe_details (gpointer data);
832 static const gchar* pipe_typename (void);
833 static gsize pipe_typesize (void);
834 static gint pipe_getfiletype (void);
835 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
836 static gboolean pipe_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
837
838 /* Pipe handles
839  */
840 static MonoW32HandleOps _wapi_pipe_ops = {
841         pipe_close,             /* close */
842         NULL,                   /* signal */
843         NULL,                   /* own */
844         NULL,                   /* is_owned */
845         NULL,                   /* special_wait */
846         NULL,                   /* prewait */
847         pipe_details,   /* details */
848         pipe_typename,  /* typename */
849         pipe_typesize,  /* typesize */
850 };
851
852 static const struct {
853         /* File, console and pipe handles */
854         gint (*getfiletype)(void);
855         
856         /* File, console and pipe handles */
857         gboolean (*readfile)(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread);
858         gboolean (*writefile)(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten);
859         gboolean (*flushfile)(gpointer handle);
860         
861         /* File handles */
862         guint32 (*seek)(gpointer handle, gint32 movedistance,
863                         gint32 *highmovedistance, gint method);
864         gboolean (*setendoffile)(gpointer handle);
865         guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
866         gboolean (*getfiletime)(gpointer handle, FILETIME *create_time,
867                                 FILETIME *access_time,
868                                 FILETIME *write_time);
869         gboolean (*setfiletime)(gpointer handle,
870                                 const FILETIME *create_time,
871                                 const FILETIME *access_time,
872                                 const FILETIME *write_time);
873 } io_ops[MONO_W32HANDLE_COUNT]={
874         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
875         /* file */
876         {file_getfiletype,
877          file_read, file_write,
878          file_flush, file_seek,
879          file_setendoffile,
880          file_getfilesize,
881          file_getfiletime,
882          file_setfiletime},
883         /* console */
884         {console_getfiletype,
885          console_read,
886          console_write,
887          NULL, NULL, NULL, NULL, NULL, NULL},
888         /* thread */
889         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
890         /* sem */
891         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
892         /* mutex */
893         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
894         /* event */
895         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
896         /* socket (will need at least read and write) */
897         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
898         /* find */
899         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
900         /* process */
901         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
902         /* pipe */
903         {pipe_getfiletype,
904          pipe_read,
905          pipe_write,
906          NULL, NULL, NULL, NULL, NULL, NULL},
907 };
908
909 static gboolean lock_while_writing = FALSE;
910
911 /* Some utility functions.
912  */
913
914 /*
915  * Check if a file is writable by the current user.
916  *
917  * This is is a best effort kind of thing. It assumes a reasonable sane set
918  * of permissions by the underlying OS.
919  *
920  * We generally assume that basic unix permission bits are authoritative. Which might not
921  * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc)
922  *
923  * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file.
924  *
925  * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent
926  * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous
927  * and should not be used with a dynamic runtime.
928  */
929 static gboolean
930 is_file_writable (struct stat *st, const gchar *path)
931 {
932 #if __APPLE__
933         // OS X Finder "locked" or `ls -lO` "uchg".
934         // This only covers one of several cases where an OS X file could be unwritable through special flags.
935         if (st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
936                 return 0;
937 #endif
938
939         /* Is it globally writable? */
940         if (st->st_mode & S_IWOTH)
941                 return 1;
942
943         /* Am I the owner? */
944         if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR))
945                 return 1;
946
947         /* Am I in the same group? */
948         if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP))
949                 return 1;
950
951         /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid
952          * but it's the only sane option we have on unix.
953          */
954         return access (path, W_OK) == 0;
955 }
956
957
958 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
959                                               struct stat *buf,
960                                               struct stat *lbuf)
961 {
962         guint32 attrs = 0;
963         gchar *filename;
964         
965         /* FIXME: this could definitely be better, but there seems to
966          * be no pattern to the attributes that are set
967          */
968
969         /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
970         if (S_ISSOCK (buf->st_mode))
971                 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
972
973         filename = _wapi_basename (pathname);
974
975         if (S_ISDIR (buf->st_mode)) {
976                 attrs = FILE_ATTRIBUTE_DIRECTORY;
977                 if (!is_file_writable (buf, pathname)) {
978                         attrs |= FILE_ATTRIBUTE_READONLY;
979                 }
980                 if (filename[0] == '.') {
981                         attrs |= FILE_ATTRIBUTE_HIDDEN;
982                 }
983         } else {
984                 if (!is_file_writable (buf, pathname)) {
985                         attrs = FILE_ATTRIBUTE_READONLY;
986
987                         if (filename[0] == '.') {
988                                 attrs |= FILE_ATTRIBUTE_HIDDEN;
989                         }
990                 } else if (filename[0] == '.') {
991                         attrs = FILE_ATTRIBUTE_HIDDEN;
992                 } else {
993                         attrs = FILE_ATTRIBUTE_NORMAL;
994                 }
995         }
996
997         if (lbuf != NULL) {
998                 if (S_ISLNK (lbuf->st_mode)) {
999                         attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
1000                 }
1001         }
1002         
1003         g_free (filename);
1004         
1005         return attrs;
1006 }
1007
1008 static void
1009 _wapi_set_last_error_from_errno (void)
1010 {
1011         mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
1012 }
1013
1014 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
1015                                                   const gchar *path)
1016 {
1017         if (errno == ENOENT) {
1018                 /* Check the path - if it's a missing directory then
1019                  * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
1020                  */
1021                 gchar *dirname;
1022
1023
1024                 if (dir == NULL) {
1025                         dirname = _wapi_dirname (path);
1026                 } else {
1027                         dirname = g_strdup (dir);
1028                 }
1029                 
1030                 if (_wapi_access (dirname, F_OK) == 0) {
1031                         mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1032                 } else {
1033                         mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
1034                 }
1035
1036                 g_free (dirname);
1037         } else {
1038                 _wapi_set_last_error_from_errno ();
1039         }
1040 }
1041
1042 /* Handle ops.
1043  */
1044 static void file_close (gpointer handle, gpointer data)
1045 {
1046         MonoW32HandleFile *file_handle = (MonoW32HandleFile *)data;
1047         gint fd = file_handle->fd;
1048         
1049         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing file handle %p [%s]", __func__, handle,
1050                   file_handle->filename);
1051
1052         if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
1053                 _wapi_unlink (file_handle->filename);
1054         
1055         g_free (file_handle->filename);
1056         
1057         if (file_handle->share_info)
1058                 file_share_release (file_handle->share_info);
1059         
1060         close (fd);
1061 }
1062
1063 static void file_details (gpointer data)
1064 {
1065         MonoW32HandleFile *file = (MonoW32HandleFile *)data;
1066         
1067         g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
1068                  file->filename,
1069                  file->fileaccess&GENERIC_READ?'R':'.',
1070                  file->fileaccess&GENERIC_WRITE?'W':'.',
1071                  file->fileaccess&GENERIC_EXECUTE?'X':'.',
1072                  file->sharemode&FILE_SHARE_READ?'R':'.',
1073                  file->sharemode&FILE_SHARE_WRITE?'W':'.',
1074                  file->sharemode&FILE_SHARE_DELETE?'D':'.',
1075                  file->attrs);
1076 }
1077
1078 static const gchar* file_typename (void)
1079 {
1080         return "File";
1081 }
1082
1083 static gsize file_typesize (void)
1084 {
1085         return sizeof (MonoW32HandleFile);
1086 }
1087
1088 static gint file_getfiletype(void)
1089 {
1090         return(FILE_TYPE_DISK);
1091 }
1092
1093 static gboolean
1094 file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1095 {
1096         MonoW32HandleFile *file_handle;
1097         gboolean ok;
1098         gint fd, ret;
1099         MonoThreadInfo *info = mono_thread_info_current ();
1100         
1101         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1102                                 (gpointer *)&file_handle);
1103         if(ok==FALSE) {
1104                 g_warning ("%s: error looking up file handle %p", __func__,
1105                            handle);
1106                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1107                 return(FALSE);
1108         }
1109
1110         fd = file_handle->fd;
1111         if(bytesread!=NULL) {
1112                 *bytesread=0;
1113         }
1114         
1115         if(!(file_handle->fileaccess & GENERIC_READ) &&
1116            !(file_handle->fileaccess & GENERIC_ALL)) {
1117                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1118                           __func__, handle, file_handle->fileaccess);
1119
1120                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1121                 return(FALSE);
1122         }
1123
1124         do {
1125                 ret = read (fd, buffer, numbytes);
1126         } while (ret == -1 && errno == EINTR &&
1127                  !mono_thread_info_is_interrupt_state (info));
1128                         
1129         if(ret==-1) {
1130                 gint err = errno;
1131
1132                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
1133                           handle, strerror(err));
1134                 mono_w32error_set_last (mono_w32error_unix_to_win32 (err));
1135                 return(FALSE);
1136         }
1137                 
1138         if (bytesread != NULL) {
1139                 *bytesread = ret;
1140         }
1141                 
1142         return(TRUE);
1143 }
1144
1145 static gboolean
1146 file_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1147 {
1148         MonoW32HandleFile *file_handle;
1149         gboolean ok;
1150         gint ret, fd;
1151         off_t current_pos = 0;
1152         MonoThreadInfo *info = mono_thread_info_current ();
1153         
1154         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1155                                 (gpointer *)&file_handle);
1156         if(ok==FALSE) {
1157                 g_warning ("%s: error looking up file handle %p", __func__,
1158                            handle);
1159                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1160                 return(FALSE);
1161         }
1162
1163         fd = file_handle->fd;
1164         
1165         if(byteswritten!=NULL) {
1166                 *byteswritten=0;
1167         }
1168         
1169         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1170            !(file_handle->fileaccess & GENERIC_ALL)) {
1171                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1172
1173                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1174                 return(FALSE);
1175         }
1176         
1177         if (lock_while_writing) {
1178                 /* Need to lock the region we're about to write to,
1179                  * because we only do advisory locking on POSIX
1180                  * systems
1181                  */
1182                 current_pos = lseek (fd, (off_t)0, SEEK_CUR);
1183                 if (current_pos == -1) {
1184                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1185                                    handle, strerror (errno));
1186                         _wapi_set_last_error_from_errno ();
1187                         return(FALSE);
1188                 }
1189                 
1190                 if (_wapi_lock_file_region (fd, current_pos,
1191                                             numbytes) == FALSE) {
1192                         /* The error has already been set */
1193                         return(FALSE);
1194                 }
1195         }
1196                 
1197         do {
1198                 ret = write (fd, buffer, numbytes);
1199         } while (ret == -1 && errno == EINTR &&
1200                  !mono_thread_info_is_interrupt_state (info));
1201         
1202         if (lock_while_writing) {
1203                 _wapi_unlock_file_region (fd, current_pos, numbytes);
1204         }
1205
1206         if (ret == -1) {
1207                 if (errno == EINTR) {
1208                         ret = 0;
1209                 } else {
1210                         _wapi_set_last_error_from_errno ();
1211                                 
1212                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1213                                   __func__, handle, strerror(errno));
1214
1215                         return(FALSE);
1216                 }
1217         }
1218         if (byteswritten != NULL) {
1219                 *byteswritten = ret;
1220         }
1221         return(TRUE);
1222 }
1223
1224 static gboolean file_flush(gpointer handle)
1225 {
1226         MonoW32HandleFile *file_handle;
1227         gboolean ok;
1228         gint ret, fd;
1229         
1230         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1231                                 (gpointer *)&file_handle);
1232         if(ok==FALSE) {
1233                 g_warning ("%s: error looking up file handle %p", __func__,
1234                            handle);
1235                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1236                 return(FALSE);
1237         }
1238
1239         fd = file_handle->fd;
1240
1241         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1242            !(file_handle->fileaccess & GENERIC_ALL)) {
1243                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1244
1245                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1246                 return(FALSE);
1247         }
1248
1249         ret=fsync(fd);
1250         if (ret==-1) {
1251                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fsync of handle %p error: %s", __func__, handle,
1252                           strerror(errno));
1253
1254                 _wapi_set_last_error_from_errno ();
1255                 return(FALSE);
1256         }
1257         
1258         return(TRUE);
1259 }
1260
1261 static guint32 file_seek(gpointer handle, gint32 movedistance,
1262                          gint32 *highmovedistance, gint method)
1263 {
1264         MonoW32HandleFile *file_handle;
1265         gboolean ok;
1266         gint64 offset, newpos;
1267         gint whence, fd;
1268         guint32 ret;
1269         
1270         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1271                                 (gpointer *)&file_handle);
1272         if(ok==FALSE) {
1273                 g_warning ("%s: error looking up file handle %p", __func__,
1274                            handle);
1275                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1276                 return(INVALID_SET_FILE_POINTER);
1277         }
1278         
1279         fd = file_handle->fd;
1280
1281         if(!(file_handle->fileaccess & GENERIC_READ) &&
1282            !(file_handle->fileaccess & GENERIC_WRITE) &&
1283            !(file_handle->fileaccess & GENERIC_ALL)) {
1284                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1285
1286                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1287                 return(INVALID_SET_FILE_POINTER);
1288         }
1289
1290         switch(method) {
1291         case FILE_BEGIN:
1292                 whence=SEEK_SET;
1293                 break;
1294         case FILE_CURRENT:
1295                 whence=SEEK_CUR;
1296                 break;
1297         case FILE_END:
1298                 whence=SEEK_END;
1299                 break;
1300         default:
1301                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: invalid seek type %d", __func__, method);
1302
1303                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1304                 return(INVALID_SET_FILE_POINTER);
1305         }
1306
1307 #ifdef HAVE_LARGE_FILE_SUPPORT
1308         if(highmovedistance==NULL) {
1309                 offset=movedistance;
1310                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld (low %d)", __func__,
1311                           offset, movedistance);
1312         } else {
1313                 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
1314                 
1315                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
1316         }
1317 #else
1318         offset=movedistance;
1319 #endif
1320
1321         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: moving handle %p by %lld bytes from %d", __func__,
1322                    handle, (long long)offset, whence);
1323
1324 #ifdef PLATFORM_ANDROID
1325         /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */
1326         newpos=lseek64(fd, offset, whence);
1327 #else
1328         newpos=lseek(fd, offset, whence);
1329 #endif
1330         if(newpos==-1) {
1331                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek on handle %p returned error %s",
1332                           __func__, handle, strerror(errno));
1333
1334                 _wapi_set_last_error_from_errno ();
1335                 return(INVALID_SET_FILE_POINTER);
1336         }
1337
1338         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lseek returns %lld", __func__, newpos);
1339
1340 #ifdef HAVE_LARGE_FILE_SUPPORT
1341         ret=newpos & 0xFFFFFFFF;
1342         if(highmovedistance!=NULL) {
1343                 *highmovedistance=newpos>>32;
1344         }
1345 #else
1346         ret=newpos;
1347         if(highmovedistance!=NULL) {
1348                 /* Accurate, but potentially dodgy :-) */
1349                 *highmovedistance=0;
1350         }
1351 #endif
1352
1353         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: move of handle %p returning %d/%d", __func__,
1354                    handle, ret, highmovedistance==NULL?0:*highmovedistance);
1355
1356         return(ret);
1357 }
1358
1359 static gboolean file_setendoffile(gpointer handle)
1360 {
1361         MonoW32HandleFile *file_handle;
1362         gboolean ok;
1363         struct stat statbuf;
1364         off_t pos;
1365         gint ret, fd;
1366         MonoThreadInfo *info = mono_thread_info_current ();
1367         
1368         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1369                                 (gpointer *)&file_handle);
1370         if(ok==FALSE) {
1371                 g_warning ("%s: error looking up file handle %p", __func__,
1372                            handle);
1373                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1374                 return(FALSE);
1375         }
1376         fd = file_handle->fd;
1377         
1378         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1379            !(file_handle->fileaccess & GENERIC_ALL)) {
1380                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1381
1382                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1383                 return(FALSE);
1384         }
1385
1386         /* Find the current file position, and the file length.  If
1387          * the file position is greater than the length, write to
1388          * extend the file with a hole.  If the file position is less
1389          * than the length, truncate the file.
1390          */
1391         
1392         ret=fstat(fd, &statbuf);
1393         if(ret==-1) {
1394                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1395                            handle, strerror(errno));
1396
1397                 _wapi_set_last_error_from_errno ();
1398                 return(FALSE);
1399         }
1400
1401         pos=lseek(fd, (off_t)0, SEEK_CUR);
1402         if(pos==-1) {
1403                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p lseek failed: %s", __func__,
1404                           handle, strerror(errno));
1405
1406                 _wapi_set_last_error_from_errno ();
1407                 return(FALSE);
1408         }
1409         
1410 #ifdef FTRUNCATE_DOESNT_EXTEND
1411         off_t size = statbuf.st_size;
1412         /* I haven't bothered to write the configure.ac stuff for this
1413          * because I don't know if any platform needs it.  I'm leaving
1414          * this code just in case though
1415          */
1416         if(pos>size) {
1417                 /* Extend the file.  Use write() here, because some
1418                  * manuals say that ftruncate() behaviour is undefined
1419                  * when the file needs extending.  The POSIX spec says
1420                  * that on XSI-conformant systems it extends, so if
1421                  * every system we care about conforms, then we can
1422                  * drop this write.
1423                  */
1424                 do {
1425                         ret = write (fd, "", 1);
1426                 } while (ret == -1 && errno == EINTR &&
1427                          !mono_thread_info_is_interrupt_state (info));
1428
1429                 if(ret==-1) {
1430                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p extend write failed: %s", __func__, handle, strerror(errno));
1431
1432                         _wapi_set_last_error_from_errno ();
1433                         return(FALSE);
1434                 }
1435
1436                 /* And put the file position back after the write */
1437                 ret = lseek (fd, pos, SEEK_SET);
1438                 if (ret == -1) {
1439                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p second lseek failed: %s",
1440                                    __func__, handle, strerror(errno));
1441
1442                         _wapi_set_last_error_from_errno ();
1443                         return(FALSE);
1444                 }
1445         }
1446 #endif
1447
1448 /* Native Client has no ftruncate function, even in standalone sel_ldr. */
1449 #ifndef __native_client__
1450         /* always truncate, because the extend write() adds an extra
1451          * byte to the end of the file
1452          */
1453         do {
1454                 ret=ftruncate(fd, pos);
1455         }
1456         while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info)); 
1457         if(ret==-1) {
1458                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ftruncate failed: %s", __func__,
1459                           handle, strerror(errno));
1460                 
1461                 _wapi_set_last_error_from_errno ();
1462                 return(FALSE);
1463         }
1464 #endif
1465                 
1466         return(TRUE);
1467 }
1468
1469 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
1470 {
1471         MonoW32HandleFile *file_handle;
1472         gboolean ok;
1473         struct stat statbuf;
1474         guint32 size;
1475         gint ret;
1476         gint fd;
1477         
1478         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1479                                 (gpointer *)&file_handle);
1480         if(ok==FALSE) {
1481                 g_warning ("%s: error looking up file handle %p", __func__,
1482                            handle);
1483                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1484                 return(INVALID_FILE_SIZE);
1485         }
1486         fd = file_handle->fd;
1487         
1488         if(!(file_handle->fileaccess & GENERIC_READ) &&
1489            !(file_handle->fileaccess & GENERIC_WRITE) &&
1490            !(file_handle->fileaccess & GENERIC_ALL)) {
1491                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1492
1493                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1494                 return(INVALID_FILE_SIZE);
1495         }
1496
1497         /* If the file has a size with the low bits 0xFFFFFFFF the
1498          * caller can't tell if this is an error, so clear the error
1499          * value
1500          */
1501         mono_w32error_set_last (ERROR_SUCCESS);
1502         
1503         ret = fstat(fd, &statbuf);
1504         if (ret == -1) {
1505                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__,
1506                            handle, strerror(errno));
1507
1508                 _wapi_set_last_error_from_errno ();
1509                 return(INVALID_FILE_SIZE);
1510         }
1511         
1512         /* fstat indicates block devices as zero-length, so go a different path */
1513 #ifdef BLKGETSIZE64
1514         if (S_ISBLK(statbuf.st_mode)) {
1515                 guint64 bigsize;
1516                 if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) {
1517                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p ioctl BLKGETSIZE64 failed: %s",
1518                                    __func__, handle, strerror(errno));
1519
1520                         _wapi_set_last_error_from_errno ();
1521                         return(INVALID_FILE_SIZE);
1522                 }
1523                 
1524                 size = bigsize & 0xFFFFFFFF;
1525                 if (highsize != NULL) {
1526                         *highsize = bigsize>>32;
1527                 }
1528
1529                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning block device size %d/%d",
1530                            __func__, size, *highsize);
1531         
1532                 return(size);
1533         }
1534 #endif
1535         
1536 #ifdef HAVE_LARGE_FILE_SUPPORT
1537         size = statbuf.st_size & 0xFFFFFFFF;
1538         if (highsize != NULL) {
1539                 *highsize = statbuf.st_size>>32;
1540         }
1541 #else
1542         if (highsize != NULL) {
1543                 /* Accurate, but potentially dodgy :-) */
1544                 *highsize = 0;
1545         }
1546         size = statbuf.st_size;
1547 #endif
1548
1549         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning size %d/%d", __func__, size, *highsize);
1550         
1551         return(size);
1552 }
1553
1554 static gboolean file_getfiletime(gpointer handle, FILETIME *create_time,
1555                                  FILETIME *access_time,
1556                                  FILETIME *write_time)
1557 {
1558         MonoW32HandleFile *file_handle;
1559         gboolean ok;
1560         struct stat statbuf;
1561         guint64 create_ticks, access_ticks, write_ticks;
1562         gint ret, fd;
1563         
1564         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1565                                 (gpointer *)&file_handle);
1566         if(ok==FALSE) {
1567                 g_warning ("%s: error looking up file handle %p", __func__,
1568                            handle);
1569                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1570                 return(FALSE);
1571         }
1572         fd = file_handle->fd;
1573
1574         if(!(file_handle->fileaccess & GENERIC_READ) &&
1575            !(file_handle->fileaccess & GENERIC_ALL)) {
1576                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1577                           __func__, handle, file_handle->fileaccess);
1578
1579                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1580                 return(FALSE);
1581         }
1582         
1583         ret=fstat(fd, &statbuf);
1584         if(ret==-1) {
1585                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1586                           strerror(errno));
1587
1588                 _wapi_set_last_error_from_errno ();
1589                 return(FALSE);
1590         }
1591
1592         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: atime: %ld ctime: %ld mtime: %ld", __func__,
1593                   statbuf.st_atime, statbuf.st_ctime,
1594                   statbuf.st_mtime);
1595
1596         /* Try and guess a meaningful create time by using the older
1597          * of atime or ctime
1598          */
1599         /* The magic constant comes from msdn documentation
1600          * "Converting a time_t Value to a File Time"
1601          */
1602         if(statbuf.st_atime < statbuf.st_ctime) {
1603                 create_ticks=((guint64)statbuf.st_atime*10000000)
1604                         + 116444736000000000ULL;
1605         } else {
1606                 create_ticks=((guint64)statbuf.st_ctime*10000000)
1607                         + 116444736000000000ULL;
1608         }
1609         
1610         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
1611         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
1612         
1613         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: aticks: %llu cticks: %llu wticks: %llu", __func__,
1614                   access_ticks, create_ticks, write_ticks);
1615
1616         if(create_time!=NULL) {
1617                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
1618                 create_time->dwHighDateTime = create_ticks >> 32;
1619         }
1620         
1621         if(access_time!=NULL) {
1622                 access_time->dwLowDateTime = access_ticks & 0xFFFFFFFF;
1623                 access_time->dwHighDateTime = access_ticks >> 32;
1624         }
1625         
1626         if(write_time!=NULL) {
1627                 write_time->dwLowDateTime = write_ticks & 0xFFFFFFFF;
1628                 write_time->dwHighDateTime = write_ticks >> 32;
1629         }
1630
1631         return(TRUE);
1632 }
1633
1634 static gboolean file_setfiletime(gpointer handle,
1635                                  const FILETIME *create_time G_GNUC_UNUSED,
1636                                  const FILETIME *access_time,
1637                                  const FILETIME *write_time)
1638 {
1639         MonoW32HandleFile *file_handle;
1640         gboolean ok;
1641         struct utimbuf utbuf;
1642         struct stat statbuf;
1643         guint64 access_ticks, write_ticks;
1644         gint ret, fd;
1645         
1646         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE,
1647                                 (gpointer *)&file_handle);
1648         if(ok==FALSE) {
1649                 g_warning ("%s: error looking up file handle %p", __func__,
1650                            handle);
1651                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1652                 return(FALSE);
1653         }
1654         fd = file_handle->fd;
1655         
1656         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
1657            !(file_handle->fileaccess & GENERIC_ALL)) {
1658                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
1659
1660                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1661                 return(FALSE);
1662         }
1663
1664         if(file_handle->filename == NULL) {
1665                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p unknown filename", __func__, handle);
1666
1667                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1668                 return(FALSE);
1669         }
1670         
1671         /* Get the current times, so we can put the same times back in
1672          * the event that one of the FileTime structs is NULL
1673          */
1674         ret=fstat (fd, &statbuf);
1675         if(ret==-1) {
1676                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p fstat failed: %s", __func__, handle,
1677                           strerror(errno));
1678
1679                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1680                 return(FALSE);
1681         }
1682
1683         if(access_time!=NULL) {
1684                 access_ticks=((guint64)access_time->dwHighDateTime << 32) +
1685                         access_time->dwLowDateTime;
1686                 /* This is (time_t)0.  We can actually go to INT_MIN,
1687                  * but this will do for now.
1688                  */
1689                 if (access_ticks < 116444736000000000ULL) {
1690                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set access time too early",
1691                                    __func__);
1692                         mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1693                         return(FALSE);
1694                 }
1695
1696                 if (sizeof (utbuf.actime) == 4 && ((access_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1697                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t",
1698                                    __func__);
1699                         mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1700                         return(FALSE);
1701                 }
1702
1703                 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
1704         } else {
1705                 utbuf.actime=statbuf.st_atime;
1706         }
1707
1708         if(write_time!=NULL) {
1709                 write_ticks=((guint64)write_time->dwHighDateTime << 32) +
1710                         write_time->dwLowDateTime;
1711                 /* This is (time_t)0.  We can actually go to INT_MIN,
1712                  * but this will do for now.
1713                  */
1714                 if (write_ticks < 116444736000000000ULL) {
1715                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time too early",
1716                                    __func__);
1717                         mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1718                         return(FALSE);
1719                 }
1720                 if (sizeof (utbuf.modtime) == 4 && ((write_ticks - 116444736000000000ULL) / 10000000) > INT_MAX) {
1721                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt to set write time that is too big for a 32bits time_t",
1722                                    __func__);
1723                         mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1724                         return(FALSE);
1725                 }
1726                 
1727                 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
1728         } else {
1729                 utbuf.modtime=statbuf.st_mtime;
1730         }
1731
1732         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting handle %p access %ld write %ld", __func__,
1733                    handle, utbuf.actime, utbuf.modtime);
1734
1735         ret = _wapi_utime (file_handle->filename, &utbuf);
1736         if (ret == -1) {
1737                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p [%s] utime failed: %s", __func__,
1738                            handle, file_handle->filename, strerror(errno));
1739
1740                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
1741                 return(FALSE);
1742         }
1743         
1744         return(TRUE);
1745 }
1746
1747 static void console_close (gpointer handle, gpointer data)
1748 {
1749         MonoW32HandleFile *console_handle = (MonoW32HandleFile *)data;
1750         gint fd = console_handle->fd;
1751         
1752         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing console handle %p", __func__, handle);
1753
1754         g_free (console_handle->filename);
1755
1756         if (fd > 2) {
1757                 if (console_handle->share_info)
1758                         file_share_release (console_handle->share_info);
1759                 close (fd);
1760         }
1761 }
1762
1763 static void console_details (gpointer data)
1764 {
1765         file_details (data);
1766 }
1767
1768 static const gchar* console_typename (void)
1769 {
1770         return "Console";
1771 }
1772
1773 static gsize console_typesize (void)
1774 {
1775         return sizeof (MonoW32HandleFile);
1776 }
1777
1778 static gint console_getfiletype(void)
1779 {
1780         return(FILE_TYPE_CHAR);
1781 }
1782
1783 static gboolean
1784 console_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1785 {
1786         MonoW32HandleFile *console_handle;
1787         gboolean ok;
1788         gint ret, fd;
1789         MonoThreadInfo *info = mono_thread_info_current ();
1790
1791         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1792                                 (gpointer *)&console_handle);
1793         if(ok==FALSE) {
1794                 g_warning ("%s: error looking up console handle %p", __func__,
1795                            handle);
1796                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1797                 return(FALSE);
1798         }
1799         fd = console_handle->fd;
1800         
1801         if(bytesread!=NULL) {
1802                 *bytesread=0;
1803         }
1804         
1805         if(!(console_handle->fileaccess & GENERIC_READ) &&
1806            !(console_handle->fileaccess & GENERIC_ALL)) {
1807                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1808                            __func__, handle, console_handle->fileaccess);
1809
1810                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1811                 return(FALSE);
1812         }
1813         
1814         do {
1815                 ret=read(fd, buffer, numbytes);
1816         } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1817
1818         if(ret==-1) {
1819                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__, handle,
1820                           strerror(errno));
1821
1822                 _wapi_set_last_error_from_errno ();
1823                 return(FALSE);
1824         }
1825         
1826         if(bytesread!=NULL) {
1827                 *bytesread=ret;
1828         }
1829         
1830         return(TRUE);
1831 }
1832
1833 static gboolean
1834 console_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1835 {
1836         MonoW32HandleFile *console_handle;
1837         gboolean ok;
1838         gint ret, fd;
1839         MonoThreadInfo *info = mono_thread_info_current ();
1840         
1841         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
1842                                 (gpointer *)&console_handle);
1843         if(ok==FALSE) {
1844                 g_warning ("%s: error looking up console handle %p", __func__,
1845                            handle);
1846                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1847                 return(FALSE);
1848         }
1849         fd = console_handle->fd;
1850         
1851         if(byteswritten!=NULL) {
1852                 *byteswritten=0;
1853         }
1854         
1855         if(!(console_handle->fileaccess & GENERIC_WRITE) &&
1856            !(console_handle->fileaccess & GENERIC_ALL)) {
1857                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess);
1858
1859                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1860                 return(FALSE);
1861         }
1862         
1863         do {
1864                 ret = write(fd, buffer, numbytes);
1865         } while (ret == -1 && errno == EINTR &&
1866                  !mono_thread_info_is_interrupt_state (info));
1867
1868         if (ret == -1) {
1869                 if (errno == EINTR) {
1870                         ret = 0;
1871                 } else {
1872                         _wapi_set_last_error_from_errno ();
1873                         
1874                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s",
1875                                    __func__, handle, strerror(errno));
1876
1877                         return(FALSE);
1878                 }
1879         }
1880         if(byteswritten!=NULL) {
1881                 *byteswritten=ret;
1882         }
1883         
1884         return(TRUE);
1885 }
1886
1887 static const gchar* find_typename (void)
1888 {
1889         return "Find";
1890 }
1891
1892 static gsize find_typesize (void)
1893 {
1894         return sizeof (MonoW32HandleFind);
1895 }
1896
1897 static void pipe_close (gpointer handle, gpointer data)
1898 {
1899         MonoW32HandleFile *pipe_handle = (MonoW32HandleFile*)data;
1900         gint fd = pipe_handle->fd;
1901
1902         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: closing pipe handle %p fd %d", __func__, handle, fd);
1903
1904         /* No filename with pipe handles */
1905
1906         if (pipe_handle->share_info)
1907                 file_share_release (pipe_handle->share_info);
1908
1909         close (fd);
1910 }
1911
1912 static void pipe_details (gpointer data)
1913 {
1914         file_details (data);
1915 }
1916
1917 static const gchar* pipe_typename (void)
1918 {
1919         return "Pipe";
1920 }
1921
1922 static gsize pipe_typesize (void)
1923 {
1924         return sizeof (MonoW32HandleFile);
1925 }
1926
1927 static gint pipe_getfiletype(void)
1928 {
1929         return(FILE_TYPE_PIPE);
1930 }
1931
1932 static gboolean
1933 pipe_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
1934 {
1935         MonoW32HandleFile *pipe_handle;
1936         gboolean ok;
1937         gint ret, fd;
1938         MonoThreadInfo *info = mono_thread_info_current ();
1939
1940         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
1941                                 (gpointer *)&pipe_handle);
1942         if(ok==FALSE) {
1943                 g_warning ("%s: error looking up pipe handle %p", __func__,
1944                            handle);
1945                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
1946                 return(FALSE);
1947         }
1948         fd = pipe_handle->fd;
1949
1950         if(bytesread!=NULL) {
1951                 *bytesread=0;
1952         }
1953         
1954         if(!(pipe_handle->fileaccess & GENERIC_READ) &&
1955            !(pipe_handle->fileaccess & GENERIC_ALL)) {
1956                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ access: %u",
1957                           __func__, handle, pipe_handle->fileaccess);
1958
1959                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
1960                 return(FALSE);
1961         }
1962         
1963         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: reading up to %d bytes from pipe %p", __func__,
1964                    numbytes, handle);
1965
1966         do {
1967                 ret=read(fd, buffer, numbytes);
1968         } while (ret==-1 && errno==EINTR && !mono_thread_info_is_interrupt_state (info));
1969                 
1970         if (ret == -1) {
1971                 if (errno == EINTR) {
1972                         ret = 0;
1973                 } else {
1974                         _wapi_set_last_error_from_errno ();
1975                         
1976                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read of handle %p error: %s", __func__,
1977                                   handle, strerror(errno));
1978
1979                         return(FALSE);
1980                 }
1981         }
1982         
1983         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: read %d bytes from pipe %p", __func__, ret, handle);
1984
1985         if(bytesread!=NULL) {
1986                 *bytesread=ret;
1987         }
1988         
1989         return(TRUE);
1990 }
1991
1992 static gboolean
1993 pipe_write(gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
1994 {
1995         MonoW32HandleFile *pipe_handle;
1996         gboolean ok;
1997         gint ret, fd;
1998         MonoThreadInfo *info = mono_thread_info_current ();
1999         
2000         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_PIPE,
2001                                 (gpointer *)&pipe_handle);
2002         if(ok==FALSE) {
2003                 g_warning ("%s: error looking up pipe handle %p", __func__,
2004                            handle);
2005                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2006                 return(FALSE);
2007         }
2008         fd = pipe_handle->fd;
2009         
2010         if(byteswritten!=NULL) {
2011                 *byteswritten=0;
2012         }
2013         
2014         if(!(pipe_handle->fileaccess & GENERIC_WRITE) &&
2015            !(pipe_handle->fileaccess & GENERIC_ALL)) {
2016                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess);
2017
2018                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2019                 return(FALSE);
2020         }
2021         
2022         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: writing up to %d bytes to pipe %p", __func__, numbytes,
2023                    handle);
2024
2025         do {
2026                 ret = write (fd, buffer, numbytes);
2027         } while (ret == -1 && errno == EINTR &&
2028                  !mono_thread_info_is_interrupt_state (info));
2029
2030         if (ret == -1) {
2031                 if (errno == EINTR) {
2032                         ret = 0;
2033                 } else {
2034                         _wapi_set_last_error_from_errno ();
2035                         
2036                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write of handle %p error: %s", __func__,
2037                                   handle, strerror(errno));
2038
2039                         return(FALSE);
2040                 }
2041         }
2042         if(byteswritten!=NULL) {
2043                 *byteswritten=ret;
2044         }
2045         
2046         return(TRUE);
2047 }
2048
2049 static gint convert_flags(guint32 fileaccess, guint32 createmode)
2050 {
2051         gint flags=0;
2052         
2053         switch(fileaccess) {
2054         case GENERIC_READ:
2055                 flags=O_RDONLY;
2056                 break;
2057         case GENERIC_WRITE:
2058                 flags=O_WRONLY;
2059                 break;
2060         case GENERIC_READ|GENERIC_WRITE:
2061                 flags=O_RDWR;
2062                 break;
2063         default:
2064                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown access type 0x%x", __func__,
2065                           fileaccess);
2066                 break;
2067         }
2068
2069         switch(createmode) {
2070         case CREATE_NEW:
2071                 flags|=O_CREAT|O_EXCL;
2072                 break;
2073         case CREATE_ALWAYS:
2074                 flags|=O_CREAT|O_TRUNC;
2075                 break;
2076         case OPEN_EXISTING:
2077                 break;
2078         case OPEN_ALWAYS:
2079                 flags|=O_CREAT;
2080                 break;
2081         case TRUNCATE_EXISTING:
2082                 flags|=O_TRUNC;
2083                 break;
2084         default:
2085                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unknown create mode 0x%x", __func__,
2086                           createmode);
2087                 break;
2088         }
2089         
2090         return(flags);
2091 }
2092
2093 #if 0 /* unused */
2094 static mode_t convert_perms(guint32 sharemode)
2095 {
2096         mode_t perms=0600;
2097         
2098         if(sharemode&FILE_SHARE_READ) {
2099                 perms|=044;
2100         }
2101         if(sharemode&FILE_SHARE_WRITE) {
2102                 perms|=022;
2103         }
2104
2105         return(perms);
2106 }
2107 #endif
2108
2109 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
2110                                    guint32 fileaccess,
2111                                    FileShare **share_info)
2112 {
2113         gboolean file_already_shared;
2114         guint32 file_existing_share, file_existing_access;
2115
2116         file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
2117         
2118         if (file_already_shared) {
2119                 /* The reference to this share info was incremented
2120                  * when we looked it up, so be careful to put it back
2121                  * if we conclude we can't use this file.
2122                  */
2123                 if (file_existing_share == 0) {
2124                         /* Quick and easy, no possibility to share */
2125                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess);
2126
2127                         file_share_release (*share_info);
2128                         
2129                         return(FALSE);
2130                 }
2131
2132                 if (((file_existing_share == FILE_SHARE_READ) &&
2133                      (fileaccess != GENERIC_READ)) ||
2134                     ((file_existing_share == FILE_SHARE_WRITE) &&
2135                      (fileaccess != GENERIC_WRITE))) {
2136                         /* New access mode doesn't match up */
2137                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share);
2138
2139                         file_share_release (*share_info);
2140                 
2141                         return(FALSE);
2142                 }
2143
2144                 if (((file_existing_access & GENERIC_READ) &&
2145                      !(sharemode & FILE_SHARE_READ)) ||
2146                     ((file_existing_access & GENERIC_WRITE) &&
2147                      !(sharemode & FILE_SHARE_WRITE))) {
2148                         /* New share mode doesn't match up */
2149                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access);
2150
2151                         file_share_release (*share_info);
2152                 
2153                         return(FALSE);
2154                 }
2155         } else {
2156                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2157         }
2158
2159         return(TRUE);
2160 }
2161
2162
2163 static gboolean
2164 share_allows_delete (struct stat *statbuf, FileShare **share_info)
2165 {
2166         gboolean file_already_shared;
2167         guint32 file_existing_share, file_existing_access;
2168
2169         file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info);
2170
2171         if (file_already_shared) {
2172                 /* The reference to this share info was incremented
2173                  * when we looked it up, so be careful to put it back
2174                  * if we conclude we can't use this file.
2175                  */
2176                 if (file_existing_share == 0) {
2177                         /* Quick and easy, no possibility to share */
2178                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, (*share_info)->access);
2179
2180                         file_share_release (*share_info);
2181
2182                         return(FALSE);
2183                 }
2184
2185                 if (!(file_existing_share & FILE_SHARE_DELETE)) {
2186                         /* New access mode doesn't match up */
2187                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, (*share_info)->access, file_existing_share);
2188
2189                         file_share_release (*share_info);
2190
2191                         return(FALSE);
2192                 }
2193         } else {
2194                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: New file!", __func__);
2195         }
2196
2197         return(TRUE);
2198 }
2199
2200 gpointer
2201 mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs)
2202 {
2203         MonoW32HandleFile file_handle = {0};
2204         gpointer handle;
2205         gint flags=convert_flags(fileaccess, createmode);
2206         /*mode_t perms=convert_perms(sharemode);*/
2207         /* we don't use sharemode, because that relates to sharing of
2208          * the file when the file is open and is already handled by
2209          * other code, perms instead are the on-disk permissions and
2210          * this is a sane default.
2211          */
2212         mode_t perms=0666;
2213         gchar *filename;
2214         gint fd, ret;
2215         MonoW32HandleType handle_type;
2216         struct stat statbuf;
2217
2218         if (attrs & FILE_ATTRIBUTE_TEMPORARY)
2219                 perms = 0600;
2220         
2221         if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
2222                 mono_w32error_set_last (ERROR_ENCRYPTION_FAILED);
2223                 return INVALID_HANDLE_VALUE;
2224         }
2225         
2226         if (name == NULL) {
2227                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2228
2229                 mono_w32error_set_last (ERROR_INVALID_NAME);
2230                 return(INVALID_HANDLE_VALUE);
2231         }
2232
2233         filename = mono_unicode_to_external (name);
2234         if (filename == NULL) {
2235                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2236
2237                 mono_w32error_set_last (ERROR_INVALID_NAME);
2238                 return(INVALID_HANDLE_VALUE);
2239         }
2240         
2241         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening %s with share 0x%x and access 0x%x", __func__,
2242                    filename, sharemode, fileaccess);
2243         
2244         fd = _wapi_open (filename, flags, perms);
2245     
2246         /* If we were trying to open a directory with write permissions
2247          * (e.g. O_WRONLY or O_RDWR), this call will fail with
2248          * EISDIR. However, this is a bit bogus because calls to
2249          * manipulate the directory (e.g. mono_w32file_set_times) will still work on
2250          * the directory because they use other API calls
2251          * (e.g. utime()). Hence, if we failed with the EISDIR error, try
2252          * to open the directory again without write permission.
2253          */
2254         if (fd == -1 && errno == EISDIR)
2255         {
2256                 /* Try again but don't try to make it writable */
2257                 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
2258         }
2259         
2260         if (fd == -1) {
2261                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s: %s", __func__, filename,
2262                           strerror(errno));
2263                 _wapi_set_last_path_error_from_errno (NULL, filename);
2264                 g_free (filename);
2265
2266                 return(INVALID_HANDLE_VALUE);
2267         }
2268
2269         if (fd >= mono_w32handle_fd_reserve) {
2270                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
2271
2272                 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2273                 
2274                 close (fd);
2275                 g_free (filename);
2276                 
2277                 return(INVALID_HANDLE_VALUE);
2278         }
2279
2280         ret = fstat (fd, &statbuf);
2281         if (ret == -1) {
2282                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fstat error of file %s: %s", __func__,
2283                            filename, strerror (errno));
2284                 _wapi_set_last_error_from_errno ();
2285                 g_free (filename);
2286                 close (fd);
2287                 
2288                 return(INVALID_HANDLE_VALUE);
2289         }
2290 #ifdef __native_client__
2291         /* Workaround: Native Client currently returns the same fake inode
2292          * for all files, so do a simple hash on the filename so we don't
2293          * use the same share info for each file.
2294          */
2295         statbuf.st_ino = g_str_hash(filename);
2296 #endif
2297
2298         if (share_allows_open (&statbuf, sharemode, fileaccess,
2299                          &file_handle.share_info) == FALSE) {
2300                 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2301                 g_free (filename);
2302                 close (fd);
2303                 
2304                 return (INVALID_HANDLE_VALUE);
2305         }
2306         if (file_handle.share_info == NULL) {
2307                 /* No space, so no more files can be opened */
2308                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No space in the share table", __func__);
2309
2310                 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
2311                 close (fd);
2312                 g_free (filename);
2313                 
2314                 return(INVALID_HANDLE_VALUE);
2315         }
2316         
2317         file_handle.filename = filename;
2318         
2319         file_handle.fd = fd;
2320         file_handle.fileaccess=fileaccess;
2321         file_handle.sharemode=sharemode;
2322         file_handle.attrs=attrs;
2323
2324 #ifdef HAVE_POSIX_FADVISE
2325         if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
2326                 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
2327         if (attrs & FILE_FLAG_RANDOM_ACCESS)
2328                 posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
2329 #endif
2330
2331 #ifdef F_RDAHEAD
2332         if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
2333                 fcntl(fd, F_RDAHEAD, 1);
2334 #endif
2335
2336 #ifndef S_ISFIFO
2337 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
2338 #endif
2339         if (S_ISFIFO (statbuf.st_mode)) {
2340                 handle_type = MONO_W32HANDLE_PIPE;
2341                 /* maintain invariant that pipes have no filename */
2342                 file_handle.filename = NULL;
2343                 g_free (filename);
2344                 filename = NULL;
2345         } else if (S_ISCHR (statbuf.st_mode)) {
2346                 handle_type = MONO_W32HANDLE_CONSOLE;
2347         } else {
2348                 handle_type = MONO_W32HANDLE_FILE;
2349         }
2350
2351         handle = mono_w32handle_new_fd (handle_type, fd, &file_handle);
2352         if (handle == INVALID_HANDLE_VALUE) {
2353                 g_warning ("%s: error creating file handle", __func__);
2354                 g_free (filename);
2355                 close (fd);
2356                 
2357                 mono_w32error_set_last (ERROR_GEN_FAILURE);
2358                 return(INVALID_HANDLE_VALUE);
2359         }
2360         
2361         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
2362         
2363         return(handle);
2364 }
2365
2366 gboolean
2367 mono_w32file_close (gpointer handle)
2368 {
2369         return mono_w32handle_close (handle);
2370 }
2371
2372 gboolean mono_w32file_delete(const gunichar2 *name)
2373 {
2374         gchar *filename;
2375         gint retval;
2376         gboolean ret = FALSE;
2377         guint32 attrs;
2378 #if 0
2379         struct stat statbuf;
2380         FileShare *shareinfo;
2381 #endif
2382         
2383         if(name==NULL) {
2384                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2385
2386                 mono_w32error_set_last (ERROR_INVALID_NAME);
2387                 return(FALSE);
2388         }
2389
2390         filename=mono_unicode_to_external(name);
2391         if(filename==NULL) {
2392                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2393
2394                 mono_w32error_set_last (ERROR_INVALID_NAME);
2395                 return(FALSE);
2396         }
2397
2398         attrs = mono_w32file_get_attributes (name);
2399         if (attrs == INVALID_FILE_ATTRIBUTES) {
2400                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file attributes error", __func__);
2401                 /* Error set by mono_w32file_get_attributes() */
2402                 g_free (filename);
2403                 return(FALSE);
2404         }
2405
2406 #if 0
2407         /* Check to make sure sharing allows us to open the file for
2408          * writing.  See bug 323389.
2409          *
2410          * Do the checks that don't need an open file descriptor, for
2411          * simplicity's sake.  If we really have to do the full checks
2412          * then we can implement that later.
2413          */
2414         if (_wapi_stat (filename, &statbuf) < 0) {
2415                 _wapi_set_last_path_error_from_errno (NULL, filename);
2416                 g_free (filename);
2417                 return(FALSE);
2418         }
2419         
2420         if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
2421                                &shareinfo) == FALSE) {
2422                 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2423                 g_free (filename);
2424                 return FALSE;
2425         }
2426         if (shareinfo)
2427                 file_share_release (shareinfo);
2428 #endif
2429
2430         retval = _wapi_unlink (filename);
2431         
2432         if (retval == -1) {
2433                 _wapi_set_last_path_error_from_errno (NULL, filename);
2434         } else {
2435                 ret = TRUE;
2436         }
2437
2438         g_free(filename);
2439
2440         return(ret);
2441 }
2442
2443 static gboolean
2444 MoveFile (gunichar2 *name, gunichar2 *dest_name)
2445 {
2446         gchar *utf8_name, *utf8_dest_name;
2447         gint result, errno_copy;
2448         struct stat stat_src, stat_dest;
2449         gboolean ret = FALSE;
2450         FileShare *shareinfo;
2451         
2452         if(name==NULL) {
2453                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2454
2455                 mono_w32error_set_last (ERROR_INVALID_NAME);
2456                 return(FALSE);
2457         }
2458
2459         utf8_name = mono_unicode_to_external (name);
2460         if (utf8_name == NULL) {
2461                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2462                 
2463                 mono_w32error_set_last (ERROR_INVALID_NAME);
2464                 return FALSE;
2465         }
2466         
2467         if(dest_name==NULL) {
2468                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2469
2470                 g_free (utf8_name);
2471                 mono_w32error_set_last (ERROR_INVALID_NAME);
2472                 return(FALSE);
2473         }
2474
2475         utf8_dest_name = mono_unicode_to_external (dest_name);
2476         if (utf8_dest_name == NULL) {
2477                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
2478
2479                 g_free (utf8_name);
2480                 mono_w32error_set_last (ERROR_INVALID_NAME);
2481                 return FALSE;
2482         }
2483
2484         /*
2485          * In C# land we check for the existence of src, but not for dest.
2486          * We check it here and return the failure if dest exists and is not
2487          * the same file as src.
2488          */
2489         if (_wapi_stat (utf8_name, &stat_src) < 0) {
2490                 if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
2491                         _wapi_set_last_path_error_from_errno (NULL, utf8_name);
2492                         g_free (utf8_name);
2493                         g_free (utf8_dest_name);
2494                         return FALSE;
2495                 }
2496         }
2497         
2498         if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
2499                 if (stat_dest.st_dev != stat_src.st_dev ||
2500                     stat_dest.st_ino != stat_src.st_ino) {
2501                         g_free (utf8_name);
2502                         g_free (utf8_dest_name);
2503                         mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2504                         return FALSE;
2505                 }
2506         }
2507
2508         /* Check to make that we have delete sharing permission.
2509          * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009
2510          *
2511          * Do the checks that don't need an open file descriptor, for
2512          * simplicity's sake.  If we really have to do the full checks
2513          * then we can implement that later.
2514          */
2515         if (share_allows_delete (&stat_src, &shareinfo) == FALSE) {
2516                 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2517                 return FALSE;
2518         }
2519         if (shareinfo)
2520                 file_share_release (shareinfo);
2521
2522         result = _wapi_rename (utf8_name, utf8_dest_name);
2523         errno_copy = errno;
2524         
2525         if (result == -1) {
2526                 switch(errno_copy) {
2527                 case EEXIST:
2528                         mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2529                         break;
2530
2531                 case EXDEV:
2532                         /* Ignore here, it is dealt with below */
2533                         break;
2534
2535                 case ENOENT:
2536                         /* We already know src exists. Must be dest that doesn't exist. */
2537                         _wapi_set_last_path_error_from_errno (NULL, utf8_dest_name);
2538                         break;
2539
2540                 default:
2541                         _wapi_set_last_error_from_errno ();
2542                 }
2543         }
2544         
2545         g_free (utf8_name);
2546         g_free (utf8_dest_name);
2547
2548         if (result != 0 && errno_copy == EXDEV) {
2549                 gint32 copy_error;
2550
2551                 if (S_ISDIR (stat_src.st_mode)) {
2552                         mono_w32error_set_last (ERROR_NOT_SAME_DEVICE);
2553                         return FALSE;
2554                 }
2555                 /* Try a copy to the new location, and delete the source */
2556                 if (!mono_w32file_copy (name, dest_name, FALSE, &copy_error)) {
2557                         /* mono_w32file_copy will set the error */
2558                         return(FALSE);
2559                 }
2560                 
2561                 return(mono_w32file_delete (name));
2562         }
2563
2564         if (result == 0) {
2565                 ret = TRUE;
2566         }
2567
2568         return(ret);
2569 }
2570
2571 static gboolean
2572 write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_errors)
2573 {
2574         gint remain, n;
2575         gchar *buf, *wbuf;
2576         gint buf_size = st_src->st_blksize;
2577         MonoThreadInfo *info = mono_thread_info_current ();
2578
2579         buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
2580         buf = (gchar *) g_malloc (buf_size);
2581
2582         for (;;) {
2583                 remain = read (src_fd, buf, buf_size);
2584                 if (remain < 0) {
2585                         if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2586                                 continue;
2587
2588                         if (report_errors)
2589                                 _wapi_set_last_error_from_errno ();
2590
2591                         g_free (buf);
2592                         return FALSE;
2593                 }
2594                 if (remain == 0) {
2595                         break;
2596                 }
2597
2598                 wbuf = buf;
2599                 while (remain > 0) {
2600                         if ((n = write (dest_fd, wbuf, remain)) < 0) {
2601                                 if (errno == EINTR && !mono_thread_info_is_interrupt_state (info))
2602                                         continue;
2603
2604                                 if (report_errors)
2605                                         _wapi_set_last_error_from_errno ();
2606                                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: write failed.", __func__);
2607                                 g_free (buf);
2608                                 return FALSE;
2609                         }
2610
2611                         remain -= n;
2612                         wbuf += n;
2613                 }
2614         }
2615
2616         g_free (buf);
2617         return TRUE ;
2618 }
2619
2620 static gboolean
2621 CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
2622 {
2623         gchar *utf8_src, *utf8_dest;
2624         gint src_fd, dest_fd;
2625         struct stat st, dest_st;
2626         struct utimbuf dest_time;
2627         gboolean ret = TRUE;
2628         gint ret_utime;
2629         
2630         if(name==NULL) {
2631                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
2632
2633                 mono_w32error_set_last (ERROR_INVALID_NAME);
2634                 return(FALSE);
2635         }
2636         
2637         utf8_src = mono_unicode_to_external (name);
2638         if (utf8_src == NULL) {
2639                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of source returned NULL",
2640                            __func__);
2641
2642                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2643                 return(FALSE);
2644         }
2645         
2646         if(dest_name==NULL) {
2647                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: dest is NULL", __func__);
2648
2649                 g_free (utf8_src);
2650                 mono_w32error_set_last (ERROR_INVALID_NAME);
2651                 return(FALSE);
2652         }
2653         
2654         utf8_dest = mono_unicode_to_external (dest_name);
2655         if (utf8_dest == NULL) {
2656                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of dest returned NULL",
2657                            __func__);
2658
2659                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2660
2661                 g_free (utf8_src);
2662                 
2663                 return(FALSE);
2664         }
2665         
2666         src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2667         if (src_fd < 0) {
2668                 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2669                 
2670                 g_free (utf8_src);
2671                 g_free (utf8_dest);
2672                 
2673                 return(FALSE);
2674         }
2675
2676         if (fstat (src_fd, &st) < 0) {
2677                 _wapi_set_last_error_from_errno ();
2678
2679                 g_free (utf8_src);
2680                 g_free (utf8_dest);
2681                 close (src_fd);
2682                 
2683                 return(FALSE);
2684         }
2685
2686         /* Before trying to open/create the dest, we need to report a 'file busy'
2687          * error if src and dest are actually the same file. We do the check here to take
2688          * advantage of the IOMAP capability */
2689         if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && 
2690                         st.st_ino == dest_st.st_ino) {
2691
2692                 g_free (utf8_src);
2693                 g_free (utf8_dest);
2694                 close (src_fd);
2695
2696                 mono_w32error_set_last (ERROR_SHARING_VIOLATION);
2697                 return (FALSE);
2698         }
2699         
2700         if (fail_if_exists) {
2701                 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2702         } else {
2703                 /* FIXME: it kinda sucks that this code path potentially scans
2704                  * the directory twice due to the weird mono_w32error_set_last()
2705                  * behavior. */
2706                 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2707                 if (dest_fd < 0) {
2708                         /* The file does not exist, try creating it */
2709                         dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2710                 } else {
2711                         /* Apparently this error is set if we
2712                          * overwrite the dest file
2713                          */
2714                         mono_w32error_set_last (ERROR_ALREADY_EXISTS);
2715                 }
2716         }
2717         if (dest_fd < 0) {
2718                 _wapi_set_last_error_from_errno ();
2719
2720                 g_free (utf8_src);
2721                 g_free (utf8_dest);
2722                 close (src_fd);
2723
2724                 return(FALSE);
2725         }
2726
2727         if (!write_file (src_fd, dest_fd, &st, TRUE))
2728                 ret = FALSE;
2729
2730         close (src_fd);
2731         close (dest_fd);
2732         
2733         dest_time.modtime = st.st_mtime;
2734         dest_time.actime = st.st_atime;
2735         ret_utime = utime (utf8_dest, &dest_time);
2736         if (ret_utime == -1)
2737                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno));
2738         
2739         g_free (utf8_src);
2740         g_free (utf8_dest);
2741
2742         return ret;
2743 }
2744
2745 static gchar*
2746 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2747 {
2748         gchar *utf8_ret;
2749
2750         if (arg == NULL) {
2751                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s is NULL", __func__, arg_name);
2752                 mono_w32error_set_last (ERROR_INVALID_NAME);
2753                 return NULL;
2754         }
2755
2756         utf8_ret = mono_unicode_to_external (arg);
2757         if (utf8_ret == NULL) {
2758                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion of %s returned NULL",
2759                            __func__, arg_name);
2760                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2761                 return NULL;
2762         }
2763
2764         return utf8_ret;
2765 }
2766
2767 static gboolean
2768 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved)
2769 {
2770         gint result, backup_fd = -1,replaced_fd = -1;
2771         gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2772         struct stat stBackup;
2773         gboolean ret = FALSE;
2774
2775         if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2776                 return FALSE;
2777         if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2778                 goto replace_cleanup;
2779         if (backupFileName != NULL) {
2780                 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2781                         goto replace_cleanup;
2782         }
2783
2784         if (utf8_backupFileName) {
2785                 // Open the backup file for read so we can restore the file if an error occurs.
2786                 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2787                 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2788                 if (result == -1)
2789                         goto replace_cleanup;
2790         }
2791
2792         result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2793         if (result == -1) {
2794                 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2795                 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2796                 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2797                         replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2798                                                   stBackup.st_mode);
2799                         
2800                         if (replaced_fd == -1)
2801                                 goto replace_cleanup;
2802
2803                         write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2804                 }
2805
2806                 goto replace_cleanup;
2807         }
2808
2809         ret = TRUE;
2810
2811 replace_cleanup:
2812         g_free (utf8_replacedFileName);
2813         g_free (utf8_replacementFileName);
2814         g_free (utf8_backupFileName);
2815         if (backup_fd != -1)
2816                 close (backup_fd);
2817         if (replaced_fd != -1)
2818                 close (replaced_fd);
2819         return ret;
2820 }
2821
2822 static mono_mutex_t stdhandle_mutex;
2823
2824 static gpointer
2825 _wapi_stdhandle_create (gint fd, const gchar *name)
2826 {
2827         gpointer handle;
2828         gint flags;
2829         MonoW32HandleFile file_handle = {0};
2830
2831         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating standard handle type %s, fd %d", __func__, name, fd);
2832
2833 #if !defined(__native_client__)
2834         /* Check if fd is valid */
2835         do {
2836                 flags = fcntl(fd, F_GETFL);
2837         } while (flags == -1 && errno == EINTR);
2838
2839         if (flags == -1) {
2840                 /* Invalid fd.  Not really much point checking for EBADF
2841                  * specifically
2842                  */
2843                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fcntl error on fd %d: %s", __func__, fd, strerror(errno));
2844
2845                 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
2846                 return INVALID_HANDLE_VALUE;
2847         }
2848
2849         switch (flags & (O_RDONLY|O_WRONLY|O_RDWR)) {
2850         case O_RDONLY:
2851                 file_handle.fileaccess = GENERIC_READ;
2852                 break;
2853         case O_WRONLY:
2854                 file_handle.fileaccess = GENERIC_WRITE;
2855                 break;
2856         case O_RDWR:
2857                 file_handle.fileaccess = GENERIC_READ | GENERIC_WRITE;
2858                 break;
2859         default:
2860                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't figure out flags 0x%x", __func__, flags);
2861                 file_handle.fileaccess = 0;
2862                 break;
2863         }
2864 #else
2865         /*
2866          * fcntl will return -1 in nacl, as there is no real file system API.
2867          * Yet, standard streams are available.
2868          */
2869         file_handle.fileaccess = (fd == STDIN_FILENO) ? GENERIC_READ : GENERIC_WRITE;
2870 #endif
2871
2872         file_handle.fd = fd;
2873         file_handle.filename = g_strdup(name);
2874         /* some default security attributes might be needed */
2875         file_handle.security_attributes = 0;
2876
2877         /* Apparently input handles can't be written to.  (I don't
2878          * know if output or error handles can't be read from.)
2879          */
2880         if (fd == 0)
2881                 file_handle.fileaccess &= ~GENERIC_WRITE;
2882
2883         file_handle.sharemode = 0;
2884         file_handle.attrs = 0;
2885
2886         handle = mono_w32handle_new_fd (MONO_W32HANDLE_CONSOLE, fd, &file_handle);
2887         if (handle == INVALID_HANDLE_VALUE) {
2888                 g_warning ("%s: error creating file handle", __func__);
2889                 mono_w32error_set_last (ERROR_GEN_FAILURE);
2890                 return INVALID_HANDLE_VALUE;
2891         }
2892
2893         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p", __func__, handle);
2894
2895         return handle;
2896 }
2897
2898 enum {
2899         STD_INPUT_HANDLE  = -10,
2900         STD_OUTPUT_HANDLE = -11,
2901         STD_ERROR_HANDLE  = -12,
2902 };
2903
2904 static gpointer
2905 mono_w32file_get_std_handle (gint stdhandle)
2906 {
2907         MonoW32HandleFile *file_handle;
2908         gpointer handle;
2909         gint fd;
2910         const gchar *name;
2911         gboolean ok;
2912         
2913         switch(stdhandle) {
2914         case STD_INPUT_HANDLE:
2915                 fd = 0;
2916                 name = "<stdin>";
2917                 break;
2918
2919         case STD_OUTPUT_HANDLE:
2920                 fd = 1;
2921                 name = "<stdout>";
2922                 break;
2923
2924         case STD_ERROR_HANDLE:
2925                 fd = 2;
2926                 name = "<stderr>";
2927                 break;
2928
2929         default:
2930                 g_assert_not_reached ();
2931         }
2932
2933         handle = GINT_TO_POINTER (fd);
2934
2935         mono_os_mutex_lock (&stdhandle_mutex);
2936
2937         ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_CONSOLE,
2938                                   (gpointer *)&file_handle);
2939         if (ok == FALSE) {
2940                 /* Need to create this console handle */
2941                 handle = _wapi_stdhandle_create (fd, name);
2942                 
2943                 if (handle == INVALID_HANDLE_VALUE) {
2944                         mono_w32error_set_last (ERROR_NO_MORE_FILES);
2945                         goto done;
2946                 }
2947         } else {
2948                 /* Add a reference to this handle */
2949                 mono_w32handle_ref (handle);
2950         }
2951         
2952   done:
2953         mono_os_mutex_unlock (&stdhandle_mutex);
2954         
2955         return(handle);
2956 }
2957
2958 gboolean
2959 mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread)
2960 {
2961         MonoW32HandleType type;
2962
2963         type = mono_w32handle_get_type (handle);
2964         
2965         if(io_ops[type].readfile==NULL) {
2966                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2967                 return(FALSE);
2968         }
2969         
2970         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread));
2971 }
2972
2973 gboolean
2974 mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten)
2975 {
2976         MonoW32HandleType type;
2977
2978         type = mono_w32handle_get_type (handle);
2979         
2980         if(io_ops[type].writefile==NULL) {
2981                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2982                 return(FALSE);
2983         }
2984         
2985         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten));
2986 }
2987
2988 gboolean
2989 mono_w32file_flush (gpointer handle)
2990 {
2991         MonoW32HandleType type;
2992
2993         type = mono_w32handle_get_type (handle);
2994         
2995         if(io_ops[type].flushfile==NULL) {
2996                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
2997                 return(FALSE);
2998         }
2999         
3000         return(io_ops[type].flushfile (handle));
3001 }
3002
3003 gboolean
3004 mono_w32file_truncate (gpointer handle)
3005 {
3006         MonoW32HandleType type;
3007
3008         type = mono_w32handle_get_type (handle);
3009         
3010         if (io_ops[type].setendoffile == NULL) {
3011                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3012                 return(FALSE);
3013         }
3014         
3015         return(io_ops[type].setendoffile (handle));
3016 }
3017
3018 guint32
3019 mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method)
3020 {
3021         MonoW32HandleType type;
3022
3023         type = mono_w32handle_get_type (handle);
3024         
3025         if (io_ops[type].seek == NULL) {
3026                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3027                 return(INVALID_SET_FILE_POINTER);
3028         }
3029         
3030         return(io_ops[type].seek (handle, movedistance, highmovedistance,
3031                                   method));
3032 }
3033
3034 gint
3035 mono_w32file_get_type(gpointer handle)
3036 {
3037         MonoW32HandleType type;
3038
3039         type = mono_w32handle_get_type (handle);
3040         
3041         if (io_ops[type].getfiletype == NULL) {
3042                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3043                 return(FILE_TYPE_UNKNOWN);
3044         }
3045         
3046         return(io_ops[type].getfiletype ());
3047 }
3048
3049 static guint32
3050 GetFileSize(gpointer handle, guint32 *highsize)
3051 {
3052         MonoW32HandleType type;
3053
3054         type = mono_w32handle_get_type (handle);
3055         
3056         if (io_ops[type].getfilesize == NULL) {
3057                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3058                 return(INVALID_FILE_SIZE);
3059         }
3060         
3061         return(io_ops[type].getfilesize (handle, highsize));
3062 }
3063
3064 gboolean
3065 mono_w32file_get_times(gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time)
3066 {
3067         MonoW32HandleType type;
3068
3069         type = mono_w32handle_get_type (handle);
3070         
3071         if (io_ops[type].getfiletime == NULL) {
3072                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3073                 return(FALSE);
3074         }
3075         
3076         return(io_ops[type].getfiletime (handle, create_time, access_time,
3077                                          write_time));
3078 }
3079
3080 gboolean
3081 mono_w32file_set_times(gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time)
3082 {
3083         MonoW32HandleType type;
3084
3085         type = mono_w32handle_get_type (handle);
3086         
3087         if (io_ops[type].setfiletime == NULL) {
3088                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3089                 return(FALSE);
3090         }
3091         
3092         return(io_ops[type].setfiletime (handle, create_time, access_time,
3093                                          write_time));
3094 }
3095
3096 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
3097  * January 1 1601 GMT
3098  */
3099
3100 #define TICKS_PER_MILLISECOND 10000L
3101 #define TICKS_PER_SECOND 10000000L
3102 #define TICKS_PER_MINUTE 600000000L
3103 #define TICKS_PER_HOUR 36000000000LL
3104 #define TICKS_PER_DAY 864000000000LL
3105
3106 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
3107
3108 static const guint16 mon_yday[2][13]={
3109         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3110         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
3111 };
3112
3113 gboolean
3114 mono_w32file_filetime_to_systemtime(const FILETIME *file_time, SYSTEMTIME *system_time)
3115 {
3116         gint64 file_ticks, totaldays, rem, y;
3117         const guint16 *ip;
3118         
3119         if(system_time==NULL) {
3120                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: system_time NULL", __func__);
3121
3122                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3123                 return(FALSE);
3124         }
3125         
3126         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
3127                 file_time->dwLowDateTime;
3128         
3129         /* Really compares if file_ticks>=0x8000000000000000
3130          * (LLONG_MAX+1) but we're working with a signed value for the
3131          * year and day calculation to work later
3132          */
3133         if(file_ticks<0) {
3134                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file_time too big", __func__);
3135
3136                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3137                 return(FALSE);
3138         }
3139
3140         totaldays=(file_ticks / TICKS_PER_DAY);
3141         rem = file_ticks % TICKS_PER_DAY;
3142         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
3143
3144         system_time->wHour=rem/TICKS_PER_HOUR;
3145         rem %= TICKS_PER_HOUR;
3146         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
3147         
3148         system_time->wMinute = rem / TICKS_PER_MINUTE;
3149         rem %= TICKS_PER_MINUTE;
3150         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
3151                   rem);
3152         
3153         system_time->wSecond = rem / TICKS_PER_SECOND;
3154         rem %= TICKS_PER_SECOND;
3155         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Second: %d rem: %lld", __func__, system_time->wSecond,
3156                   rem);
3157         
3158         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
3159         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Milliseconds: %d", __func__,
3160                   system_time->wMilliseconds);
3161
3162         /* January 1, 1601 was a Monday, according to Emacs calendar */
3163         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
3164         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day of week: %d", __func__, system_time->wDayOfWeek);
3165         
3166         /* This algorithm to find year and month given days from epoch
3167          * from glibc
3168          */
3169         y=1601;
3170         
3171 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
3172 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
3173
3174         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
3175                 /* Guess a corrected year, assuming 365 days per year */
3176                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
3177                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld yg: %lld y: %lld", __func__,
3178                           totaldays, yg,
3179                           y);
3180                 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
3181                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
3182                 
3183                 /* Adjust days and y to match the guessed year. */
3184                 totaldays -= ((yg - y) * 365
3185                               + LEAPS_THRU_END_OF (yg - 1)
3186                               - LEAPS_THRU_END_OF (y - 1));
3187                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3188                 y = yg;
3189                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: y: %lld", __func__, y);
3190         }
3191         
3192         system_time->wYear = y;
3193         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Year: %d", __func__, system_time->wYear);
3194
3195         ip = mon_yday[isleap(y)];
3196         
3197         for(y=11; totaldays < ip[y]; --y) {
3198                 continue;
3199         }
3200         totaldays-=ip[y];
3201         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: totaldays: %lld", __func__, totaldays);
3202         
3203         system_time->wMonth = y + 1;
3204         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Month: %d", __func__, system_time->wMonth);
3205
3206         system_time->wDay = totaldays + 1;
3207         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Day: %d", __func__, system_time->wDay);
3208         
3209         return(TRUE);
3210 }
3211
3212 gpointer
3213 mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data)
3214 {
3215         MonoW32HandleFind find_handle = {0};
3216         gpointer handle;
3217         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
3218         gint result;
3219         
3220         if (pattern == NULL) {
3221                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pattern is NULL", __func__);
3222
3223                 mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
3224                 return(INVALID_HANDLE_VALUE);
3225         }
3226
3227         utf8_pattern = mono_unicode_to_external (pattern);
3228         if (utf8_pattern == NULL) {
3229                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3230                 
3231                 mono_w32error_set_last (ERROR_INVALID_NAME);
3232                 return(INVALID_HANDLE_VALUE);
3233         }
3234
3235         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for [%s]", __func__, utf8_pattern);
3236         
3237         /* Figure out which bit of the pattern is the directory */
3238         dir_part = _wapi_dirname (utf8_pattern);
3239         entry_part = _wapi_basename (utf8_pattern);
3240
3241 #if 0
3242         /* Don't do this check for now, it breaks if directories
3243          * really do have metachars in their names (see bug 58116).
3244          * FIXME: Figure out a better solution to keep some checks...
3245          */
3246         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
3247                 mono_w32error_set_last (ERROR_INVALID_NAME);
3248                 g_free (dir_part);
3249                 g_free (entry_part);
3250                 g_free (utf8_pattern);
3251                 return(INVALID_HANDLE_VALUE);
3252         }
3253 #endif
3254
3255         /* The pattern can specify a directory or a set of files.
3256          *
3257          * The pattern can have wildcard characters ? and *, but only
3258          * in the section after the last directory delimiter.  (Return
3259          * ERROR_INVALID_NAME if there are wildcards in earlier path
3260          * sections.)  "*" has the usual 0-or-more chars meaning.  "?" 
3261          * means "match one character", "??" seems to mean "match one
3262          * or two characters", "???" seems to mean "match one, two or
3263          * three characters", etc.  Windows will also try and match
3264          * the mangled "short name" of files, so 8 character patterns
3265          * with wildcards will show some surprising results.
3266          *
3267          * All the written documentation I can find says that '?' 
3268          * should only match one character, and doesn't mention '??',
3269          * '???' etc.  I'm going to assume that the strict behaviour
3270          * (ie '???' means three and only three characters) is the
3271          * correct one, because that lets me use fnmatch(3) rather
3272          * than mess around with regexes.
3273          */
3274
3275         find_handle.namelist = NULL;
3276         result = _wapi_io_scandir (dir_part, entry_part,
3277                                    &find_handle.namelist);
3278         
3279         if (result == 0) {
3280                 /* No files, which windows seems to call
3281                  * FILE_NOT_FOUND
3282                  */
3283                 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
3284                 g_free (utf8_pattern);
3285                 g_free (entry_part);
3286                 g_free (dir_part);
3287                 return (INVALID_HANDLE_VALUE);
3288         }
3289         
3290         if (result < 0) {
3291                 _wapi_set_last_path_error_from_errno (dir_part, NULL);
3292                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: scandir error: %s", __func__, g_strerror (errno));
3293                 g_free (utf8_pattern);
3294                 g_free (entry_part);
3295                 g_free (dir_part);
3296                 return (INVALID_HANDLE_VALUE);
3297         }
3298
3299         g_free (utf8_pattern);
3300         g_free (entry_part);
3301         
3302         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Got %d matches", __func__, result);
3303
3304         find_handle.dir_part = dir_part;
3305         find_handle.num = result;
3306         find_handle.count = 0;
3307         
3308         handle = mono_w32handle_new (MONO_W32HANDLE_FIND, &find_handle);
3309         if (handle == INVALID_HANDLE_VALUE) {
3310                 g_warning ("%s: error creating find handle", __func__);
3311                 g_free (dir_part);
3312                 g_free (entry_part);
3313                 g_free (utf8_pattern);
3314                 mono_w32error_set_last (ERROR_GEN_FAILURE);
3315                 
3316                 return(INVALID_HANDLE_VALUE);
3317         }
3318
3319         if (handle != INVALID_HANDLE_VALUE &&
3320             !mono_w32file_find_next (handle, find_data)) {
3321                 mono_w32file_find_close (handle);
3322                 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3323                 handle = INVALID_HANDLE_VALUE;
3324         }
3325
3326         return (handle);
3327 }
3328
3329 gboolean
3330 mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data)
3331 {
3332         MonoW32HandleFind *find_handle;
3333         gboolean ok;
3334         struct stat buf, linkbuf;
3335         gint result;
3336         gchar *filename;
3337         gchar *utf8_filename, *utf8_basename;
3338         gunichar2 *utf16_basename;
3339         time_t create_time;
3340         glong bytes;
3341         gboolean ret = FALSE;
3342         
3343         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3344                                 (gpointer *)&find_handle);
3345         if(ok==FALSE) {
3346                 g_warning ("%s: error looking up find handle %p", __func__,
3347                            handle);
3348                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3349                 return(FALSE);
3350         }
3351
3352         mono_w32handle_lock_handle (handle);
3353         
3354 retry:
3355         if (find_handle->count >= find_handle->num) {
3356                 mono_w32error_set_last (ERROR_NO_MORE_FILES);
3357                 goto cleanup;
3358         }
3359
3360         /* stat next match */
3361
3362         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
3363
3364         result = _wapi_stat (filename, &buf);
3365         if (result == -1 && errno == ENOENT) {
3366                 /* Might be a dangling symlink */
3367                 result = _wapi_lstat (filename, &buf);
3368         }
3369         
3370         if (result != 0) {
3371                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: stat failed: %s", __func__, filename);
3372
3373                 g_free (filename);
3374                 goto retry;
3375         }
3376
3377 #ifndef __native_client__
3378         result = _wapi_lstat (filename, &linkbuf);
3379         if (result != 0) {
3380                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: lstat failed: %s", __func__, filename);
3381
3382                 g_free (filename);
3383                 goto retry;
3384         }
3385 #endif
3386
3387         utf8_filename = mono_utf8_from_external (filename);
3388         if (utf8_filename == NULL) {
3389                 /* We couldn't turn this filename into utf8 (eg the
3390                  * encoding of the name wasn't convertible), so just
3391                  * ignore it.
3392                  */
3393                 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
3394                 
3395                 g_free (filename);
3396                 goto retry;
3397         }
3398         g_free (filename);
3399         
3400         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Found [%s]", __func__, utf8_filename);
3401         
3402         /* fill data block */
3403
3404         if (buf.st_mtime < buf.st_ctime)
3405                 create_time = buf.st_mtime;
3406         else
3407                 create_time = buf.st_ctime;
3408         
3409 #ifdef __native_client__
3410         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL);
3411 #else
3412         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3413 #endif
3414
3415         time_t_to_filetime (create_time, &find_data->ftCreationTime);
3416         time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3417         time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3418
3419         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3420                 find_data->nFileSizeHigh = 0;
3421                 find_data->nFileSizeLow = 0;
3422         } else {
3423                 find_data->nFileSizeHigh = buf.st_size >> 32;
3424                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3425         }
3426
3427         find_data->dwReserved0 = 0;
3428         find_data->dwReserved1 = 0;
3429
3430         utf8_basename = _wapi_basename (utf8_filename);
3431         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3432                                           NULL);
3433         if(utf16_basename==NULL) {
3434                 g_free (utf8_basename);
3435                 g_free (utf8_filename);
3436                 goto retry;
3437         }
3438         ret = TRUE;
3439         
3440         /* utf16 is 2 * utf8 */
3441         bytes *= 2;
3442
3443         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3444
3445         /* Truncating a utf16 string like this might leave the last
3446          * gchar incomplete
3447          */
3448         memcpy (find_data->cFileName, utf16_basename,
3449                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3450
3451         find_data->cAlternateFileName [0] = 0;  /* not used */
3452
3453         g_free (utf8_basename);
3454         g_free (utf8_filename);
3455         g_free (utf16_basename);
3456
3457 cleanup:
3458         mono_w32handle_unlock_handle (handle);
3459         
3460         return(ret);
3461 }
3462
3463 gboolean
3464 mono_w32file_find_close (gpointer handle)
3465 {
3466         MonoW32HandleFind *find_handle;
3467         gboolean ok;
3468
3469         if (handle == NULL) {
3470                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3471                 return(FALSE);
3472         }
3473         
3474         ok=mono_w32handle_lookup (handle, MONO_W32HANDLE_FIND,
3475                                 (gpointer *)&find_handle);
3476         if(ok==FALSE) {
3477                 g_warning ("%s: error looking up find handle %p", __func__,
3478                            handle);
3479                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
3480                 return(FALSE);
3481         }
3482
3483         mono_w32handle_lock_handle (handle);
3484         
3485         g_strfreev (find_handle->namelist);
3486         g_free (find_handle->dir_part);
3487
3488         mono_w32handle_unlock_handle (handle);
3489         
3490         mono_w32handle_unref (handle);
3491         
3492         return(TRUE);
3493 }
3494
3495 gboolean
3496 mono_w32file_create_directory (const gunichar2 *name)
3497 {
3498         gchar *utf8_name;
3499         gint result;
3500         
3501         if (name == NULL) {
3502                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3503
3504                 mono_w32error_set_last (ERROR_INVALID_NAME);
3505                 return(FALSE);
3506         }
3507         
3508         utf8_name = mono_unicode_to_external (name);
3509         if (utf8_name == NULL) {
3510                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3511         
3512                 mono_w32error_set_last (ERROR_INVALID_NAME);
3513                 return FALSE;
3514         }
3515
3516         result = _wapi_mkdir (utf8_name, 0777);
3517
3518         if (result == 0) {
3519                 g_free (utf8_name);
3520                 return TRUE;
3521         }
3522
3523         _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3524         g_free (utf8_name);
3525         return FALSE;
3526 }
3527
3528 gboolean
3529 mono_w32file_remove_directory (const gunichar2 *name)
3530 {
3531         gchar *utf8_name;
3532         gint result;
3533         
3534         if (name == NULL) {
3535                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3536
3537                 mono_w32error_set_last (ERROR_INVALID_NAME);
3538                 return(FALSE);
3539         }
3540
3541         utf8_name = mono_unicode_to_external (name);
3542         if (utf8_name == NULL) {
3543                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3544                 
3545                 mono_w32error_set_last (ERROR_INVALID_NAME);
3546                 return FALSE;
3547         }
3548
3549         result = _wapi_rmdir (utf8_name);
3550         if (result == -1) {
3551                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3552                 g_free (utf8_name);
3553                 
3554                 return(FALSE);
3555         }
3556         g_free (utf8_name);
3557
3558         return(TRUE);
3559 }
3560
3561 guint32
3562 mono_w32file_get_attributes (const gunichar2 *name)
3563 {
3564         gchar *utf8_name;
3565         struct stat buf, linkbuf;
3566         gint result;
3567         guint32 ret;
3568         
3569         if (name == NULL) {
3570                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3571
3572                 mono_w32error_set_last (ERROR_INVALID_NAME);
3573                 return(FALSE);
3574         }
3575         
3576         utf8_name = mono_unicode_to_external (name);
3577         if (utf8_name == NULL) {
3578                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3579
3580                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3581                 return (INVALID_FILE_ATTRIBUTES);
3582         }
3583
3584         result = _wapi_stat (utf8_name, &buf);
3585         if (result == -1 && errno == ENOENT) {
3586                 /* Might be a dangling symlink... */
3587                 result = _wapi_lstat (utf8_name, &buf);
3588         }
3589
3590         if (result != 0) {
3591                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3592                 g_free (utf8_name);
3593                 return (INVALID_FILE_ATTRIBUTES);
3594         }
3595
3596 #ifndef __native_client__
3597         result = _wapi_lstat (utf8_name, &linkbuf);
3598         if (result != 0) {
3599                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3600                 g_free (utf8_name);
3601                 return (INVALID_FILE_ATTRIBUTES);
3602         }
3603 #endif
3604         
3605 #ifdef __native_client__
3606         ret = _wapi_stat_to_file_attributes (utf8_name, &buf, NULL);
3607 #else
3608         ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3609 #endif
3610         
3611         g_free (utf8_name);
3612
3613         return(ret);
3614 }
3615
3616 gboolean
3617 mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat)
3618 {
3619         gchar *utf8_name;
3620
3621         struct stat buf, linkbuf;
3622         gint result;
3623         
3624         if (name == NULL) {
3625                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3626
3627                 mono_w32error_set_last (ERROR_INVALID_NAME);
3628                 return(FALSE);
3629         }
3630
3631         utf8_name = mono_unicode_to_external (name);
3632         if (utf8_name == NULL) {
3633                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3634
3635                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3636                 return FALSE;
3637         }
3638
3639         result = _wapi_stat (utf8_name, &buf);
3640         if (result == -1 && errno == ENOENT) {
3641                 /* Might be a dangling symlink... */
3642                 result = _wapi_lstat (utf8_name, &buf);
3643         }
3644         
3645         if (result != 0) {
3646                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3647                 g_free (utf8_name);
3648                 return FALSE;
3649         }
3650
3651         result = _wapi_lstat (utf8_name, &linkbuf);
3652         if (result != 0) {
3653                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3654                 g_free (utf8_name);
3655                 return(FALSE);
3656         }
3657
3658         /* fill stat block */
3659
3660         stat->attributes = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3661         stat->creation_time = (((guint64) (buf.st_mtime < buf.st_ctime ? buf.st_mtime : buf.st_ctime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3662         stat->last_access_time = (((guint64) (buf.st_atime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3663         stat->last_write_time = (((guint64) (buf.st_mtime)) * 10 * 1000 * 1000) + 116444736000000000ULL;
3664         stat->length = (stat->attributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : buf.st_size;
3665
3666         g_free (utf8_name);
3667         return TRUE;
3668 }
3669
3670 gboolean
3671 mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs)
3672 {
3673         /* FIXME: think of something clever to do on unix */
3674         gchar *utf8_name;
3675         struct stat buf;
3676         gint result;
3677
3678         /*
3679          * Currently we only handle one *internal* case, with a value that is
3680          * not standard: 0x80000000, which means `set executable bit'
3681          */
3682         
3683         if (name == NULL) {
3684                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is NULL", __func__);
3685
3686                 mono_w32error_set_last (ERROR_INVALID_NAME);
3687                 return(FALSE);
3688         }
3689
3690         utf8_name = mono_unicode_to_external (name);
3691         if (utf8_name == NULL) {
3692                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
3693
3694                 mono_w32error_set_last (ERROR_INVALID_NAME);
3695                 return FALSE;
3696         }
3697
3698         result = _wapi_stat (utf8_name, &buf);
3699         if (result == -1 && errno == ENOENT) {
3700                 /* Might be a dangling symlink... */
3701                 result = _wapi_lstat (utf8_name, &buf);
3702         }
3703
3704         if (result != 0) {
3705                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3706                 g_free (utf8_name);
3707                 return FALSE;
3708         }
3709
3710         /* Contrary to the documentation, ms allows NORMAL to be
3711          * specified along with other attributes, so dont bother to
3712          * catch that case here.
3713          */
3714         if (attrs & FILE_ATTRIBUTE_READONLY) {
3715                 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
3716         } else {
3717                 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWUSR);
3718         }
3719
3720         /* Ignore the other attributes for now */
3721
3722         if (attrs & 0x80000000){
3723                 mode_t exec_mask = 0;
3724
3725                 if ((buf.st_mode & S_IRUSR) != 0)
3726                         exec_mask |= S_IXUSR;
3727
3728                 if ((buf.st_mode & S_IRGRP) != 0)
3729                         exec_mask |= S_IXGRP;
3730
3731                 if ((buf.st_mode & S_IROTH) != 0)
3732                         exec_mask |= S_IXOTH;
3733
3734                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3735         }
3736         /* Don't bother to reset executable (might need to change this
3737          * policy)
3738          */
3739         
3740         g_free (utf8_name);
3741
3742         return(TRUE);
3743 }
3744
3745 guint32
3746 mono_w32file_get_cwd (guint32 length, gunichar2 *buffer)
3747 {
3748         gunichar2 *utf16_path;
3749         glong count;
3750         gsize bytes;
3751
3752 #ifdef __native_client__
3753         gchar *path = g_get_current_dir ();
3754         if (length < strlen(path) + 1 || path == NULL)
3755                 return 0;
3756         memcpy (buffer, path, strlen(path) + 1);
3757 #else
3758         if (getcwd ((gchar*)buffer, length) == NULL) {
3759                 if (errno == ERANGE) { /*buffer length is not big enough */ 
3760                         gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3761                         if (path == NULL)
3762                                 return 0;
3763                         utf16_path = mono_unicode_from_external (path, &bytes);
3764                         g_free (utf16_path);
3765                         g_free (path);
3766                         return (bytes/2)+1;
3767                 }
3768                 _wapi_set_last_error_from_errno ();
3769                 return 0;
3770         }
3771 #endif
3772
3773         utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3774         count = (bytes/2)+1;
3775         g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3776
3777         /* Add the terminator */
3778         memset (buffer, '\0', bytes+2);
3779         memcpy (buffer, utf16_path, bytes);
3780         
3781         g_free (utf16_path);
3782
3783         return count;
3784 }
3785
3786 gboolean
3787 mono_w32file_set_cwd (const gunichar2 *path)
3788 {
3789         gchar *utf8_path;
3790         gboolean result;
3791
3792         if (path == NULL) {
3793                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
3794                 return(FALSE);
3795         }
3796         
3797         utf8_path = mono_unicode_to_external (path);
3798         if (_wapi_chdir (utf8_path) != 0) {
3799                 _wapi_set_last_error_from_errno ();
3800                 result = FALSE;
3801         }
3802         else
3803                 result = TRUE;
3804
3805         g_free (utf8_path);
3806         return result;
3807 }
3808
3809 gboolean
3810 mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size)
3811 {
3812         MonoW32HandleFile pipe_read_handle = {0};
3813         MonoW32HandleFile pipe_write_handle = {0};
3814         gpointer read_handle;
3815         gpointer write_handle;
3816         gint filedes[2];
3817         gint ret;
3818         
3819         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating pipe", __func__);
3820
3821         ret=pipe (filedes);
3822         if(ret==-1) {
3823                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error creating pipe: %s", __func__,
3824                            strerror (errno));
3825                 
3826                 _wapi_set_last_error_from_errno ();
3827                 return(FALSE);
3828         }
3829
3830         if (filedes[0] >= mono_w32handle_fd_reserve ||
3831             filedes[1] >= mono_w32handle_fd_reserve) {
3832                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File descriptor is too big", __func__);
3833
3834                 mono_w32error_set_last (ERROR_TOO_MANY_OPEN_FILES);
3835                 
3836                 close (filedes[0]);
3837                 close (filedes[1]);
3838                 
3839                 return(FALSE);
3840         }
3841         
3842         /* filedes[0] is open for reading, filedes[1] for writing */
3843
3844         pipe_read_handle.fd = filedes [0];
3845         pipe_read_handle.fileaccess = GENERIC_READ;
3846         read_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[0],
3847                                            &pipe_read_handle);
3848         if (read_handle == INVALID_HANDLE_VALUE) {
3849                 g_warning ("%s: error creating pipe read handle", __func__);
3850                 close (filedes[0]);
3851                 close (filedes[1]);
3852                 mono_w32error_set_last (ERROR_GEN_FAILURE);
3853                 
3854                 return(FALSE);
3855         }
3856         
3857         pipe_write_handle.fd = filedes [1];
3858         pipe_write_handle.fileaccess = GENERIC_WRITE;
3859         write_handle = mono_w32handle_new_fd (MONO_W32HANDLE_PIPE, filedes[1],
3860                                             &pipe_write_handle);
3861         if (write_handle == INVALID_HANDLE_VALUE) {
3862                 g_warning ("%s: error creating pipe write handle", __func__);
3863                 mono_w32handle_unref (read_handle);
3864                 
3865                 close (filedes[0]);
3866                 close (filedes[1]);
3867                 mono_w32error_set_last (ERROR_GEN_FAILURE);
3868                 
3869                 return(FALSE);
3870         }
3871         
3872         *readpipe = read_handle;
3873         *writepipe = write_handle;
3874
3875         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning pipe: read handle %p, write handle %p",
3876                    __func__, read_handle, write_handle);
3877
3878         return(TRUE);
3879 }
3880
3881 #ifdef HAVE_GETFSSTAT
3882 /* Darwin has getfsstat */
3883 gint32
3884 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3885 {
3886         struct statfs *stats;
3887         gint size, n, i;
3888         gunichar2 *dir;
3889         glong length, total = 0;
3890         
3891         n = getfsstat (NULL, 0, MNT_NOWAIT);
3892         if (n == -1)
3893                 return 0;
3894         size = n * sizeof (struct statfs);
3895         stats = (struct statfs *) g_malloc (size);
3896         if (stats == NULL)
3897                 return 0;
3898         if (getfsstat (stats, size, MNT_NOWAIT) == -1){
3899                 g_free (stats);
3900                 return 0;
3901         }
3902         for (i = 0; i < n; i++){
3903                 dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
3904                 if (total + length < len){
3905                         memcpy (buf + total, dir, sizeof (gunichar2) * length);
3906                         buf [total+length] = 0;
3907                 } 
3908                 g_free (dir);
3909                 total += length + 1;
3910         }
3911         if (total < len)
3912                 buf [total] = 0;
3913         total++;
3914         g_free (stats);
3915         return total;
3916 }
3917 #else
3918 /* In-place octal sequence replacement */
3919 static void
3920 unescape_octal (gchar *str)
3921 {
3922         gchar *rptr;
3923         gchar *wptr;
3924
3925         if (str == NULL)
3926                 return;
3927
3928         rptr = wptr = str;
3929         while (*rptr != '\0') {
3930                 if (*rptr == '\\') {
3931                         gchar c;
3932                         rptr++;
3933                         c = (*(rptr++) - '0') << 6;
3934                         c += (*(rptr++) - '0') << 3;
3935                         c += *(rptr++) - '0';
3936                         *wptr++ = c;
3937                 } else if (wptr != rptr) {
3938                         *wptr++ = *rptr++;
3939                 } else {
3940                         rptr++; wptr++;
3941                 }
3942         }
3943         *wptr = '\0';
3944 }
3945 static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
3946
3947 #if __linux__
3948 #define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
3949 #define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
3950 #define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
3951
3952 typedef struct 
3953 {
3954         glong total;
3955         guint32 buffer_index;
3956         guint32 mountpoint_index;
3957         guint32 field_number;
3958         guint32 allocated_size;
3959         guint32 fsname_index;
3960         guint32 fstype_index;
3961         gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
3962         gchar *mountpoint_allocated;
3963         gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
3964         gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3965         gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
3966         ssize_t nbytes;
3967         gchar delimiter;
3968         gboolean check_mount_source;
3969 } LinuxMountInfoParseState;
3970
3971 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3972 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3973 static void append_to_mountpoint (LinuxMountInfoParseState *state);
3974 static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
3975
3976 gint32
3977 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
3978 {
3979         gint fd;
3980         gint32 ret = 0;
3981         LinuxMountInfoParseState state;
3982         gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
3983
3984         memset (buf, 0, len * sizeof (gunichar2));
3985         fd = open ("/proc/self/mountinfo", O_RDONLY);
3986         if (fd != -1)
3987                 parser = GetLogicalDriveStrings_MountInfo;
3988         else {
3989                 fd = open ("/proc/mounts", O_RDONLY);
3990                 if (fd != -1)
3991                         parser = GetLogicalDriveStrings_Mounts;
3992         }
3993
3994         if (!parser) {
3995                 ret = GetLogicalDriveStrings_Mtab (len, buf);
3996                 goto done_and_out;
3997         }
3998
3999         memset (&state, 0, sizeof (LinuxMountInfoParseState));
4000         state.field_number = 1;
4001         state.delimiter = ' ';
4002
4003         while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) {
4004                 state.buffer_index = 0;
4005
4006                 while ((*parser)(len, buf, &state)) {
4007                         if (state.buffer [state.buffer_index] == '\n') {
4008                                 gboolean quit = add_drive_string (len, buf, &state);
4009                                 state.field_number = 1;
4010                                 state.buffer_index++;
4011                                 if (state.mountpoint_allocated) {
4012                                         g_free (state.mountpoint_allocated);
4013                                         state.mountpoint_allocated = NULL;
4014                                 }
4015                                 if (quit) {
4016                                         ret = state.total;
4017                                         goto done_and_out;
4018                                 }
4019                         }
4020                 }
4021         };
4022         ret = state.total;
4023
4024   done_and_out:
4025         if (fd != -1)
4026                 close (fd);
4027         return ret;
4028 }
4029
4030 static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4031 {
4032         gchar *ptr;
4033
4034         if (state->field_number == 1)
4035                 state->check_mount_source = TRUE;
4036
4037         while (state->buffer_index < (guint32)state->nbytes) {
4038                 if (state->buffer [state->buffer_index] == state->delimiter) {
4039                         state->field_number++;
4040                         switch (state->field_number) {
4041                                 case 2:
4042                                         state->mountpoint_index = 0;
4043                                         break;
4044
4045                                 case 3:
4046                                         if (state->mountpoint_allocated)
4047                                                 state->mountpoint_allocated [state->mountpoint_index] = 0;
4048                                         else
4049                                                 state->mountpoint [state->mountpoint_index] = 0;
4050                                         break;
4051
4052                                 default:
4053                                         ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
4054                                         if (ptr)
4055                                                 state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
4056                                         else
4057                                                 state->buffer_index = state->nbytes;
4058                                         return TRUE;
4059                         }
4060                         state->buffer_index++;
4061                         continue;
4062                 } else if (state->buffer [state->buffer_index] == '\n')
4063                         return TRUE;
4064
4065                 switch (state->field_number) {
4066                         case 1:
4067                                 if (state->check_mount_source) {
4068                                         if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4069                                                 /* We can ignore the rest, it's a device
4070                                                  * path */
4071                                                 state->check_mount_source = FALSE;
4072                                                 state->fsname [state->fsname_index++] = '/';
4073                                                 break;
4074                                         }
4075                                         if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4076                                                 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4077                                 }
4078                                 break;
4079
4080                         case 2:
4081                                 append_to_mountpoint (state);
4082                                 break;
4083
4084                         case 3:
4085                                 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4086                                         state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4087                                 break;
4088                 }
4089
4090                 state->buffer_index++;
4091         }
4092
4093         return FALSE;
4094 }
4095
4096 static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4097 {
4098         while (state->buffer_index < (guint32)state->nbytes) {
4099                 if (state->buffer [state->buffer_index] == state->delimiter) {
4100                         state->field_number++;
4101                         switch (state->field_number) {
4102                                 case 5:
4103                                         state->mountpoint_index = 0;
4104                                         break;
4105
4106                                 case 6:
4107                                         if (state->mountpoint_allocated)
4108                                                 state->mountpoint_allocated [state->mountpoint_index] = 0;
4109                                         else
4110                                                 state->mountpoint [state->mountpoint_index] = 0;
4111                                         break;
4112
4113                                 case 7:
4114                                         state->delimiter = '-';
4115                                         break;
4116
4117                                 case 8:
4118                                         state->delimiter = ' ';
4119                                         break;
4120
4121                                 case 10:
4122                                         state->check_mount_source = TRUE;
4123                                         break;
4124                         }
4125                         state->buffer_index++;
4126                         continue;
4127                 } else if (state->buffer [state->buffer_index] == '\n')
4128                         return TRUE;
4129
4130                 switch (state->field_number) {
4131                         case 5:
4132                                 append_to_mountpoint (state);
4133                                 break;
4134
4135                         case 9:
4136                                 if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4137                                         state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
4138                                 break;
4139
4140                         case 10:
4141                                 if (state->check_mount_source) {
4142                                         if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
4143                                                 /* We can ignore the rest, it's a device
4144                                                  * path */
4145                                                 state->check_mount_source = FALSE;
4146                                                 state->fsname [state->fsname_index++] = '/';
4147                                                 break;
4148                                         }
4149                                         if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
4150                                                 state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
4151                                 }
4152                                 break;
4153                 }
4154
4155                 state->buffer_index++;
4156         }
4157
4158         return FALSE;
4159 }
4160
4161 static void
4162 append_to_mountpoint (LinuxMountInfoParseState *state)
4163 {
4164         gchar ch = state->buffer [state->buffer_index];
4165         if (state->mountpoint_allocated) {
4166                 if (state->mountpoint_index >= state->allocated_size) {
4167                         guint32 newsize = (state->allocated_size << 1) + 1;
4168                         gchar *newbuf = (gchar *)g_malloc0 (newsize * sizeof (gchar));
4169
4170                         memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
4171                         g_free (state->mountpoint_allocated);
4172                         state->mountpoint_allocated = newbuf;
4173                         state->allocated_size = newsize;
4174                 }
4175                 state->mountpoint_allocated [state->mountpoint_index++] = ch;
4176         } else {
4177                 if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
4178                         state->allocated_size = (state->mountpoint_index << 1) + 1;
4179                         state->mountpoint_allocated = (gchar *)g_malloc0 (state->allocated_size * sizeof (gchar));
4180                         memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
4181                         state->mountpoint_allocated [state->mountpoint_index++] = ch;
4182                 } else
4183                         state->mountpoint [state->mountpoint_index++] = ch;
4184         }
4185 }
4186
4187 static gboolean
4188 add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
4189 {
4190         gboolean quit = FALSE;
4191         gboolean ignore_entry;
4192
4193         if (state->fsname_index == 1 && state->fsname [0] == '/')
4194                 ignore_entry = FALSE;
4195         else if (memcmp ("overlay", state->fsname, state->fsname_index) == 0 ||
4196                 memcmp ("aufs", state->fstype, state->fstype_index) == 0) {
4197                 /* Don't ignore overlayfs and aufs - these might be used on Docker
4198                  * (https://bugzilla.xamarin.com/show_bug.cgi?id=31021) */
4199                 ignore_entry = FALSE;
4200         } else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) {
4201                 ignore_entry = TRUE;
4202         } else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
4203                 /* Ignore GNOME's gvfs */
4204                 if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
4205                         ignore_entry = TRUE;
4206                 else
4207                         ignore_entry = FALSE;
4208         } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
4209                 ignore_entry = FALSE;
4210         else
4211                 ignore_entry = TRUE;
4212
4213         if (!ignore_entry) {
4214                 gunichar2 *dir;
4215                 glong length;
4216                 gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
4217
4218                 unescape_octal (mountpoint);
4219                 dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
4220                 if (state->total + length + 1 > len) {
4221                         quit = TRUE;
4222                         state->total = len * 2;
4223                 } else {
4224                         length++;
4225                         memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
4226                         state->total += length;
4227                 }
4228                 g_free (dir);
4229         }
4230         state->fsname_index = 0;
4231         state->fstype_index = 0;
4232
4233         return quit;
4234 }
4235 #else
4236 gint32
4237 mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf)
4238 {
4239         return GetLogicalDriveStrings_Mtab (len, buf);
4240 }
4241 #endif
4242 static gint32
4243 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
4244 {
4245         FILE *fp;
4246         gunichar2 *ptr, *dir;
4247         glong length, total = 0;
4248         gchar buffer [512];
4249         gchar **splitted;
4250
4251         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
4252         buf [0] = '/';
4253         buf [1] = 0;
4254         buf [2] = 0;
4255
4256         /* Sigh, mntent and friends don't work well.
4257          * It stops on the first line that doesn't begin with a '/'.
4258          * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
4259         fp = fopen ("/etc/mtab", "rt");
4260         if (fp == NULL) {
4261                 fp = fopen ("/etc/mnttab", "rt");
4262                 if (fp == NULL)
4263                         return 1;
4264         }
4265
4266         ptr = buf;
4267         while (fgets (buffer, 512, fp) != NULL) {
4268                 if (*buffer != '/')
4269                         continue;
4270
4271                 splitted = g_strsplit (buffer, " ", 0);
4272                 if (!*splitted || !*(splitted + 1)) {
4273                         g_strfreev (splitted);
4274                         continue;
4275                 }
4276
4277                 unescape_octal (*(splitted + 1));
4278                 dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
4279                 g_strfreev (splitted);
4280                 if (total + length + 1 > len) {
4281                         fclose (fp);
4282                         g_free (dir);
4283                         return len * 2; /* guess */
4284                 }
4285
4286                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
4287                 g_free (dir);
4288                 total += length + 1;
4289         }
4290
4291         fclose (fp);
4292         return total;
4293 /* Commented out, does not work with my mtab!!! - Gonz */
4294 #ifdef NOTENABLED /* HAVE_MNTENT_H */
4295 {
4296         FILE *fp;
4297         struct mntent *mnt;
4298         gunichar2 *ptr, *dir;
4299         glong len, total = 0;
4300         
4301
4302         fp = setmntent ("/etc/mtab", "rt");
4303         if (fp == NULL) {
4304                 fp = setmntent ("/etc/mnttab", "rt");
4305                 if (fp == NULL)
4306                         return;
4307         }
4308
4309         ptr = buf;
4310         while ((mnt = getmntent (fp)) != NULL) {
4311                 g_print ("GOT %s\n", mnt->mnt_dir);
4312                 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
4313                 if (total + len + 1 > len) {
4314                         return len * 2; /* guess */
4315                 }
4316
4317                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
4318                 g_free (dir);
4319                 total += len + 1;
4320         }
4321
4322         endmntent (fp);
4323         return total;
4324 }
4325 #endif
4326 }
4327 #endif
4328
4329 #if defined(HAVE_STATVFS) || defined(HAVE_STATFS)
4330 gboolean
4331 mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes)
4332 {
4333 #ifdef HAVE_STATVFS
4334         struct statvfs fsstat;
4335 #elif defined(HAVE_STATFS)
4336         struct statfs fsstat;
4337 #endif
4338         gboolean isreadonly;
4339         gchar *utf8_path_name;
4340         gint ret;
4341         unsigned long block_size;
4342
4343         if (path_name == NULL) {
4344                 utf8_path_name = g_strdup (g_get_current_dir());
4345                 if (utf8_path_name == NULL) {
4346                         mono_w32error_set_last (ERROR_DIRECTORY);
4347                         return(FALSE);
4348                 }
4349         }
4350         else {
4351                 utf8_path_name = mono_unicode_to_external (path_name);
4352                 if (utf8_path_name == NULL) {
4353                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4354
4355                         mono_w32error_set_last (ERROR_INVALID_NAME);
4356                         return(FALSE);
4357                 }
4358         }
4359
4360         do {
4361 #ifdef HAVE_STATVFS
4362                 ret = statvfs (utf8_path_name, &fsstat);
4363                 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
4364                 block_size = fsstat.f_frsize;
4365 #elif defined(HAVE_STATFS)
4366                 ret = statfs (utf8_path_name, &fsstat);
4367 #if defined (MNT_RDONLY)
4368                 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
4369 #elif defined (MS_RDONLY)
4370                 isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY);
4371 #endif
4372                 block_size = fsstat.f_bsize;
4373 #endif
4374         } while(ret == -1 && errno == EINTR);
4375
4376         g_free(utf8_path_name);
4377
4378         if (ret == -1) {
4379                 _wapi_set_last_error_from_errno ();
4380                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: statvfs failed: %s", __func__, strerror (errno));
4381                 return(FALSE);
4382         }
4383
4384         /* total number of free bytes for non-root */
4385         if (free_bytes_avail != NULL) {
4386                 if (isreadonly) {
4387                         *free_bytes_avail = 0;
4388                 }
4389                 else {
4390                         *free_bytes_avail = block_size * (guint64)fsstat.f_bavail;
4391                 }
4392         }
4393
4394         /* total number of bytes available for non-root */
4395         if (total_number_of_bytes != NULL) {
4396                 *total_number_of_bytes = block_size * (guint64)fsstat.f_blocks;
4397         }
4398
4399         /* total number of bytes available for root */
4400         if (total_number_of_free_bytes != NULL) {
4401                 if (isreadonly) {
4402                         *total_number_of_free_bytes = 0;
4403                 }
4404                 else {
4405                         *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree;
4406                 }
4407         }
4408         
4409         return(TRUE);
4410 }
4411 #else
4412 gboolean
4413 mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes)
4414 {
4415         if (free_bytes_avail != NULL) {
4416                 *free_bytes_avail = (guint64) -1;
4417         }
4418
4419         if (total_number_of_bytes != NULL) {
4420                 *total_number_of_bytes = (guint64) -1;
4421         }
4422
4423         if (total_number_of_free_bytes != NULL) {
4424                 *total_number_of_free_bytes = (guint64) -1;
4425         }
4426
4427         return(TRUE);
4428 }
4429 #endif
4430
4431 /*
4432  * General Unix support
4433  */
4434 typedef struct {
4435         guint32 drive_type;
4436 #if __linux__
4437         const long fstypeid;
4438 #endif
4439         const gchar* fstype;
4440 } _wapi_drive_type;
4441
4442 static _wapi_drive_type _wapi_drive_types[] = {
4443 #if PLATFORM_MACOSX
4444         { DRIVE_REMOTE, "afp" },
4445         { DRIVE_REMOTE, "autofs" },
4446         { DRIVE_CDROM, "cddafs" },
4447         { DRIVE_CDROM, "cd9660" },
4448         { DRIVE_RAMDISK, "devfs" },
4449         { DRIVE_FIXED, "exfat" },
4450         { DRIVE_RAMDISK, "fdesc" },
4451         { DRIVE_REMOTE, "ftp" },
4452         { DRIVE_FIXED, "hfs" },
4453         { DRIVE_FIXED, "msdos" },
4454         { DRIVE_REMOTE, "nfs" },
4455         { DRIVE_FIXED, "ntfs" },
4456         { DRIVE_REMOTE, "smbfs" },
4457         { DRIVE_FIXED, "udf" },
4458         { DRIVE_REMOTE, "webdav" },
4459         { DRIVE_UNKNOWN, NULL }
4460 #elif __linux__
4461         { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
4462         { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
4463         { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
4464         { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
4465         { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
4466         { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
4467         { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
4468         { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
4469         { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
4470         { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
4471         { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
4472         { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
4473         { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
4474         { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
4475         { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
4476         { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
4477         { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
4478         { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
4479         { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
4480         { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
4481         { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
4482         { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
4483         { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
4484         { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
4485         { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
4486         { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
4487         { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
4488         { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
4489         { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
4490         { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
4491         { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
4492         { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
4493         { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
4494         { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
4495         { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
4496         { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
4497         { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
4498         { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
4499         { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
4500         { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
4501         { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
4502         { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
4503         { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
4504         { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
4505         { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
4506         { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
4507         { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
4508         { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
4509         { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
4510         { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
4511         { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
4512         { DRIVE_FIXED, UFS_MAGIC, "ufs"},
4513         { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
4514         { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
4515         { DRIVE_FIXED, UFS_CIGAM, "ufs"},
4516         { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
4517         { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
4518         { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
4519         { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
4520         { DRIVE_FIXED, V9FS_MAGIC, "9p"},
4521         { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
4522         { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
4523         { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
4524         { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
4525         { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
4526         { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
4527         { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
4528         { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
4529         { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
4530         { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
4531         { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
4532         { DRIVE_UNKNOWN, 0, NULL}
4533 #else
4534         { DRIVE_RAMDISK, "ramfs"      },
4535         { DRIVE_RAMDISK, "tmpfs"      },
4536         { DRIVE_RAMDISK, "proc"       },
4537         { DRIVE_RAMDISK, "sysfs"      },
4538         { DRIVE_RAMDISK, "debugfs"    },
4539         { DRIVE_RAMDISK, "devpts"     },
4540         { DRIVE_RAMDISK, "securityfs" },
4541         { DRIVE_CDROM,   "iso9660"    },
4542         { DRIVE_FIXED,   "ext2"       },
4543         { DRIVE_FIXED,   "ext3"       },
4544         { DRIVE_FIXED,   "ext4"       },
4545         { DRIVE_FIXED,   "sysv"       },
4546         { DRIVE_FIXED,   "reiserfs"   },
4547         { DRIVE_FIXED,   "ufs"        },
4548         { DRIVE_FIXED,   "vfat"       },
4549         { DRIVE_FIXED,   "msdos"      },
4550         { DRIVE_FIXED,   "udf"        },
4551         { DRIVE_FIXED,   "hfs"        },
4552         { DRIVE_FIXED,   "hpfs"       },
4553         { DRIVE_FIXED,   "qnx4"       },
4554         { DRIVE_FIXED,   "ntfs"       },
4555         { DRIVE_FIXED,   "ntfs-3g"    },
4556         { DRIVE_REMOTE,  "smbfs"      },
4557         { DRIVE_REMOTE,  "fuse"       },
4558         { DRIVE_REMOTE,  "nfs"        },
4559         { DRIVE_REMOTE,  "nfs4"       },
4560         { DRIVE_REMOTE,  "cifs"       },
4561         { DRIVE_REMOTE,  "ncpfs"      },
4562         { DRIVE_REMOTE,  "coda"       },
4563         { DRIVE_REMOTE,  "afs"        },
4564         { DRIVE_UNKNOWN, NULL         }
4565 #endif
4566 };
4567
4568 #if __linux__
4569 static guint32 _wapi_get_drive_type(long f_type)
4570 {
4571         _wapi_drive_type *current;
4572
4573         current = &_wapi_drive_types[0];
4574         while (current->drive_type != DRIVE_UNKNOWN) {
4575                 if (current->fstypeid == f_type)
4576                         return current->drive_type;
4577                 current++;
4578         }
4579
4580         return DRIVE_UNKNOWN;
4581 }
4582 #else
4583 static guint32 _wapi_get_drive_type(const gchar* fstype)
4584 {
4585         _wapi_drive_type *current;
4586
4587         current = &_wapi_drive_types[0];
4588         while (current->drive_type != DRIVE_UNKNOWN) {
4589                 if (strcmp (current->fstype, fstype) == 0)
4590                         break;
4591
4592                 current++;
4593         }
4594         
4595         return current->drive_type;
4596 }
4597 #endif
4598
4599 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4600 static guint32
4601 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4602 {
4603         struct statfs buf;
4604         
4605         if (statfs (utf8_root_path_name, &buf) == -1)
4606                 return DRIVE_UNKNOWN;
4607 #if PLATFORM_MACOSX
4608         return _wapi_get_drive_type (buf.f_fstypename);
4609 #else
4610         return _wapi_get_drive_type (buf.f_type);
4611 #endif
4612 }
4613 #else
4614 static guint32
4615 GetDriveTypeFromPath (const gchar *utf8_root_path_name)
4616 {
4617         guint32 drive_type;
4618         FILE *fp;
4619         gchar buffer [512];
4620         gchar **splitted;
4621
4622         fp = fopen ("/etc/mtab", "rt");
4623         if (fp == NULL) {
4624                 fp = fopen ("/etc/mnttab", "rt");
4625                 if (fp == NULL) 
4626                         return(DRIVE_UNKNOWN);
4627         }
4628
4629         drive_type = DRIVE_NO_ROOT_DIR;
4630         while (fgets (buffer, 512, fp) != NULL) {
4631                 splitted = g_strsplit (buffer, " ", 0);
4632                 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
4633                         g_strfreev (splitted);
4634                         continue;
4635                 }
4636
4637                 /* compare given root_path_name with the one from mtab, 
4638                   if length of utf8_root_path_name is zero it must be the root dir */
4639                 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
4640                     (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
4641                         drive_type = _wapi_get_drive_type (*(splitted + 2));
4642                         /* it is possible this path might be mounted again with
4643                            a known type...keep looking */
4644                         if (drive_type != DRIVE_UNKNOWN) {
4645                                 g_strfreev (splitted);
4646                                 break;
4647                         }
4648                 }
4649
4650                 g_strfreev (splitted);
4651         }
4652
4653         fclose (fp);
4654         return drive_type;
4655 }
4656 #endif
4657
4658 guint32
4659 mono_w32file_get_drive_type(const gunichar2 *root_path_name)
4660 {
4661         gchar *utf8_root_path_name;
4662         guint32 drive_type;
4663
4664         if (root_path_name == NULL) {
4665                 utf8_root_path_name = g_strdup (g_get_current_dir());
4666                 if (utf8_root_path_name == NULL) {
4667                         return(DRIVE_NO_ROOT_DIR);
4668                 }
4669         }
4670         else {
4671                 utf8_root_path_name = mono_unicode_to_external (root_path_name);
4672                 if (utf8_root_path_name == NULL) {
4673                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
4674                         return(DRIVE_NO_ROOT_DIR);
4675                 }
4676                 
4677                 /* strip trailing slash for compare below */
4678                 if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) {
4679                         utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
4680                 }
4681         }
4682         drive_type = GetDriveTypeFromPath (utf8_root_path_name);
4683         g_free (utf8_root_path_name);
4684
4685         return (drive_type);
4686 }
4687
4688 #if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) || defined(__FreeBSD_kernel__)
4689 static gchar*
4690 get_fstypename (gchar *utfpath)
4691 {
4692 #if defined (PLATFORM_MACOSX) || defined (__linux__)
4693         struct statfs stat;
4694 #if __linux__
4695         _wapi_drive_type *current;
4696 #endif
4697         if (statfs (utfpath, &stat) == -1)
4698                 return NULL;
4699 #if PLATFORM_MACOSX
4700         return g_strdup (stat.f_fstypename);
4701 #else
4702         current = &_wapi_drive_types[0];
4703         while (current->drive_type != DRIVE_UNKNOWN) {
4704                 if (stat.f_type == current->fstypeid)
4705                         return g_strdup (current->fstype);
4706                 current++;
4707         }
4708         return NULL;
4709 #endif
4710 #else
4711         return NULL;
4712 #endif
4713 }
4714
4715 /* Linux has struct statfs which has a different layout */
4716 gboolean
4717 mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize)
4718 {
4719         gchar *utfpath;
4720         gchar *fstypename;
4721         gboolean status = FALSE;
4722         glong len;
4723         
4724         // We only support getting the file system type
4725         if (fsbuffer == NULL)
4726                 return 0;
4727         
4728         utfpath = mono_unicode_to_external (path);
4729         if ((fstypename = get_fstypename (utfpath)) != NULL){
4730                 gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL);
4731                 if (ret != NULL && len < fsbuffersize){
4732                         memcpy (fsbuffer, ret, len * sizeof (gunichar2));
4733                         fsbuffer [len] = 0;
4734                         status = TRUE;
4735                 }
4736                 if (ret != NULL)
4737                         g_free (ret);
4738                 g_free (fstypename);
4739         }
4740         g_free (utfpath);
4741         return status;
4742 }
4743 #endif
4744
4745 static gboolean
4746 LockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4747 {
4748         MonoW32HandleFile *file_handle;
4749         off_t offset, length;
4750
4751         if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4752                 g_warning ("%s: error looking up file handle %p", __func__, handle);
4753                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4754                 return FALSE;
4755         }
4756
4757         if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4758                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4759                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4760                 return FALSE;
4761         }
4762
4763 #ifdef HAVE_LARGE_FILE_SUPPORT
4764         offset = ((gint64)offset_high << 32) | offset_low;
4765         length = ((gint64)length_high << 32) | length_low;
4766
4767         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
4768 #else
4769         if (offset_high > 0 || length_high > 0) {
4770                 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
4771                 return FALSE;
4772         }
4773         offset = offset_low;
4774         length = length_low;
4775
4776         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
4777 #endif
4778
4779         return _wapi_lock_file_region (GPOINTER_TO_UINT(handle), offset, length);
4780 }
4781
4782 static gboolean
4783 UnlockFile (gpointer handle, guint32 offset_low, guint32 offset_high, guint32 length_low, guint32 length_high)
4784 {
4785         MonoW32HandleFile *file_handle;
4786         off_t offset, length;
4787
4788         if (!mono_w32handle_lookup (handle, MONO_W32HANDLE_FILE, (gpointer *)&file_handle)) {
4789                 g_warning ("%s: error looking up file handle %p", __func__, handle);
4790                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
4791                 return FALSE;
4792         }
4793
4794         if (!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) {
4795                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4796                 mono_w32error_set_last (ERROR_ACCESS_DENIED);
4797                 return FALSE;
4798         }
4799
4800 #ifdef HAVE_LARGE_FILE_SUPPORT
4801         offset = ((gint64)offset_high << 32) | offset_low;
4802         length = ((gint64)length_high << 32) | length_low;
4803
4804         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %lld, length %lld", __func__, handle, offset, length);
4805 #else
4806         offset = offset_low;
4807         length = length_low;
4808
4809         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Unlocking handle %p, offset %ld, length %ld", __func__, handle, offset, length);
4810 #endif
4811
4812         return _wapi_unlock_file_region (GPOINTER_TO_UINT(handle), offset, length);
4813 }
4814
4815 void
4816 mono_w32file_init (void)
4817 {
4818         mono_os_mutex_init (&stdhandle_mutex);
4819         mono_os_mutex_init (&file_share_mutex);
4820
4821         mono_w32handle_register_ops (MONO_W32HANDLE_FILE,    &_wapi_file_ops);
4822         mono_w32handle_register_ops (MONO_W32HANDLE_CONSOLE, &_wapi_console_ops);
4823         mono_w32handle_register_ops (MONO_W32HANDLE_FIND,    &_wapi_find_ops);
4824         mono_w32handle_register_ops (MONO_W32HANDLE_PIPE,    &_wapi_pipe_ops);
4825
4826 /*      mono_w32handle_register_capabilities (MONO_W32HANDLE_FILE, */
4827 /*                                          MONO_W32HANDLE_CAP_WAIT); */
4828 /*      mono_w32handle_register_capabilities (MONO_W32HANDLE_CONSOLE, */
4829 /*                                          MONO_W32HANDLE_CAP_WAIT); */
4830
4831         if (g_getenv ("MONO_STRICT_IO_EMULATION"))
4832                 lock_while_writing = TRUE;
4833 }
4834
4835 void
4836 mono_w32file_cleanup (void)
4837 {
4838         mono_os_mutex_destroy (&file_share_mutex);
4839
4840         if (file_share_table)
4841                 g_hash_table_destroy (file_share_table);
4842 }
4843
4844 gboolean
4845 mono_w32file_move (gunichar2 *path, gunichar2 *dest, gint32 *error)
4846 {
4847         gboolean result;
4848
4849         MONO_ENTER_GC_SAFE;
4850
4851         result = MoveFile (path, dest);
4852         if (!result)
4853                 *error = mono_w32error_get_last ();
4854
4855         MONO_EXIT_GC_SAFE;
4856
4857         return result;
4858 }
4859
4860 gboolean
4861 mono_w32file_copy (gunichar2 *path, gunichar2 *dest, gboolean overwrite, gint32 *error)
4862 {
4863         gboolean result;
4864
4865         MONO_ENTER_GC_SAFE;
4866
4867         result = CopyFile (path, dest, !overwrite);
4868         if (!result)
4869                 *error = mono_w32error_get_last ();
4870
4871         MONO_EXIT_GC_SAFE;
4872
4873         return result;
4874 }
4875
4876 gboolean
4877 mono_w32file_replace (gunichar2 *destinationFileName, gunichar2 *sourceFileName, gunichar2 *destinationBackupFileName, guint32 flags, gint32 *error)
4878 {
4879         gboolean result;
4880
4881         MONO_ENTER_GC_SAFE;
4882
4883         result = ReplaceFile (destinationFileName, sourceFileName, destinationBackupFileName, flags, NULL, NULL);
4884         if (!result)
4885                 *error = mono_w32error_get_last ();
4886
4887         MONO_EXIT_GC_SAFE;
4888
4889         return result;
4890 }
4891
4892 gint64
4893 mono_w32file_get_file_size (gpointer handle, gint32 *error)
4894 {
4895         gint64 length;
4896         guint32 length_hi;
4897
4898         MONO_ENTER_GC_SAFE;
4899
4900         length = GetFileSize (handle, &length_hi);
4901         if(length==INVALID_FILE_SIZE) {
4902                 *error=mono_w32error_get_last ();
4903         }
4904
4905         MONO_EXIT_GC_SAFE;
4906
4907         return length | ((gint64)length_hi << 32);
4908 }
4909
4910 gboolean
4911 mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4912 {
4913         gboolean result;
4914
4915         MONO_ENTER_GC_SAFE;
4916
4917         result = LockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4918         if (!result)
4919                 *error = mono_w32error_get_last ();
4920
4921         MONO_EXIT_GC_SAFE;
4922
4923         return result;
4924 }
4925
4926 gboolean
4927 mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error)
4928 {
4929         gboolean result;
4930
4931         MONO_ENTER_GC_SAFE;
4932
4933         result = UnlockFile (handle, position & 0xFFFFFFFF, position >> 32, length & 0xFFFFFFFF, length >> 32);
4934         if (!result)
4935                 *error = mono_w32error_get_last ();
4936
4937         MONO_EXIT_GC_SAFE;
4938
4939         return result;
4940 }
4941
4942 gpointer
4943 mono_w32file_get_console_input (void)
4944 {
4945         gpointer handle;
4946
4947         MONO_ENTER_GC_SAFE;
4948         handle = mono_w32file_get_std_handle (STD_INPUT_HANDLE);
4949         MONO_EXIT_GC_SAFE;
4950
4951         return handle;
4952 }
4953
4954 gpointer
4955 mono_w32file_get_console_output (void)
4956 {
4957         gpointer handle;
4958
4959         MONO_ENTER_GC_SAFE;
4960         handle = mono_w32file_get_std_handle (STD_OUTPUT_HANDLE);
4961         MONO_EXIT_GC_SAFE;
4962
4963         return handle;
4964 }
4965
4966 gpointer
4967 mono_w32file_get_console_error (void)
4968 {
4969         gpointer handle;
4970
4971         MONO_ENTER_GC_SAFE;
4972         handle = mono_w32file_get_std_handle (STD_ERROR_HANDLE);
4973         MONO_EXIT_GC_SAFE;
4974
4975         return handle;
4976 }