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