diff options
Diffstat (limited to 'mono/io-layer/io.c')
-rwxr-xr-x | mono/io-layer/io.c | 103 |
1 files changed, 86 insertions, 17 deletions
diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c index 579c48dd89..37166faa53 100755 --- a/mono/io-layer/io.c +++ b/mono/io-layer/io.c @@ -240,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) @@ -259,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] == '.') { @@ -523,7 +560,7 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, { struct _WapiHandle_file *file_handle; gboolean ok; - off_t offset, newpos; + gint64 offset, newpos; int whence, fd; guint32 ret; @@ -578,15 +615,15 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, offset=movedistance; #endif -#ifdef HAVE_LARGE_FILE_SUPPORT DEBUG ("%s: moving handle %p by %lld bytes from %d", __func__, - handle, offset, whence); -#else - DEBUG ("%s: moving handle %p fd %d by %ld bytes from %d", __func__, - handle, offset, whence); -#endif + 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) { DEBUG("%s: lseek on handle %p returned error %s", __func__, handle, strerror(errno)); @@ -595,11 +632,7 @@ static guint32 file_seek(gpointer handle, gint32 movedistance, return(INVALID_SET_FILE_POINTER); } -#ifdef HAVE_LARGE_FILE_SUPPORT DEBUG ("%s: lseek returns %lld", __func__, newpos); -#else - DEBUG ("%s: lseek returns %ld", __func__, newpos); -#endif #ifdef HAVE_LARGE_FILE_SUPPORT ret=newpos & 0xFFFFFFFF; @@ -1362,6 +1395,43 @@ static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, 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) @@ -1744,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; } |