Merge pull request #2994 from ludovic-henry/remove-nacl
[mono.git] / mono / utils / mono-io-portability.c
index e8e6c3fd0b92e204f99381789b56d590932d198e..740db4c7543cb72bc2b8f1d6c68775849bb35cbd 100644 (file)
@@ -1,19 +1,30 @@
 #include "config.h"
 
-#include <glib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
 #include <mono/utils/mono-io-portability.h>
+#include <mono/metadata/profiler-private.h>
+
+#ifndef DISABLE_PORTABILITY
+
+#include <dirent.h>
 
-int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
+int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
+
+static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
 
 void mono_portability_helpers_init (void)
 {
         const gchar *env;
 
-       if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
+       if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
                return;
        
-        __mono_io_portability_helpers = PORTABILITY_NONE;
-        
+        mono_io_portability_helpers = PORTABILITY_NONE;
+       
         env = g_getenv ("MONO_IOMAP");
         if (env != NULL) {
                 /* parse the environment setting and set up some vars
@@ -33,13 +44,351 @@ void mono_portability_helpers_init (void)
                                    options[i]);
 #endif
                         if (!strncasecmp (options[i], "drive", 5)) {
-                                __mono_io_portability_helpers |= PORTABILITY_DRIVE;
+                                mono_io_portability_helpers |= PORTABILITY_DRIVE;
                         } else if (!strncasecmp (options[i], "case", 4)) {
-                                __mono_io_portability_helpers |= PORTABILITY_CASE;
+                                mono_io_portability_helpers |= PORTABILITY_CASE;
                         } else if (!strncasecmp (options[i], "all", 3)) {
-                                __mono_io_portability_helpers |= (PORTABILITY_DRIVE |
-                                                                 PORTABILITY_CASE);
-                        }
+                                mono_io_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);
+}
+
+static inline void append_report (GString **report, const gchar *format, ...)
+{
+#if defined (_EGLIB_MAJOR) || GLIB_CHECK_VERSION(2,14,0)
+       va_list ap;
+       if (!*report)
+               *report = g_string_new ("");
+
+       va_start (ap, format);
+       g_string_append_vprintf (*report, format, ap);
+       va_end (ap);
+#else
+       g_assert_not_reached ();
+#endif
+}
+
+static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
+{
+       char *rep = NULL;
+       GString *tmp = report ? *report : NULL;
+
+       if (tmp) {
+               if (tmp->len > 0)
+                       rep = g_string_free (tmp, FALSE);
+               else
+                       g_string_free (tmp, TRUE);
+               *report = NULL;
+       }
+
+       mono_profiler_iomap (rep, pathname, new_pathname);
+       g_free (rep);
+}
+
+gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
+{
+       GString *report = NULL;
+       gchar *ret;
+       
+       if (!pathname || !pathname [0])
+               return NULL;
+       ret = mono_portability_find_file_internal (&report, pathname, last_exists);
+
+       if (report)
+               g_string_free (report, TRUE);
+
+       return ret;
+}
+
+/* Returns newly-allocated string or NULL on failure */
+static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
+{
+       gchar *new_pathname, **components, **new_components;
+       int num_components = 0, component = 0;
+       DIR *scanning = NULL;
+       size_t len;
+       gboolean drive_stripped = FALSE;
+       gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
+
+       if (IS_PORTABILITY_NONE) {
+               return(NULL);
+       }
+
+       if (do_report)
+               append_report (report, " - Requested file path: '%s'\n", pathname);
+
+       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 (IS_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';
+
+               if (do_report) {
+                       append_report (report, " - Stripped drive letter.\n");
+                       drive_stripped = TRUE;
+               }
+#ifdef DEBUG
+               g_message ("%s: Stripped drive letter, now looking for [%s]\n",
+                          __func__, new_pathname);
+#endif
+       }
+
+       len = strlen (new_pathname);
+       if (len > 1 && new_pathname [len - 1] == '/') {
+               new_pathname [len - 1] = 0;
+#ifdef DEBUG
+               g_message ("%s: requested name had a trailing /, rewritten to '%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
+               if (do_report && drive_stripped)
+                       do_mono_profiler_iomap (report, pathname, new_pathname);
+
+               return(new_pathname);
+       }
+
+       /* OK, have to work harder.  Take each path component in turn
+        * and do a case-insensitive directory scan for it
+        */
+
+       if (!(IS_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_free (new_pathname);
+       
+       if (num_components == 0){
+               return NULL;
+       }
+       
+
+       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)) {
+               if (do_report && strcmp (pathname, new_pathname) != 0)
+                       do_mono_profiler_iomap (report, pathname, new_pathname);
+
+               return(new_pathname);
+       }
+
+       g_free (new_pathname);
+       return(NULL);
 }
+#endif