X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fio-layer%2Fio-portability.c;h=fa7697528d5d8c107f02e596a150752b11988ef6;hb=7c539954696baa007b3b5b006898fd3ccb889454;hp=f739b1fac770519ed07ca6587689171836d256ea;hpb=987f8c63e214937c50dcb308149f7558a2cbba41;p=mono.git diff --git a/mono/io-layer/io-portability.c b/mono/io-layer/io-portability.c index f739b1fac77..fa7697528d5 100644 --- a/mono/io-layer/io-portability.c +++ b/mono/io-layer/io-portability.c @@ -18,334 +18,28 @@ #include #include #include -#include +#include +#ifdef HAVE_DIRENT_H +# include +#endif #include #include #include +#include +#include #include +#include #undef DEBUG -enum { - PORTABILITY_NONE = 0x00, - PORTABILITY_UNKNOWN = 0x01, - PORTABILITY_DRIVE = 0x02, - PORTABILITY_CASE = 0x04, -}; - -static mono_once_t options_once = MONO_ONCE_INIT; -static int portability_helpers = PORTABILITY_UNKNOWN; - -static void options_init (void) -{ - const gchar *env; - - portability_helpers = PORTABILITY_NONE; - - env = g_getenv ("MONO_IOMAP"); - if (env != NULL) { - /* parse the environment setting and set up some vars - * here - */ - gchar **options = g_strsplit (env, ":", 0); - int i; - - if (options == NULL) { - /* This shouldn't happen */ - return; - } - - for (i = 0; options[i] != NULL; i++) { -#ifdef DEBUG - g_message ("%s: Setting option [%s]", __func__, - options[i]); -#endif - if (!strncasecmp (options[i], "drive", 5)) { - portability_helpers |= PORTABILITY_DRIVE; - } else if (!strncasecmp (options[i], "case", 4)) { - portability_helpers |= PORTABILITY_CASE; - } else if (!strncasecmp (options[i], "all", 3)) { - portability_helpers |= (PORTABILITY_DRIVE | - PORTABILITY_CASE); - } - } - } -} - -/* Returns newly allocated string, or NULL on failure */ -static gchar *find_in_dir (DIR *current, const gchar *name) -{ - struct dirent *entry; - -#ifdef DEBUG - g_message ("%s: looking for [%s]\n", __func__, name); -#endif - - while((entry = readdir (current)) != NULL) { -#ifdef DEBUGX - g_message ("%s: found [%s]\n", __func__, entry->d_name); -#endif - - if (!g_ascii_strcasecmp (name, entry->d_name)) { - char *ret; - -#ifdef DEBUG - g_message ("%s: matched [%s] to [%s]\n", __func__, - entry->d_name, name); -#endif - - ret = g_strdup (entry->d_name); - closedir (current); - return ret; - } - } - -#ifdef DEBUG - g_message ("%s: returning NULL\n", __func__); -#endif - - closedir (current); - - return(NULL); -} - -/* Returns newly-allocated string or NULL on failure */ -static gchar *find_file (const gchar *pathname, gboolean last_exists) -{ - gchar *new_pathname, **components, **new_components; - int num_components = 0, component = 0; - DIR *scanning; - - mono_once (&options_once, options_init); - - if (portability_helpers == PORTABILITY_NONE) { - return(NULL); - } - - new_pathname = g_strdup (pathname); - -#ifdef DEBUG - g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname, - last_exists?"TRUE":"FALSE"); -#endif - - if (last_exists && - access (new_pathname, F_OK) == 0) { -#ifdef DEBUG - g_message ("%s: Found it without doing anything\n", __func__); -#endif - return(new_pathname); - } - - /* First turn '\' into '/' and strip any drive letters */ - g_strdelimit (new_pathname, "\\", '/'); - -#ifdef DEBUG - g_message ("%s: Fixed slashes, now have [%s]\n", __func__, - new_pathname); -#endif - - if (portability_helpers & PORTABILITY_DRIVE && - g_ascii_isalpha (new_pathname[0]) && - (new_pathname[1] == ':')) { - int len = strlen (new_pathname); - - g_memmove (new_pathname, new_pathname+2, len - 2); - new_pathname[len - 2] = '\0'; - -#ifdef DEBUG - g_message ("%s: Stripped drive letter, now looking for [%s]\n", - __func__, new_pathname); -#endif - } - - if (last_exists && - access (new_pathname, F_OK) == 0) { -#ifdef DEBUG - g_message ("%s: Found it\n", __func__); -#endif - - return(new_pathname); - } - - /* OK, have to work harder. Take each path component in turn - * and do a case-insensitive directory scan for it - */ - - if (!(portability_helpers & PORTABILITY_CASE)) { - g_free (new_pathname); - return(NULL); - } - - components = g_strsplit (new_pathname, "/", 0); - if (components == NULL) { - /* This shouldn't happen */ - g_free (new_pathname); - return(NULL); - } - - while(components[num_components] != NULL) { - num_components++; - } - g_assert (num_components > 0); - - g_free (new_pathname); - - new_components = (gchar **)g_new0 (gchar **, num_components + 1); - - if (num_components > 1) { - if (strcmp (components[0], "") == 0) { - /* first component blank, so start at / */ - scanning = opendir ("/"); - if (scanning == NULL) { -#ifdef DEBUG - g_message ("%s: opendir 1 error: %s", __func__, - g_strerror (errno)); -#endif - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - new_components[component++] = g_strdup (""); - } else { - DIR *current; - gchar *entry; - - current = opendir ("."); - if (current == NULL) { -#ifdef DEBUG - g_message ("%s: opendir 2 error: %s", __func__, - g_strerror (errno)); -#endif - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - entry = find_in_dir (current, components[0]); - if (entry == NULL) { - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - scanning = opendir (entry); - if (scanning == NULL) { -#ifdef DEBUG - g_message ("%s: opendir 3 error: %s", __func__, - g_strerror (errno)); -#endif - g_free (entry); - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - new_components[component++] = entry; - } - } else { - if (last_exists) { - if (strcmp (components[0], "") == 0) { - /* First and only component blank */ - new_components[component++] = g_strdup (""); - } else { - DIR *current; - gchar *entry; - - current = opendir ("."); - if (current == NULL) { -#ifdef DEBUG - g_message ("%s: opendir 4 error: %s", - __func__, - g_strerror (errno)); -#endif - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - entry = find_in_dir (current, components[0]); - if (entry == NULL) { - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - - new_components[component++] = entry; - } - } else { - new_components[component++] = g_strdup (components[0]); - } - } - -#ifdef DEBUG - g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]); -#endif - - g_assert (component == 1); - - for(; component < num_components; component++) { - gchar *entry; - gchar *path_so_far; - - if (!last_exists && - component == num_components -1) { - entry = g_strdup (components[component]); - closedir (scanning); - } else { - entry = find_in_dir (scanning, components[component]); - if (entry == NULL) { - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - } - - new_components[component] = entry; - - if (component < num_components -1) { - path_so_far = g_strjoinv ("/", new_components); - - scanning = opendir (path_so_far); - g_free (path_so_far); - if (scanning == NULL) { - g_strfreev (new_components); - g_strfreev (components); - return(NULL); - } - } - } - - g_strfreev (components); - - new_pathname = g_strjoinv ("/", new_components); - -#ifdef DEBUG - g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname, - new_pathname); -#endif - - g_strfreev (new_components); - - if ((last_exists && - access (new_pathname, F_OK) == 0) || - (!last_exists)) { - return(new_pathname); - } - - g_free (new_pathname); - return(NULL); -} - int _wapi_open (const char *pathname, int flags, mode_t mode) { int fd; gchar *located_filename; if (flags & O_CREAT) { - located_filename = find_file (pathname, FALSE); + located_filename = mono_portability_find_file (pathname, FALSE); if (located_filename == NULL) { fd = open (pathname, flags, mode); } else { @@ -356,9 +50,9 @@ int _wapi_open (const char *pathname, int flags, mode_t mode) fd = open (pathname, flags, mode); if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - located_filename = find_file (pathname, TRUE); + located_filename = mono_portability_find_file (pathname, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -381,9 +75,9 @@ int _wapi_access (const char *pathname, int mode) ret = access (pathname, mode); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (pathname, TRUE); + gchar *located_filename = mono_portability_find_file (pathname, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -404,9 +98,9 @@ int _wapi_chmod (const char *pathname, mode_t mode) ret = chmod (pathname, mode); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (pathname, TRUE); + gchar *located_filename = mono_portability_find_file (pathname, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -427,9 +121,9 @@ int _wapi_utime (const char *filename, const struct utimbuf *buf) ret = utime (filename, buf); if (ret == -1 && errno == ENOENT && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (filename, TRUE); + gchar *located_filename = mono_portability_find_file (filename, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -450,9 +144,9 @@ int _wapi_unlink (const char *pathname) ret = unlink (pathname); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (pathname, TRUE); + gchar *located_filename = mono_portability_find_file (pathname, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -469,7 +163,7 @@ int _wapi_unlink (const char *pathname) int _wapi_rename (const char *oldpath, const char *newpath) { int ret; - gchar *located_newpath = find_file (newpath, FALSE); + gchar *located_newpath = mono_portability_find_file (newpath, FALSE); if (located_newpath == NULL) { ret = rename (oldpath, newpath); @@ -479,9 +173,9 @@ int _wapi_rename (const char *oldpath, const char *newpath) if (ret == -1 && (errno == EISDIR || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EXDEV) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_oldpath = find_file (oldpath, TRUE); + gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE); if (located_oldpath == NULL) { g_free (located_oldpath); @@ -507,9 +201,9 @@ int _wapi_stat (const char *path, struct stat *buf) ret = stat (path, buf); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (path, TRUE); + gchar *located_filename = mono_portability_find_file (path, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -530,9 +224,9 @@ int _wapi_lstat (const char *path, struct stat *buf) ret = lstat (path, buf); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (path, TRUE); + gchar *located_filename = mono_portability_find_file (path, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -549,7 +243,7 @@ int _wapi_lstat (const char *path, struct stat *buf) int _wapi_mkdir (const char *pathname, mode_t mode) { int ret; - gchar *located_filename = find_file (pathname, FALSE); + gchar *located_filename = mono_portability_find_file (pathname, FALSE); if (located_filename == NULL) { ret = mkdir (pathname, mode); @@ -568,9 +262,9 @@ int _wapi_rmdir (const char *pathname) ret = rmdir (pathname); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (pathname, TRUE); + gchar *located_filename = mono_portability_find_file (pathname, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -591,9 +285,9 @@ int _wapi_chdir (const char *path) ret = chdir (path); if (ret == -1 && (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) && - portability_helpers > 0) { + IS_PORTABILITY_SET) { int saved_errno = errno; - gchar *located_filename = find_file (path, TRUE); + gchar *located_filename = mono_portability_find_file (path, TRUE); if (located_filename == NULL) { errno = saved_errno; @@ -611,11 +305,11 @@ gchar *_wapi_basename (const gchar *filename) { gchar *new_filename = g_strdup (filename), *ret; - mono_once (&options_once, options_init); - - g_strdelimit (new_filename, "\\", '/'); + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } - if (portability_helpers & PORTABILITY_DRIVE && + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { int len = strlen (new_filename); @@ -634,11 +328,11 @@ gchar *_wapi_dirname (const gchar *filename) { gchar *new_filename = g_strdup (filename), *ret; - mono_once (&options_once, options_init); - - g_strdelimit (new_filename, "\\", '/'); + if (IS_PORTABILITY_SET) { + g_strdelimit (new_filename, "\\", '/'); + } - if (portability_helpers & PORTABILITY_DRIVE && + if (IS_PORTABILITY_DRIVE && g_ascii_isalpha (new_filename[0]) && (new_filename[1] == ':')) { int len = strlen (new_filename); @@ -662,9 +356,9 @@ GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error) ((*error)->code == G_FILE_ERROR_NOENT || (*error)->code == G_FILE_ERROR_NOTDIR || (*error)->code == G_FILE_ERROR_NAMETOOLONG) && - portability_helpers > 0) { - gchar *located_filename = find_file (path, TRUE); - GError *tmp_error; + IS_PORTABILITY_SET) { + gchar *located_filename = mono_portability_find_file (path, TRUE); + GError *tmp_error = NULL; if (located_filename == NULL) { return(NULL); @@ -672,10 +366,216 @@ GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error) ret = g_dir_open (located_filename, flags, &tmp_error); g_free (located_filename); - if (tmp_error != NULL) { - g_propagate_error (error, tmp_error); + if (tmp_error == NULL) { + g_clear_error (error); } } return(ret); } + + +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 */ +gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern, + gchar ***namelist) +{ + GError *error = NULL; + GDir *dir; + GPtrArray *names; + gint result; + wapi_glob_t glob_buf; + int flags = 0, i; + + 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; + } + + if (IS_PORTABILITY_CASE) { + flags = WAPI_GLOB_IGNORECASE; + } + + result = _wapi_glob (dir, pattern, flags, &glob_buf); + if (g_str_has_suffix (pattern, ".*")) { + /* Special-case the patterns ending in '.*', as + * windows also matches entries with no extension with + * this pattern. + * + * TODO: should this be a MONO_IOMAP option? + */ + gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2); + gint result2; + + g_dir_rewind (dir); + result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf); + + g_free (pattern2); + + if (result != 0) { + result = result2; + } + } + + g_dir_close (dir); + if (glob_buf.gl_pathc == 0) { + return(0); + } else if (result != 0) { + return(-1); + } + + names = g_ptr_array_new (); + for (i = 0; i < glob_buf.gl_pathc; i++) { + g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i])); + } + + _wapi_globfree (&glob_buf); + + 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; +}