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