X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fio.c;h=b67a9ca9b706d13f9cfe010554b3c759a04dbe86;hb=424595ea7043f30b0553f2842654db13baeda3ca;hp=e060a7d3724205f07768a0c4454352471cb2dc73;hpb=5b558abeeb255a3179d4ca6a85617e051c6abd38;p=mono.git diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c old mode 100644 new mode 100755 index e060a7d3724..b67a9ca9b70 --- 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 @@ -17,9 +18,11 @@ #include #ifdef HAVE_SYS_STATVFS_H #include -#elif defined(HAVE_SYS_STATFS_H) +#endif +#if defined(HAVE_SYS_STATFS_H) #include -#elif defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) +#endif +#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H) #include #include #endif @@ -31,6 +34,7 @@ #ifdef __linux__ #include #include +#include #endif #include @@ -42,7 +46,12 @@ #include #include -#undef DEBUG +#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); @@ -64,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 = { @@ -230,6 +240,43 @@ static void io_ops_init (void) /* Some utility functions. */ +/* + * Check if a file is writable by the current user. + * + * This is is a best effort kind of thing. It assumes a reasonable sane set + * of permissions by the underlying OS. + * + * We assume that basic unix permission bits are authoritative. Which might not + * be the case under systems with extended permissions systems (posix ACLs, SELinux, OSX/iOS sandboxing, etc) + * + * The choice of access as the fallback is due to the expected lower overhead compared to trying to open the file. + * + * The only expected problem with using access are for root, setuid or setgid programs as access is not consistent + * under those situations. It's to be expected that this should not happen in practice as those bits are very dangerous + * and should not be used with a dynamic runtime. + */ +static gboolean +is_file_writable (struct stat *st, const char *path) +{ + /* Is it globally writable? */ + if (st->st_mode & S_IWOTH) + return 1; + + /* Am I the owner? */ + if ((st->st_uid == geteuid ()) && (st->st_mode & S_IWUSR)) + return 1; + + /* Am I in the same group? */ + if ((st->st_gid == getegid ()) && (st->st_mode & S_IWGRP)) + return 1; + + /* Fallback to using access(2). It's not ideal as it might not take into consideration euid/egid + * but it's the only sane option we have on unix. + */ + return access (path, W_OK) == 0; +} + + static guint32 _wapi_stat_to_file_attributes (const gchar *pathname, struct stat *buf, struct stat *lbuf) @@ -249,14 +296,14 @@ static guint32 _wapi_stat_to_file_attributes (const gchar *pathname, if (S_ISDIR (buf->st_mode)) { attrs = FILE_ATTRIBUTE_DIRECTORY; - if (!(buf->st_mode & S_IWUSR)) { + if (!is_file_writable (buf, pathname)) { attrs |= FILE_ATTRIBUTE_READONLY; } if (filename[0] == '.') { attrs |= FILE_ATTRIBUTE_HIDDEN; } } else { - if (!(buf->st_mode & S_IWUSR)) { + if (!is_file_writable (buf, pathname)) { attrs = FILE_ATTRIBUTE_READONLY; if (filename[0] == '.') { @@ -319,11 +366,10 @@ 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); @@ -333,7 +379,7 @@ static void file_close (gpointer handle, gpointer data) 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) @@ -347,8 +393,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); @@ -359,16 +404,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); @@ -382,10 +426,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); } @@ -403,9 +445,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, { struct _WapiHandle_file *file_handle; gboolean ok; - int ret; + int ret, fd; off_t current_pos = 0; - int fd = GPOINTER_TO_UINT(handle); ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -415,6 +456,8 @@ static gboolean file_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + + fd = file_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -422,9 +465,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); @@ -437,10 +478,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); } @@ -467,10 +506,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); } @@ -485,8 +522,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); @@ -497,11 +533,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); @@ -509,10 +545,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); @@ -526,10 +560,9 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, { struct _WapiHandle_file *file_handle; gboolean ok; - off_t offset, newpos; - int whence; + gint64 offset, newpos; + int whence, fd; guint32 ret; - int fd = GPOINTER_TO_UINT(handle); ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, (gpointer *)&file_handle); @@ -540,12 +573,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); @@ -562,9 +595,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); @@ -573,49 +604,35 @@ 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__, - handle, offset, whence); -#else - g_message("%s: moving handle %p fd %d by %ld bytes from %d", __func__, - handle, offset, whence); -#endif -#endif + DEBUG ("%s: moving handle %p by %lld bytes from %d", __func__, + handle, (long long)offset, whence); +#ifdef PLATFORM_ANDROID + /* bionic doesn't support -D_FILE_OFFSET_BITS=64 */ + newpos=lseek64(fd, offset, whence); +#else newpos=lseek(fd, offset, whence); +#endif 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); -#else - g_message ("%s: lseek returns %ld", __func__, newpos); -#endif -#endif + DEBUG ("%s: lseek returns %lld", __func__, newpos); #ifdef HAVE_LARGE_FILE_SUPPORT ret=newpos & 0xFFFFFFFF; @@ -630,10 +647,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); } @@ -644,8 +659,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); @@ -655,12 +669,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); @@ -674,10 +687,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); @@ -686,10 +697,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); @@ -714,9 +723,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); @@ -725,10 +732,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); @@ -736,6 +741,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 */ @@ -744,14 +751,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); } @@ -763,7 +769,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); @@ -773,13 +779,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); @@ -793,10 +798,8 @@ 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); @@ -807,10 +810,8 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) if (S_ISBLK(statbuf.st_mode)) { guint64 bigsize; if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) { -#ifdef DEBUG - g_message ("%s: handle %p ioctl BLKGETSIZE64 failed: %s", + DEBUG ("%s: handle %p ioctl BLKGETSIZE64 failed: %s", __func__, handle, strerror(errno)); -#endif _wapi_set_last_error_from_errno (); return(INVALID_FILE_SIZE); @@ -821,10 +822,8 @@ static guint32 file_getfilesize(gpointer handle, guint32 *highsize) *highsize = bigsize>>32; } -#ifdef DEBUG - g_message ("%s: Returning block device size %d/%d", + DEBUG ("%s: Returning block device size %d/%d", __func__, size, *highsize); -#endif return(size); } @@ -843,9 +842,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); } @@ -858,8 +855,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); @@ -869,13 +865,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); @@ -883,20 +878,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 @@ -915,10 +906,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; @@ -948,8 +937,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); @@ -959,21 +947,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); @@ -984,10 +969,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); @@ -1000,10 +983,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); } @@ -1020,10 +1001,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); } @@ -1033,18 +1012,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); } @@ -1055,14 +1030,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) @@ -1076,9 +1050,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) { @@ -1087,6 +1060,7 @@ static gboolean console_read(gpointer handle, gpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = console_handle->fd; if(bytesread!=NULL) { *bytesread=0; @@ -1094,10 +1068,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); @@ -1108,10 +1080,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); @@ -1130,8 +1100,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); @@ -1141,6 +1110,7 @@ static gboolean console_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = console_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -1148,9 +1118,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); @@ -1167,10 +1135,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); } @@ -1182,15 +1148,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) @@ -1204,9 +1171,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) { @@ -1215,6 +1181,7 @@ static gboolean pipe_read (gpointer handle, gpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = pipe_handle->fd; if(bytesread!=NULL) { *bytesread=0; @@ -1222,19 +1189,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); @@ -1246,18 +1209,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; @@ -1272,8 +1231,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); @@ -1283,6 +1241,7 @@ static gboolean pipe_write(gpointer handle, gconstpointer buffer, SetLastError (ERROR_INVALID_HANDLE); return(FALSE); } + fd = pipe_handle->fd; if(byteswritten!=NULL) { *byteswritten=0; @@ -1290,18 +1249,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); @@ -1314,10 +1269,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); } @@ -1344,10 +1297,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; } @@ -1367,10 +1318,8 @@ 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; } @@ -1409,9 +1358,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); @@ -1423,9 +1370,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); @@ -1437,23 +1382,56 @@ 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); } + +static gboolean +share_allows_delete (struct stat *statbuf, struct _WapiFileShare **share_info) +{ + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; + + file_already_shared = _wapi_handle_get_or_set_share (statbuf->st_dev, statbuf->st_ino, FILE_SHARE_DELETE, GENERIC_READ, &file_existing_share, &file_existing_access, share_info); + + if (file_already_shared) { + /* The reference to this share info was incremented + * when we looked it up, so be careful to put it back + * if we conclude we can't use this file. + */ + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ + DEBUG ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess); + + _wapi_handle_share_release (*share_info); + + return(FALSE); + } + + if (!(file_existing_share & FILE_SHARE_DELETE)) { + /* New access mode doesn't match up */ + 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); + + return(FALSE); + } + } else { + DEBUG ("%s: New file!", __func__); + } + + return(TRUE); +} static gboolean share_check (struct stat *statbuf, guint32 sharemode, guint32 fileaccess, struct _WapiFileShare **share_info, int fd) @@ -1544,9 +1522,7 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, } 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); @@ -1554,18 +1530,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); @@ -1584,10 +1556,8 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, } 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); @@ -1595,9 +1565,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); @@ -1609,16 +1577,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) { @@ -1630,9 +1603,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); @@ -1648,6 +1619,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; @@ -1680,9 +1652,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); } @@ -1708,9 +1678,7 @@ gboolean DeleteFile(const gunichar2 *name) #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); @@ -1718,9 +1686,7 @@ 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); @@ -1728,23 +1694,12 @@ gboolean DeleteFile(const gunichar2 *name) attrs = GetFileAttributes (name); if (attrs == INVALID_FILE_ATTRIBUTES) { -#ifdef DEBUG - g_message ("%s: file attributes error", __func__); -#endif + DEBUG ("%s: file attributes error", __func__); /* Error set by GetFileAttributes() */ g_free (filename); return(FALSE); } - if (attrs & FILE_ATTRIBUTE_READONLY) { -#ifdef DEBUG - g_message ("%s: file %s is readonly", __func__, filename); -#endif - SetLastError (ERROR_ACCESS_DENIED); - g_free (filename); - return(FALSE); - } - #if 0 /* Check to make sure sharing allows us to open the file for * writing. See bug 323389. @@ -1804,9 +1759,7 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) 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); @@ -1814,18 +1767,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); @@ -1834,9 +1783,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); @@ -1867,15 +1814,14 @@ gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name) } } - /* Check to make sure sharing allows us to open the file for - * writing. See bug 377049. + /* Check to make that we have delete sharing permission. + * See https://bugzilla.xamarin.com/show_bug.cgi?id=17009 * * 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) { + if (share_allows_delete (&stat_src, &shareinfo) == FALSE) { SetLastError (ERROR_SHARING_VIOLATION); return FALSE; } @@ -1958,9 +1904,7 @@ write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors if (report_errors) _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message ("%s: write failed.", __func__); -#endif + DEBUG ("%s: write failed.", __func__); free (buf); return FALSE; } @@ -1992,12 +1936,12 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gchar *utf8_src, *utf8_dest; int src_fd, dest_fd; 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); @@ -2005,19 +1949,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: dest is NULL", __func__); -#endif + DEBUG("%s: dest is NULL", __func__); g_free (utf8_src); SetLastError (ERROR_INVALID_NAME); @@ -2026,10 +1966,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); @@ -2102,10 +2040,17 @@ gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, if (!write_file (src_fd, dest_fd, &st, TRUE)) ret = FALSE; - g_free (utf8_src); - g_free (utf8_dest); close (src_fd); close (dest_fd); + + 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); return ret; } @@ -2116,19 +2061,15 @@ convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name) gchar *utf8_ret; if (arg == NULL) { -#ifdef DEBUG - g_message ("%s: %s is NULL", __func__, arg_name); -#endif + DEBUG ("%s: %s is NULL", __func__, arg_name); SetLastError (ERROR_INVALID_NAME); return NULL; } utf8_ret = mono_unicode_to_external (arg); if (utf8_ret == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion of %s returned NULL", + DEBUG ("%s: unicode conversion of %s returned NULL", __func__, arg_name); -#endif SetLastError (ERROR_INVALID_PARAMETER); return NULL; } @@ -2141,7 +2082,7 @@ ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFile const gunichar2 *backupFileName, guint32 replaceFlags, gpointer exclude, gpointer reserved) { - int result, errno_copy, backup_fd = -1,replaced_fd = -1; + int result, backup_fd = -1,replaced_fd = -1; gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL; struct stat stBackup; gboolean ret = FALSE; @@ -2159,13 +2100,11 @@ ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFile // 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; } 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); @@ -2205,7 +2144,7 @@ replace_cleanup: * Return value: the handle, or %INVALID_HANDLE_VALUE on error */ -static mono_mutex_t stdhandle_mutex = MONO_MUTEX_INITIALIZER; +static mono_mutex_t stdhandle_mutex; gpointer GetStdHandle(WapiStdHandle stdhandle) { @@ -2232,9 +2171,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); @@ -2623,9 +2560,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); @@ -2639,9 +2574,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); @@ -2649,41 +2582,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 @@ -2696,31 +2617,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)]; @@ -2728,19 +2641,13 @@ 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); } @@ -2753,9 +2660,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); @@ -2763,17 +2668,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); @@ -2829,14 +2730,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); @@ -2846,9 +2745,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; @@ -2920,23 +2817,21 @@ retry: } if (result != 0) { -#ifdef DEBUG - g_message ("%s: stat failed: %s", __func__, filename); -#endif + DEBUG ("%s: stat failed: %s", __func__, filename); g_free (filename); goto retry; } +#ifndef __native_client__ result = _wapi_lstat (filename, &linkbuf); if (result != 0) { -#ifdef DEBUG - g_message ("%s: lstat failed: %s", __func__, filename); -#endif + DEBUG ("%s: lstat failed: %s", __func__, filename); g_free (filename); goto retry; } +#endif utf8_filename = mono_utf8_from_external (filename); if (utf8_filename == NULL) { @@ -2951,9 +2846,7 @@ 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 */ @@ -2962,7 +2855,11 @@ retry: else create_time = buf.st_ctime; +#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); @@ -3076,9 +2973,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); @@ -3086,9 +2981,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; @@ -3121,9 +3014,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); @@ -3131,9 +3022,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; @@ -3167,9 +3056,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); @@ -3177,9 +3064,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); @@ -3197,14 +3082,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); @@ -3231,19 +3122,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); @@ -3251,9 +3138,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; @@ -3331,9 +3216,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); @@ -3341,15 +3224,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); @@ -3406,6 +3292,12 @@ extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer) glong count; gsize bytes; +#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*/ @@ -3419,6 +3311,7 @@ extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer) _wapi_set_last_error_from_errno (); return 0; } +#endif utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes); count = (bytes/2)+1; @@ -3475,16 +3368,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); @@ -3492,9 +3381,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); @@ -3506,6 +3393,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); @@ -3518,6 +3406,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); @@ -3535,10 +3424,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); } @@ -3564,10 +3451,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 { @@ -3587,6 +3472,42 @@ 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) @@ -3614,9 +3535,297 @@ unescape_octal (gchar *str) } *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; @@ -3700,6 +3909,7 @@ GetLogicalDriveStrings (guint32 len, gunichar2 *buf) } #endif } +#endif #if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID) gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail, @@ -3726,9 +3936,7 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_ else { utf8_path_name = mono_unicode_to_external (path_name); if (utf8_path_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); @@ -3751,9 +3959,7 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_ if (ret == -1) { _wapi_set_last_error_from_errno (); -#ifdef DEBUG - g_message ("%s: statvfs failed: %s", __func__, strerror (errno)); -#endif + DEBUG ("%s: statvfs failed: %s", __func__, strerror (errno)); return(FALSE); } @@ -3805,12 +4011,109 @@ gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_ } #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" }, @@ -3842,8 +4145,24 @@ static _wapi_drive_type _wapi_drive_types[] = { { DRIVE_REMOTE, "coda" }, { DRIVE_REMOTE, "afs" }, { DRIVE_UNKNOWN, NULL } +#endif }; +#if __linux__ +static guint32 _wapi_get_drive_type(long f_type) +{ + _wapi_drive_type *current; + + 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) { _wapi_drive_type *current; @@ -3858,43 +4177,36 @@ static guint32 _wapi_get_drive_type(const gchar* fstype) return current->drive_type; } +#endif -guint32 GetDriveType(const gunichar2 *root_path_name) +#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; - 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) { -#ifdef DEBUG - g_message("%s: unicode conversion returned NULL", __func__); -#endif - return(DRIVE_NO_ROOT_DIR); - } - - /* strip trailing slash for compare below */ - if (g_str_has_suffix(utf8_root_path_name, "/")) { - utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0; - } - } fp = fopen ("/etc/mtab", "rt"); if (fp == NULL) { fp = fopen ("/etc/mnttab", "rt"); - if (fp == NULL) { - g_free (utf8_root_path_name); + if (fp == NULL) return(DRIVE_UNKNOWN); - } } drive_type = DRIVE_NO_ROOT_DIR; @@ -3910,16 +4222,111 @@ guint32 GetDriveType(const gunichar2 *root_path_name) if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 || (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) { drive_type = _wapi_get_drive_type (*(splitted + 2)); - g_strfreev (splitted); - break; + /* 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); } fclose (fp); + return drive_type; +} +#endif + +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 +} + +/* 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 + + +void +_wapi_io_init (void) +{ + mono_mutex_init (&stdhandle_mutex); +}