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