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