X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fio.c;h=a3e5ad15b56aaa87d2e9c89b1266c7bd1753a363;hb=1e726ce7a38a92860acab28f4427813d2ba14c13;hp=72be9df6c943ddb39fae9ad2cb127275106fc55c;hpb=b2262f41726a89c8209facb3ea9e4be9582422b5;p=mono.git diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c old mode 100644 new mode 100755 index 72be9df6c94..a3e5ad15b56 --- a/mono/io-layer/io.c +++ b/mono/io-layer/io.c @@ -6,6 +6,7 @@ * * (C) 2002 Ximian, Inc. * Copyright (c) 2002-2006 Novell, Inc. + * Copyright 2011 Xamarin Inc (http://www.xamarin.com). */ #include @@ -15,11 +16,26 @@ #include #include #include +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#if defined(HAVE_SYS_STATFS_H) +#include +#endif +#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) +#include +#include +#endif #include #include #include #include #include +#ifdef __linux__ +#include +#include +#include +#endif #include #include @@ -30,10 +46,12 @@ #include #include -#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); +#if 0 +#define DEBUG(...) g_message(__VA_ARGS__) +#define DEBUG_ENABLED 1 +#else +#define DEBUG(...) +#endif static void file_close (gpointer handle, gpointer data); static WapiFileType file_getfiletype(void); @@ -55,6 +73,7 @@ static gboolean file_setfiletime(gpointer handle, const WapiFileTime *create_time, const WapiFileTime *last_access, const WapiFileTime *last_write); +static guint32 GetDriveTypeFromPath (const gchar *utf8_root_path_name); /* File handle is only signalled for overlapped IO */ struct _WapiHandleOps _wapi_file_ops = { @@ -310,20 +329,20 @@ static void _wapi_set_last_path_error_from_errno (const gchar *dir, static void file_close (gpointer handle, gpointer data) { struct _WapiHandle_file *file_handle = (struct _WapiHandle_file *)data; + int fd = file_handle->fd; -#ifdef DEBUG - g_message("%s: closing file handle %p [%s]", __func__, handle, + DEBUG("%s: closing file handle %p [%s]", __func__, handle, file_handle->filename); -#endif if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE) _wapi_unlink (file_handle->filename); 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)); + close (fd); } static WapiFileType file_getfiletype(void) @@ -337,8 +356,7 @@ static gboolean file_read(gpointer handle, gpointer buffer, { struct _WapiHandle_file *file_handle; gboolean ok; - int fd = GPOINTER_TO_UINT(handle); - int ret; + int fd, ret; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -349,16 +367,15 @@ static gboolean file_read(gpointer handle, gpointer buffer, return(FALSE); } + fd = file_handle->fd; if(bytesread!=NULL) { *bytesread=0; } if(!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_READ access: %u", + DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u", __func__, handle, file_handle->fileaccess); -#endif SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -372,10 +389,8 @@ static gboolean file_read(gpointer handle, gpointer buffer, if(ret==-1) { gint err = errno; -#ifdef DEBUG - g_message("%s: read of handle %p error: %s", __func__, + DEBUG("%s: read of handle %p error: %s", __func__, handle, strerror(err)); -#endif SetLastError (_wapi_get_win32_file_error (err)); return(FALSE); } @@ -393,9 +408,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, { struct _WapiHandle_file *file_handle; gboolean ok; - int ret; - off_t current_pos; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; + off_t current_pos = 0; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -405,6 +419,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + + fd = file_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -412,9 +428,7 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, if(!(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -427,10 +441,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, */ current_pos = lseek (fd, (off_t)0, SEEK_CUR); if (current_pos == -1) { -#ifdef DEBUG - g_message ("%s: handle %p lseek failed: %s", __func__, + DEBUG ("%s: handle %p lseek failed: %s", __func__, handle, strerror (errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); } @@ -457,10 +469,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, } else { _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message("%s: write of handle %p error: %s", + DEBUG("%s: write of handle %p error: %s", __func__, handle, strerror(errno)); -#endif return(FALSE); } @@ -475,8 +485,7 @@ static gboolean file_flush(gpointer handle) { struct _WapiHandle_file *file_handle; gboolean ok; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -487,11 +496,11 @@ static gboolean file_flush(gpointer handle) return(FALSE); } + fd = file_handle->fd; + if(!(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -499,10 +508,8 @@ static gboolean file_flush(gpointer handle) ret=fsync(fd); if (ret==-1) { -#ifdef DEBUG - g_message("%s: fsync of handle %p error: %s", __func__, handle, + DEBUG("%s: fsync of handle %p error: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -517,9 +524,8 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, struct _WapiHandle_file *file_handle; gboolean ok; off_t offset, newpos; - int whence; + int whence, fd; guint32 ret; - int fd = GPOINTER_TO_UINT(handle); ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -530,12 +536,12 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, return(INVALID_SET_FILE_POINTER); } + fd = file_handle->fd; + 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 + DEBUG ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(INVALID_SET_FILE_POINTER); @@ -552,9 +558,7 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, whence=SEEK_END; break; default: -#ifdef DEBUG - g_message("%s: invalid seek type %d", __func__, method); -#endif + DEBUG("%s: invalid seek type %d", __func__, method); SetLastError (ERROR_INVALID_PARAMETER); return(INVALID_SET_FILE_POINTER); @@ -563,48 +567,38 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, #ifdef HAVE_LARGE_FILE_SUPPORT if(highmovedistance==NULL) { offset=movedistance; -#ifdef DEBUG - g_message("%s: setting offset to %lld (low %d)", __func__, + DEBUG("%s: setting offset to %lld (low %d)", __func__, offset, movedistance); -#endif } else { offset=((gint64) *highmovedistance << 32) | (guint32)movedistance; -#ifdef DEBUG - g_message("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); -#endif + DEBUG("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance); } #else offset=movedistance; #endif -#ifdef DEBUG #ifdef HAVE_LARGE_FILE_SUPPORT - g_message("%s: moving handle %p by %lld bytes from %d", __func__, + DEBUG ("%s: moving handle %p by %lld bytes from %d", __func__, handle, offset, whence); #else - g_message("%s: moving handle %p fd %d by %ld bytes from %d", __func__, + DEBUG ("%s: moving handle %p fd %d by %ld bytes from %d", __func__, handle, offset, whence); -#endif #endif newpos=lseek(fd, offset, whence); if(newpos==-1) { -#ifdef DEBUG - g_message("%s: lseek on handle %p returned error %s", + DEBUG("%s: lseek on handle %p returned error %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(INVALID_SET_FILE_POINTER); } -#ifdef DEBUG #ifdef HAVE_LARGE_FILE_SUPPORT - g_message("%s: lseek returns %lld", __func__, newpos); + DEBUG ("%s: lseek returns %lld", __func__, newpos); #else - g_message ("%s: lseek returns %ld", __func__, newpos); -#endif + DEBUG ("%s: lseek returns %ld", __func__, newpos); #endif #ifdef HAVE_LARGE_FILE_SUPPORT @@ -620,10 +614,8 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, } #endif -#ifdef DEBUG - g_message ("%s: move of handle %p returning %d/%d", __func__, + DEBUG ("%s: move of handle %p returning %d/%d", __func__, handle, ret, highmovedistance==NULL?0:*highmovedistance); -#endif return(ret); } @@ -634,8 +626,7 @@ static gboolean file_setendoffile(gpointer handle) gboolean ok; struct stat statbuf; off_t size, pos; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -645,12 +636,11 @@ static gboolean file_setendoffile(gpointer handle) SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = file_handle->fd; if(!(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -664,10 +654,8 @@ static gboolean file_setendoffile(gpointer handle) ret=fstat(fd, &statbuf); if(ret==-1) { -#ifdef DEBUG - g_message ("%s: handle %p fstat failed: %s", __func__, + DEBUG ("%s: handle %p fstat failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -676,10 +664,8 @@ static gboolean file_setendoffile(gpointer handle) pos=lseek(fd, (off_t)0, SEEK_CUR); if(pos==-1) { -#ifdef DEBUG - g_message("%s: handle %p lseek failed: %s", __func__, + DEBUG("%s: handle %p lseek failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -704,9 +690,7 @@ static gboolean file_setendoffile(gpointer handle) !_wapi_thread_cur_apc_pending()); if(ret==-1) { -#ifdef DEBUG - g_message("%s: handle %p extend write failed: %s", __func__, handle, strerror(errno)); -#endif + DEBUG("%s: handle %p extend write failed: %s", __func__, handle, strerror(errno)); _wapi_set_last_error_from_errno (); return(FALSE); @@ -715,10 +699,8 @@ static gboolean file_setendoffile(gpointer handle) /* And put the file position back after the write */ ret = lseek (fd, pos, SEEK_SET); if (ret == -1) { -#ifdef DEBUG - g_message ("%s: handle %p second lseek failed: %s", + DEBUG ("%s: handle %p second lseek failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -726,6 +708,8 @@ static gboolean file_setendoffile(gpointer handle) } #endif +/* Native Client has no ftruncate function, even in standalone sel_ldr. */ +#ifndef __native_client__ /* always truncate, because the extend write() adds an extra * byte to the end of the file */ @@ -734,14 +718,13 @@ static gboolean file_setendoffile(gpointer handle) } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); if(ret==-1) { -#ifdef DEBUG - g_message("%s: handle %p ftruncate failed: %s", __func__, + DEBUG("%s: handle %p ftruncate failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); } +#endif return(TRUE); } @@ -753,7 +736,7 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) struct stat statbuf; guint32 size; int ret; - int fd = GPOINTER_TO_UINT(handle); + int fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -763,13 +746,12 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) SetLastError (ERROR_INVALID_HANDLE); return(INVALID_FILE_SIZE); } + fd = file_handle->fd; 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 + DEBUG("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(INVALID_FILE_SIZE); @@ -783,15 +765,37 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) ret = fstat(fd, &statbuf); if (ret == -1) { -#ifdef DEBUG - g_message ("%s: handle %p fstat failed: %s", __func__, + DEBUG ("%s: handle %p fstat failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); 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) { + DEBUG ("%s: handle %p ioctl BLKGETSIZE64 failed: %s", + __func__, handle, strerror(errno)); + + _wapi_set_last_error_from_errno (); + return(INVALID_FILE_SIZE); + } + + size = bigsize & 0xFFFFFFFF; + if (highsize != NULL) { + *highsize = bigsize>>32; + } + + DEBUG ("%s: Returning block device size %d/%d", + __func__, size, *highsize); + + return(size); + } +#endif + #ifdef HAVE_LARGE_FILE_SUPPORT size = statbuf.st_size & 0xFFFFFFFF; if (highsize != NULL) { @@ -805,9 +809,7 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) size = statbuf.st_size; #endif -#ifdef DEBUG - g_message ("%s: Returning size %d/%d", __func__, size, *highsize); -#endif + DEBUG ("%s: Returning size %d/%d", __func__, size, *highsize); return(size); } @@ -820,8 +822,7 @@ static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, gboolean ok; struct stat statbuf; guint64 create_ticks, access_ticks, write_ticks; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -831,13 +832,12 @@ static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = file_handle->fd; if(!(file_handle->fileaccess & GENERIC_READ) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_READ access: %u", + DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u", __func__, handle, file_handle->fileaccess); -#endif SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -845,20 +845,16 @@ static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, ret=fstat(fd, &statbuf); if(ret==-1) { -#ifdef DEBUG - g_message("%s: handle %p fstat failed: %s", __func__, handle, + DEBUG("%s: handle %p fstat failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); } -#ifdef DEBUG - g_message("%s: atime: %ld ctime: %ld mtime: %ld", __func__, + DEBUG("%s: atime: %ld ctime: %ld mtime: %ld", __func__, statbuf.st_atime, statbuf.st_ctime, statbuf.st_mtime); -#endif /* Try and guess a meaningful create time by using the older * of atime or ctime @@ -877,10 +873,8 @@ static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time, access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL; write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL; -#ifdef DEBUG - g_message("%s: aticks: %llu cticks: %llu wticks: %llu", __func__, + DEBUG("%s: aticks: %llu cticks: %llu wticks: %llu", __func__, access_ticks, create_ticks, write_ticks); -#endif if(create_time!=NULL) { create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF; @@ -910,8 +904,7 @@ static gboolean file_setfiletime(gpointer handle, struct utimbuf utbuf; struct stat statbuf; guint64 access_ticks, write_ticks; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -921,21 +914,18 @@ static gboolean file_setfiletime(gpointer handle, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = file_handle->fd; if(!(file_handle->fileaccess & GENERIC_WRITE) && !(file_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); } if(file_handle->filename == NULL) { -#ifdef DEBUG - g_message("%s: handle %p unknown filename", __func__, handle); -#endif + DEBUG("%s: handle %p unknown filename", __func__, handle); SetLastError (ERROR_INVALID_HANDLE); return(FALSE); @@ -946,10 +936,8 @@ static gboolean file_setfiletime(gpointer handle, */ ret=fstat (fd, &statbuf); if(ret==-1) { -#ifdef DEBUG - g_message("%s: handle %p fstat failed: %s", __func__, handle, + DEBUG("%s: handle %p fstat failed: %s", __func__, handle, strerror(errno)); -#endif SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); @@ -962,10 +950,8 @@ static gboolean file_setfiletime(gpointer handle, * but this will do for now. */ if (access_ticks < 116444736000000000ULL) { -#ifdef DEBUG - g_message ("%s: attempt to set access time too early", + DEBUG ("%s: attempt to set access time too early", __func__); -#endif SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); } @@ -982,10 +968,8 @@ static gboolean file_setfiletime(gpointer handle, * but this will do for now. */ if (write_ticks < 116444736000000000ULL) { -#ifdef DEBUG - g_message ("%s: attempt to set write time too early", + DEBUG ("%s: attempt to set write time too early", __func__); -#endif SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); } @@ -995,18 +979,14 @@ static gboolean file_setfiletime(gpointer handle, utbuf.modtime=statbuf.st_mtime; } -#ifdef DEBUG - g_message ("%s: setting handle %p access %ld write %ld", __func__, + DEBUG ("%s: setting handle %p access %ld write %ld", __func__, handle, utbuf.actime, utbuf.modtime); -#endif ret = _wapi_utime (file_handle->filename, &utbuf); if (ret == -1) { -#ifdef DEBUG - g_message ("%s: handle %p [%s] utime failed: %s", __func__, + DEBUG ("%s: handle %p [%s] utime failed: %s", __func__, handle, file_handle->filename, strerror(errno)); -#endif SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); } @@ -1017,14 +997,13 @@ static gboolean file_setfiletime(gpointer handle, static void console_close (gpointer handle, gpointer data) { struct _WapiHandle_file *console_handle = (struct _WapiHandle_file *)data; + int fd = console_handle->fd; -#ifdef DEBUG - g_message("%s: closing console handle %p", __func__, handle); -#endif + DEBUG("%s: closing console handle %p", __func__, handle); g_free (console_handle->filename); - close (GPOINTER_TO_UINT(handle)); + close (fd); } static WapiFileType console_getfiletype(void) @@ -1038,9 +1017,8 @@ static gboolean console_read(gpointer handle, gpointer buffer, { struct _WapiHandle_file *console_handle; gboolean ok; - int ret; - int fd = GPOINTER_TO_UINT(handle); - + int ret, fd; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, (gpointer *)&console_handle); if(ok==FALSE) { @@ -1049,6 +1027,7 @@ static gboolean console_read(gpointer handle, gpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = console_handle->fd; if(bytesread!=NULL) { *bytesread=0; @@ -1056,10 +1035,8 @@ static gboolean console_read(gpointer handle, gpointer buffer, if(!(console_handle->fileaccess & GENERIC_READ) && !(console_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message ("%s: handle %p doesn't have GENERIC_READ access: %u", + DEBUG ("%s: handle %p doesn't have GENERIC_READ access: %u", __func__, handle, console_handle->fileaccess); -#endif SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -1070,10 +1047,8 @@ static gboolean console_read(gpointer handle, gpointer buffer, } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); if(ret==-1) { -#ifdef DEBUG - g_message("%s: read of handle %p error: %s", __func__, handle, + DEBUG("%s: read of handle %p error: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -1092,8 +1067,7 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, { struct _WapiHandle_file *console_handle; gboolean ok; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, (gpointer *)&console_handle); @@ -1103,6 +1077,7 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = console_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -1110,9 +1085,7 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, if(!(console_handle->fileaccess & GENERIC_WRITE) && !(console_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); @@ -1129,10 +1102,8 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, } else { _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message ("%s: write of handle %p error: %s", + DEBUG ("%s: write of handle %p error: %s", __func__, handle, strerror(errno)); -#endif return(FALSE); } @@ -1144,15 +1115,16 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, return(TRUE); } -static void pipe_close (gpointer handle, gpointer data G_GNUC_UNUSED) +static void pipe_close (gpointer handle, gpointer data) { -#ifdef DEBUG - g_message("%s: closing pipe handle %p", __func__, handle); -#endif + struct _WapiHandle_file *pipe_handle = (struct _WapiHandle_file*)data; + int fd = pipe_handle->fd; + + DEBUG("%s: closing pipe handle %p", __func__, handle); /* No filename with pipe handles */ - close(GPOINTER_TO_UINT(handle)); + close (fd); } static WapiFileType pipe_getfiletype(void) @@ -1166,9 +1138,8 @@ static gboolean pipe_read (gpointer handle, gpointer buffer, { struct _WapiHandle_file *pipe_handle; gboolean ok; - int ret; - int fd = GPOINTER_TO_UINT(handle); - + int ret, fd; + ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, (gpointer *)&pipe_handle); if(ok==FALSE) { @@ -1177,6 +1148,7 @@ static gboolean pipe_read (gpointer handle, gpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = pipe_handle->fd; if(bytesread!=NULL) { *bytesread=0; @@ -1184,19 +1156,15 @@ static gboolean pipe_read (gpointer handle, gpointer buffer, if(!(pipe_handle->fileaccess & GENERIC_READ) && !(pipe_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_READ access: %u", + DEBUG("%s: handle %p doesn't have GENERIC_READ access: %u", __func__, handle, pipe_handle->fileaccess); -#endif SetLastError (ERROR_ACCESS_DENIED); return(FALSE); } -#ifdef DEBUG - g_message ("%s: reading up to %d bytes from pipe %p", __func__, + DEBUG ("%s: reading up to %d bytes from pipe %p", __func__, numbytes, handle); -#endif do { ret=read(fd, buffer, numbytes); @@ -1208,18 +1176,14 @@ static gboolean pipe_read (gpointer handle, gpointer buffer, } else { _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message("%s: read of handle %p error: %s", __func__, + DEBUG("%s: read of handle %p error: %s", __func__, handle, strerror(errno)); -#endif return(FALSE); } } -#ifdef DEBUG - g_message ("%s: read %d bytes from pipe", __func__, ret); -#endif + DEBUG ("%s: read %d bytes from pipe", __func__, ret); if(bytesread!=NULL) { *bytesread=ret; @@ -1234,8 +1198,7 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, { struct _WapiHandle_file *pipe_handle; gboolean ok; - int ret; - int fd = GPOINTER_TO_UINT(handle); + int ret, fd; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, (gpointer *)&pipe_handle); @@ -1245,6 +1208,7 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = pipe_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -1252,18 +1216,14 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, if(!(pipe_handle->fileaccess & GENERIC_WRITE) && !(pipe_handle->fileaccess & GENERIC_ALL)) { -#ifdef DEBUG - g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess); -#endif + DEBUG("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess); SetLastError (ERROR_ACCESS_DENIED); return(FALSE); } -#ifdef DEBUG - g_message ("%s: writing up to %d bytes to pipe %p", __func__, numbytes, + DEBUG ("%s: writing up to %d bytes to pipe %p", __func__, numbytes, handle); -#endif do { ret = write (fd, buffer, numbytes); @@ -1276,10 +1236,8 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, } else { _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message("%s: write of handle %p error: %s", __func__, + DEBUG("%s: write of handle %p error: %s", __func__, handle, strerror(errno)); -#endif return(FALSE); } @@ -1306,10 +1264,8 @@ static int convert_flags(guint32 fileaccess, guint32 createmode) flags=O_RDWR; break; default: -#ifdef DEBUG - g_message("%s: Unknown access type 0x%x", __func__, + DEBUG("%s: Unknown access type 0x%x", __func__, fileaccess); -#endif break; } @@ -1329,41 +1285,14 @@ static int convert_flags(guint32 fileaccess, guint32 createmode) flags|=O_TRUNC; break; default: -#ifdef DEBUG - g_message("%s: Unknown create mode 0x%x", __func__, + DEBUG("%s: Unknown create mode 0x%x", __func__, createmode); -#endif break; } 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) { @@ -1396,9 +1325,7 @@ static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, */ if (file_existing_share == 0) { /* Quick and easy, no possibility to share */ -#ifdef DEBUG - g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess); -#endif + DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess); _wapi_handle_share_release (*share_info); @@ -1410,9 +1337,7 @@ static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, ((file_existing_share == FILE_SHARE_WRITE) && (fileaccess != GENERIC_WRITE))) { /* New access mode doesn't match up */ -#ifdef DEBUG - g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share); -#endif + DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share); _wapi_handle_share_release (*share_info); @@ -1424,18 +1349,14 @@ static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, ((file_existing_access & GENERIC_WRITE) && !(sharemode & FILE_SHARE_WRITE))) { /* New share mode doesn't match up */ -#ifdef DEBUG - g_message ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access); -#endif + DEBUG ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access); _wapi_handle_share_release (*share_info); return(FALSE); } } else { -#ifdef DEBUG - g_message ("%s: New file!", __func__); -#endif + DEBUG ("%s: New file!", __func__); } return(TRUE); @@ -1514,7 +1435,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,15 +1443,16 @@ 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; } if (name == NULL) { -#ifdef DEBUG - g_message ("%s: name is NULL", __func__); -#endif + DEBUG ("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(INVALID_HANDLE_VALUE); @@ -1538,18 +1460,14 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, filename = mono_unicode_to_external (name); if (filename == NULL) { -#ifdef DEBUG - g_message("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(INVALID_HANDLE_VALUE); } -#ifdef DEBUG - g_message ("%s: Opening %s with share 0x%x and access 0x%x", __func__, + DEBUG ("%s: Opening %s with share 0x%x and access 0x%x", __func__, filename, sharemode, fileaccess); -#endif fd = _wapi_open (filename, flags, perms); @@ -1564,14 +1482,12 @@ 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) { -#ifdef DEBUG - g_message("%s: Error opening file %s: %s", __func__, filename, + DEBUG("%s: Error opening file %s: %s", __func__, filename, strerror(errno)); -#endif _wapi_set_last_path_error_from_errno (NULL, filename); g_free (filename); @@ -1579,9 +1495,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, } if (fd >= _wapi_fd_reserve) { -#ifdef DEBUG - g_message ("%s: File descriptor is too big", __func__); -#endif + DEBUG ("%s: File descriptor is too big", __func__); SetLastError (ERROR_TOO_MANY_OPEN_FILES); @@ -1593,16 +1507,21 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, ret = fstat (fd, &statbuf); if (ret == -1) { -#ifdef DEBUG - g_message ("%s: fstat error of file %s: %s", __func__, + DEBUG ("%s: fstat error of file %s: %s", __func__, filename, strerror (errno)); -#endif _wapi_set_last_error_from_errno (); g_free (filename); close (fd); return(INVALID_HANDLE_VALUE); } +#ifdef __native_client__ + /* Workaround: Native Client currently returns the same fake inode + * for all files, so do a simple hash on the filename so we don't + * use the same share info for each file. + */ + statbuf.st_ino = g_str_hash(filename); +#endif if (share_check (&statbuf, sharemode, fileaccess, &file_handle.share_info, fd) == FALSE) { @@ -1614,9 +1533,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, } if (file_handle.share_info == NULL) { /* No space, so no more files can be opened */ -#ifdef DEBUG - g_message ("%s: No space in the share table", __func__); -#endif + DEBUG ("%s: No space in the share table", __func__); SetLastError (ERROR_TOO_MANY_OPEN_FILES); close (fd); @@ -1632,6 +1549,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, //security, sizeof(WapiSecurityAttributes)); } + file_handle.fd = fd; file_handle.fileaccess=fileaccess; file_handle.sharemode=sharemode; file_handle.attrs=attrs; @@ -1664,9 +1582,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, return(INVALID_HANDLE_VALUE); } -#ifdef DEBUG - g_message("%s: returning handle %p", __func__, handle); -#endif + DEBUG("%s: returning handle %p", __func__, handle); return(handle); } @@ -1685,11 +1601,14 @@ 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 - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -1697,14 +1616,44 @@ gboolean DeleteFile(const gunichar2 *name) filename=mono_unicode_to_external(name); if(filename==NULL) { -#ifdef DEBUG - g_message("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); } + + attrs = GetFileAttributes (name); + if (attrs == INVALID_FILE_ATTRIBUTES) { + DEBUG ("%s: file attributes error", __func__); + /* Error set by GetFileAttributes() */ + 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,14 +1683,13 @@ 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 - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -1749,18 +1697,14 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return FALSE; } if(dest_name==NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); g_free (utf8_name); SetLastError (ERROR_INVALID_NAME); @@ -1769,9 +1713,7 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) utf8_dest_name = mono_unicode_to_external (dest_name); if (utf8_dest_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); g_free (utf8_name); SetLastError (ERROR_INVALID_NAME); @@ -1783,22 +1725,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 +1780,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 +1801,54 @@ 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 (); + DEBUG ("%s: write failed.", __func__); + 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,15 +1866,13 @@ 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; + struct utimbuf dest_time; + gboolean ret = TRUE; + int ret_utime; if(name==NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -1858,19 +1880,15 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, utf8_src = mono_unicode_to_external (name); if (utf8_src == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion of source returned NULL", + DEBUG ("%s: unicode conversion of source returned NULL", __func__); -#endif SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); } if(dest_name==NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: dest is NULL", __func__); g_free (utf8_src); SetLastError (ERROR_INVALID_NAME); @@ -1879,10 +1897,8 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, utf8_dest = mono_unicode_to_external (dest_name); if (utf8_dest == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion of dest returned NULL", + DEBUG ("%s: unicode conversion of dest returned NULL", __func__); -#endif SetLastError (ERROR_INVALID_PARAMETER); @@ -1910,18 +1926,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 +1964,107 @@ 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; + if (!write_file (src_fd, dest_fd, &st, TRUE)) + ret = FALSE; - _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message ("%s: write failed.", __func__); -#endif + close (src_fd); + close (dest_fd); + + dest_time.modtime = st.st_mtime; + dest_time.actime = st.st_atime; + ret_utime = utime (utf8_dest, &dest_time); + if (ret_utime == -1) + DEBUG ("%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno)); + + g_free (utf8_src); + g_free (utf8_dest); - g_free (utf8_src); - g_free (utf8_dest); - close (src_fd); - close (dest_fd); + return ret; +} - return (FALSE); - } +static gchar* +convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name) +{ + gchar *utf8_ret; - remain -= n; - } + if (arg == NULL) { + DEBUG ("%s: %s is NULL", __func__, arg_name); + SetLastError (ERROR_INVALID_NAME); + return NULL; } - g_free (utf8_src); - g_free (utf8_dest); - close (src_fd); - close (dest_fd); + utf8_ret = mono_unicode_to_external (arg); + if (utf8_ret == NULL) { + DEBUG ("%s: unicode conversion of %s returned NULL", + __func__, arg_name); + SetLastError (ERROR_INVALID_PARAMETER); + return NULL; + } - return(TRUE); + return utf8_ret; } -static gpointer stdhandle_create (int fd, const gchar *name) +gboolean +ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName, + const gunichar2 *backupFileName, guint32 replaceFlags, + gpointer exclude, gpointer reserved) { - 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); + 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(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 + 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; + } - _wapi_set_last_error_from_errno (); - 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; } - 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); + 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; - /* 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; + write_file (backup_fd, replaced_fd, &stBackup, FALSE); + } - 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); + goto replace_cleanup; } - -#ifdef DEBUG - g_message("%s: returning handle %p", __func__, handle); -#endif - return(handle); + 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; } /** @@ -2089,9 +2104,7 @@ gpointer GetStdHandle(WapiStdHandle stdhandle) break; default: -#ifdef DEBUG - g_message("%s: unknown standard handle type", __func__); -#endif + DEBUG("%s: unknown standard handle type", __func__); SetLastError (ERROR_INVALID_PARAMETER); return(INVALID_HANDLE_VALUE); @@ -2108,7 +2121,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); @@ -2480,9 +2493,7 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, const guint16 *ip; if(system_time==NULL) { -#ifdef DEBUG - g_message("%s: system_time NULL", __func__); -#endif + DEBUG("%s: system_time NULL", __func__); SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); @@ -2496,9 +2507,7 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, * year and day calculation to work later */ if(file_ticks<0) { -#ifdef DEBUG - g_message("%s: file_time too big", __func__); -#endif + DEBUG("%s: file_time too big", __func__); SetLastError (ERROR_INVALID_PARAMETER); return(FALSE); @@ -2506,41 +2515,29 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, totaldays=(file_ticks / TICKS_PER_DAY); rem = file_ticks % TICKS_PER_DAY; -#ifdef DEBUG - g_message("%s: totaldays: %lld rem: %lld", __func__, totaldays, rem); -#endif + DEBUG("%s: totaldays: %lld rem: %lld", __func__, totaldays, rem); system_time->wHour=rem/TICKS_PER_HOUR; rem %= TICKS_PER_HOUR; -#ifdef DEBUG - g_message("%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem); -#endif + DEBUG("%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem); system_time->wMinute = rem / TICKS_PER_MINUTE; rem %= TICKS_PER_MINUTE; -#ifdef DEBUG - g_message("%s: Minute: %d rem: %lld", __func__, system_time->wMinute, + DEBUG("%s: Minute: %d rem: %lld", __func__, system_time->wMinute, rem); -#endif system_time->wSecond = rem / TICKS_PER_SECOND; rem %= TICKS_PER_SECOND; -#ifdef DEBUG - g_message("%s: Second: %d rem: %lld", __func__, system_time->wSecond, + DEBUG("%s: Second: %d rem: %lld", __func__, system_time->wSecond, rem); -#endif system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND; -#ifdef DEBUG - g_message("%s: Milliseconds: %d", __func__, + DEBUG("%s: Milliseconds: %d", __func__, system_time->wMilliseconds); -#endif /* January 1, 1601 was a Monday, according to Emacs calendar */ system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1; -#ifdef DEBUG - g_message("%s: Day of week: %d", __func__, system_time->wDayOfWeek); -#endif + DEBUG("%s: Day of week: %d", __func__, system_time->wDayOfWeek); /* This algorithm to find year and month given days from epoch * from glibc @@ -2553,31 +2550,23 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) { /* Guess a corrected year, assuming 365 days per year */ gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0); -#ifdef DEBUG - g_message("%s: totaldays: %lld yg: %lld y: %lld", __func__, + DEBUG("%s: totaldays: %lld yg: %lld y: %lld", __func__, totaldays, yg, y); g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__, LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1)); -#endif /* Adjust days and y to match the guessed year. */ totaldays -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) - LEAPS_THRU_END_OF (y - 1)); -#ifdef DEBUG - g_message("%s: totaldays: %lld", __func__, totaldays); -#endif + DEBUG("%s: totaldays: %lld", __func__, totaldays); y = yg; -#ifdef DEBUG - g_message("%s: y: %lld", __func__, y); -#endif + DEBUG("%s: y: %lld", __func__, y); } system_time->wYear = y; -#ifdef DEBUG - g_message("%s: Year: %d", __func__, system_time->wYear); -#endif + DEBUG("%s: Year: %d", __func__, system_time->wYear); ip = mon_yday[isleap(y)]; @@ -2585,198 +2574,17 @@ gboolean FileTimeToSystemTime(const WapiFileTime *file_time, continue; } totaldays-=ip[y]; -#ifdef DEBUG - g_message("%s: totaldays: %lld", __func__, totaldays); -#endif + DEBUG("%s: totaldays: %lld", __func__, totaldays); system_time->wMonth = y + 1; -#ifdef DEBUG - g_message("%s: Month: %d", __func__, system_time->wMonth); -#endif + DEBUG("%s: Month: %d", __func__, system_time->wMonth); system_time->wDay = totaldays + 1; -#ifdef DEBUG - g_message("%s: Day: %d", __func__, system_time->wDay); -#endif + DEBUG("%s: Day: %d", __func__, system_time->wDay); 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}; @@ -2785,9 +2593,7 @@ gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) int result; if (pattern == NULL) { -#ifdef DEBUG - g_message ("%s: pattern is NULL", __func__); -#endif + DEBUG ("%s: pattern is NULL", __func__); SetLastError (ERROR_PATH_NOT_FOUND); return(INVALID_HANDLE_VALUE); @@ -2795,17 +2601,13 @@ gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) utf8_pattern = mono_unicode_to_external (pattern); if (utf8_pattern == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(INVALID_HANDLE_VALUE); } -#ifdef DEBUG - g_message ("%s: looking for [%s]", __func__, utf8_pattern); -#endif + DEBUG ("%s: looking for [%s]", __func__, utf8_pattern); /* Figure out which bit of the pattern is the directory */ dir_part = _wapi_dirname (utf8_pattern); @@ -2846,7 +2648,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 @@ -2860,14 +2663,12 @@ gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) } if (result < 0) { -#ifdef DEBUG +#ifdef DEBUG_ENABLED gint errnum = errno; #endif _wapi_set_last_path_error_from_errno (dir_part, NULL); -#ifdef DEBUG - g_message ("%s: scandir error: %s", __func__, + DEBUG ("%s: scandir error: %s", __func__, g_strerror (errnum)); -#endif g_free (utf8_pattern); g_free (entry_part); g_free (dir_part); @@ -2877,9 +2678,7 @@ gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data) g_free (utf8_pattern); g_free (entry_part); -#ifdef DEBUG - g_message ("%s: Got %d matches", __func__, result); -#endif + DEBUG ("%s: Got %d matches", __func__, result); find_handle.dir_part = dir_part; find_handle.num = result; @@ -2910,7 +2709,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,29 +2742,44 @@ 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) { -#ifdef DEBUG - g_message ("%s: stat failed: %s", __func__, filename); -#endif + + result = _wapi_stat (filename, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink */ + result = _wapi_lstat (filename, &buf); + } + + if (result != 0) { + DEBUG ("%s: stat failed: %s", __func__, filename); g_free (filename); goto retry; } +#ifndef __native_client__ + result = _wapi_lstat (filename, &linkbuf); + if (result != 0) { + DEBUG ("%s: lstat failed: %s", __func__, filename); + + g_free (filename); + goto retry; + } +#endif + 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; } g_free (filename); -#ifdef DEBUG - g_message ("%s: Found [%s]", __func__, utf8_filename); -#endif + DEBUG ("%s: Found [%s]", __func__, utf8_filename); /* fill data block */ @@ -2973,7 +2788,11 @@ retry: else create_time = buf.st_ctime; - find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &buf); +#ifdef __native_client__ + find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL); +#else + find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf); +#endif _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime); _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime); @@ -3087,9 +2906,7 @@ gboolean CreateDirectory (const gunichar2 *name, int result; if (name == NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -3097,9 +2914,7 @@ gboolean CreateDirectory (const gunichar2 *name, utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return FALSE; @@ -3132,9 +2947,7 @@ gboolean RemoveDirectory (const gunichar2 *name) int result; if (name == NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -3142,9 +2955,7 @@ gboolean RemoveDirectory (const gunichar2 *name) utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return FALSE; @@ -3178,9 +2989,7 @@ guint32 GetFileAttributes (const gunichar2 *name) guint32 ret; if (name == NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -3188,9 +2997,7 @@ guint32 GetFileAttributes (const gunichar2 *name) utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_PARAMETER); return (INVALID_FILE_ATTRIBUTES); @@ -3208,14 +3015,20 @@ guint32 GetFileAttributes (const gunichar2 *name) return (INVALID_FILE_ATTRIBUTES); } +#ifndef __native_client__ result = _wapi_lstat (utf8_name, &linkbuf); if (result != 0) { _wapi_set_last_path_error_from_errno (NULL, utf8_name); g_free (utf8_name); return (INVALID_FILE_ATTRIBUTES); } +#endif +#ifdef __native_client__ + ret = _wapi_stat_to_file_attributes (utf8_name, &buf, NULL); +#else ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf); +#endif g_free (utf8_name); @@ -3242,19 +3055,15 @@ gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels lev int result; if (level != GetFileExInfoStandard) { -#ifdef DEBUG - g_message ("%s: info level %d not supported.", __func__, + DEBUG ("%s: info level %d not supported.", __func__, level); -#endif SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } if (name == NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -3262,9 +3071,7 @@ gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels lev utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_PARAMETER); return FALSE; @@ -3342,9 +3149,7 @@ extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs) */ if (name == NULL) { -#ifdef DEBUG - g_message("%s: name is NULL", __func__); -#endif + DEBUG("%s: name is NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(FALSE); @@ -3352,15 +3157,18 @@ extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs) utf8_name = mono_unicode_to_external (name); if (utf8_name == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return FALSE; } result = _wapi_stat (utf8_name, &buf); + if (result == -1 && errno == ENOENT) { + /* Might be a dangling symlink... */ + result = _wapi_lstat (utf8_name, &buf); + } + if (result != 0) { _wapi_set_last_path_error_from_errno (NULL, utf8_name); g_free (utf8_name); @@ -3413,35 +3221,40 @@ 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) + +#ifdef __native_client__ + gchar *path = g_get_current_dir (); + if (length < strlen(path) + 1 || path == NULL) + return 0; + memcpy (buffer, path, strlen(path) + 1); +#else + 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; + } +#endif - 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; } @@ -3488,16 +3301,12 @@ gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, mono_once (&io_ops_once, io_ops_init); -#ifdef DEBUG - g_message ("%s: Creating pipe", __func__); -#endif + DEBUG ("%s: Creating pipe", __func__); ret=pipe (filedes); if(ret==-1) { -#ifdef DEBUG - g_message ("%s: Error creating pipe: %s", __func__, + DEBUG ("%s: Error creating pipe: %s", __func__, strerror (errno)); -#endif _wapi_set_last_error_from_errno (); return(FALSE); @@ -3505,9 +3314,7 @@ gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, if (filedes[0] >= _wapi_fd_reserve || filedes[1] >= _wapi_fd_reserve) { -#ifdef DEBUG - g_message ("%s: File descriptor is too big", __func__); -#endif + DEBUG ("%s: File descriptor is too big", __func__); SetLastError (ERROR_TOO_MANY_OPEN_FILES); @@ -3519,6 +3326,7 @@ gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, /* filedes[0] is open for reading, filedes[1] for writing */ + pipe_read_handle.fd = filedes [0]; pipe_read_handle.fileaccess = GENERIC_READ; read_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[0], &pipe_read_handle); @@ -3531,6 +3339,7 @@ gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, return(FALSE); } + pipe_write_handle.fd = filedes [1]; pipe_write_handle.fileaccess = GENERIC_WRITE; write_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[1], &pipe_write_handle); @@ -3548,10 +3357,8 @@ gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe, *readpipe = read_handle; *writepipe = write_handle; -#ifdef DEBUG - g_message ("%s: Returning pipe: read handle %p, write handle %p", + DEBUG ("%s: Returning pipe: read handle %p, write handle %p", __func__, read_handle, write_handle); -#endif return(TRUE); } @@ -3577,10 +3384,8 @@ guint32 GetTempPath (guint32 len, gunichar2 *buf) dirlen=(bytes/2); if(dirlen+1>len) { -#ifdef DEBUG - g_message ("%s: Size %d smaller than needed (%ld)", + DEBUG ("%s: Size %d smaller than needed (%ld)", __func__, len, dirlen+1); -#endif ret=dirlen+1; } else { @@ -3600,8 +3405,360 @@ guint32 GetTempPath (guint32 len, gunichar2 *buf) return(ret); } +#ifdef HAVE_GETFSSTAT +/* Darwin has getfsstat */ +gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf) +{ + struct statfs *stats; + int size, n, i; + gunichar2 *dir; + glong length, total = 0; + + n = getfsstat (NULL, 0, MNT_NOWAIT); + if (n == -1) + return 0; + size = n * sizeof (struct statfs); + stats = (struct statfs *) g_malloc (size); + if (stats == NULL) + return 0; + if (getfsstat (stats, size, MNT_NOWAIT) == -1){ + g_free (stats); + return 0; + } + for (i = 0; i < n; i++){ + dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL); + if (total + length < len){ + memcpy (buf + total, dir, sizeof (gunichar2) * length); + buf [total+length] = 0; + } + g_free (dir); + total += length + 1; + } + if (total < len) + buf [total] = 0; + total++; + g_free (stats); + return total; +} +#else +/* 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'; +} +static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf); + +#if __linux__ +#define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512 +#define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64 + +typedef struct +{ + glong total; + guint32 buffer_index; + guint32 mountpoint_index; + guint32 field_number; + guint32 allocated_size; + guint32 fsname_index; + guint32 fstype_index; + gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1]; + gchar *mountpoint_allocated; + gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER]; + gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1]; + ssize_t nbytes; + gchar delimiter; + gboolean check_mount_source; +} LinuxMountInfoParseState; + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); +static void append_to_mountpoint (LinuxMountInfoParseState *state); +static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state); + +gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf) +{ + int fd; + gint32 ret = 0; + LinuxMountInfoParseState state; + gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL; + + memset (buf, 0, len * sizeof (gunichar2)); + fd = open ("/proc/self/mountinfo", O_RDONLY); + if (fd != -1) + parser = GetLogicalDriveStrings_MountInfo; + else { + fd = open ("/proc/mounts", O_RDONLY); + if (fd != -1) + parser = GetLogicalDriveStrings_Mounts; + } + + if (!parser) { + ret = GetLogicalDriveStrings_Mtab (len, buf); + goto done_and_out; + } + + memset (&state, 0, sizeof (LinuxMountInfoParseState)); + state.field_number = 1; + state.delimiter = ' '; + + while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) { + state.buffer_index = 0; + + while ((*parser)(len, buf, &state)) { + if (state.buffer [state.buffer_index] == '\n') { + gboolean quit = add_drive_string (len, buf, &state); + state.field_number = 1; + state.buffer_index++; + if (state.mountpoint_allocated) { + g_free (state.mountpoint_allocated); + state.mountpoint_allocated = NULL; + } + if (quit) { + ret = state.total; + goto done_and_out; + } + } + } + }; + ret = state.total; + + done_and_out: + if (fd != -1) + close (fd); + return ret; +} + +static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gchar *ptr; + + if (state->field_number == 1) + state->check_mount_source = TRUE; + + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 2: + state->mountpoint_index = 0; + break; + + case 3: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + default: + ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index); + if (ptr) + state->buffer_index = (ptr - (gchar*)state->buffer) - 1; + else + state->buffer_index = state->nbytes; + return TRUE; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 1: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + + case 2: + append_to_mountpoint (state); + break; + + case 3: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + while (state->buffer_index < (guint32)state->nbytes) { + if (state->buffer [state->buffer_index] == state->delimiter) { + state->field_number++; + switch (state->field_number) { + case 5: + state->mountpoint_index = 0; + break; + + case 6: + if (state->mountpoint_allocated) + state->mountpoint_allocated [state->mountpoint_index] = 0; + else + state->mountpoint [state->mountpoint_index] = 0; + break; + + case 7: + state->delimiter = '-'; + break; + + case 8: + state->delimiter = ' '; + break; + + case 10: + state->check_mount_source = TRUE; + break; + } + state->buffer_index++; + continue; + } else if (state->buffer [state->buffer_index] == '\n') + return TRUE; + + switch (state->field_number) { + case 5: + append_to_mountpoint (state); + break; + + case 9: + if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fstype [state->fstype_index++] = state->buffer [state->buffer_index]; + break; + + case 10: + if (state->check_mount_source) { + if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') { + /* We can ignore the rest, it's a device + * path */ + state->check_mount_source = FALSE; + state->fsname [state->fsname_index++] = '/'; + break; + } + if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER) + state->fsname [state->fsname_index++] = state->buffer [state->buffer_index]; + } + break; + } + + state->buffer_index++; + } + + return FALSE; +} + +static void +append_to_mountpoint (LinuxMountInfoParseState *state) +{ + gchar ch = state->buffer [state->buffer_index]; + if (state->mountpoint_allocated) { + if (state->mountpoint_index >= state->allocated_size) { + guint32 newsize = (state->allocated_size << 1) + 1; + gchar *newbuf = g_malloc0 (newsize * sizeof (gchar)); + + memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index); + g_free (state->mountpoint_allocated); + state->mountpoint_allocated = newbuf; + state->allocated_size = newsize; + } + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else { + if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) { + state->allocated_size = (state->mountpoint_index << 1) + 1; + state->mountpoint_allocated = g_malloc0 (state->allocated_size * sizeof (gchar)); + memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index); + state->mountpoint_allocated [state->mountpoint_index++] = ch; + } else + state->mountpoint [state->mountpoint_index++] = ch; + } +} + +static gboolean +add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state) +{ + gboolean quit = FALSE; + gboolean ignore_entry; + + if (state->fsname_index == 1 && state->fsname [0] == '/') + ignore_entry = FALSE; + else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0) + ignore_entry = TRUE; + else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) { + /* Ignore GNOME's gvfs */ + if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0) + ignore_entry = TRUE; + else + ignore_entry = FALSE; + } else + ignore_entry = TRUE; + + if (!ignore_entry) { + gunichar2 *dir; + glong length; + gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint; + + unescape_octal (mountpoint); + dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL); + if (state->total + length + 1 > len) { + quit = TRUE; + state->total = len * 2; + } else { + length++; + memcpy (buf + state->total, dir, sizeof (gunichar2) * length); + state->total += length; + } + g_free (dir); + } + state->fsname_index = 0; + state->fstype_index = 0; + + return quit; +} +#else gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf) +{ + return GetLogicalDriveStrings_Mtab (len, buf); +} +#endif +static gint32 +GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf) { FILE *fp; gunichar2 *ptr, *dir; @@ -3635,10 +3792,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 */ } @@ -3683,182 +3842,417 @@ GetLogicalDriveStrings (guint32 len, gunichar2 *buf) } #endif } +#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; + + 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) { + DEBUG("%s: unicode conversion returned NULL", __func__); + + SetLastError (ERROR_INVALID_NAME); + return(FALSE); + } + } - 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); +#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 -#endif - ) { - return (TRUE); - } - - SetLastError (ERROR_LOCK_VIOLATION); + _wapi_set_last_error_from_errno (); + DEBUG ("%s: statvfs failed: %s", __func__, strerror (errno)); return(FALSE); } - return(TRUE); -} + /* 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; + } + } -static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length) -{ - struct flock lock_data; - int ret; + /* 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; + } - 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); + /* 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; } - - SetLastError (ERROR_LOCK_VIOLATION); - return(FALSE); } - + 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 +/* + * General Unix support + */ +typedef struct { + guint32 drive_type; +#if __linux__ + const long fstypeid; +#endif + const gchar* fstype; +} _wapi_drive_type; + +static _wapi_drive_type _wapi_drive_types[] = { +#if PLATFORM_MACOSX + { DRIVE_REMOTE, "afp" }, + { DRIVE_REMOTE, "autofs" }, + { DRIVE_CDROM, "cddafs" }, + { DRIVE_CDROM, "cd9660" }, + { DRIVE_RAMDISK, "devfs" }, + { DRIVE_FIXED, "exfat" }, + { DRIVE_RAMDISK, "fdesc" }, + { DRIVE_REMOTE, "ftp" }, + { DRIVE_FIXED, "hfs" }, + { DRIVE_FIXED, "msdos" }, + { DRIVE_REMOTE, "nfs" }, + { DRIVE_FIXED, "ntfs" }, + { DRIVE_REMOTE, "smbfs" }, + { DRIVE_FIXED, "udf" }, + { DRIVE_REMOTE, "webdav" }, + { DRIVE_UNKNOWN, NULL } +#elif __linux__ + { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"}, + { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"}, + { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"}, + { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"}, + { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"}, + { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" }, + { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"}, + { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"}, + { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"}, + { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"}, + { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"}, + { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"}, + { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"}, + { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"}, + { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"}, + { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"}, + { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"}, + { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"}, + { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"}, + { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"}, + { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"}, + { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"}, + { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"}, + { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"}, + { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"}, + { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"}, + { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"}, + { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"}, + { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"}, + { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"}, + { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"}, + { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"}, + { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"}, + { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"}, + { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"}, + { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"}, + { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"}, + { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"}, + { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"}, + { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"}, + { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"}, + { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"}, + { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"}, + { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"}, + { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"}, + { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"}, + { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"}, + { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"}, + { DRIVE_FIXED, UFS_MAGIC, "ufs"}, + { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"}, + { DRIVE_FIXED, UFS2_MAGIC, "ufs2"}, + { DRIVE_FIXED, UFS_CIGAM, "ufs"}, + { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"}, + { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"}, + { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"}, + { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"}, + { DRIVE_FIXED, V9FS_MAGIC, "9p"}, + { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"}, + { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"}, + { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"}, + { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"}, + { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"}, + { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"}, + { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"}, + { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"}, + { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"}, + { DRIVE_FIXED, OMFS_MAGIC, "omfs"}, + { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"}, + { DRIVE_UNKNOWN, 0, NULL} +#else + { 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 } #endif +}; - return(_wapi_lock_file_region (fd, offset, length)); -} +#if __linux__ +static guint32 _wapi_get_drive_type(long f_type) +{ + _wapi_drive_type *current; -gboolean UnlockFile (gpointer handle, guint32 offset_low, - guint32 offset_high, guint32 length_low, - guint32 length_high) + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (current->fstypeid == f_type) + return current->drive_type; + current++; + } + + return DRIVE_UNKNOWN; +} +#else +static guint32 _wapi_get_drive_type(const gchar* fstype) { - 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); + _wapi_drive_type *current; + + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (strcmp (current->fstype, fstype) == 0) + break; + + current++; } - 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); + return current->drive_type; +} #endif - SetLastError (ERROR_ACCESS_DENIED); - return(FALSE); + +#if defined (PLATFORM_MACOSX) || defined (__linux__) +static guint32 +GetDriveTypeFromPath (const char *utf8_root_path_name) +{ + struct statfs buf; + + if (statfs (utf8_root_path_name, &buf) == -1) + return DRIVE_UNKNOWN; +#if PLATFORM_MACOSX + return _wapi_get_drive_type (buf.f_fstypename); +#else + return _wapi_get_drive_type (buf.f_type); +#endif +} +#else +static guint32 +GetDriveTypeFromPath (const gchar *utf8_root_path_name) +{ + guint32 drive_type; + FILE *fp; + gchar buffer [512]; + gchar **splitted; + + fp = fopen ("/etc/mtab", "rt"); + if (fp == NULL) { + fp = fopen ("/etc/mnttab", "rt"); + if (fp == NULL) + return(DRIVE_UNKNOWN); } -#ifdef HAVE_LARGE_FILE_SUPPORT - offset = ((gint64)offset_high << 32) | offset_low; - length = ((gint64)length_high << 32) | 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; + } + + /* 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)); + /* it is possible this path might be mounted again with + a known type...keep looking */ + if (drive_type != DRIVE_UNKNOWN) { + g_strfreev (splitted); + break; + } + } + + g_strfreev (splitted); + } -#ifdef DEBUG - g_message ("%s: Unlocking handle %p, offset %lld, length %lld", - __func__, handle, offset, length); + fclose (fp); + return drive_type; +} #endif -#else - offset = offset_low; - length = length_low; -#ifdef DEBUG - g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__, - handle, offset, length); +guint32 GetDriveType(const gunichar2 *root_path_name) +{ + 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); + } + } + else { + utf8_root_path_name = mono_unicode_to_external (root_path_name); + if (utf8_root_path_name == NULL) { + DEBUG("%s: unicode conversion returned NULL", __func__); + return(DRIVE_NO_ROOT_DIR); + } + + /* strip trailing slash for compare below */ + if (g_str_has_suffix(utf8_root_path_name, "/") && utf8_root_path_name [1] != 0) { + utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0; + } + } + drive_type = GetDriveTypeFromPath (utf8_root_path_name); + g_free (utf8_root_path_name); + + return (drive_type); +} + +static gchar* +get_fstypename (gchar *utfpath) +{ +#if defined (PLATFORM_MACOSX) || defined (__linux__) + struct statfs stat; +#if __linux__ + _wapi_drive_type *current; +#endif + if (statfs (utfpath, &stat) == -1) + return NULL; +#if PLATFORM_MACOSX + return g_strdup (stat.f_fstypename); +#else + current = &_wapi_drive_types[0]; + while (current->drive_type != DRIVE_UNKNOWN) { + if (stat.f_type == current->fstypeid) + return g_strdup (current->fstype); + current++; + } + return NULL; #endif +#else + return NULL; #endif +} - return(_wapi_unlock_file_region (fd, offset, length)); +/* Linux has struct statfs which has a different layout */ +#if defined (PLATFORM_MACOSX) || defined (__linux__) || defined(PLATFORM_BSD) || defined(__native_client__) +gboolean +GetVolumeInformation (const gunichar2 *path, gunichar2 *volumename, int volumesize, int *outserial, int *maxcomp, int *fsflags, gunichar2 *fsbuffer, int fsbuffersize) +{ + gchar *utfpath; + gchar *fstypename; + gboolean status = FALSE; + glong len; + + // We only support getting the file system type + if (fsbuffer == NULL) + return 0; + + utfpath = mono_unicode_to_external (path); + if ((fstypename = get_fstypename (utfpath)) != NULL){ + gunichar2 *ret = g_utf8_to_utf16 (fstypename, -1, NULL, &len, NULL); + if (ret != NULL && len < fsbuffersize){ + memcpy (fsbuffer, ret, len * sizeof (gunichar2)); + fsbuffer [len] = 0; + status = TRUE; + } + if (ret != NULL) + g_free (ret); + g_free (fstypename); + } + g_free (utfpath); + return status; } +#endif