/* 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)
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] == '.') {
{
struct _WapiHandle_file *file_handle;
gboolean ok;
- off_t offset, newpos;
+ gint64 offset, newpos;
int whence, fd;
guint32 ret;
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));
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;
}
#ifdef FTRUNCATE_DOESNT_EXTEND
- /* I haven't bothered to write the configure.in stuff for this
+ /* I haven't bothered to write the configure.ac stuff for this
* because I don't know if any platform needs it. I'm leaving
* this code just in case though
*/
}
#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
*/
_wapi_set_last_error_from_errno ();
return(FALSE);
}
+#endif
return(TRUE);
}
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)
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) {
}
}
- /* 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;
}
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) {
DEBUG("%s: name is NULL", __func__);
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;
}
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;
// 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);
* 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)
{
handle = GINT_TO_POINTER (fd);
- pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
- (void *)&stdhandle_mutex);
thr_ret = mono_mutex_lock (&stdhandle_mutex);
g_assert (thr_ret == 0);
done:
thr_ret = mono_mutex_unlock (&stdhandle_mutex);
g_assert (thr_ret == 0);
- pthread_cleanup_pop (0);
return(handle);
}
return(FALSE);
}
- pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
- handle);
thr_ret = _wapi_handle_lock_handle (handle);
g_assert (thr_ret == 0);
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) {
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);
cleanup:
thr_ret = _wapi_handle_unlock_handle (handle);
g_assert (thr_ret == 0);
- pthread_cleanup_pop (0);
return(ret);
}
return(FALSE);
}
- pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
- handle);
thr_ret = _wapi_handle_lock_handle (handle);
g_assert (thr_ret == 0);
thr_ret = _wapi_handle_unlock_handle (handle);
g_assert (thr_ret == 0);
- pthread_cleanup_pop (0);
_wapi_handle_unref (handle);
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);
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*/
_wapi_set_last_error_from_errno ();
return 0;
}
+#endif
utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
count = (bytes/2)+1;
ignore_entry = TRUE;
else
ignore_entry = FALSE;
- } else
+ } else if (state->fstype_index == 3 && memcmp ("nfs", state->fstype, state->fstype_index) == 0)
+ ignore_entry = FALSE;
+ else
ignore_entry = TRUE;
if (!ignore_entry) {
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);
return (drive_type);
}
-static const gchar*
+static gchar*
get_fstypename (gchar *utfpath)
{
#if defined (PLATFORM_MACOSX) || defined (__linux__)
if (statfs (utfpath, &stat) == -1)
return NULL;
#if PLATFORM_MACOSX
- return stat.f_fstypename;
+ 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 current->fstype;
+ return g_strdup (current->fstype);
current++;
}
return NULL;
GetVolumeInformation (const gunichar2 *path, gunichar2 *volumename, int volumesize, int *outserial, int *maxcomp, int *fsflags, gunichar2 *fsbuffer, int fsbuffersize)
{
gchar *utfpath;
- const gchar *fstypename;
+ gchar *fstypename;
gboolean status = FALSE;
glong len;
}
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);
+}