Cleanup unneeded code and definitions. Add MIT/X11 license header to file. Code submi...
[mono.git] / mono / io-layer / io.c
index 72be9df6c943ddb39fae9ad2cb127275106fc55c..e060a7d3724205f07768a0c4454352471cb2dc73 100644 (file)
 #include <errno.h>
 #include <string.h>
 #include <sys/stat.h>
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#elif defined(HAVE_SYS_STATFS_H)
+#include <sys/statfs.h>
+#elif defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
 #include <sys/types.h>
 #include <dirent.h>
 #include <fnmatch.h>
 #include <stdio.h>
 #include <utime.h>
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#endif
 
 #include <mono/io-layer/wapi.h>
 #include <mono/io-layer/wapi-private.h>
@@ -32,9 +44,6 @@
 
 #undef DEBUG
 
-static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
-static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
-
 static void file_close (gpointer handle, gpointer data);
 static WapiFileType file_getfiletype(void);
 static gboolean file_read(gpointer handle, gpointer buffer,
@@ -321,7 +330,8 @@ static void file_close (gpointer handle, gpointer data)
        
        g_free (file_handle->filename);
        
-       _wapi_handle_share_release (file_handle->share_info);
+       if (file_handle->share_info)
+               _wapi_handle_share_release (file_handle->share_info);
        
        close (GPOINTER_TO_UINT(handle));
 }
@@ -394,7 +404,7 @@ static gboolean file_write(gpointer handle, gconstpointer buffer,
        struct _WapiHandle_file *file_handle;
        gboolean ok;
        int ret;
-       off_t current_pos;
+       off_t current_pos = 0;
        int fd = GPOINTER_TO_UINT(handle);
        
        ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
@@ -792,6 +802,34 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
                return(INVALID_FILE_SIZE);
        }
        
+       /* fstat indicates block devices as zero-length, so go a different path */
+#ifdef BLKGETSIZE64
+       if (S_ISBLK(statbuf.st_mode)) {
+               guint64 bigsize;
+               if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) {
+#ifdef DEBUG
+                       g_message ("%s: handle %p ioctl BLKGETSIZE64 failed: %s",
+                                  __func__, handle, strerror(errno));
+#endif
+
+                       _wapi_set_last_error_from_errno ();
+                       return(INVALID_FILE_SIZE);
+               }
+               
+               size = bigsize & 0xFFFFFFFF;
+               if (highsize != NULL) {
+                       *highsize = bigsize>>32;
+               }
+
+#ifdef DEBUG
+               g_message ("%s: Returning block device size %d/%d",
+                          __func__, size, *highsize);
+#endif
+       
+               return(size);
+       }
+#endif
+       
 #ifdef HAVE_LARGE_FILE_SUPPORT
        size = statbuf.st_size & 0xFFFFFFFF;
        if (highsize != NULL) {
@@ -1339,31 +1377,6 @@ static int convert_flags(guint32 fileaccess, guint32 createmode)
        return(flags);
 }
 
-static guint32 convert_from_flags(int flags)
-{
-       guint32 fileaccess=0;
-       
-#ifndef O_ACCMODE
-#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
-#endif
-
-       if((flags & O_ACCMODE) == O_RDONLY) {
-               fileaccess=GENERIC_READ;
-       } else if ((flags & O_ACCMODE) == O_WRONLY) {
-               fileaccess=GENERIC_WRITE;
-       } else if ((flags & O_ACCMODE) == O_RDWR) {
-               fileaccess=GENERIC_READ|GENERIC_WRITE;
-       } else {
-#ifdef DEBUG
-               g_message("%s: Can't figure out flags 0x%x", __func__, flags);
-#endif
-       }
-
-       /* Maybe sort out create mode too */
-
-       return(fileaccess);
-}
-
 #if 0 /* unused */
 static mode_t convert_perms(guint32 sharemode)
 {
@@ -1514,7 +1527,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
         * other code, perms instead are the on-disk permissions and
         * this is a sane default.
         */
-       mode_t perms=0644;
+       mode_t perms=0666;
        gchar *filename;
        int fd, ret;
        int handle_type;
@@ -1522,6 +1535,9 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
        
        mono_once (&io_ops_once, io_ops_init);
 
+       if (attrs & FILE_ATTRIBUTE_TEMPORARY)
+               perms = 0600;
+       
        if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
                SetLastError (ERROR_ENCRYPTION_FAILED);
                return INVALID_HANDLE_VALUE;
@@ -1564,7 +1580,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
        if (fd == -1 && errno == EISDIR)
        {
                /* Try again but don't try to make it writable */
-               fd = open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
+               fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
        }
        
        if (fd == -1) {
@@ -1685,6 +1701,11 @@ gboolean DeleteFile(const gunichar2 *name)
        gchar *filename;
        int retval;
        gboolean ret = FALSE;
+       guint32 attrs;
+#if 0
+       struct stat statbuf;
+       struct _WapiFileShare *shareinfo;
+#endif
        
        if(name==NULL) {
 #ifdef DEBUG
@@ -1704,7 +1725,50 @@ gboolean DeleteFile(const gunichar2 *name)
                SetLastError (ERROR_INVALID_NAME);
                return(FALSE);
        }
+
+       attrs = GetFileAttributes (name);
+       if (attrs == INVALID_FILE_ATTRIBUTES) {
+#ifdef DEBUG
+               g_message ("%s: file attributes error", __func__);
+#endif
+               /* Error set by GetFileAttributes() */
+               g_free (filename);
+               return(FALSE);
+       }
+
+       if (attrs & FILE_ATTRIBUTE_READONLY) {
+#ifdef DEBUG
+               g_message ("%s: file %s is readonly", __func__, filename);
+#endif
+               SetLastError (ERROR_ACCESS_DENIED);
+               g_free (filename);
+               return(FALSE);
+       }
+
+#if 0
+       /* Check to make sure sharing allows us to open the file for
+        * writing.  See bug 323389.
+        *
+        * Do the checks that don't need an open file descriptor, for
+        * simplicity's sake.  If we really have to do the full checks
+        * then we can implement that later.
+        */
+       if (_wapi_stat (filename, &statbuf) < 0) {
+               _wapi_set_last_path_error_from_errno (NULL, filename);
+               g_free (filename);
+               return(FALSE);
+       }
        
+       if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
+                              &shareinfo) == FALSE) {
+               SetLastError (ERROR_SHARING_VIOLATION);
+               g_free (filename);
+               return FALSE;
+       }
+       if (shareinfo)
+               _wapi_handle_share_release (shareinfo);
+#endif
+
        retval = _wapi_unlink (filename);
        
        if (retval == -1) {
@@ -1734,9 +1798,10 @@ gboolean DeleteFile(const gunichar2 *name)
 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
 {
        gchar *utf8_name, *utf8_dest_name;
-       int result;
+       int result, errno_copy;
        struct stat stat_src, stat_dest;
        gboolean ret = FALSE;
+       struct _WapiFileShare *shareinfo;
        
        if(name==NULL) {
 #ifdef DEBUG
@@ -1783,22 +1848,52 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
         * We check it here and return the failure if dest exists and is not
         * the same file as src.
         */
-       if (!_wapi_stat (utf8_dest_name, &stat_dest) &&
-           !_wapi_stat (utf8_name, &stat_src)) {
+       if (_wapi_stat (utf8_name, &stat_src) < 0) {
+               if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
+                       _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+                       g_free (utf8_name);
+                       g_free (utf8_dest_name);
+                       return FALSE;
+               }
+       }
+       
+       if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
                if (stat_dest.st_dev != stat_src.st_dev ||
                    stat_dest.st_ino != stat_src.st_ino) {
+                       g_free (utf8_name);
+                       g_free (utf8_dest_name);
                        SetLastError (ERROR_ALREADY_EXISTS);
                        return FALSE;
-               }       
+               }
        }
 
-       result = _wapi_rename (utf8_name, utf8_dest_name);
+       /* Check to make sure sharing allows us to open the file for
+        * writing.  See bug 377049.
+        *
+        * Do the checks that don't need an open file descriptor, for
+        * simplicity's sake.  If we really have to do the full checks
+        * then we can implement that later.
+        */
+       if (share_allows_open (&stat_src, 0, GENERIC_WRITE,
+                              &shareinfo) == FALSE) {
+               SetLastError (ERROR_SHARING_VIOLATION);
+               return FALSE;
+       }
+       if (shareinfo)
+               _wapi_handle_share_release (shareinfo);
 
+       result = _wapi_rename (utf8_name, utf8_dest_name);
+       errno_copy = errno;
+       
        if (result == -1) {
-               switch(errno) {
+               switch(errno_copy) {
                case EEXIST:
                        SetLastError (ERROR_ALREADY_EXISTS);
                        break;
+
+               case EXDEV:
+                       /* Ignore here, it is dealt with below */
+                       break;
                        
                default:
                        _wapi_set_last_path_error_from_errno (NULL, utf8_name);
@@ -1808,7 +1903,11 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
        g_free (utf8_name);
        g_free (utf8_dest_name);
 
-       if (result != 0 && errno == EXDEV) {
+       if (result != 0 && errno_copy == EXDEV) {
+               if (S_ISDIR (stat_src.st_mode)) {
+                       SetLastError (ERROR_NOT_SAME_DEVICE);
+                       return FALSE;
+               }
                /* Try a copy to the new location, and delete the source */
                if (CopyFile (name, dest_name, TRUE)==FALSE) {
                        /* CopyFile will set the error */
@@ -1825,6 +1924,56 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
        return(ret);
 }
 
+static gboolean
+write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors)
+{
+       int remain, n;
+       char *buf, *wbuf;
+       int buf_size = st_src->st_blksize;
+
+       buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
+       buf = (char *) malloc (buf_size);
+
+       for (;;) {
+               remain = read (src_fd, buf, buf_size);
+               if (remain < 0) {
+                       if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
+                               continue;
+
+                       if (report_errors)
+                               _wapi_set_last_error_from_errno ();
+
+                       free (buf);
+                       return FALSE;
+               }
+               if (remain == 0) {
+                       break;
+               }
+
+               wbuf = buf;
+               while (remain > 0) {
+                       if ((n = write (dest_fd, wbuf, remain)) < 0) {
+                               if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
+                                       continue;
+
+                               if (report_errors)
+                                       _wapi_set_last_error_from_errno ();
+#ifdef DEBUG
+                               g_message ("%s: write failed.", __func__);
+#endif
+                               free (buf);
+                               return FALSE;
+                       }
+
+                       remain -= n;
+                       wbuf += n;
+               }
+       }
+
+       free (buf);
+       return TRUE ;
+}
+
 /**
  * CopyFile:
  * @name: a pointer to a NULL-terminated unicode string, that names
@@ -1842,10 +1991,8 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
 {
        gchar *utf8_src, *utf8_dest;
        int src_fd, dest_fd;
-       int buf_size;
-       char *buf;
-       int remain, n;
-       struct stat st;
+       struct stat st, dest_st;
+       gboolean ret = TRUE;
        
        if(name==NULL) {
 #ifdef DEBUG
@@ -1869,7 +2016,7 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
        
        if(dest_name==NULL) {
 #ifdef DEBUG
-               g_message("%s: name is NULL", __func__);
+               g_message("%s: dest is NULL", __func__);
 #endif
 
                g_free (utf8_src);
@@ -1910,18 +2057,31 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
                
                return(FALSE);
        }
+
+       /* Before trying to open/create the dest, we need to report a 'file busy'
+        * error if src and dest are actually the same file. We do the check here to take
+        * advantage of the IOMAP capability */
+       if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && 
+                       st.st_ino == dest_st.st_ino) {
+
+               g_free (utf8_src);
+               g_free (utf8_dest);
+               close (src_fd);
+
+               SetLastError (ERROR_SHARING_VIOLATION);
+               return (FALSE);
+       }
        
        if (fail_if_exists) {
-               dest_fd = open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL,
-                               st.st_mode);
+               dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
        } else {
-               dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
+               /* FIXME: it kinda sucks that this code path potentially scans
+                * the directory twice due to the weird SetLastError()
+                * behavior. */
+               dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
                if (dest_fd < 0) {
-                       /* O_TRUNC might cause a fail if the file
-                        * doesn't exist
-                        */
-                       dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
-                                       st.st_mode);
+                       /* The file does not exist, try creating it */
+                       dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
                } else {
                        /* Apparently this error is set if we
                         * overwrite the dest file
@@ -1935,121 +2095,104 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
                g_free (utf8_src);
                g_free (utf8_dest);
                close (src_fd);
-               
+
                return(FALSE);
        }
-       
-       buf_size = st.st_blksize;
-       buf = (char *) alloca (buf_size);
-       
-       for (;;) {
-               remain = read (src_fd, buf, buf_size);
-               
-               if (remain < 0) {
-                       if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
-                               continue;
-                       }
-                       
-                       _wapi_set_last_error_from_errno ();
-
-                       g_free (utf8_src);
-                       g_free (utf8_dest);
-                       close (src_fd);
-                       close (dest_fd);
-                       
-                       return(FALSE);
-               }
-               
-               if (remain == 0) {
-                       break;
-               }
-
-               while (remain > 0) {
-                       if ((n = write (dest_fd, buf, remain)) < 0) {
-                               if (errno == EINTR && !_wapi_thread_cur_apc_pending())
-                                       continue;
-
-                               _wapi_set_last_error_from_errno ();
-#ifdef DEBUG
-                               g_message ("%s: write failed.", __func__);
-#endif
 
-                               g_free (utf8_src);
-                               g_free (utf8_dest);
-                               close (src_fd);
-                               close (dest_fd);
-
-                               return (FALSE);
-                       }
-
-                       remain -= n;
-               }
-       }
+       if (!write_file (src_fd, dest_fd, &st, TRUE))
+               ret = FALSE;
 
        g_free (utf8_src);
        g_free (utf8_dest);
        close (src_fd);
        close (dest_fd);
 
-       return(TRUE);
+       return ret;
 }
 
-static gpointer stdhandle_create (int fd, const gchar *name)
+static gchar*
+convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
 {
-       struct _WapiHandle_file file_handle = {0};
-       gpointer handle;
-       int flags;
-       
+       gchar *utf8_ret;
+
+       if (arg == NULL) {
 #ifdef DEBUG
-       g_message("%s: creating standard handle type %s, fd %d", __func__,
-                 name, fd);
+               g_message ("%s: %s is NULL", __func__, arg_name);
 #endif
-       
-       /* Check if fd is valid */
-       do {
-               flags=fcntl(fd, F_GETFL);
-       } while (flags == -1 && errno == EINTR);
+               SetLastError (ERROR_INVALID_NAME);
+               return NULL;
+       }
 
-       if(flags==-1) {
-               /* Invalid fd.  Not really much point checking for EBADF
-                * specifically
-                */
+       utf8_ret = mono_unicode_to_external (arg);
+       if (utf8_ret == NULL) {
 #ifdef DEBUG
-               g_message("%s: fcntl error on fd %d: %s", __func__, fd,
-                         strerror(errno));
+               g_message ("%s: unicode conversion of %s returned NULL",
+                          __func__, arg_name);
 #endif
-
-               _wapi_set_last_error_from_errno ();
-               return(INVALID_HANDLE_VALUE);
+               SetLastError (ERROR_INVALID_PARAMETER);
+               return NULL;
        }
 
-       file_handle.filename = g_strdup(name);
-       /* some default security attributes might be needed */
-       file_handle.security_attributes=0;
-       file_handle.fileaccess=convert_from_flags(flags);
+       return utf8_ret;
+}
 
-       /* Apparently input handles can't be written to.  (I don't
-        * know if output or error handles can't be read from.)
-        */
-       if (fd == 0) {
-               file_handle.fileaccess &= ~GENERIC_WRITE;
+gboolean
+ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
+                     const gunichar2 *backupFileName, guint32 replaceFlags, 
+                     gpointer exclude, gpointer reserved)
+{
+       int result, errno_copy, backup_fd = -1,replaced_fd = -1;
+       gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
+       struct stat stBackup;
+       gboolean ret = FALSE;
+
+       if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
+               return FALSE;
+       if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
+               goto replace_cleanup;
+       if (backupFileName != NULL) {
+               if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
+                       goto replace_cleanup;
        }
-       
-       file_handle.sharemode=0;
-       file_handle.attrs=0;
 
-       handle = _wapi_handle_new_fd (WAPI_HANDLE_CONSOLE, fd, &file_handle);
-       if (handle == _WAPI_HANDLE_INVALID) {
-               g_warning ("%s: error creating file handle", __func__);
-               SetLastError (ERROR_GEN_FAILURE);
-               return(INVALID_HANDLE_VALUE);
+       if (utf8_backupFileName) {
+               // Open the backup file for read so we can restore the file if an error occurs.
+               backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
+               result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
+               errno_copy = errno;
+               if (result == -1)
+                       goto replace_cleanup;
        }
-       
-#ifdef DEBUG
-       g_message("%s: returning handle %p", __func__, handle);
-#endif
 
-       return(handle);
+       result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
+       errno_copy = errno;
+       if (result == -1) {
+               _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
+               _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
+               if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
+                       replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
+                                                 stBackup.st_mode);
+                       
+                       if (replaced_fd == -1)
+                               goto replace_cleanup;
+
+                       write_file (backup_fd, replaced_fd, &stBackup, FALSE);
+               }
+
+               goto replace_cleanup;
+       }
+
+       ret = TRUE;
+
+replace_cleanup:
+       g_free (utf8_replacedFileName);
+       g_free (utf8_replacementFileName);
+       g_free (utf8_backupFileName);
+       if (backup_fd != -1)
+               close (backup_fd);
+       if (replaced_fd != -1)
+               close (replaced_fd);
+       return ret;
 }
 
 /**
@@ -2108,7 +2251,7 @@ gpointer GetStdHandle(WapiStdHandle stdhandle)
                                  (gpointer *)&file_handle);
        if (ok == FALSE) {
                /* Need to create this console handle */
-               handle = stdhandle_create (fd, name);
+               handle = _wapi_stdhandle_create (fd, name);
                
                if (handle == INVALID_HANDLE_VALUE) {
                        SetLastError (ERROR_NO_MORE_FILES);
@@ -2602,181 +2745,6 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
        return(TRUE);
 }
 
-static gint
-file_compare (gconstpointer a, gconstpointer b)
-{
-       gchar *astr = *(gchar **) a;
-       gchar *bstr = *(gchar **) b;
-
-       return strcmp (astr, bstr);
-}
-
-static gint
-get_errno_from_g_file_error (gint error)
-{
-       switch (error) {
-#ifdef EACCESS
-       case G_FILE_ERROR_ACCES:
-               error = EACCES;
-               break;
-#endif
-#ifdef ENAMETOOLONG
-       case G_FILE_ERROR_NAMETOOLONG:
-               error = ENAMETOOLONG;
-               break;
-#endif
-#ifdef ENOENT
-       case G_FILE_ERROR_NOENT:
-               error = ENOENT;
-               break;
-#endif
-#ifdef ENOTDIR
-       case G_FILE_ERROR_NOTDIR:
-               error = ENOTDIR;
-               break;
-#endif
-#ifdef ENXIO
-       case G_FILE_ERROR_NXIO:
-               error = ENXIO;
-               break;
-#endif
-#ifdef ENODEV
-       case G_FILE_ERROR_NODEV:
-               error = ENODEV;
-               break;
-#endif
-#ifdef EROFS
-       case G_FILE_ERROR_ROFS:
-               error = EROFS;
-               break;
-#endif
-#ifdef ETXTBSY
-       case G_FILE_ERROR_TXTBSY:
-               error = ETXTBSY;
-               break;
-#endif
-#ifdef EFAULT
-       case G_FILE_ERROR_FAULT:
-               error = EFAULT;
-               break;
-#endif
-#ifdef ELOOP
-       case G_FILE_ERROR_LOOP:
-               error = ELOOP;
-               break;
-#endif
-#ifdef ENOSPC
-       case G_FILE_ERROR_NOSPC:
-               error = ENOSPC;
-               break;
-#endif
-#ifdef ENOMEM
-       case G_FILE_ERROR_NOMEM:
-               error = ENOMEM;
-               break;
-#endif
-#ifdef EMFILE
-       case G_FILE_ERROR_MFILE:
-               error = EMFILE;
-               break;
-#endif
-#ifdef ENFILE
-       case G_FILE_ERROR_NFILE:
-               error = ENFILE;
-               break;
-#endif
-#ifdef EBADF
-       case G_FILE_ERROR_BADF:
-               error = EBADF;
-               break;
-#endif
-#ifdef EINVAL
-       case G_FILE_ERROR_INVAL:
-               error = EINVAL;
-               break;
-#endif
-#ifdef EPIPE
-       case G_FILE_ERROR_PIPE:
-               error = EPIPE;
-               break;
-#endif
-#ifdef EAGAIN
-       case G_FILE_ERROR_AGAIN:
-               error = EAGAIN;
-               break;
-#endif
-#ifdef EINTR
-       case G_FILE_ERROR_INTR:
-               error = EINTR;
-               break;
-#endif
-#ifdef EWIO
-       case G_FILE_ERROR_IO:
-               error = EIO;
-               break;
-#endif
-#ifdef EPERM
-       case G_FILE_ERROR_PERM:
-               error = EPERM;
-               break;
-#endif
-       case G_FILE_ERROR_FAILED:
-               error = ERROR_INVALID_PARAMETER;
-               break;
-       }
-
-       return error;
-}
-
-/* scandir using glib */
-static gint
-mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
-{
-       GError *error = NULL;
-       GDir *dir;
-       GPtrArray *names;
-       const gchar *name;
-       gint result;
-       GPatternSpec *patspec;
-
-       dir = _wapi_g_dir_open (dirname, 0, &error);
-       if (dir == NULL) {
-               /* g_dir_open returns ENOENT on directories on which we don't
-                * have read/x permission */
-               gint errnum = get_errno_from_g_file_error (error->code);
-               g_error_free (error);
-               if (errnum == ENOENT &&
-                   !_wapi_access (dirname, F_OK) &&
-                   _wapi_access (dirname, R_OK|X_OK)) {
-                       errnum = EACCES;
-               }
-
-               errno = errnum;
-               return -1;
-       }
-
-       patspec = g_pattern_spec_new (pattern);
-       names = g_ptr_array_new ();
-       while ((name = g_dir_read_name (dir)) != NULL) {
-               if (g_pattern_match_string (patspec, name))
-                       g_ptr_array_add (names, g_strdup (name));
-       }
-       
-       g_pattern_spec_free (patspec);
-       g_dir_close (dir);
-       result = names->len;
-       if (result > 0) {
-               g_ptr_array_sort (names, file_compare);
-               g_ptr_array_set_size (names, result + 1);
-
-               *namelist = (gchar **) g_ptr_array_free (names, FALSE);
-       } else {
-               g_ptr_array_free (names, TRUE);
-       }
-
-       return result;
-}
-
 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
 {
        struct _WapiHandle_find find_handle = {0};
@@ -2846,7 +2814,8 @@ gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
         */
 
        find_handle.namelist = NULL;
-       result = mono_io_scandir (dir_part, entry_part, &find_handle.namelist);
+       result = _wapi_io_scandir (dir_part, entry_part,
+                                  &find_handle.namelist);
        
        if (result == 0) {
                /* No files, which windows seems to call
@@ -2910,7 +2879,8 @@ gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
 {
        struct _WapiHandle_find *find_handle;
        gboolean ok;
-       struct stat buf;
+       struct stat buf, linkbuf;
+       int result;
        gchar *filename;
        gchar *utf8_filename, *utf8_basename;
        gunichar2 *utf16_basename;
@@ -2942,7 +2912,14 @@ retry:
        /* stat next match */
 
        filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
-       if (_wapi_lstat (filename, &buf) != 0) {
+
+       result = _wapi_stat (filename, &buf);
+       if (result == -1 && errno == ENOENT) {
+               /* Might be a dangling symlink */
+               result = _wapi_lstat (filename, &buf);
+       }
+       
+       if (result != 0) {
 #ifdef DEBUG
                g_message ("%s: stat failed: %s", __func__, filename);
 #endif
@@ -2951,12 +2928,24 @@ retry:
                goto retry;
        }
 
+       result = _wapi_lstat (filename, &linkbuf);
+       if (result != 0) {
+#ifdef DEBUG
+               g_message ("%s: lstat failed: %s", __func__, filename);
+#endif
+
+               g_free (filename);
+               goto retry;
+       }
+
        utf8_filename = mono_utf8_from_external (filename);
        if (utf8_filename == NULL) {
                /* We couldn't turn this filename into utf8 (eg the
                 * encoding of the name wasn't convertible), so just
                 * ignore it.
                 */
+               g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
+               
                g_free (filename);
                goto retry;
        }
@@ -2973,7 +2962,7 @@ retry:
        else
                create_time = buf.st_ctime;
        
-       find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &buf);
+       find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
 
        _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
        _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
@@ -3413,35 +3402,33 @@ extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
  */
 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
 {
-       gchar *path;
        gunichar2 *utf16_path;
        glong count;
        gsize bytes;
-       
-       path = g_get_current_dir ();
-       if (path == NULL)
+
+       if (getcwd ((char*)buffer, length) == NULL) {
+               if (errno == ERANGE) { /*buffer length is not big enough */ 
+                       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*/
+                       if (path == NULL)
+                               return 0;
+                       utf16_path = mono_unicode_from_external (path, &bytes);
+                       g_free (utf16_path);
+                       g_free (path);
+                       return (bytes/2)+1;
+               }
+               _wapi_set_last_error_from_errno ();
                return 0;
+       }
 
-       utf16_path=mono_unicode_from_external (path, &bytes);
-       
-       /* if buffer too small, return number of characters required.
-        * this is plain dumb.
-        */
-       
+       utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
        count = (bytes/2)+1;
-       if (count > length) {
-               g_free(path);
-               g_free (utf16_path);
-               
-               return (count);
-       }
+       g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
 
        /* Add the terminator */
        memset (buffer, '\0', bytes+2);
        memcpy (buffer, utf16_path, bytes);
        
        g_free (utf16_path);
-       g_free (path);
 
        return count;
 }
@@ -3600,6 +3587,34 @@ guint32 GetTempPath (guint32 len, gunichar2 *buf)
        return(ret);
 }
 
+/* In-place octal sequence replacement */
+static void
+unescape_octal (gchar *str)
+{
+       gchar *rptr;
+       gchar *wptr;
+
+       if (str == NULL)
+               return;
+
+       rptr = wptr = str;
+       while (*rptr != '\0') {
+               if (*rptr == '\\') {
+                       char c;
+                       rptr++;
+                       c = (*(rptr++) - '0') << 6;
+                       c += (*(rptr++) - '0') << 3;
+                       c += *(rptr++) - '0';
+                       *wptr++ = c;
+               } else if (wptr != rptr) {
+                       *wptr++ = *rptr++;
+               } else {
+                       rptr++; wptr++;
+               }
+       }
+       *wptr = '\0';
+}
+
 gint32
 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
 {
@@ -3635,10 +3650,12 @@ GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
                        continue;
                }
 
-               dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
+               unescape_octal (*(splitted + 1));
+               dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
                g_strfreev (splitted);
                if (total + length + 1 > len) {
                        fclose (fp);
+                       g_free (dir);
                        return len * 2; /* guess */
                }
 
@@ -3684,181 +3701,225 @@ GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
 #endif
 }
 
-static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
+#if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID)
+gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
+                           WapiULargeInteger *total_number_of_bytes,
+                           WapiULargeInteger *total_number_of_free_bytes)
 {
-       struct flock lock_data;
+#ifdef HAVE_STATVFS
+       struct statvfs fsstat;
+#elif defined(HAVE_STATFS)
+       struct statfs fsstat;
+#endif
+       gboolean isreadonly;
+       gchar *utf8_path_name;
        int ret;
+       unsigned long block_size;
 
-       lock_data.l_type = F_WRLCK;
-       lock_data.l_whence = SEEK_SET;
-       lock_data.l_start = offset;
-       lock_data.l_len = length;
-       
-       do {
-               ret = fcntl (fd, F_SETLK, &lock_data);
-       } while(ret == -1 && errno == EINTR);
-       
+       if (path_name == NULL) {
+               utf8_path_name = g_strdup (g_get_current_dir());
+               if (utf8_path_name == NULL) {
+                       SetLastError (ERROR_DIRECTORY);
+                       return(FALSE);
+               }
+       }
+       else {
+               utf8_path_name = mono_unicode_to_external (path_name);
+               if (utf8_path_name == NULL) {
 #ifdef DEBUG
-       g_message ("%s: fcntl returns %d", __func__, ret);
+                       g_message("%s: unicode conversion returned NULL", __func__);
 #endif
 
-       if (ret == -1) {
-               /*
-                * if locks are not available (NFS for example),
-                * ignore the error
-                */
-               if (errno == ENOLCK
-#ifdef EOPNOTSUPP
-                   || errno == EOPNOTSUPP
-#endif
-#ifdef ENOTSUP
-                   || errno == ENOTSUP
-#endif
-                  ) {
-                       return (TRUE);
+                       SetLastError (ERROR_INVALID_NAME);
+                       return(FALSE);
                }
-               
-               SetLastError (ERROR_LOCK_VIOLATION);
-               return(FALSE);
        }
 
-       return(TRUE);
-}
-
-static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
-{
-       struct flock lock_data;
-       int ret;
-
-       lock_data.l_type = F_UNLCK;
-       lock_data.l_whence = SEEK_SET;
-       lock_data.l_start = offset;
-       lock_data.l_len = length;
-       
        do {
-               ret = fcntl (fd, F_SETLK, &lock_data);
-       } while(ret == -1 && errno == EINTR);
-       
-#ifdef DEBUG
-       g_message ("%s: fcntl returns %d", __func__, ret);
+#ifdef HAVE_STATVFS
+               ret = statvfs (utf8_path_name, &fsstat);
+               isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
+               block_size = fsstat.f_frsize;
+#elif defined(HAVE_STATFS)
+               ret = statfs (utf8_path_name, &fsstat);
+               isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
+               block_size = fsstat.f_bsize;
 #endif
-       
+       } while(ret == -1 && errno == EINTR);
+
+       g_free(utf8_path_name);
+
        if (ret == -1) {
-               /*
-                * if locks are not available (NFS for example),
-                * ignore the error
-                */
-               if (errno == ENOLCK
-#ifdef EOPNOTSUPP
-                   || errno == EOPNOTSUPP
-#endif
-#ifdef ENOTSUP
-                   || errno == ENOTSUP
+               _wapi_set_last_error_from_errno ();
+#ifdef DEBUG
+               g_message ("%s: statvfs failed: %s", __func__, strerror (errno));
 #endif
-                  ) {
-                       return (TRUE);
-               }
-               
-               SetLastError (ERROR_LOCK_VIOLATION);
                return(FALSE);
        }
 
+       /* total number of free bytes for non-root */
+       if (free_bytes_avail != NULL) {
+               if (isreadonly) {
+                       free_bytes_avail->QuadPart = 0;
+               }
+               else {
+                       free_bytes_avail->QuadPart = block_size * (guint64)fsstat.f_bavail;
+               }
+       }
+
+       /* total number of bytes available for non-root */
+       if (total_number_of_bytes != NULL) {
+               total_number_of_bytes->QuadPart = block_size * (guint64)fsstat.f_blocks;
+       }
+
+       /* total number of bytes available for root */
+       if (total_number_of_free_bytes != NULL) {
+               if (isreadonly) {
+                       total_number_of_free_bytes->QuadPart = 0;
+               }
+               else {
+                       total_number_of_free_bytes->QuadPart = block_size * (guint64)fsstat.f_bfree;
+               }
+       }
+       
        return(TRUE);
 }
-
-gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
-                  guint32 length_low, guint32 length_high)
+#else
+gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
+                           WapiULargeInteger *total_number_of_bytes,
+                           WapiULargeInteger *total_number_of_free_bytes)
 {
-       struct _WapiHandle_file *file_handle;
-       gboolean ok;
-       off_t offset, length;
-       int fd = GPOINTER_TO_UINT(handle);
-       
-       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
-                                 (gpointer *)&file_handle);
-       if (ok == FALSE) {
-               g_warning ("%s: error looking up file handle %p", __func__,
-                          handle);
-               SetLastError (ERROR_INVALID_HANDLE);
-               return(FALSE);
+       if (free_bytes_avail != NULL) {
+               free_bytes_avail->QuadPart = (guint64) -1;
        }
 
-       if (!(file_handle->fileaccess & GENERIC_READ) &&
-           !(file_handle->fileaccess & GENERIC_WRITE) &&
-           !(file_handle->fileaccess & GENERIC_ALL)) {
-#ifdef DEBUG
-               g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
-#endif
-               SetLastError (ERROR_ACCESS_DENIED);
-               return(FALSE);
+       if (total_number_of_bytes != NULL) {
+               total_number_of_bytes->QuadPart = (guint64) -1;
        }
 
-#ifdef HAVE_LARGE_FILE_SUPPORT
-       offset = ((gint64)offset_high << 32) | offset_low;
-       length = ((gint64)length_high << 32) | length_low;
+       if (total_number_of_free_bytes != NULL) {
+               total_number_of_free_bytes->QuadPart = (guint64) -1;
+       }
 
-#ifdef DEBUG
-       g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
-                  handle, offset, length);
+       return(TRUE);
+}
 #endif
-#else
-       offset = offset_low;
-       length = length_low;
 
-#ifdef DEBUG
-       g_message ("%s: Locking handle %p, offset %ld, length %ld", __func__,
-                  handle, offset, length);
-#endif
-#endif
+typedef struct {
+       guint32 drive_type;
+       const gchar* fstype;
+} _wapi_drive_type;
+
+static _wapi_drive_type _wapi_drive_types[] = {
+       { DRIVE_RAMDISK, "ramfs"      },
+       { DRIVE_RAMDISK, "tmpfs"      },
+       { DRIVE_RAMDISK, "proc"       },
+       { DRIVE_RAMDISK, "sysfs"      },
+       { DRIVE_RAMDISK, "debugfs"    },
+       { DRIVE_RAMDISK, "devpts"     },
+       { DRIVE_RAMDISK, "securityfs" },
+       { DRIVE_CDROM,   "iso9660"    },
+       { DRIVE_FIXED,   "ext2"       },
+       { DRIVE_FIXED,   "ext3"       },
+       { DRIVE_FIXED,   "ext4"       },
+       { DRIVE_FIXED,   "sysv"       },
+       { DRIVE_FIXED,   "reiserfs"   },
+       { DRIVE_FIXED,   "ufs"        },
+       { DRIVE_FIXED,   "vfat"       },
+       { DRIVE_FIXED,   "msdos"      },
+       { DRIVE_FIXED,   "udf"        },
+       { DRIVE_FIXED,   "hfs"        },
+       { DRIVE_FIXED,   "hpfs"       },
+       { DRIVE_FIXED,   "qnx4"       },
+       { DRIVE_FIXED,   "ntfs"       },
+       { DRIVE_FIXED,   "ntfs-3g"    },
+       { DRIVE_REMOTE,  "smbfs"      },
+       { DRIVE_REMOTE,  "fuse"       },
+       { DRIVE_REMOTE,  "nfs"        },
+       { DRIVE_REMOTE,  "nfs4"       },
+       { DRIVE_REMOTE,  "cifs"       },
+       { DRIVE_REMOTE,  "ncpfs"      },
+       { DRIVE_REMOTE,  "coda"       },
+       { DRIVE_REMOTE,  "afs"        },
+       { DRIVE_UNKNOWN, NULL         }
+};
+
+static guint32 _wapi_get_drive_type(const gchar* fstype)
+{
+       _wapi_drive_type *current;
 
-       return(_wapi_lock_file_region (fd, offset, length));
+       current = &_wapi_drive_types[0];
+       while (current->drive_type != DRIVE_UNKNOWN) {
+               if (strcmp (current->fstype, fstype) == 0)
+                       break;
+
+               current++;
+       }
+       
+       return current->drive_type;
 }
 
-gboolean UnlockFile (gpointer handle, guint32 offset_low,
-                    guint32 offset_high, guint32 length_low,
-                    guint32 length_high)
+guint32 GetDriveType(const gunichar2 *root_path_name)
 {
-       struct _WapiHandle_file *file_handle;
-       gboolean ok;
-       off_t offset, length;
-       int fd = GPOINTER_TO_UINT(handle);
-       
-       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
-                                 (gpointer *)&file_handle);
-       if (ok == FALSE) {
-               g_warning ("%s: error looking up file handle %p", __func__,
-                          handle);
-               SetLastError (ERROR_INVALID_HANDLE);
-               return(FALSE);
+       FILE *fp;
+       gchar buffer [512];
+       gchar **splitted;
+       gchar *utf8_root_path_name;
+       guint32 drive_type;
+
+       if (root_path_name == NULL) {
+               utf8_root_path_name = g_strdup (g_get_current_dir());
+               if (utf8_root_path_name == NULL) {
+                       return(DRIVE_NO_ROOT_DIR);
+               }
        }
-       
-       if (!(file_handle->fileaccess & GENERIC_READ) &&
-           !(file_handle->fileaccess & GENERIC_WRITE) &&
-           !(file_handle->fileaccess & GENERIC_ALL)) {
+       else {
+               utf8_root_path_name = mono_unicode_to_external (root_path_name);
+               if (utf8_root_path_name == NULL) {
 #ifdef DEBUG
-               g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
+                       g_message("%s: unicode conversion returned NULL", __func__);
 #endif
-               SetLastError (ERROR_ACCESS_DENIED);
-               return(FALSE);
+                       return(DRIVE_NO_ROOT_DIR);
+               }
+               
+               /* strip trailing slash for compare below */
+               if (g_str_has_suffix(utf8_root_path_name, "/")) {
+                       utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
+               }
        }
 
-#ifdef HAVE_LARGE_FILE_SUPPORT
-       offset = ((gint64)offset_high << 32) | offset_low;
-       length = ((gint64)length_high << 32) | length_low;
+       fp = fopen ("/etc/mtab", "rt");
+       if (fp == NULL) {
+               fp = fopen ("/etc/mnttab", "rt");
+               if (fp == NULL) {
+                       g_free (utf8_root_path_name);
+                       return(DRIVE_UNKNOWN);
+               }
+       }
 
-#ifdef DEBUG
-       g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
-                  __func__, handle, offset, length);
-#endif
-#else
-       offset = offset_low;
-       length = length_low;
+       drive_type = DRIVE_NO_ROOT_DIR;
+       while (fgets (buffer, 512, fp) != NULL) {
+               splitted = g_strsplit (buffer, " ", 0);
+               if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
+                       g_strfreev (splitted);
+                       continue;
+               }
 
-#ifdef DEBUG
-       g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
-                  handle, offset, length);
-#endif
-#endif
+               /* compare given root_path_name with the one from mtab, 
+                 if length of utf8_root_path_name is zero it must be the root dir */
+               if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
+                   (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
+                       drive_type = _wapi_get_drive_type (*(splitted + 2));
+                       g_strfreev (splitted);
+                       break;
+               }
 
-       return(_wapi_unlock_file_region (fd, offset, length));
+               g_strfreev (splitted);
+       }
+
+       fclose (fp);
+       g_free (utf8_root_path_name);
+
+       return (drive_type);
 }
+