#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>
#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,
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 (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) {
g_free (utf8_dest_name);
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 */
{
gchar *utf8_src, *utf8_dest;
int src_fd, dest_fd;
- struct stat st;
+ struct stat st, dest_st;
gboolean ret = TRUE;
if(name==NULL) {
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
_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 = open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
- stBackup.st_mode);
- if (replaced_fd == -1) {
- replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT,
- stBackup.st_mode);
- }
+ replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
+ stBackup.st_mode);
+
if (replaced_fd == -1)
goto replace_cleanup;
return ret;
}
-static gpointer stdhandle_create (int fd, const gchar *name)
-{
- 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
-
- _wapi_set_last_error_from_errno ();
- return(INVALID_HANDLE_VALUE);
- }
-
- 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);
-
- /* 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;
- }
-
- 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);
- }
-
-#ifdef DEBUG
- g_message("%s: returning handle %p", __func__, handle);
-#endif
-
- return(handle);
-}
-
/**
* GetStdHandle:
* @stdhandle: specifies the file descriptor
(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);
{
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;
}
+ 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
else
create_time = buf.st_ctime;
- find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &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);
*/
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;
}
#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;
- 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);
+#elif defined(HAVE_STATFS)
+ ret = statfs (utf8_path_name, &fsstat);
+ isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
#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 = fsstat.f_bsize * fsstat.f_bavail;
+ }
+ }
+
+ /* total number of bytes available for non-root */
+ if (total_number_of_bytes != NULL) {
+ total_number_of_bytes->QuadPart = fsstat.f_bsize * 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 = fsstat.f_bsize * 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);
}
+