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