Cleanup unneeded code and definitions. Add MIT/X11 license header to file. Code submi...
[mono.git] / mono / io-layer / io.c
index 7132f88ac9991f0f453338f7eeb54416bc87889d..e060a7d3724205f07768a0c4454352471cb2dc73 100644 (file)
 #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>
@@ -40,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,
@@ -329,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));
 }
@@ -800,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) {
@@ -1347,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)
 {
@@ -1575,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) {
@@ -1760,7 +1765,8 @@ gboolean DeleteFile(const gunichar2 *name)
                g_free (filename);
                return FALSE;
        }
-       _wapi_handle_share_release (shareinfo);
+       if (shareinfo)
+               _wapi_handle_share_release (shareinfo);
 #endif
 
        retval = _wapi_unlink (filename);
@@ -1843,10 +1849,12 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
         * the same file as src.
         */
        if (_wapi_stat (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 (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)) {
@@ -1871,7 +1879,8 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
                SetLastError (ERROR_SHARING_VIOLATION);
                return FALSE;
        }
-       _wapi_handle_share_release (shareinfo);
+       if (shareinfo)
+               _wapi_handle_share_release (shareinfo);
 
        result = _wapi_rename (utf8_name, utf8_dest_name);
        errno_copy = errno;
@@ -1895,6 +1904,10 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
        g_free (utf8_dest_name);
 
        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 */
@@ -1978,7 +1991,7 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
 {
        gchar *utf8_src, *utf8_dest;
        int src_fd, dest_fd;
-       struct stat st;
+       struct stat st, dest_st;
        gboolean ret = TRUE;
        
        if(name==NULL) {
@@ -2003,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);
@@ -2044,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
@@ -2144,12 +2170,9 @@ ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFile
                _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 = open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
-                                            stBackup.st_mode);
-                       if (replaced_fd == -1) {
-                               replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT,
-                                            stBackup.st_mode);
-                       }
+                       replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
+                                                 stBackup.st_mode);
+                       
                        if (replaced_fd == -1)
                                goto replace_cleanup;
 
@@ -2172,64 +2195,6 @@ replace_cleanup:
        return ret;
 }
 
-static gpointer stdhandle_create (int fd, const gchar *name)
-{
-       struct _WapiHandle_file file_handle = {0};
-       gpointer handle;
-       int flags;
-       
-#ifdef DEBUG
-       g_message("%s: creating standard handle type %s, fd %d", __func__,
-                 name, fd);
-#endif
-       
-       /* Check if fd is valid */
-       do {
-               flags=fcntl(fd, F_GETFL);
-       } while (flags == -1 && errno == EINTR);
-
-       if(flags==-1) {
-               /* Invalid fd.  Not really much point checking for EBADF
-                * specifically
-                */
-#ifdef DEBUG
-               g_message("%s: fcntl error on fd %d: %s", __func__, fd,
-                         strerror(errno));
-#endif
-
-               _wapi_set_last_error_from_errno ();
-               return(INVALID_HANDLE_VALUE);
-       }
-
-       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);
-
-       /* 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;
-       }
-       
-       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);
-       }
-       
-#ifdef DEBUG
-       g_message("%s: returning handle %p", __func__, handle);
-#endif
-
-       return(handle);
-}
-
 /**
  * GetStdHandle:
  * @stdhandle: specifies the file descriptor
@@ -2286,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);
@@ -3622,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)
 {
@@ -3657,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 */
                }
 
@@ -3719,6 +3714,7 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_
        gboolean isreadonly;
        gchar *utf8_path_name;
        int ret;
+       unsigned long block_size;
 
        if (path_name == NULL) {
                utf8_path_name = g_strdup (g_get_current_dir());
@@ -3743,9 +3739,11 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_
 #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);
 
@@ -3765,13 +3763,13 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_
                        free_bytes_avail->QuadPart = 0;
                }
                else {
-                       free_bytes_avail->QuadPart = fsstat.f_bsize * fsstat.f_bavail;
+                       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 = fsstat.f_bsize * fsstat.f_blocks;
+               total_number_of_bytes->QuadPart = block_size * (guint64)fsstat.f_blocks;
        }
 
        /* total number of bytes available for root */
@@ -3780,7 +3778,7 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_
                        total_number_of_free_bytes->QuadPart = 0;
                }
                else {
-                       total_number_of_free_bytes->QuadPart = fsstat.f_bsize * fsstat.f_bfree;
+                       total_number_of_free_bytes->QuadPart = block_size * (guint64)fsstat.f_bfree;
                }
        }
        
@@ -3925,181 +3923,3 @@ guint32 GetDriveType(const gunichar2 *root_path_name)
        return (drive_type);
 }
 
-static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
-{
-       struct flock lock_data;
-       int ret;
-
-       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);
-       
-#ifdef DEBUG
-       g_message ("%s: fcntl returns %d", __func__, ret);
-#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_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);
-#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_LOCK_VIOLATION);
-               return(FALSE);
-       }
-
-       return(TRUE);
-}
-
-gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
-                  guint32 length_low, guint32 length_high)
-{
-       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 (!(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);
-       }
-
-#ifdef HAVE_LARGE_FILE_SUPPORT
-       offset = ((gint64)offset_high << 32) | offset_low;
-       length = ((gint64)length_high << 32) | length_low;
-
-#ifdef DEBUG
-       g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
-                  handle, offset, length);
-#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
-
-       return(_wapi_lock_file_region (fd, offset, length));
-}
-
-gboolean UnlockFile (gpointer handle, guint32 offset_low,
-                    guint32 offset_high, guint32 length_low,
-                    guint32 length_high)
-{
-       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 (!(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);
-       }
-
-#ifdef HAVE_LARGE_FILE_SUPPORT
-       offset = ((gint64)offset_high << 32) | offset_low;
-       length = ((gint64)length_high << 32) | length_low;
-
-#ifdef DEBUG
-       g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
-                  __func__, handle, offset, length);
-#endif
-#else
-       offset = offset_low;
-       length = length_low;
-
-#ifdef DEBUG
-       g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
-                  handle, offset, length);
-#endif
-#endif
-
-       return(_wapi_unlock_file_region (fd, offset, length));
-}