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