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