* Dick Porter (dick@ximian.com)
*
* (C) 2002 Ximian, Inc.
+ * Copyright (c) 2002-2006 Novell, Inc.
+ * Copyright 2011 Xamarin Inc (http://www.xamarin.com).
*/
#include <config.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#if defined(HAVE_SYS_STATFS_H)
+#include <sys/statfs.h>
+#endif
+#if defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
#include <sys/types.h>
#include <dirent.h>
#include <fnmatch.h>
#include <stdio.h>
#include <utime.h>
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <mono/utils/linux_magic.h>
+#endif
#include <mono/io-layer/wapi.h>
#include <mono/io-layer/wapi-private.h>
#include <mono/io-layer/io-private.h>
#include <mono/io-layer/timefuncs-private.h>
#include <mono/io-layer/thread-private.h>
+#include <mono/io-layer/io-portability.h>
#include <mono/utils/strenc.h>
-#undef DEBUG
-
-static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
-static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
+#if 0
+#define DEBUG(...) g_message(__VA_ARGS__)
+#define DEBUG_ENABLED 1
+#else
+#define DEBUG(...)
+#endif
static void file_close (gpointer handle, gpointer data);
static WapiFileType file_getfiletype(void);
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 = {
NULL, /* signal */
NULL, /* own */
NULL, /* is_owned */
+ NULL, /* special_wait */
+ NULL /* prewait */
};
void _wapi_file_details (gpointer handle_info)
NULL, /* signal */
NULL, /* own */
NULL, /* is_owned */
+ NULL, /* special_wait */
+ NULL /* prewait */
};
void _wapi_console_details (gpointer handle_info)
NULL, /* signal */
NULL, /* own */
NULL, /* is_owned */
+ NULL, /* special_wait */
+ NULL /* prewait */
};
static void pipe_close (gpointer handle, gpointer data);
NULL, /* signal */
NULL, /* own */
NULL, /* is_owned */
+ NULL, /* special_wait */
+ NULL /* prewait */
};
void _wapi_pipe_details (gpointer handle_info)
NULL, NULL, NULL, NULL, NULL, NULL},
};
-#define NO_SIGPIPE(x) do { \
- void (*old_sigpipe)(int) = signal (SIGPIPE, SIG_IGN); \
- x; \
- signal (SIGPIPE, old_sigpipe); \
- } while (0)
-
static mono_once_t io_ops_once=MONO_ONCE_INIT;
+static gboolean lock_while_writing = FALSE;
static void io_ops_init (void)
{
/* WAPI_HANDLE_CAP_WAIT); */
/* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
/* WAPI_HANDLE_CAP_WAIT); */
+
+ if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL) {
+ lock_while_writing = TRUE;
+ }
}
/* Some utility functions.
*/
-static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
+/*
+ * 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)
{
guint32 attrs = 0;
+ gchar *filename;
+
+ /* FIXME: this could definitely be better, but there seems to
+ * be no pattern to the attributes that are set
+ */
- /* FIXME: this could definitely be better */
+ /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
+ if (S_ISSOCK (buf->st_mode))
+ buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
- if (S_ISDIR (buf->st_mode))
- attrs |= FILE_ATTRIBUTE_DIRECTORY;
- else
- attrs |= FILE_ATTRIBUTE_ARCHIVE;
+ filename = _wapi_basename (pathname);
+
+ if (S_ISDIR (buf->st_mode)) {
+ attrs = FILE_ATTRIBUTE_DIRECTORY;
+ if (!is_file_writable (buf, pathname)) {
+ attrs |= FILE_ATTRIBUTE_READONLY;
+ }
+ if (filename[0] == '.') {
+ attrs |= FILE_ATTRIBUTE_HIDDEN;
+ }
+ } else {
+ if (!is_file_writable (buf, pathname)) {
+ attrs = FILE_ATTRIBUTE_READONLY;
+
+ if (filename[0] == '.') {
+ attrs |= FILE_ATTRIBUTE_HIDDEN;
+ }
+ } else if (filename[0] == '.') {
+ attrs = FILE_ATTRIBUTE_HIDDEN;
+ } else {
+ attrs = FILE_ATTRIBUTE_NORMAL;
+ }
+ }
+
+ if (lbuf != NULL) {
+ if (S_ISLNK (lbuf->st_mode)) {
+ attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
+ }
+ }
- if (!(buf->st_mode & S_IWUSR))
- attrs |= FILE_ATTRIBUTE_READONLY;
+ g_free (filename);
return attrs;
}
SetLastError (_wapi_get_win32_file_error (errno));
}
+static void _wapi_set_last_path_error_from_errno (const gchar *dir,
+ const gchar *path)
+{
+ if (errno == ENOENT) {
+ /* Check the path - if it's a missing directory then
+ * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
+ */
+ gchar *dirname;
+
+
+ if (dir == NULL) {
+ dirname = _wapi_dirname (path);
+ } else {
+ dirname = g_strdup (dir);
+ }
+
+ if (_wapi_access (dirname, F_OK) == 0) {
+ SetLastError (ERROR_FILE_NOT_FOUND);
+ } else {
+ SetLastError (ERROR_PATH_NOT_FOUND);
+ }
+
+ g_free (dirname);
+ } else {
+ _wapi_set_last_error_from_errno ();
+ }
+}
+
/* Handle ops.
*/
static void file_close (gpointer handle, gpointer data)
{
struct _WapiHandle_file *file_handle = (struct _WapiHandle_file *)data;
+ int fd = file_handle->fd;
-#ifdef DEBUG
- g_message("%s: closing file handle %p [%s]", __func__, handle,
+ DEBUG("%s: closing file handle %p [%s]", __func__, handle,
file_handle->filename);
-#endif
+ if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
+ _wapi_unlink (file_handle->filename);
+
g_free (file_handle->filename);
- _wapi_handle_share_release (file_handle->share_info);
+ if (file_handle->share_info)
+ _wapi_handle_share_release (file_handle->share_info);
- close (GPOINTER_TO_UINT(handle));
+ close (fd);
}
static WapiFileType file_getfiletype(void)
{
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);
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);
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);
}
{
struct _WapiHandle_file *file_handle;
gboolean ok;
- int ret;
- off_t current_pos;
- int fd = GPOINTER_TO_UINT(handle);
+ int ret, fd;
+ off_t current_pos = 0;
ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
(gpointer *)&file_handle);
SetLastError (ERROR_INVALID_HANDLE);
return(FALSE);
}
+
+ fd = file_handle->fd;
if(byteswritten!=NULL) {
*byteswritten=0;
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);
}
- /* Need to lock the region we're about to write to, because we
- * only do advisory locking on POSIX systems
- */
- current_pos = lseek (fd, (off_t)0, SEEK_CUR);
- if (current_pos == -1) {
-#ifdef DEBUG
- g_message ("%s: handle %p lseek failed: %s", __func__, handle,
- strerror (errno));
-#endif
- _wapi_set_last_error_from_errno ();
- return(FALSE);
- }
+ if (lock_while_writing) {
+ /* Need to lock the region we're about to write to,
+ * because we only do advisory locking on POSIX
+ * systems
+ */
+ current_pos = lseek (fd, (off_t)0, SEEK_CUR);
+ if (current_pos == -1) {
+ DEBUG ("%s: handle %p lseek failed: %s", __func__,
+ handle, strerror (errno));
+ _wapi_set_last_error_from_errno ();
+ return(FALSE);
+ }
- if (_wapi_lock_file_region (fd, current_pos, numbytes) == FALSE) {
- /* The error has already been set */
- return(FALSE);
+ if (_wapi_lock_file_region (fd, current_pos,
+ numbytes) == FALSE) {
+ /* The error has already been set */
+ return(FALSE);
+ }
}
- NO_SIGPIPE(
- do {
- ret = write (fd, buffer, numbytes);
- } while (ret == -1 && errno == EINTR &&
- !_wapi_thread_cur_apc_pending());
- );
+ do {
+ ret = write (fd, buffer, numbytes);
+ } while (ret == -1 && errno == EINTR &&
+ !_wapi_thread_cur_apc_pending());
- _wapi_unlock_file_region (fd, current_pos, numbytes);
+ if (lock_while_writing) {
+ _wapi_unlock_file_region (fd, current_pos, numbytes);
+ }
if (ret == -1) {
if (errno == EINTR) {
} 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);
}
{
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);
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);
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);
{
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);
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);
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);
#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;
}
#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);
}
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);
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);
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);
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);
}
+#ifdef FTRUNCATE_DOESNT_EXTEND
+ /* I haven't bothered to write the configure.in stuff for this
+ * because I don't know if any platform needs it. I'm leaving
+ * this code just in case though
+ */
if(pos>size) {
/* Extend the file. Use write() here, because some
* manuals say that ftruncate() behaviour is undefined
* every system we care about conforms, then we can
* drop this write.
*/
- NO_SIGPIPE(
- do {
- ret = write (fd, "", 1);
- } while (ret == -1 && errno == EINTR &&
- !_wapi_thread_cur_apc_pending());
- );
+ do {
+ ret = write (fd, "", 1);
+ } while (ret == -1 && errno == EINTR &&
+ !_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);
+ }
+
+ /* And put the file position back after the write */
+ ret = lseek (fd, pos, SEEK_SET);
+ if (ret == -1) {
+ DEBUG ("%s: handle %p second lseek failed: %s",
+ __func__, handle, strerror(errno));
_wapi_set_last_error_from_errno ();
return(FALSE);
}
}
+#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
*/
}
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);
}
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);
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);
ret = fstat(fd, &statbuf);
if (ret == -1) {
-#ifdef DEBUG
- g_message ("%s: handle %p fstat failed: %s", __func__,
+ DEBUG ("%s: handle %p fstat failed: %s", __func__,
handle, strerror(errno));
-#endif
_wapi_set_last_error_from_errno ();
return(INVALID_FILE_SIZE);
}
+ /* fstat indicates block devices as zero-length, so go a different path */
+#ifdef BLKGETSIZE64
+ if (S_ISBLK(statbuf.st_mode)) {
+ guint64 bigsize;
+ if (ioctl(fd, BLKGETSIZE64, &bigsize) < 0) {
+ DEBUG ("%s: handle %p ioctl BLKGETSIZE64 failed: %s",
+ __func__, handle, strerror(errno));
+
+ _wapi_set_last_error_from_errno ();
+ return(INVALID_FILE_SIZE);
+ }
+
+ size = bigsize & 0xFFFFFFFF;
+ if (highsize != NULL) {
+ *highsize = bigsize>>32;
+ }
+
+ DEBUG ("%s: Returning block device size %d/%d",
+ __func__, size, *highsize);
+
+ return(size);
+ }
+#endif
+
#ifdef HAVE_LARGE_FILE_SUPPORT
size = statbuf.st_size & 0xFFFFFFFF;
if (highsize != NULL) {
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);
}
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);
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);
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
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;
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);
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);
*/
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);
* 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);
}
* 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);
}
utbuf.modtime=statbuf.st_mtime;
}
-#ifdef DEBUG
- g_message("%s: setting handle %p access %ld write %ld", __func__,
- handle, utbuf.actime, utbuf.modtime);
-#endif
+ DEBUG ("%s: setting handle %p access %ld write %ld", __func__,
+ handle, utbuf.actime, utbuf.modtime);
- ret=utime(file_handle->filename, &utbuf);
- if(ret==-1) {
-#ifdef DEBUG
- g_message("%s: handle %p [%s] utime failed: %s", __func__,
- handle, file_handle->filename, strerror(errno));
+ ret = _wapi_utime (file_handle->filename, &utbuf);
+ if (ret == -1) {
+ DEBUG ("%s: handle %p [%s] utime failed: %s", __func__,
+ handle, file_handle->filename, strerror(errno));
-#endif
SetLastError (ERROR_INVALID_PARAMETER);
return(FALSE);
}
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)
{
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) {
SetLastError (ERROR_INVALID_HANDLE);
return(FALSE);
}
+ fd = console_handle->fd;
if(bytesread!=NULL) {
*bytesread=0;
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);
} 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);
{
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);
SetLastError (ERROR_INVALID_HANDLE);
return(FALSE);
}
+ fd = console_handle->fd;
if(byteswritten!=NULL) {
*byteswritten=0;
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);
}
- NO_SIGPIPE(
- do {
- ret = write(fd, buffer, numbytes);
- } while (ret == -1 && errno == EINTR &&
- !_wapi_thread_cur_apc_pending());
- );
+ do {
+ ret = write(fd, buffer, numbytes);
+ } while (ret == -1 && errno == EINTR &&
+ !_wapi_thread_cur_apc_pending());
if (ret == -1) {
if (errno == EINTR) {
} 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);
}
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)
{
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) {
SetLastError (ERROR_INVALID_HANDLE);
return(FALSE);
}
+ fd = pipe_handle->fd;
if(bytesread!=NULL) {
*bytesread=0;
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);
} 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;
{
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);
SetLastError (ERROR_INVALID_HANDLE);
return(FALSE);
}
+ fd = pipe_handle->fd;
if(byteswritten!=NULL) {
*byteswritten=0;
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
- NO_SIGPIPE(
- do {
- ret = write (fd, buffer, numbytes);
- } while (ret == -1 && errno == EINTR &&
- !_wapi_thread_cur_apc_pending());
- );
+ do {
+ ret = write (fd, buffer, numbytes);
+ } while (ret == -1 && errno == EINTR &&
+ !_wapi_thread_cur_apc_pending());
if (ret == -1) {
if (errno == EINTR) {
} 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);
}
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;
}
flags|=O_TRUNC;
break;
default:
-#ifdef DEBUG
- g_message("%s: Unknown create mode 0x%x", __func__,
+ DEBUG("%s: Unknown create mode 0x%x", __func__,
createmode);
-#endif
break;
}
return(flags);
}
-static guint32 convert_from_flags(int flags)
-{
- guint32 fileaccess=0;
-
-#ifndef O_ACCMODE
-#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
-#endif
-
- if((flags & O_ACCMODE) == O_RDONLY) {
- fileaccess=GENERIC_READ;
- } else if ((flags & O_ACCMODE) == O_WRONLY) {
- fileaccess=GENERIC_WRITE;
- } else if ((flags & O_ACCMODE) == O_RDWR) {
- fileaccess=GENERIC_READ|GENERIC_WRITE;
- } else {
-#ifdef DEBUG
- g_message("%s: Can't figure out flags 0x%x", __func__, flags);
-#endif
- }
-
- /* Maybe sort out create mode too */
-
- return(fileaccess);
-}
-
#if 0 /* unused */
static mode_t convert_perms(guint32 sharemode)
{
*/
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);
((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);
((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)
* other code, perms instead are the on-disk permissions and
* this is a sane default.
*/
- mode_t perms=0644;
+ mode_t perms=0666;
gchar *filename;
int fd, ret;
int handle_type;
mono_once (&io_ops_once, io_ops_init);
+ if (attrs & FILE_ATTRIBUTE_TEMPORARY)
+ perms = 0600;
+
+ if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
+ SetLastError (ERROR_ENCRYPTION_FAILED);
+ return INVALID_HANDLE_VALUE;
+ }
+
if (name == NULL) {
-#ifdef DEBUG
- g_message ("%s: name is NULL", __func__);
-#endif
+ DEBUG ("%s: name is NULL", __func__);
SetLastError (ERROR_INVALID_NAME);
return(INVALID_HANDLE_VALUE);
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 = open(filename, flags, perms);
+ fd = _wapi_open (filename, flags, perms);
/* If we were trying to open a directory with write permissions
* (e.g. O_WRONLY or O_RDWR), this call will fail with
if (fd == -1 && errno == EISDIR)
{
/* Try again but don't try to make it writable */
- fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms);
+ fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
}
if (fd == -1) {
-#ifdef DEBUG
- g_message("%s: Error opening file %s: %s", __func__, filename,
+ DEBUG("%s: Error opening file %s: %s", __func__, filename,
strerror(errno));
-#endif
- _wapi_set_last_error_from_errno ();
+ _wapi_set_last_path_error_from_errno (NULL, filename);
g_free (filename);
return(INVALID_HANDLE_VALUE);
}
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);
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) {
}
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);
//security, sizeof(WapiSecurityAttributes));
}
+ file_handle.fd = fd;
file_handle.fileaccess=fileaccess;
file_handle.sharemode=sharemode;
file_handle.attrs=attrs;
+#ifdef HAVE_POSIX_FADVISE
+ if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
+ posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+ if (attrs & FILE_FLAG_RANDOM_ACCESS)
+ posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
+#endif
+
#ifndef S_ISFIFO
#define S_ISFIFO(m) ((m & S_IFIFO) != 0)
#endif
- handle_type = (S_ISFIFO (statbuf.st_mode)) ? WAPI_HANDLE_PIPE : WAPI_HANDLE_FILE;
+ if (S_ISFIFO (statbuf.st_mode)) {
+ handle_type = WAPI_HANDLE_PIPE;
+ } else if (S_ISCHR (statbuf.st_mode)) {
+ handle_type = WAPI_HANDLE_CONSOLE;
+ } else {
+ handle_type = WAPI_HANDLE_FILE;
+ }
+
handle = _wapi_handle_new_fd (handle_type, fd, &file_handle);
if (handle == _WAPI_HANDLE_INVALID) {
g_warning ("%s: error creating file handle", __func__);
return(INVALID_HANDLE_VALUE);
}
-#ifdef DEBUG
- g_message("%s: returning handle %p", __func__, handle);
-#endif
+ DEBUG("%s: returning handle %p", __func__, handle);
return(handle);
}
gboolean DeleteFile(const gunichar2 *name)
{
gchar *filename;
- int ret;
+ int retval;
+ gboolean ret = FALSE;
+ guint32 attrs;
+#if 0
+ struct stat statbuf;
+ struct _WapiFileShare *shareinfo;
+#endif
if(name==NULL) {
-#ifdef DEBUG
- g_message("%s: name is NULL", __func__);
-#endif
+ DEBUG("%s: name is NULL", __func__);
SetLastError (ERROR_INVALID_NAME);
return(FALSE);
filename=mono_unicode_to_external(name);
if(filename==NULL) {
-#ifdef DEBUG
- g_message("%s: unicode conversion returned NULL", __func__);
-#endif
+ DEBUG("%s: unicode conversion returned NULL", __func__);
SetLastError (ERROR_INVALID_NAME);
return(FALSE);
}
+
+ attrs = GetFileAttributes (name);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ DEBUG ("%s: file attributes error", __func__);
+ /* Error set by GetFileAttributes() */
+ g_free (filename);
+ return(FALSE);
+ }
+
+#if 0
+ /* Check to make sure sharing allows us to open the file for
+ * writing. See bug 323389.
+ *
+ * Do the checks that don't need an open file descriptor, for
+ * simplicity's sake. If we really have to do the full checks
+ * then we can implement that later.
+ */
+ if (_wapi_stat (filename, &statbuf) < 0) {
+ _wapi_set_last_path_error_from_errno (NULL, filename);
+ g_free (filename);
+ return(FALSE);
+ }
- ret=unlink(filename);
-
- g_free(filename);
+ if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
+ &shareinfo) == FALSE) {
+ SetLastError (ERROR_SHARING_VIOLATION);
+ g_free (filename);
+ return FALSE;
+ }
+ if (shareinfo)
+ _wapi_handle_share_release (shareinfo);
+#endif
- if(ret==0) {
- return(TRUE);
+ retval = _wapi_unlink (filename);
+
+ if (retval == -1) {
+ _wapi_set_last_path_error_from_errno (NULL, filename);
+ } else {
+ ret = TRUE;
}
- _wapi_set_last_error_from_errno ();
- return(FALSE);
+ g_free(filename);
+
+ return(ret);
}
/**
gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
{
gchar *utf8_name, *utf8_dest_name;
- int result;
+ int result, errno_copy;
struct stat stat_src, stat_dest;
+ gboolean ret = FALSE;
+ struct _WapiFileShare *shareinfo;
if(name==NULL) {
-#ifdef DEBUG
- g_message("%s: name is NULL", __func__);
-#endif
+ DEBUG("%s: name is NULL", __func__);
SetLastError (ERROR_INVALID_NAME);
return(FALSE);
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);
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);
* We check it here and return the failure if dest exists and is not
* the same file as src.
*/
- if (!stat (utf8_dest_name, &stat_dest) && !stat (utf8_name, &stat_src)) {
- if (stat_dest.st_dev != stat_src.st_dev || stat_dest.st_ino != stat_src.st_ino) {
+ if (_wapi_stat (utf8_name, &stat_src) < 0) {
+ if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ g_free (utf8_name);
+ g_free (utf8_dest_name);
+ return FALSE;
+ }
+ }
+
+ if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
+ if (stat_dest.st_dev != stat_src.st_dev ||
+ stat_dest.st_ino != stat_src.st_ino) {
+ g_free (utf8_name);
+ g_free (utf8_dest_name);
SetLastError (ERROR_ALREADY_EXISTS);
return FALSE;
- }
+ }
+ }
+
+ /* 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_delete (&stat_src, &shareinfo) == FALSE) {
+ SetLastError (ERROR_SHARING_VIOLATION);
+ return FALSE;
}
+ if (shareinfo)
+ _wapi_handle_share_release (shareinfo);
+
+ result = _wapi_rename (utf8_name, utf8_dest_name);
+ errno_copy = errno;
+
+ if (result == -1) {
+ switch(errno_copy) {
+ case EEXIST:
+ SetLastError (ERROR_ALREADY_EXISTS);
+ break;
- result = rename (utf8_name, utf8_dest_name);
+ case EXDEV:
+ /* Ignore here, it is dealt with below */
+ break;
+
+ default:
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ }
+ }
+
g_free (utf8_name);
g_free (utf8_dest_name);
- if (result != 0 && errno == EXDEV) {
+ if (result != 0 && errno_copy == EXDEV) {
+ if (S_ISDIR (stat_src.st_mode)) {
+ SetLastError (ERROR_NOT_SAME_DEVICE);
+ return FALSE;
+ }
/* Try a copy to the new location, and delete the source */
if (CopyFile (name, dest_name, TRUE)==FALSE) {
/* CopyFile will set the error */
}
if (result == 0) {
- return TRUE;
+ ret = TRUE;
}
-
- switch (errno) {
- case EEXIST:
- SetLastError (ERROR_ALREADY_EXISTS);
- break;
-
- default:
- _wapi_set_last_error_from_errno ();
- break;
+
+ return(ret);
+}
+
+static gboolean
+write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors)
+{
+ int remain, n;
+ char *buf, *wbuf;
+ int buf_size = st_src->st_blksize;
+
+ buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
+ buf = (char *) malloc (buf_size);
+
+ for (;;) {
+ remain = read (src_fd, buf, buf_size);
+ if (remain < 0) {
+ if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
+ continue;
+
+ if (report_errors)
+ _wapi_set_last_error_from_errno ();
+
+ free (buf);
+ return FALSE;
+ }
+ if (remain == 0) {
+ break;
+ }
+
+ wbuf = buf;
+ while (remain > 0) {
+ if ((n = write (dest_fd, wbuf, remain)) < 0) {
+ if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
+ continue;
+
+ if (report_errors)
+ _wapi_set_last_error_from_errno ();
+ DEBUG ("%s: write failed.", __func__);
+ free (buf);
+ return FALSE;
+ }
+
+ remain -= n;
+ wbuf += n;
+ }
}
- return FALSE;
+ free (buf);
+ return TRUE ;
}
/**
{
gchar *utf8_src, *utf8_dest;
int src_fd, dest_fd;
- int buf_size;
- char *buf;
- int remain, n;
- struct stat st;
+ struct stat st, dest_st;
+ struct utimbuf dest_time;
+ gboolean ret = TRUE;
+ int ret_utime;
if(name==NULL) {
-#ifdef DEBUG
- g_message("%s: name is NULL", __func__);
-#endif
+ DEBUG("%s: name is NULL", __func__);
SetLastError (ERROR_INVALID_NAME);
return(FALSE);
utf8_src = mono_unicode_to_external (name);
if (utf8_src == NULL) {
-#ifdef DEBUG
- g_message ("%s: unicode conversion of source returned NULL",
+ DEBUG ("%s: unicode conversion of source returned NULL",
__func__);
-#endif
SetLastError (ERROR_INVALID_PARAMETER);
return(FALSE);
}
if(dest_name==NULL) {
-#ifdef DEBUG
- g_message("%s: name is NULL", __func__);
-#endif
+ DEBUG("%s: dest is NULL", __func__);
g_free (utf8_src);
SetLastError (ERROR_INVALID_NAME);
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);
return(FALSE);
}
- src_fd = open (utf8_src, O_RDONLY);
+ src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
if (src_fd < 0) {
- _wapi_set_last_error_from_errno ();
-
+ _wapi_set_last_path_error_from_errno (NULL, utf8_src);
+
g_free (utf8_src);
g_free (utf8_dest);
return(FALSE);
}
+
+ /* Before trying to open/create the dest, we need to report a 'file busy'
+ * error if src and dest are actually the same file. We do the check here to take
+ * advantage of the IOMAP capability */
+ if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev &&
+ st.st_ino == dest_st.st_ino) {
+
+ g_free (utf8_src);
+ g_free (utf8_dest);
+ close (src_fd);
+
+ SetLastError (ERROR_SHARING_VIOLATION);
+ return (FALSE);
+ }
if (fail_if_exists) {
- dest_fd = open (utf8_dest, O_WRONLY | O_CREAT, st.st_mode);
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
} else {
- dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
+ /* FIXME: it kinda sucks that this code path potentially scans
+ * the directory twice due to the weird SetLastError()
+ * behavior. */
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
if (dest_fd < 0) {
- /* O_TRUNC might cause a fail if the file
- * doesn't exist
+ /* The file does not exist, try creating it */
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
+ } else {
+ /* Apparently this error is set if we
+ * overwrite the dest file
*/
- dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
- st.st_mode);
+ SetLastError (ERROR_ALREADY_EXISTS);
}
}
if (dest_fd < 0) {
g_free (utf8_src);
g_free (utf8_dest);
close (src_fd);
-
+
return(FALSE);
}
-
- buf_size = st.st_blksize;
- buf = (char *) alloca (buf_size);
-
- for (;;) {
- remain = read (src_fd, buf, buf_size);
-
- if (remain < 0) {
- if (errno == EINTR && !_wapi_thread_cur_apc_pending()) {
- continue;
- }
-
- _wapi_set_last_error_from_errno ();
-
- g_free (utf8_src);
- g_free (utf8_dest);
- close (src_fd);
- close (dest_fd);
-
- return(FALSE);
- }
-
- if (remain == 0) {
- break;
- }
- while (remain > 0) {
- if ((n = write (dest_fd, buf, remain)) < 0) {
- if (errno == EINTR && !_wapi_thread_cur_apc_pending())
- continue;
+ if (!write_file (src_fd, dest_fd, &st, TRUE))
+ ret = FALSE;
- _wapi_set_last_error_from_errno ();
-#ifdef DEBUG
- g_message ("%s: write failed.", __func__);
-#endif
+ close (src_fd);
+ close (dest_fd);
+
+ dest_time.modtime = st.st_mtime;
+ dest_time.actime = st.st_atime;
+ ret_utime = utime (utf8_dest, &dest_time);
+ if (ret_utime == -1)
+ DEBUG ("%s: file [%s] utime failed: %s", __func__, utf8_dest, strerror(errno));
+
+ g_free (utf8_src);
+ g_free (utf8_dest);
- g_free (utf8_src);
- g_free (utf8_dest);
- close (src_fd);
- close (dest_fd);
+ return ret;
+}
- return (FALSE);
- }
+static gchar*
+convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
+{
+ gchar *utf8_ret;
- remain -= n;
- }
+ if (arg == NULL) {
+ DEBUG ("%s: %s is NULL", __func__, arg_name);
+ SetLastError (ERROR_INVALID_NAME);
+ return NULL;
}
- g_free (utf8_src);
- g_free (utf8_dest);
- close (src_fd);
- close (dest_fd);
+ utf8_ret = mono_unicode_to_external (arg);
+ if (utf8_ret == NULL) {
+ DEBUG ("%s: unicode conversion of %s returned NULL",
+ __func__, arg_name);
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
- return(TRUE);
+ return utf8_ret;
}
-static mono_once_t stdhandle_once=MONO_ONCE_INIT;
-static gpointer stdin_handle=INVALID_HANDLE_VALUE;
-static gpointer stdout_handle=INVALID_HANDLE_VALUE;
-static gpointer stderr_handle=INVALID_HANDLE_VALUE;
-
-static gpointer stdhandle_create (int fd, const guchar *name)
+gboolean
+ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
+ const gunichar2 *backupFileName, guint32 replaceFlags,
+ gpointer exclude, gpointer reserved)
{
- struct _WapiHandle_file file_handle = {0};
- gpointer handle;
- int flags;
-
-#ifdef DEBUG
- g_message("%s: creating standard handle type %s, fd %d", __func__,
- name, fd);
-#endif
-
- /* Check if fd is valid */
- do {
- flags=fcntl(fd, F_GETFL);
- } while (flags == -1 && errno == EINTR);
-
- if(flags==-1) {
- /* Invalid fd. Not really much point checking for EBADF
- * specifically
- */
-#ifdef DEBUG
- g_message("%s: fcntl error on fd %d: %s", __func__, fd,
- strerror(errno));
-#endif
+ int result, backup_fd = -1,replaced_fd = -1;
+ gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
+ struct stat stBackup;
+ gboolean ret = FALSE;
- _wapi_set_last_error_from_errno ();
- return(INVALID_HANDLE_VALUE);
- }
+ if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
+ return FALSE;
+ if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
+ goto replace_cleanup;
+ if (backupFileName != NULL) {
+ if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
+ goto replace_cleanup;
+ }
+
+ if (utf8_backupFileName) {
+ // Open the backup file for read so we can restore the file if an error occurs.
+ backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
+ result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
+ if (result == -1)
+ goto replace_cleanup;
+ }
+
+ result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
+ if (result == -1) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
+ _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
+ if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
+ replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
+ stBackup.st_mode);
+
+ if (replaced_fd == -1)
+ goto replace_cleanup;
- file_handle.filename = g_strdup(name);
- /* some default security attributes might be needed */
- file_handle.security_attributes=0;
- file_handle.fileaccess=convert_from_flags(flags);
- file_handle.sharemode=0;
- file_handle.attrs=0;
+ write_file (backup_fd, replaced_fd, &stBackup, FALSE);
+ }
- handle = _wapi_handle_new_fd (WAPI_HANDLE_CONSOLE, fd, &file_handle);
- if (handle == _WAPI_HANDLE_INVALID) {
- g_warning ("%s: error creating file handle", __func__);
- SetLastError (ERROR_GEN_FAILURE);
- return(INVALID_HANDLE_VALUE);
+ goto replace_cleanup;
}
-
-#ifdef DEBUG
- g_message("%s: returning handle %p", __func__, handle);
-#endif
- return(handle);
-}
+ ret = TRUE;
-static void stdhandle_init (void)
-{
- stdin_handle=stdhandle_create (0, "<stdin>");
- stdout_handle=stdhandle_create (1, "<stdout>");
- stderr_handle=stdhandle_create (2, "<stderr>");
+replace_cleanup:
+ g_free (utf8_replacedFileName);
+ g_free (utf8_replacementFileName);
+ g_free (utf8_backupFileName);
+ if (backup_fd != -1)
+ close (backup_fd);
+ if (replaced_fd != -1)
+ close (replaced_fd);
+ return ret;
}
/**
* Return value: the handle, or %INVALID_HANDLE_VALUE on error
*/
+static mono_mutex_t stdhandle_mutex;
+
gpointer GetStdHandle(WapiStdHandle stdhandle)
{
+ struct _WapiHandle_file *file_handle;
gpointer handle;
-
- mono_once (&io_ops_once, io_ops_init);
- mono_once (&stdhandle_once, stdhandle_init);
+ int thr_ret, fd;
+ const gchar *name;
+ gboolean ok;
switch(stdhandle) {
case STD_INPUT_HANDLE:
- handle=stdin_handle;
+ fd = 0;
+ name = "<stdin>";
break;
case STD_OUTPUT_HANDLE:
- handle=stdout_handle;
+ fd = 1;
+ name = "<stdout>";
break;
case STD_ERROR_HANDLE:
- handle=stderr_handle;
+ fd = 2;
+ name = "<stderr>";
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);
}
- if (handle == INVALID_HANDLE_VALUE) {
- SetLastError (ERROR_NO_MORE_FILES);
- return(INVALID_HANDLE_VALUE);
+ 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);
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
+ (gpointer *)&file_handle);
+ if (ok == FALSE) {
+ /* Need to create this console handle */
+ handle = _wapi_stdhandle_create (fd, name);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ SetLastError (ERROR_NO_MORE_FILES);
+ goto done;
+ }
+ } else {
+ /* Add a reference to this handle */
+ _wapi_handle_ref (handle);
}
- /* Add a reference to this handle */
- _wapi_handle_ref (handle);
+ done:
+ thr_ret = mono_mutex_unlock (&stdhandle_mutex);
+ g_assert (thr_ret == 0);
+ pthread_cleanup_pop (0);
return(handle);
}
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);
* 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);
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
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)];
continue;
}
totaldays-=ip[y];
-#ifdef DEBUG
- g_message("%s: totaldays: %lld", __func__, totaldays);
-#endif
+ DEBUG("%s: totaldays: %lld", __func__, totaldays);
system_time->wMonth = y + 1;
-#ifdef DEBUG
- g_message("%s: Month: %d", __func__, system_time->wMonth);
-#endif
+ DEBUG("%s: Month: %d", __func__, system_time->wMonth);
system_time->wDay = totaldays + 1;
-#ifdef DEBUG
- g_message("%s: Day: %d", __func__, system_time->wDay);
-#endif
+ DEBUG("%s: Day: %d", __func__, system_time->wDay);
return(TRUE);
}
-static gint
-file_compare (gconstpointer a, gconstpointer b)
+gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
{
- gchar *astr = *(gchar **) a;
- gchar *bstr = *(gchar **) b;
+ struct _WapiHandle_find find_handle = {0};
+ gpointer handle;
+ gchar *utf8_pattern = NULL, *dir_part, *entry_part;
+ int result;
+
+ if (pattern == NULL) {
+ DEBUG ("%s: pattern is NULL", __func__);
- return strcmp (astr, bstr);
-}
+ SetLastError (ERROR_PATH_NOT_FOUND);
+ return(INVALID_HANDLE_VALUE);
+ }
-static gint
-get_errno_from_g_file_error (gint error)
-{
- switch (error) {
-#ifdef EACCESS
- case G_FILE_ERROR_ACCES:
- error = EACCES;
- break;
-#endif
-#ifdef ENAMETOOLONG
- case G_FILE_ERROR_NAMETOOLONG:
- error = ENAMETOOLONG;
- break;
-#endif
-#ifdef ENOENT
- case G_FILE_ERROR_NOENT:
- error = ENOENT;
- break;
-#endif
-#ifdef ENOTDIR
- case G_FILE_ERROR_NOTDIR:
- error = ENOTDIR;
- break;
-#endif
-#ifdef ENXIO
- case G_FILE_ERROR_NXIO:
- error = ENXIO;
- break;
-#endif
-#ifdef ENODEV
- case G_FILE_ERROR_NODEV:
- error = ENODEV;
- break;
-#endif
-#ifdef EROFS
- case G_FILE_ERROR_ROFS:
- error = EROFS;
- break;
-#endif
-#ifdef ETXTBSY
- case G_FILE_ERROR_TXTBSY:
- error = ETXTBSY;
- break;
-#endif
-#ifdef EFAULT
- case G_FILE_ERROR_FAULT:
- error = EFAULT;
- break;
-#endif
-#ifdef ELOOP
- case G_FILE_ERROR_LOOP:
- error = ELOOP;
- break;
-#endif
-#ifdef ENOSPC
- case G_FILE_ERROR_NOSPC:
- error = ENOSPC;
- break;
-#endif
-#ifdef ENOMEM
- case G_FILE_ERROR_NOMEM:
- error = ENOMEM;
- break;
-#endif
-#ifdef EMFILE
- case G_FILE_ERROR_MFILE:
- error = EMFILE;
- break;
-#endif
-#ifdef ENFILE
- case G_FILE_ERROR_NFILE:
- error = ENFILE;
- break;
-#endif
-#ifdef EBADF
- case G_FILE_ERROR_BADF:
- error = EBADF;
- break;
-#endif
-#ifdef EINVAL
- case G_FILE_ERROR_INVAL:
- error = EINVAL;
- break;
-#endif
-#ifdef EPIPE
- case G_FILE_ERROR_PIPE:
- error = EPIPE;
- break;
-#endif
-#ifdef EAGAIN
- case G_FILE_ERROR_AGAIN:
- error = EAGAIN;
- break;
-#endif
-#ifdef EINTR
- case G_FILE_ERROR_INTR:
- error = EINTR;
- break;
-#endif
-#ifdef EWIO
- case G_FILE_ERROR_IO:
- error = EIO;
- break;
-#endif
-#ifdef EPERM
- case G_FILE_ERROR_PERM:
- error = EPERM;
- break;
-#endif
- case G_FILE_ERROR_FAILED:
- error = ERROR_INVALID_PARAMETER;
- break;
- }
-
- return error;
-}
-
-/* scandir using glib */
-static gint
-mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
-{
- GError *error = NULL;
- GDir *dir;
- GPtrArray *names;
- const gchar *name;
- gint result;
- GPatternSpec *patspec;
-
- dir = g_dir_open (dirname, 0, &error);
- if (dir == NULL) {
- /* g_dir_open returns ENOENT on directories on which we don't
- * have read/x permission */
- gint errnum = get_errno_from_g_file_error (error->code);
- g_error_free (error);
- if (errnum == ENOENT && g_file_test (dirname, G_FILE_TEST_IS_DIR))
- errnum = EACCES;
-
- errno = errnum;
- return -1;
- }
-
- patspec = g_pattern_spec_new (pattern);
- names = g_ptr_array_new ();
- while ((name = g_dir_read_name (dir)) != NULL) {
- if (g_pattern_match_string (patspec, name))
- g_ptr_array_add (names, g_strdup (name));
- }
-
- g_pattern_spec_free (patspec);
- g_dir_close (dir);
- result = names->len;
- if (result > 0) {
- g_ptr_array_sort (names, file_compare);
- g_ptr_array_set_size (names, result + 1);
-
- *namelist = (gchar **) g_ptr_array_free (names, FALSE);
- } else {
- g_ptr_array_free (names, TRUE);
- }
-
- return result;
-}
-
-gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
-{
- struct _WapiHandle_find find_handle = {0};
- gpointer handle;
- gchar *utf8_pattern = NULL, *dir_part, *entry_part;
- int result;
-
- if (pattern == NULL) {
-#ifdef DEBUG
- g_message ("%s: pattern is NULL", __func__);
-#endif
-
- SetLastError (ERROR_PATH_NOT_FOUND);
- return(INVALID_HANDLE_VALUE);
- }
-
- utf8_pattern = mono_unicode_to_external (pattern);
- if (utf8_pattern == NULL) {
-#ifdef DEBUG
- g_message ("%s: unicode conversion returned NULL", __func__);
-#endif
-
- SetLastError (ERROR_INVALID_NAME);
- return(INVALID_HANDLE_VALUE);
- }
-
-#ifdef DEBUG
- g_message ("%s: looking for [%s]", __func__, utf8_pattern);
-#endif
-
- /* Figure out which bit of the pattern is the directory */
- dir_part = g_path_get_dirname (utf8_pattern);
- entry_part = g_path_get_basename (utf8_pattern);
-
-#if 0
- /* Don't do this check for now, it breaks if directories
- * really do have metachars in their names (see bug 58116).
- * FIXME: Figure out a better solution to keep some checks...
- */
- if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
- SetLastError (ERROR_INVALID_NAME);
- g_free (dir_part);
- g_free (entry_part);
- g_free (utf8_pattern);
- return(INVALID_HANDLE_VALUE);
- }
+ utf8_pattern = mono_unicode_to_external (pattern);
+ if (utf8_pattern == NULL) {
+ DEBUG ("%s: unicode conversion returned NULL", __func__);
+
+ SetLastError (ERROR_INVALID_NAME);
+ return(INVALID_HANDLE_VALUE);
+ }
+
+ DEBUG ("%s: looking for [%s]", __func__, utf8_pattern);
+
+ /* Figure out which bit of the pattern is the directory */
+ dir_part = _wapi_dirname (utf8_pattern);
+ entry_part = _wapi_basename (utf8_pattern);
+
+#if 0
+ /* Don't do this check for now, it breaks if directories
+ * really do have metachars in their names (see bug 58116).
+ * FIXME: Figure out a better solution to keep some checks...
+ */
+ if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
+ SetLastError (ERROR_INVALID_NAME);
+ g_free (dir_part);
+ g_free (entry_part);
+ g_free (utf8_pattern);
+ return(INVALID_HANDLE_VALUE);
+ }
#endif
/* The pattern can specify a directory or a set of files.
*/
find_handle.namelist = NULL;
- result = mono_io_scandir (dir_part, entry_part, &find_handle.namelist);
+ result = _wapi_io_scandir (dir_part, entry_part,
+ &find_handle.namelist);
+
+ if (result == 0) {
+ /* No files, which windows seems to call
+ * FILE_NOT_FOUND
+ */
+ SetLastError (ERROR_FILE_NOT_FOUND);
+ g_free (utf8_pattern);
+ g_free (entry_part);
+ g_free (dir_part);
+ return (INVALID_HANDLE_VALUE);
+ }
if (result < 0) {
-#ifdef DEBUG
+#ifdef DEBUG_ENABLED
gint errnum = errno;
#endif
- _wapi_set_last_error_from_errno ();
-#ifdef DEBUG
- g_message ("%s: scandir error: %s", __func__,
+ _wapi_set_last_path_error_from_errno (dir_part, NULL);
+ DEBUG ("%s: scandir error: %s", __func__,
g_strerror (errnum));
-#endif
g_free (utf8_pattern);
g_free (entry_part);
g_free (dir_part);
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;
{
struct _WapiHandle_find *find_handle;
gboolean ok;
- struct stat buf;
+ struct stat buf, linkbuf;
+ int result;
gchar *filename;
gchar *utf8_filename, *utf8_basename;
gunichar2 *utf16_basename;
/* stat next match */
filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
- if (lstat (filename, &buf) != 0) {
-#ifdef DEBUG
- g_message ("%s: stat failed: %s", __func__, filename);
-#endif
+
+ result = _wapi_stat (filename, &buf);
+ if (result == -1 && errno == ENOENT) {
+ /* Might be a dangling symlink */
+ result = _wapi_lstat (filename, &buf);
+ }
+
+ if (result != 0) {
+ DEBUG ("%s: stat failed: %s", __func__, filename);
g_free (filename);
goto retry;
}
- /* Check for dangling symlinks, and ignore them (principle of
- * least surprise, avoiding confusion where we report the file
- * exists, but when someone tries to open it we would report
- * it isn't there.)
- */
- if(S_ISLNK (buf.st_mode)) {
- if(stat (filename, &buf) != 0) {
- g_free (filename);
- goto retry;
- }
+#ifndef __native_client__
+ result = _wapi_lstat (filename, &linkbuf);
+ if (result != 0) {
+ DEBUG ("%s: lstat failed: %s", __func__, filename);
+
+ g_free (filename);
+ goto retry;
}
-
+#endif
+
utf8_filename = mono_utf8_from_external (filename);
if (utf8_filename == NULL) {
/* We couldn't turn this filename into utf8 (eg the
* encoding of the name wasn't convertible), so just
* ignore it.
*/
+ g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
+
g_free (filename);
goto retry;
}
g_free (filename);
-#ifdef DEBUG
- g_message ("%s: Found [%s]", __func__, utf8_filename);
-#endif
+ DEBUG ("%s: Found [%s]", __func__, utf8_filename);
/* fill data block */
else
create_time = buf.st_ctime;
- find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
+#ifdef __native_client__
+ find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, NULL);
+#else
+ find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
+#endif
_wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
_wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
find_data->dwReserved0 = 0;
find_data->dwReserved1 = 0;
- utf8_basename = g_path_get_basename (utf8_filename);
+ utf8_basename = _wapi_basename (utf8_filename);
utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
NULL);
if(utf16_basename==NULL) {
gboolean ok;
int thr_ret;
+ if (handle == NULL) {
+ SetLastError (ERROR_INVALID_HANDLE);
+ return(FALSE);
+ }
+
ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
(gpointer *)&find_handle);
if(ok==FALSE) {
{
gchar *utf8_name;
int result;
- struct stat buf;
- 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);
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 = mkdir (utf8_name, 0777);
+ result = _wapi_mkdir (utf8_name, 0777);
if (result == 0) {
g_free (utf8_name);
return TRUE;
}
- if (errno == EEXIST) {
- result = stat (utf8_name, &buf);
- if (result == -1) {
- _wapi_set_last_error_from_errno ();
- g_free (utf8_name);
- return FALSE;
- }
-
- g_free (utf8_name);
- attrs = _wapi_stat_to_file_attributes (&buf);
- if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
- return TRUE;
-
- errno = EEXIST;
- _wapi_set_last_error_from_errno ();
- return FALSE;
- }
-
- _wapi_set_last_error_from_errno ();
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
g_free (utf8_name);
return FALSE;
}
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);
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 = rmdir (utf8_name);
+ result = _wapi_rmdir (utf8_name);
+ if (result == -1) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ g_free (utf8_name);
+
+ return(FALSE);
+ }
g_free (utf8_name);
- if (result == 0)
- return TRUE;
-
- _wapi_set_last_error_from_errno ();
- return FALSE;
+ return(TRUE);
}
/**
guint32 GetFileAttributes (const gunichar2 *name)
{
gchar *utf8_name;
- struct stat buf;
+ struct stat buf, linkbuf;
int result;
+ 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);
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);
}
- result = stat (utf8_name, &buf);
+ 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_error_from_errno ();
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
g_free (utf8_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);
- return _wapi_stat_to_file_attributes (&buf);
+
+ return(ret);
}
/**
gchar *utf8_name;
WapiFileAttributesData *data;
- struct stat buf;
+ struct stat buf, linkbuf;
time_t create_time;
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);
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;
}
- result = stat (utf8_name, &buf);
- g_free (utf8_name);
-
+ 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) {
- SetLastError (ERROR_FILE_NOT_FOUND);
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ g_free (utf8_name);
return FALSE;
}
+ result = _wapi_lstat (utf8_name, &linkbuf);
+ if (result != 0) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ g_free (utf8_name);
+ return(FALSE);
+ }
+
/* fill data block */
data = (WapiFileAttributesData *)info;
else
create_time = buf.st_ctime;
- data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
+ data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name,
+ &buf,
+ &linkbuf);
+
+ g_free (utf8_name);
_wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
_wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
*/
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);
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 = stat (utf8_name, &buf);
+ 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);
- SetLastError (ERROR_FILE_NOT_FOUND);
return FALSE;
}
* catch that case here.
*/
if (attrs & FILE_ATTRIBUTE_READONLY) {
- result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
+ result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
} else {
- result = chmod (utf8_name, buf.st_mode | S_IWRITE);
+ result = _wapi_chmod (utf8_name, buf.st_mode | S_IWRITE);
}
/* Ignore the other attributes for now */
*/
extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
{
- gchar *path;
gunichar2 *utf16_path;
glong count;
gsize bytes;
-
- path = g_get_current_dir ();
- if (path == NULL)
+
+#ifdef __native_client__
+ gchar *path = g_get_current_dir ();
+ if (length < strlen(path) + 1 || path == NULL)
return 0;
+ memcpy (buffer, path, strlen(path) + 1);
+#else
+ if (getcwd ((char*)buffer, length) == NULL) {
+ if (errno == ERANGE) { /*buffer length is not big enough */
+ gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
+ if (path == NULL)
+ return 0;
+ utf16_path = mono_unicode_from_external (path, &bytes);
+ g_free (utf16_path);
+ g_free (path);
+ return (bytes/2)+1;
+ }
+ _wapi_set_last_error_from_errno ();
+ return 0;
+ }
+#endif
- utf16_path=mono_unicode_from_external (path, &bytes);
-
- /* if buffer too small, return number of characters required.
- * this is plain dumb.
- */
-
+ utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
count = (bytes/2)+1;
- if (count > length) {
- g_free(path);
- g_free (utf16_path);
-
- return (count);
- }
+ g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
/* Add the terminator */
memset (buffer, '\0', bytes+2);
memcpy (buffer, utf16_path, bytes);
g_free (utf16_path);
- g_free (path);
return count;
}
gchar *utf8_path;
gboolean result;
+ if (path == NULL) {
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return(FALSE);
+ }
+
utf8_path = mono_unicode_to_external (path);
- if (chdir (utf8_path) != 0) {
+ if (_wapi_chdir (utf8_path) != 0) {
_wapi_set_last_error_from_errno ();
result = FALSE;
}
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);
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);
/* 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);
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);
*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);
}
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 {
return(ret);
}
+#ifdef HAVE_GETFSSTAT
+/* Darwin has getfsstat */
+gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
+{
+ struct statfs *stats;
+ int size, n, i;
+ gunichar2 *dir;
+ glong length, total = 0;
+
+ n = getfsstat (NULL, 0, MNT_NOWAIT);
+ if (n == -1)
+ return 0;
+ size = n * sizeof (struct statfs);
+ stats = (struct statfs *) g_malloc (size);
+ if (stats == NULL)
+ return 0;
+ if (getfsstat (stats, size, MNT_NOWAIT) == -1){
+ g_free (stats);
+ return 0;
+ }
+ for (i = 0; i < n; i++){
+ dir = g_utf8_to_utf16 (stats [i].f_mntonname, -1, NULL, &length, NULL);
+ if (total + length < len){
+ memcpy (buf + total, dir, sizeof (gunichar2) * length);
+ buf [total+length] = 0;
+ }
+ g_free (dir);
+ total += length + 1;
+ }
+ if (total < len)
+ buf [total] = 0;
+ total++;
+ g_free (stats);
+ return total;
+}
+#else
+/* In-place octal sequence replacement */
+static void
+unescape_octal (gchar *str)
+{
+ gchar *rptr;
+ gchar *wptr;
+
+ if (str == NULL)
+ return;
+
+ rptr = wptr = str;
+ while (*rptr != '\0') {
+ if (*rptr == '\\') {
+ char c;
+ rptr++;
+ c = (*(rptr++) - '0') << 6;
+ c += (*(rptr++) - '0') << 3;
+ c += *(rptr++) - '0';
+ *wptr++ = c;
+ } else if (wptr != rptr) {
+ *wptr++ = *rptr++;
+ } else {
+ rptr++; wptr++;
+ }
+ }
+ *wptr = '\0';
+}
+static gint32 GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf);
+
+#if __linux__
+#define GET_LOGICAL_DRIVE_STRINGS_BUFFER 512
+#define GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER 512
+#define GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER 64
+
+typedef struct
+{
+ glong total;
+ guint32 buffer_index;
+ guint32 mountpoint_index;
+ guint32 field_number;
+ guint32 allocated_size;
+ guint32 fsname_index;
+ guint32 fstype_index;
+ gchar mountpoint [GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER + 1];
+ gchar *mountpoint_allocated;
+ gchar buffer [GET_LOGICAL_DRIVE_STRINGS_BUFFER];
+ gchar fsname [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
+ gchar fstype [GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER + 1];
+ ssize_t nbytes;
+ gchar delimiter;
+ gboolean check_mount_source;
+} LinuxMountInfoParseState;
+
+static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
+static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
+static void append_to_mountpoint (LinuxMountInfoParseState *state);
+static gboolean add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state);
+
+gint32 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
+{
+ int fd;
+ gint32 ret = 0;
+ LinuxMountInfoParseState state;
+ gboolean (*parser)(guint32, gunichar2*, LinuxMountInfoParseState*) = NULL;
+
+ memset (buf, 0, len * sizeof (gunichar2));
+ fd = open ("/proc/self/mountinfo", O_RDONLY);
+ if (fd != -1)
+ parser = GetLogicalDriveStrings_MountInfo;
+ else {
+ fd = open ("/proc/mounts", O_RDONLY);
+ if (fd != -1)
+ parser = GetLogicalDriveStrings_Mounts;
+ }
+
+ if (!parser) {
+ ret = GetLogicalDriveStrings_Mtab (len, buf);
+ goto done_and_out;
+ }
+
+ memset (&state, 0, sizeof (LinuxMountInfoParseState));
+ state.field_number = 1;
+ state.delimiter = ' ';
+
+ while ((state.nbytes = read (fd, state.buffer, GET_LOGICAL_DRIVE_STRINGS_BUFFER)) > 0) {
+ state.buffer_index = 0;
+
+ while ((*parser)(len, buf, &state)) {
+ if (state.buffer [state.buffer_index] == '\n') {
+ gboolean quit = add_drive_string (len, buf, &state);
+ state.field_number = 1;
+ state.buffer_index++;
+ if (state.mountpoint_allocated) {
+ g_free (state.mountpoint_allocated);
+ state.mountpoint_allocated = NULL;
+ }
+ if (quit) {
+ ret = state.total;
+ goto done_and_out;
+ }
+ }
+ }
+ };
+ ret = state.total;
+
+ done_and_out:
+ if (fd != -1)
+ close (fd);
+ return ret;
+}
+
+static gboolean GetLogicalDriveStrings_Mounts (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
+{
+ gchar *ptr;
+
+ if (state->field_number == 1)
+ state->check_mount_source = TRUE;
+
+ while (state->buffer_index < (guint32)state->nbytes) {
+ if (state->buffer [state->buffer_index] == state->delimiter) {
+ state->field_number++;
+ switch (state->field_number) {
+ case 2:
+ state->mountpoint_index = 0;
+ break;
+
+ case 3:
+ if (state->mountpoint_allocated)
+ state->mountpoint_allocated [state->mountpoint_index] = 0;
+ else
+ state->mountpoint [state->mountpoint_index] = 0;
+ break;
+
+ default:
+ ptr = (gchar*)memchr (state->buffer + state->buffer_index, '\n', GET_LOGICAL_DRIVE_STRINGS_BUFFER - state->buffer_index);
+ if (ptr)
+ state->buffer_index = (ptr - (gchar*)state->buffer) - 1;
+ else
+ state->buffer_index = state->nbytes;
+ return TRUE;
+ }
+ state->buffer_index++;
+ continue;
+ } else if (state->buffer [state->buffer_index] == '\n')
+ return TRUE;
+
+ switch (state->field_number) {
+ case 1:
+ if (state->check_mount_source) {
+ if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
+ /* We can ignore the rest, it's a device
+ * path */
+ state->check_mount_source = FALSE;
+ state->fsname [state->fsname_index++] = '/';
+ break;
+ }
+ if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
+ state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
+ }
+ break;
+
+ case 2:
+ append_to_mountpoint (state);
+ break;
+
+ case 3:
+ if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
+ state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
+ break;
+ }
+
+ state->buffer_index++;
+ }
+
+ return FALSE;
+}
+
+static gboolean GetLogicalDriveStrings_MountInfo (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
+{
+ while (state->buffer_index < (guint32)state->nbytes) {
+ if (state->buffer [state->buffer_index] == state->delimiter) {
+ state->field_number++;
+ switch (state->field_number) {
+ case 5:
+ state->mountpoint_index = 0;
+ break;
+
+ case 6:
+ if (state->mountpoint_allocated)
+ state->mountpoint_allocated [state->mountpoint_index] = 0;
+ else
+ state->mountpoint [state->mountpoint_index] = 0;
+ break;
+
+ case 7:
+ state->delimiter = '-';
+ break;
+
+ case 8:
+ state->delimiter = ' ';
+ break;
+
+ case 10:
+ state->check_mount_source = TRUE;
+ break;
+ }
+ state->buffer_index++;
+ continue;
+ } else if (state->buffer [state->buffer_index] == '\n')
+ return TRUE;
+
+ switch (state->field_number) {
+ case 5:
+ append_to_mountpoint (state);
+ break;
+
+ case 9:
+ if (state->fstype_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
+ state->fstype [state->fstype_index++] = state->buffer [state->buffer_index];
+ break;
+
+ case 10:
+ if (state->check_mount_source) {
+ if (state->fsname_index == 0 && state->buffer [state->buffer_index] == '/') {
+ /* We can ignore the rest, it's a device
+ * path */
+ state->check_mount_source = FALSE;
+ state->fsname [state->fsname_index++] = '/';
+ break;
+ }
+ if (state->fsname_index < GET_LOGICAL_DRIVE_STRINGS_FSNAME_BUFFER)
+ state->fsname [state->fsname_index++] = state->buffer [state->buffer_index];
+ }
+ break;
+ }
+
+ state->buffer_index++;
+ }
+
+ return FALSE;
+}
+
+static void
+append_to_mountpoint (LinuxMountInfoParseState *state)
+{
+ gchar ch = state->buffer [state->buffer_index];
+ if (state->mountpoint_allocated) {
+ if (state->mountpoint_index >= state->allocated_size) {
+ guint32 newsize = (state->allocated_size << 1) + 1;
+ gchar *newbuf = g_malloc0 (newsize * sizeof (gchar));
+
+ memcpy (newbuf, state->mountpoint_allocated, state->mountpoint_index);
+ g_free (state->mountpoint_allocated);
+ state->mountpoint_allocated = newbuf;
+ state->allocated_size = newsize;
+ }
+ state->mountpoint_allocated [state->mountpoint_index++] = ch;
+ } else {
+ if (state->mountpoint_index >= GET_LOGICAL_DRIVE_STRINGS_MOUNTPOINT_BUFFER) {
+ state->allocated_size = (state->mountpoint_index << 1) + 1;
+ state->mountpoint_allocated = g_malloc0 (state->allocated_size * sizeof (gchar));
+ memcpy (state->mountpoint_allocated, state->mountpoint, state->mountpoint_index);
+ state->mountpoint_allocated [state->mountpoint_index++] = ch;
+ } else
+ state->mountpoint [state->mountpoint_index++] = ch;
+ }
+}
+
+static gboolean
+add_drive_string (guint32 len, gunichar2 *buf, LinuxMountInfoParseState *state)
+{
+ gboolean quit = FALSE;
+ gboolean ignore_entry;
+
+ if (state->fsname_index == 1 && state->fsname [0] == '/')
+ ignore_entry = FALSE;
+ else if (state->fsname_index == 0 || memcmp ("none", state->fsname, state->fsname_index) == 0)
+ ignore_entry = TRUE;
+ else if (state->fstype_index >= 5 && memcmp ("fuse.", state->fstype, 5) == 0) {
+ /* Ignore GNOME's gvfs */
+ if (state->fstype_index == 21 && memcmp ("fuse.gvfs-fuse-daemon", state->fstype, state->fstype_index) == 0)
+ ignore_entry = TRUE;
+ else
+ ignore_entry = FALSE;
+ } else
+ ignore_entry = TRUE;
+
+ if (!ignore_entry) {
+ gunichar2 *dir;
+ glong length;
+ gchar *mountpoint = state->mountpoint_allocated ? state->mountpoint_allocated : state->mountpoint;
+
+ unescape_octal (mountpoint);
+ dir = g_utf8_to_utf16 (mountpoint, -1, NULL, &length, NULL);
+ if (state->total + length + 1 > len) {
+ quit = TRUE;
+ state->total = len * 2;
+ } else {
+ length++;
+ memcpy (buf + state->total, dir, sizeof (gunichar2) * length);
+ state->total += length;
+ }
+ g_free (dir);
+ }
+ state->fsname_index = 0;
+ state->fstype_index = 0;
+
+ return quit;
+}
+#else
gint32
GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
+{
+ return GetLogicalDriveStrings_Mtab (len, buf);
+}
+#endif
+static gint32
+GetLogicalDriveStrings_Mtab (guint32 len, gunichar2 *buf)
{
FILE *fp;
gunichar2 *ptr, *dir;
continue;
}
- dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
+ unescape_octal (*(splitted + 1));
+ dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
g_strfreev (splitted);
if (total + length + 1 > len) {
fclose (fp);
+ g_free (dir);
return len * 2; /* guess */
}
}
#endif
}
+#endif
-static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
+#if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID)
+gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
+ WapiULargeInteger *total_number_of_bytes,
+ WapiULargeInteger *total_number_of_free_bytes)
{
- struct flock lock_data;
+#ifdef HAVE_STATVFS
+ struct statvfs fsstat;
+#elif defined(HAVE_STATFS)
+ struct statfs fsstat;
+#endif
+ gboolean isreadonly;
+ gchar *utf8_path_name;
int ret;
+ unsigned long block_size;
+
+ if (path_name == NULL) {
+ utf8_path_name = g_strdup (g_get_current_dir());
+ if (utf8_path_name == NULL) {
+ SetLastError (ERROR_DIRECTORY);
+ return(FALSE);
+ }
+ }
+ else {
+ utf8_path_name = mono_unicode_to_external (path_name);
+ if (utf8_path_name == NULL) {
+ DEBUG("%s: unicode conversion returned NULL", __func__);
+
+ SetLastError (ERROR_INVALID_NAME);
+ return(FALSE);
+ }
+ }
- lock_data.l_type = F_WRLCK;
- lock_data.l_whence = SEEK_SET;
- lock_data.l_start = offset;
- lock_data.l_len = length;
-
do {
- ret = fcntl (fd, F_SETLK, &lock_data);
- } while(ret == -1 && errno == EINTR);
-
-#ifdef DEBUG
- g_message ("%s: fcntl returns %d", __func__, ret);
+#ifdef HAVE_STATVFS
+ ret = statvfs (utf8_path_name, &fsstat);
+ isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
+ block_size = fsstat.f_frsize;
+#elif defined(HAVE_STATFS)
+ ret = statfs (utf8_path_name, &fsstat);
+ isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
+ block_size = fsstat.f_bsize;
#endif
+ } while(ret == -1 && errno == EINTR);
+
+ g_free(utf8_path_name);
if (ret == -1) {
- /*
- * if locks are not available (NFS for example),
- * ignore the error
- */
- if (errno == ENOLCK
-#ifdef EOPNOTSUPP
- || errno == EOPNOTSUPP
-#endif
-#ifdef ENOTSUP
- || errno == ENOTSUP
-#endif
- ) {
- return (TRUE);
- }
-
- SetLastError (ERROR_LOCK_VIOLATION);
+ _wapi_set_last_error_from_errno ();
+ DEBUG ("%s: statvfs failed: %s", __func__, strerror (errno));
return(FALSE);
}
+ /* total number of free bytes for non-root */
+ if (free_bytes_avail != NULL) {
+ if (isreadonly) {
+ free_bytes_avail->QuadPart = 0;
+ }
+ else {
+ free_bytes_avail->QuadPart = block_size * (guint64)fsstat.f_bavail;
+ }
+ }
+
+ /* total number of bytes available for non-root */
+ if (total_number_of_bytes != NULL) {
+ total_number_of_bytes->QuadPart = block_size * (guint64)fsstat.f_blocks;
+ }
+
+ /* total number of bytes available for root */
+ if (total_number_of_free_bytes != NULL) {
+ if (isreadonly) {
+ total_number_of_free_bytes->QuadPart = 0;
+ }
+ else {
+ total_number_of_free_bytes->QuadPart = block_size * (guint64)fsstat.f_bfree;
+ }
+ }
+
+ return(TRUE);
+}
+#else
+gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
+ WapiULargeInteger *total_number_of_bytes,
+ WapiULargeInteger *total_number_of_free_bytes)
+{
+ if (free_bytes_avail != NULL) {
+ free_bytes_avail->QuadPart = (guint64) -1;
+ }
+
+ if (total_number_of_bytes != NULL) {
+ total_number_of_bytes->QuadPart = (guint64) -1;
+ }
+
+ if (total_number_of_free_bytes != NULL) {
+ total_number_of_free_bytes->QuadPart = (guint64) -1;
+ }
+
return(TRUE);
}
+#endif
+
+/*
+ * General Unix support
+ */
+typedef struct {
+ guint32 drive_type;
+#if __linux__
+ const long fstypeid;
+#endif
+ const gchar* fstype;
+} _wapi_drive_type;
+
+static _wapi_drive_type _wapi_drive_types[] = {
+#if PLATFORM_MACOSX
+ { DRIVE_REMOTE, "afp" },
+ { DRIVE_REMOTE, "autofs" },
+ { DRIVE_CDROM, "cddafs" },
+ { DRIVE_CDROM, "cd9660" },
+ { DRIVE_RAMDISK, "devfs" },
+ { DRIVE_FIXED, "exfat" },
+ { DRIVE_RAMDISK, "fdesc" },
+ { DRIVE_REMOTE, "ftp" },
+ { DRIVE_FIXED, "hfs" },
+ { DRIVE_FIXED, "msdos" },
+ { DRIVE_REMOTE, "nfs" },
+ { DRIVE_FIXED, "ntfs" },
+ { DRIVE_REMOTE, "smbfs" },
+ { DRIVE_FIXED, "udf" },
+ { DRIVE_REMOTE, "webdav" },
+ { DRIVE_UNKNOWN, NULL }
+#elif __linux__
+ { DRIVE_FIXED, ADFS_SUPER_MAGIC, "adfs"},
+ { DRIVE_FIXED, AFFS_SUPER_MAGIC, "affs"},
+ { DRIVE_REMOTE, AFS_SUPER_MAGIC, "afs"},
+ { DRIVE_RAMDISK, AUTOFS_SUPER_MAGIC, "autofs"},
+ { DRIVE_RAMDISK, AUTOFS_SBI_MAGIC, "autofs4"},
+ { DRIVE_REMOTE, CODA_SUPER_MAGIC, "coda" },
+ { DRIVE_RAMDISK, CRAMFS_MAGIC, "cramfs"},
+ { DRIVE_RAMDISK, CRAMFS_MAGIC_WEND, "cramfs"},
+ { DRIVE_REMOTE, CIFS_MAGIC_NUMBER, "cifs"},
+ { DRIVE_RAMDISK, DEBUGFS_MAGIC, "debugfs"},
+ { DRIVE_RAMDISK, SYSFS_MAGIC, "sysfs"},
+ { DRIVE_RAMDISK, SECURITYFS_MAGIC, "securityfs"},
+ { DRIVE_RAMDISK, SELINUX_MAGIC, "selinuxfs"},
+ { DRIVE_RAMDISK, RAMFS_MAGIC, "ramfs"},
+ { DRIVE_FIXED, SQUASHFS_MAGIC, "squashfs"},
+ { DRIVE_FIXED, EFS_SUPER_MAGIC, "efs"},
+ { DRIVE_FIXED, EXT2_SUPER_MAGIC, "ext"},
+ { DRIVE_FIXED, EXT3_SUPER_MAGIC, "ext"},
+ { DRIVE_FIXED, EXT4_SUPER_MAGIC, "ext"},
+ { DRIVE_REMOTE, XENFS_SUPER_MAGIC, "xenfs"},
+ { DRIVE_FIXED, BTRFS_SUPER_MAGIC, "btrfs"},
+ { DRIVE_FIXED, HFS_SUPER_MAGIC, "hfs"},
+ { DRIVE_FIXED, HFSPLUS_SUPER_MAGIC, "hfsplus"},
+ { DRIVE_FIXED, HPFS_SUPER_MAGIC, "hpfs"},
+ { DRIVE_RAMDISK, HUGETLBFS_MAGIC, "hugetlbfs"},
+ { DRIVE_CDROM, ISOFS_SUPER_MAGIC, "iso"},
+ { DRIVE_FIXED, JFFS2_SUPER_MAGIC, "jffs2"},
+ { DRIVE_RAMDISK, ANON_INODE_FS_MAGIC, "anon_inode"},
+ { DRIVE_FIXED, JFS_SUPER_MAGIC, "jfs"},
+ { DRIVE_FIXED, MINIX_SUPER_MAGIC, "minix"},
+ { DRIVE_FIXED, MINIX_SUPER_MAGIC2, "minix v2"},
+ { DRIVE_FIXED, MINIX2_SUPER_MAGIC, "minix2"},
+ { DRIVE_FIXED, MINIX2_SUPER_MAGIC2, "minix2 v2"},
+ { DRIVE_FIXED, MINIX3_SUPER_MAGIC, "minix3"},
+ { DRIVE_FIXED, MSDOS_SUPER_MAGIC, "msdos"},
+ { DRIVE_REMOTE, NCP_SUPER_MAGIC, "ncp"},
+ { DRIVE_REMOTE, NFS_SUPER_MAGIC, "nfs"},
+ { DRIVE_FIXED, NTFS_SB_MAGIC, "ntfs"},
+ { DRIVE_RAMDISK, OPENPROM_SUPER_MAGIC, "openpromfs"},
+ { DRIVE_RAMDISK, PROC_SUPER_MAGIC, "proc"},
+ { DRIVE_FIXED, QNX4_SUPER_MAGIC, "qnx4"},
+ { DRIVE_FIXED, REISERFS_SUPER_MAGIC, "reiserfs"},
+ { DRIVE_RAMDISK, ROMFS_MAGIC, "romfs"},
+ { DRIVE_REMOTE, SMB_SUPER_MAGIC, "samba"},
+ { DRIVE_RAMDISK, CGROUP_SUPER_MAGIC, "cgroupfs"},
+ { DRIVE_RAMDISK, FUTEXFS_SUPER_MAGIC, "futexfs"},
+ { DRIVE_FIXED, SYSV2_SUPER_MAGIC, "sysv2"},
+ { DRIVE_FIXED, SYSV4_SUPER_MAGIC, "sysv4"},
+ { DRIVE_RAMDISK, TMPFS_MAGIC, "tmpfs"},
+ { DRIVE_RAMDISK, DEVPTS_SUPER_MAGIC, "devpts"},
+ { DRIVE_CDROM, UDF_SUPER_MAGIC, "udf"},
+ { DRIVE_FIXED, UFS_MAGIC, "ufs"},
+ { DRIVE_FIXED, UFS_MAGIC_BW, "ufs"},
+ { DRIVE_FIXED, UFS2_MAGIC, "ufs2"},
+ { DRIVE_FIXED, UFS_CIGAM, "ufs"},
+ { DRIVE_RAMDISK, USBDEVICE_SUPER_MAGIC, "usbdev"},
+ { DRIVE_FIXED, XENIX_SUPER_MAGIC, "xenix"},
+ { DRIVE_FIXED, XFS_SB_MAGIC, "xfs"},
+ { DRIVE_RAMDISK, FUSE_SUPER_MAGIC, "fuse"},
+ { DRIVE_FIXED, V9FS_MAGIC, "9p"},
+ { DRIVE_REMOTE, CEPH_SUPER_MAGIC, "ceph"},
+ { DRIVE_RAMDISK, CONFIGFS_MAGIC, "configfs"},
+ { DRIVE_RAMDISK, ECRYPTFS_SUPER_MAGIC, "eCryptfs"},
+ { DRIVE_FIXED, EXOFS_SUPER_MAGIC, "exofs"},
+ { DRIVE_FIXED, VXFS_SUPER_MAGIC, "vxfs"},
+ { DRIVE_FIXED, VXFS_OLT_MAGIC, "vxfs_olt"},
+ { DRIVE_REMOTE, GFS2_MAGIC, "gfs2"},
+ { DRIVE_FIXED, LOGFS_MAGIC_U32, "logfs"},
+ { DRIVE_FIXED, OCFS2_SUPER_MAGIC, "ocfs2"},
+ { DRIVE_FIXED, OMFS_MAGIC, "omfs"},
+ { DRIVE_FIXED, UBIFS_SUPER_MAGIC, "ubifs"},
+ { DRIVE_UNKNOWN, 0, NULL}
+#else
+ { DRIVE_RAMDISK, "ramfs" },
+ { DRIVE_RAMDISK, "tmpfs" },
+ { DRIVE_RAMDISK, "proc" },
+ { DRIVE_RAMDISK, "sysfs" },
+ { DRIVE_RAMDISK, "debugfs" },
+ { DRIVE_RAMDISK, "devpts" },
+ { DRIVE_RAMDISK, "securityfs" },
+ { DRIVE_CDROM, "iso9660" },
+ { DRIVE_FIXED, "ext2" },
+ { DRIVE_FIXED, "ext3" },
+ { DRIVE_FIXED, "ext4" },
+ { DRIVE_FIXED, "sysv" },
+ { DRIVE_FIXED, "reiserfs" },
+ { DRIVE_FIXED, "ufs" },
+ { DRIVE_FIXED, "vfat" },
+ { DRIVE_FIXED, "msdos" },
+ { DRIVE_FIXED, "udf" },
+ { DRIVE_FIXED, "hfs" },
+ { DRIVE_FIXED, "hpfs" },
+ { DRIVE_FIXED, "qnx4" },
+ { DRIVE_FIXED, "ntfs" },
+ { DRIVE_FIXED, "ntfs-3g" },
+ { DRIVE_REMOTE, "smbfs" },
+ { DRIVE_REMOTE, "fuse" },
+ { DRIVE_REMOTE, "nfs" },
+ { DRIVE_REMOTE, "nfs4" },
+ { DRIVE_REMOTE, "cifs" },
+ { DRIVE_REMOTE, "ncpfs" },
+ { DRIVE_REMOTE, "coda" },
+ { DRIVE_REMOTE, "afs" },
+ { DRIVE_UNKNOWN, NULL }
+#endif
+};
-static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
+#if __linux__
+static guint32 _wapi_get_drive_type(long f_type)
{
- struct flock lock_data;
- int ret;
+ _wapi_drive_type *current;
- lock_data.l_type = F_UNLCK;
- lock_data.l_whence = SEEK_SET;
- lock_data.l_start = offset;
- lock_data.l_len = length;
-
- do {
- ret = fcntl (fd, F_SETLK, &lock_data);
- } while(ret == -1 && errno == EINTR);
+ 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;
+
+ current = &_wapi_drive_types[0];
+ while (current->drive_type != DRIVE_UNKNOWN) {
+ if (strcmp (current->fstype, fstype) == 0)
+ break;
+
+ current++;
+ }
-#ifdef DEBUG
- g_message ("%s: fcntl returns %d", __func__, ret);
+ return current->drive_type;
+}
#endif
+
+#if defined (PLATFORM_MACOSX) || defined (__linux__)
+static guint32
+GetDriveTypeFromPath (const char *utf8_root_path_name)
+{
+ struct statfs buf;
- if (ret == -1) {
- /*
- * if locks are not available (NFS for example),
- * ignore the error
- */
- if (errno == ENOLCK
-#ifdef EOPNOTSUPP
- || errno == EOPNOTSUPP
-#endif
-#ifdef ENOTSUP
- || errno == ENOTSUP
+ 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
- ) {
- return (TRUE);
+}
+#else
+static guint32
+GetDriveTypeFromPath (const gchar *utf8_root_path_name)
+{
+ guint32 drive_type;
+ FILE *fp;
+ gchar buffer [512];
+ gchar **splitted;
+
+ fp = fopen ("/etc/mtab", "rt");
+ if (fp == NULL) {
+ fp = fopen ("/etc/mnttab", "rt");
+ if (fp == NULL)
+ return(DRIVE_UNKNOWN);
+ }
+
+ drive_type = DRIVE_NO_ROOT_DIR;
+ while (fgets (buffer, 512, fp) != NULL) {
+ splitted = g_strsplit (buffer, " ", 0);
+ if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
+ g_strfreev (splitted);
+ continue;
}
-
- SetLastError (ERROR_LOCK_VIOLATION);
- return(FALSE);
+
+ /* compare given root_path_name with the one from mtab,
+ if length of utf8_root_path_name is zero it must be the root dir */
+ if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
+ (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
+ drive_type = _wapi_get_drive_type (*(splitted + 2));
+ /* it is possible this path might be mounted again with
+ a known type...keep looking */
+ if (drive_type != DRIVE_UNKNOWN) {
+ g_strfreev (splitted);
+ break;
+ }
+ }
+
+ g_strfreev (splitted);
}
- return(TRUE);
+ fclose (fp);
+ return drive_type;
}
+#endif
-gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
- guint32 length_low, guint32 length_high)
+guint32 GetDriveType(const gunichar2 *root_path_name)
{
- struct _WapiHandle_file *file_handle;
- gboolean ok;
- off_t offset, length;
- int fd = GPOINTER_TO_UINT(handle);
-
- ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
- (gpointer *)&file_handle);
- if (ok == FALSE) {
- g_warning ("%s: error looking up file handle %p", __func__,
- handle);
- SetLastError (ERROR_INVALID_HANDLE);
- return(FALSE);
- }
+ gchar *utf8_root_path_name;
+ guint32 drive_type;
- if (!(file_handle->fileaccess & GENERIC_READ) &&
- !(file_handle->fileaccess & GENERIC_WRITE) &&
- !(file_handle->fileaccess & GENERIC_ALL)) {
-#ifdef DEBUG
- g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
-#endif
- SetLastError (ERROR_ACCESS_DENIED);
- return(FALSE);
+ if (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);
-#ifdef HAVE_LARGE_FILE_SUPPORT
- offset = ((gint64)offset_high << 32) | offset_low;
- length = ((gint64)length_high << 32) | length_low;
+ return (drive_type);
+}
-#ifdef DEBUG
- g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
- handle, offset, length);
-#endif
+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
- offset = offset_low;
- length = length_low;
-
-#ifdef DEBUG
- g_message ("%s: Locking handle %p, offset %ld, length %ld", __func__,
- handle, offset, length);
+ current = &_wapi_drive_types[0];
+ while (current->drive_type != DRIVE_UNKNOWN) {
+ if (stat.f_type == current->fstypeid)
+ return g_strdup (current->fstype);
+ current++;
+ }
+ return NULL;
#endif
+#else
+ return NULL;
#endif
-
- return(_wapi_lock_file_region (fd, offset, length));
}
-gboolean UnlockFile (gpointer handle, guint32 offset_low,
- guint32 offset_high, guint32 length_low,
- guint32 length_high)
+/* 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)
{
- struct _WapiHandle_file *file_handle;
- gboolean ok;
- off_t offset, length;
- int fd = GPOINTER_TO_UINT(handle);
+ gchar *utfpath;
+ gchar *fstypename;
+ gboolean status = FALSE;
+ glong len;
- ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
- (gpointer *)&file_handle);
- if (ok == FALSE) {
- g_warning ("%s: error looking up file handle %p", __func__,
- handle);
- SetLastError (ERROR_INVALID_HANDLE);
- return(FALSE);
- }
+ // We only support getting the file system type
+ if (fsbuffer == NULL)
+ return 0;
- if (!(file_handle->fileaccess & GENERIC_READ) &&
- !(file_handle->fileaccess & GENERIC_WRITE) &&
- !(file_handle->fileaccess & GENERIC_ALL)) {
-#ifdef DEBUG
- g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
-#endif
- SetLastError (ERROR_ACCESS_DENIED);
- return(FALSE);
+ 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);
}
-
-#ifdef HAVE_LARGE_FILE_SUPPORT
- offset = ((gint64)offset_high << 32) | offset_low;
- length = ((gint64)length_high << 32) | length_low;
-
-#ifdef DEBUG
- g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
- __func__, handle, offset, length);
+ g_free (utfpath);
+ return status;
+}
#endif
-#else
- offset = offset_low;
- length = length_low;
-#ifdef DEBUG
- g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
- handle, offset, length);
-#endif
-#endif
- return(_wapi_unlock_file_region (fd, offset, length));
+void
+_wapi_io_init (void)
+{
+ mono_mutex_init (&stdhandle_mutex);
}