#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#elif defined(HAVE_SYS_STATFS_H)
+#include <sys/statfs.h>
+#elif 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>
+#endif
#include <mono/io-layer/wapi.h>
#include <mono/io-layer/wapi-private.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);
-
static void file_close (gpointer handle, gpointer data);
static WapiFileType file_getfiletype(void);
static gboolean file_read(gpointer handle, gpointer buffer,
*/
static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
- struct stat *buf)
+ struct stat *buf,
+ struct stat *lbuf)
{
guint32 attrs = 0;
gchar *filename;
attrs = FILE_ATTRIBUTE_NORMAL;
}
}
+
+ if (lbuf != NULL) {
+ if (S_ISLNK (lbuf->st_mode)) {
+ attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
+ }
+ }
g_free (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));
}
struct _WapiHandle_file *file_handle;
gboolean ok;
int ret;
- off_t current_pos;
+ off_t current_pos = 0;
int fd = GPOINTER_TO_UINT(handle);
ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
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) {
+#ifdef DEBUG
+ g_message ("%s: handle %p ioctl BLKGETSIZE64 failed: %s",
+ __func__, handle, strerror(errno));
+#endif
+
+ _wapi_set_last_error_from_errno ();
+ return(INVALID_FILE_SIZE);
+ }
+
+ size = bigsize & 0xFFFFFFFF;
+ if (highsize != NULL) {
+ *highsize = bigsize>>32;
+ }
+
+#ifdef DEBUG
+ g_message ("%s: Returning block device size %d/%d",
+ __func__, size, *highsize);
+#endif
+
+ return(size);
+ }
+#endif
+
#ifdef HAVE_LARGE_FILE_SUPPORT
size = statbuf.st_size & 0xFFFFFFFF;
if (highsize != NULL) {
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)
{
* 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 (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) {
gchar *filename;
int retval;
gboolean ret = FALSE;
+ guint32 attrs;
+#if 0
+ struct stat statbuf;
+ struct _WapiFileShare *shareinfo;
+#endif
if(name==NULL) {
#ifdef DEBUG
SetLastError (ERROR_INVALID_NAME);
return(FALSE);
}
+
+ attrs = GetFileAttributes (name);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+#ifdef DEBUG
+ g_message ("%s: file attributes error", __func__);
+#endif
+ /* Error set by GetFileAttributes() */
+ g_free (filename);
+ return(FALSE);
+ }
+
+ if (attrs & FILE_ATTRIBUTE_READONLY) {
+#ifdef DEBUG
+ g_message ("%s: file %s is readonly", __func__, filename);
+#endif
+ SetLastError (ERROR_ACCESS_DENIED);
+ g_free (filename);
+ return(FALSE);
+ }
+
+#if 0
+ /* Check to make sure sharing allows us to open the file for
+ * writing. See bug 323389.
+ *
+ * Do the checks that don't need an open file descriptor, for
+ * simplicity's sake. If we really have to do the full checks
+ * then we can implement that later.
+ */
+ if (_wapi_stat (filename, &statbuf) < 0) {
+ _wapi_set_last_path_error_from_errno (NULL, filename);
+ g_free (filename);
+ return(FALSE);
+ }
+ if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
+ &shareinfo) == FALSE) {
+ SetLastError (ERROR_SHARING_VIOLATION);
+ g_free (filename);
+ return FALSE;
+ }
+ if (shareinfo)
+ _wapi_handle_share_release (shareinfo);
+#endif
+
retval = _wapi_unlink (filename);
if (retval == -1) {
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
* We check it here and return the failure if dest exists and is not
* the same file as src.
*/
- if (!_wapi_stat (utf8_dest_name, &stat_dest) &&
- !_wapi_stat (utf8_name, &stat_src)) {
+ if (_wapi_stat (utf8_name, &stat_src) < 0) {
+ if (errno != ENOENT || _wapi_lstat (utf8_name, &stat_src) < 0) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_name);
+ g_free (utf8_name);
+ g_free (utf8_dest_name);
+ return FALSE;
+ }
+ }
+
+ if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
if (stat_dest.st_dev != stat_src.st_dev ||
stat_dest.st_ino != stat_src.st_ino) {
+ g_free (utf8_name);
+ g_free (utf8_dest_name);
SetLastError (ERROR_ALREADY_EXISTS);
return FALSE;
- }
+ }
}
- result = _wapi_rename (utf8_name, utf8_dest_name);
+ /* Check to make sure sharing allows us to open the file for
+ * writing. See bug 377049.
+ *
+ * Do the checks that don't need an open file descriptor, for
+ * simplicity's sake. If we really have to do the full checks
+ * then we can implement that later.
+ */
+ if (share_allows_open (&stat_src, 0, GENERIC_WRITE,
+ &shareinfo) == FALSE) {
+ SetLastError (ERROR_SHARING_VIOLATION);
+ return FALSE;
+ }
+ if (shareinfo)
+ _wapi_handle_share_release (shareinfo);
+ result = _wapi_rename (utf8_name, utf8_dest_name);
+ errno_copy = errno;
+
if (result == -1) {
- switch(errno) {
+ switch(errno_copy) {
case EEXIST:
SetLastError (ERROR_ALREADY_EXISTS);
break;
+
+ case EXDEV:
+ /* Ignore here, it is dealt with below */
+ break;
default:
_wapi_set_last_path_error_from_errno (NULL, utf8_name);
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 */
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 ();
+#ifdef DEBUG
+ g_message ("%s: write failed.", __func__);
+#endif
+ free (buf);
+ return FALSE;
+ }
+
+ remain -= n;
+ wbuf += n;
+ }
+ }
+
+ free (buf);
+ return TRUE ;
+}
+
/**
* CopyFile:
* @name: a pointer to a NULL-terminated unicode string, that names
{
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;
+ gboolean ret = TRUE;
if(name==NULL) {
#ifdef DEBUG
if(dest_name==NULL) {
#ifdef DEBUG
- g_message("%s: name is NULL", __func__);
+ g_message("%s: dest is NULL", __func__);
#endif
g_free (utf8_src);
return(FALSE);
}
+
+ /* Before trying to open/create the dest, we need to report a 'file busy'
+ * error if src and dest are actually the same file. We do the check here to take
+ * advantage of the IOMAP capability */
+ if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev &&
+ st.st_ino == dest_st.st_ino) {
+
+ g_free (utf8_src);
+ g_free (utf8_dest);
+ close (src_fd);
+
+ SetLastError (ERROR_SHARING_VIOLATION);
+ return (FALSE);
+ }
if (fail_if_exists) {
- dest_fd = open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL,
- st.st_mode);
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
} else {
- dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
+ /* FIXME: it kinda sucks that this code path potentially scans
+ * the directory twice due to the weird SetLastError()
+ * behavior. */
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
if (dest_fd < 0) {
- /* O_TRUNC might cause a fail if the file
- * doesn't exist
- */
- dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
- st.st_mode);
+ /* The file does not exist, try creating it */
+ dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
} else {
/* Apparently this error is set if we
* overwrite the dest file
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;
-
- _wapi_set_last_error_from_errno ();
-#ifdef DEBUG
- g_message ("%s: write failed.", __func__);
-#endif
-
- g_free (utf8_src);
- g_free (utf8_dest);
- close (src_fd);
- close (dest_fd);
-
- return (FALSE);
- }
- remain -= n;
- }
- }
+ if (!write_file (src_fd, dest_fd, &st, TRUE))
+ ret = FALSE;
g_free (utf8_src);
g_free (utf8_dest);
close (src_fd);
close (dest_fd);
- return(TRUE);
+ return ret;
}
-static gpointer stdhandle_create (int fd, const gchar *name)
+static gchar*
+convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
{
- struct _WapiHandle_file file_handle = {0};
- gpointer handle;
- int flags;
-
+ gchar *utf8_ret;
+
+ if (arg == NULL) {
#ifdef DEBUG
- g_message("%s: creating standard handle type %s, fd %d", __func__,
- name, fd);
+ g_message ("%s: %s is NULL", __func__, arg_name);
#endif
-
- /* Check if fd is valid */
- do {
- flags=fcntl(fd, F_GETFL);
- } while (flags == -1 && errno == EINTR);
+ SetLastError (ERROR_INVALID_NAME);
+ return NULL;
+ }
- if(flags==-1) {
- /* Invalid fd. Not really much point checking for EBADF
- * specifically
- */
+ utf8_ret = mono_unicode_to_external (arg);
+ if (utf8_ret == NULL) {
#ifdef DEBUG
- g_message("%s: fcntl error on fd %d: %s", __func__, fd,
- strerror(errno));
+ g_message ("%s: unicode conversion of %s returned NULL",
+ __func__, arg_name);
#endif
-
- _wapi_set_last_error_from_errno ();
- return(INVALID_HANDLE_VALUE);
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return NULL;
}
- 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);
+ return utf8_ret;
+}
- /* Apparently input handles can't be written to. (I don't
- * know if output or error handles can't be read from.)
- */
- if (fd == 0) {
- file_handle.fileaccess &= ~GENERIC_WRITE;
+gboolean
+ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
+ const gunichar2 *backupFileName, guint32 replaceFlags,
+ gpointer exclude, gpointer reserved)
+{
+ int result, errno_copy, backup_fd = -1,replaced_fd = -1;
+ gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
+ struct stat stBackup;
+ gboolean ret = FALSE;
+
+ if (!(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;
}
-
- file_handle.sharemode=0;
- file_handle.attrs=0;
- 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);
+ if (utf8_backupFileName) {
+ // Open the backup file for read so we can restore the file if an error occurs.
+ backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
+ result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
+ errno_copy = errno;
+ if (result == -1)
+ goto replace_cleanup;
}
-
-#ifdef DEBUG
- g_message("%s: returning handle %p", __func__, handle);
-#endif
- return(handle);
+ result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
+ errno_copy = errno;
+ if (result == -1) {
+ _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
+ _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
+ if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
+ replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
+ stBackup.st_mode);
+
+ if (replaced_fd == -1)
+ goto replace_cleanup;
+
+ write_file (backup_fd, replaced_fd, &stBackup, FALSE);
+ }
+
+ goto replace_cleanup;
+ }
+
+ ret = TRUE;
+
+replace_cleanup:
+ g_free (utf8_replacedFileName);
+ g_free (utf8_replacementFileName);
+ g_free (utf8_backupFileName);
+ if (backup_fd != -1)
+ close (backup_fd);
+ if (replaced_fd != -1)
+ close (replaced_fd);
+ return ret;
}
/**
(gpointer *)&file_handle);
if (ok == FALSE) {
/* Need to create this console handle */
- handle = stdhandle_create (fd, name);
+ handle = _wapi_stdhandle_create (fd, name);
if (handle == INVALID_HANDLE_VALUE) {
SetLastError (ERROR_NO_MORE_FILES);
return(TRUE);
}
-static gint
-file_compare (gconstpointer a, gconstpointer b)
-{
- gchar *astr = *(gchar **) a;
- gchar *bstr = *(gchar **) b;
-
- return strcmp (astr, bstr);
-}
-
-static gint
-get_errno_from_g_file_error (gint error)
-{
- switch (error) {
-#ifdef EACCESS
- case G_FILE_ERROR_ACCES:
- error = EACCES;
- break;
-#endif
-#ifdef ENAMETOOLONG
- case G_FILE_ERROR_NAMETOOLONG:
- error = ENAMETOOLONG;
- break;
-#endif
-#ifdef ENOENT
- case G_FILE_ERROR_NOENT:
- error = ENOENT;
- break;
-#endif
-#ifdef ENOTDIR
- case G_FILE_ERROR_NOTDIR:
- error = ENOTDIR;
- break;
-#endif
-#ifdef ENXIO
- case G_FILE_ERROR_NXIO:
- error = ENXIO;
- break;
-#endif
-#ifdef ENODEV
- case G_FILE_ERROR_NODEV:
- error = ENODEV;
- break;
-#endif
-#ifdef EROFS
- case G_FILE_ERROR_ROFS:
- error = EROFS;
- break;
-#endif
-#ifdef ETXTBSY
- case G_FILE_ERROR_TXTBSY:
- error = ETXTBSY;
- break;
-#endif
-#ifdef EFAULT
- case G_FILE_ERROR_FAULT:
- error = EFAULT;
- break;
-#endif
-#ifdef ELOOP
- case G_FILE_ERROR_LOOP:
- error = ELOOP;
- break;
-#endif
-#ifdef ENOSPC
- case G_FILE_ERROR_NOSPC:
- error = ENOSPC;
- break;
-#endif
-#ifdef ENOMEM
- case G_FILE_ERROR_NOMEM:
- error = ENOMEM;
- break;
-#endif
-#ifdef EMFILE
- case G_FILE_ERROR_MFILE:
- error = EMFILE;
- break;
-#endif
-#ifdef ENFILE
- case G_FILE_ERROR_NFILE:
- error = ENFILE;
- break;
-#endif
-#ifdef EBADF
- case G_FILE_ERROR_BADF:
- error = EBADF;
- break;
-#endif
-#ifdef EINVAL
- case G_FILE_ERROR_INVAL:
- error = EINVAL;
- break;
-#endif
-#ifdef EPIPE
- case G_FILE_ERROR_PIPE:
- error = EPIPE;
- break;
-#endif
-#ifdef EAGAIN
- case G_FILE_ERROR_AGAIN:
- error = EAGAIN;
- break;
-#endif
-#ifdef EINTR
- case G_FILE_ERROR_INTR:
- error = EINTR;
- break;
-#endif
-#ifdef EWIO
- case G_FILE_ERROR_IO:
- error = EIO;
- break;
-#endif
-#ifdef EPERM
- case G_FILE_ERROR_PERM:
- error = EPERM;
- break;
-#endif
- case G_FILE_ERROR_FAILED:
- error = ERROR_INVALID_PARAMETER;
- break;
- }
-
- return error;
-}
-
-/* scandir using glib */
-static gint
-mono_io_scandir (const gchar *dirname, const gchar *pattern, gchar ***namelist)
-{
- GError *error = NULL;
- GDir *dir;
- GPtrArray *names;
- const gchar *name;
- gint result;
- GPatternSpec *patspec;
-
- dir = _wapi_g_dir_open (dirname, 0, &error);
- if (dir == NULL) {
- /* g_dir_open returns ENOENT on directories on which we don't
- * have read/x permission */
- gint errnum = get_errno_from_g_file_error (error->code);
- g_error_free (error);
- if (errnum == ENOENT &&
- !_wapi_access (dirname, F_OK) &&
- _wapi_access (dirname, R_OK|X_OK)) {
- errnum = EACCES;
- }
-
- errno = errnum;
- return -1;
- }
-
- patspec = g_pattern_spec_new (pattern);
- names = g_ptr_array_new ();
- while ((name = g_dir_read_name (dir)) != NULL) {
- if (g_pattern_match_string (patspec, name))
- g_ptr_array_add (names, g_strdup (name));
- }
-
- g_pattern_spec_free (patspec);
- g_dir_close (dir);
- result = names->len;
- if (result > 0) {
- g_ptr_array_sort (names, file_compare);
- g_ptr_array_set_size (names, result + 1);
-
- *namelist = (gchar **) g_ptr_array_free (names, FALSE);
- } else {
- g_ptr_array_free (names, TRUE);
- }
-
- return result;
-}
-
gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
{
struct _WapiHandle_find find_handle = {0};
*/
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
{
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 (_wapi_lstat (filename, &buf) != 0) {
+
+ result = _wapi_stat (filename, &buf);
+ if (result == -1 && errno == ENOENT) {
+ /* Might be a dangling symlink */
+ result = _wapi_lstat (filename, &buf);
+ }
+
+ if (result != 0) {
#ifdef DEBUG
g_message ("%s: stat failed: %s", __func__, filename);
#endif
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(_wapi_stat (filename, &buf) != 0) {
- g_free (filename);
- goto retry;
- }
+ result = _wapi_lstat (filename, &linkbuf);
+ if (result != 0) {
+#ifdef DEBUG
+ g_message ("%s: lstat failed: %s", __func__, filename);
+#endif
+
+ g_free (filename);
+ goto retry;
}
-
+
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;
}
else
create_time = buf.st_ctime;
- find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf);
+ find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
_wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
_wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
guint32 GetFileAttributes (const gunichar2 *name)
{
gchar *utf8_name;
- struct stat buf;
+ struct stat buf, linkbuf;
int result;
guint32 ret;
g_free (utf8_name);
return (INVALID_FILE_ATTRIBUTES);
}
+
+ 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);
+ }
+
+ ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
- ret = _wapi_stat_to_file_attributes (utf8_name, &buf);
g_free (utf8_name);
return(ret);
gchar *utf8_name;
WapiFileAttributesData *data;
- struct stat buf;
+ struct stat buf, linkbuf;
time_t create_time;
int result;
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;
create_time = buf.st_ctime;
data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name,
- &buf);
+ &buf,
+ &linkbuf);
g_free (utf8_name);
*/
extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
{
- gchar *path;
gunichar2 *utf16_path;
glong count;
gsize bytes;
-
- path = g_get_current_dir ();
- if (path == NULL)
+
+ 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;
+ }
- 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;
}
return(ret);
}
+/* 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';
+}
+
gint32
GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
{
continue;
}
- dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
+ unescape_octal (*(splitted + 1));
+ dir = g_utf8_to_utf16 (*(splitted + 1), -1, NULL, &length, NULL);
g_strfreev (splitted);
if (total + length + 1 > len) {
fclose (fp);
+ g_free (dir);
return len * 2; /* guess */
}
#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;
- 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);
-
+ 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) {
#ifdef DEBUG
- g_message ("%s: fcntl returns %d", __func__, ret);
+ g_message("%s: unicode conversion returned NULL", __func__);
#endif
- if (ret == -1) {
- /*
- * if locks are not available (NFS for example),
- * ignore the error
- */
- if (errno == ENOLCK
-#ifdef EOPNOTSUPP
- || errno == EOPNOTSUPP
-#endif
-#ifdef ENOTSUP
- || errno == ENOTSUP
-#endif
- ) {
- return (TRUE);
+ SetLastError (ERROR_INVALID_NAME);
+ return(FALSE);
}
-
- SetLastError (ERROR_LOCK_VIOLATION);
- return(FALSE);
}
- return(TRUE);
-}
-
-static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
-{
- struct flock lock_data;
- int ret;
-
- lock_data.l_type = F_UNLCK;
- lock_data.l_whence = SEEK_SET;
- lock_data.l_start = offset;
- lock_data.l_len = length;
-
do {
- ret = fcntl (fd, F_SETLK, &lock_data);
- } while(ret == -1 && errno == EINTR);
-
-#ifdef DEBUG
- g_message ("%s: fcntl returns %d", __func__, ret);
+#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
+ _wapi_set_last_error_from_errno ();
+#ifdef DEBUG
+ g_message ("%s: statvfs failed: %s", __func__, strerror (errno));
#endif
- ) {
- return (TRUE);
- }
-
- SetLastError (ERROR_LOCK_VIOLATION);
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);
}
-
-gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
- guint32 length_low, guint32 length_high)
+#else
+gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
+ WapiULargeInteger *total_number_of_bytes,
+ WapiULargeInteger *total_number_of_free_bytes)
{
- struct _WapiHandle_file *file_handle;
- gboolean ok;
- off_t offset, length;
- int fd = GPOINTER_TO_UINT(handle);
-
- ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
- (gpointer *)&file_handle);
- if (ok == FALSE) {
- g_warning ("%s: error looking up file handle %p", __func__,
- handle);
- SetLastError (ERROR_INVALID_HANDLE);
- return(FALSE);
+ if (free_bytes_avail != NULL) {
+ free_bytes_avail->QuadPart = (guint64) -1;
}
- if (!(file_handle->fileaccess & GENERIC_READ) &&
- !(file_handle->fileaccess & GENERIC_WRITE) &&
- !(file_handle->fileaccess & GENERIC_ALL)) {
-#ifdef DEBUG
- g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
-#endif
- SetLastError (ERROR_ACCESS_DENIED);
- return(FALSE);
+ if (total_number_of_bytes != NULL) {
+ total_number_of_bytes->QuadPart = (guint64) -1;
}
-#ifdef HAVE_LARGE_FILE_SUPPORT
- offset = ((gint64)offset_high << 32) | offset_low;
- length = ((gint64)length_high << 32) | length_low;
+ if (total_number_of_free_bytes != NULL) {
+ total_number_of_free_bytes->QuadPart = (guint64) -1;
+ }
-#ifdef DEBUG
- g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
- handle, offset, length);
+ return(TRUE);
+}
#endif
-#else
- offset = offset_low;
- length = length_low;
-#ifdef DEBUG
- g_message ("%s: Locking handle %p, offset %ld, length %ld", __func__,
- handle, offset, length);
-#endif
-#endif
+typedef struct {
+ guint32 drive_type;
+ const gchar* fstype;
+} _wapi_drive_type;
+
+static _wapi_drive_type _wapi_drive_types[] = {
+ { 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 }
+};
- return(_wapi_lock_file_region (fd, offset, length));
+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++;
+ }
+
+ return current->drive_type;
}
-gboolean UnlockFile (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);
+ FILE *fp;
+ gchar buffer [512];
+ gchar **splitted;
+ gchar *utf8_root_path_name;
+ guint32 drive_type;
+
+ if (root_path_name == NULL) {
+ utf8_root_path_name = g_strdup (g_get_current_dir());
+ if (utf8_root_path_name == NULL) {
+ return(DRIVE_NO_ROOT_DIR);
+ }
}
-
- if (!(file_handle->fileaccess & GENERIC_READ) &&
- !(file_handle->fileaccess & GENERIC_WRITE) &&
- !(file_handle->fileaccess & GENERIC_ALL)) {
+ else {
+ utf8_root_path_name = mono_unicode_to_external (root_path_name);
+ if (utf8_root_path_name == NULL) {
#ifdef DEBUG
- g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
+ g_message("%s: unicode conversion returned NULL", __func__);
#endif
- SetLastError (ERROR_ACCESS_DENIED);
- return(FALSE);
+ return(DRIVE_NO_ROOT_DIR);
+ }
+
+ /* strip trailing slash for compare below */
+ if (g_str_has_suffix(utf8_root_path_name, "/")) {
+ utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
+ }
}
-#ifdef HAVE_LARGE_FILE_SUPPORT
- offset = ((gint64)offset_high << 32) | offset_low;
- length = ((gint64)length_high << 32) | length_low;
+ fp = fopen ("/etc/mtab", "rt");
+ if (fp == NULL) {
+ fp = fopen ("/etc/mnttab", "rt");
+ if (fp == NULL) {
+ g_free (utf8_root_path_name);
+ return(DRIVE_UNKNOWN);
+ }
+ }
-#ifdef DEBUG
- g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
- __func__, handle, offset, length);
-#endif
-#else
- offset = offset_low;
- length = length_low;
+ drive_type = DRIVE_NO_ROOT_DIR;
+ while (fgets (buffer, 512, fp) != NULL) {
+ splitted = g_strsplit (buffer, " ", 0);
+ if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
+ g_strfreev (splitted);
+ continue;
+ }
-#ifdef DEBUG
- g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
- handle, offset, length);
-#endif
-#endif
+ /* 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));
+ g_strfreev (splitted);
+ break;
+ }
- return(_wapi_unlock_file_region (fd, offset, length));
+ g_strfreev (splitted);
+ }
+
+ fclose (fp);
+ g_free (utf8_root_path_name);
+
+ return (drive_type);
}
+