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