1 /**
2 * \file
3 */
4
5 #include "config.h"
6
7 #include <string.h>
8 #ifdef HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11 #include <errno.h>
12 #include <mono/utils/mono-io-portability.h>
13 #include <mono/metadata/profiler-private.h>
14 #include <mono/utils/mono-compiler.h>
15
16 #ifndef DISABLE_PORTABILITY
17
18 #include <dirent.h>
19
20 int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
21
22 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
23
mono_portability_helpers_init(void)24 void mono_portability_helpers_init (void)
25 {
26 gchar *env;
27
28 if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
29 return;
30
31 mono_io_portability_helpers = PORTABILITY_NONE;
32
33 env = g_getenv ("MONO_IOMAP");
34 if (env != NULL) {
35 /* parse the environment setting and set up some vars
36 * here
37 */
38 gchar **options = g_strsplit (env, ":", 0);
39 int i;
40
41 if (options == NULL) {
42 /* This shouldn't happen */
43 return;
44 }
45
46 for (i = 0; options[i] != NULL; i++) {
47 #ifdef DEBUG
48 g_message ("%s: Setting option [%s]", __func__,
49 options[i]);
50 #endif
51 if (!strncasecmp (options[i], "drive", 5)) {
52 mono_io_portability_helpers |= PORTABILITY_DRIVE;
53 } else if (!strncasecmp (options[i], "case", 4)) {
54 mono_io_portability_helpers |= PORTABILITY_CASE;
55 } else if (!strncasecmp (options[i], "all", 3)) {
56 mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
57 }
58 }
59 g_free (env);
60 }
61 }
62
63 /* Returns newly allocated string, or NULL on failure */
find_in_dir(DIR * current,const gchar * name)64 static gchar *find_in_dir (DIR *current, const gchar *name)
65 {
66 struct dirent *entry;
67
68 #ifdef DEBUG
69 g_message ("%s: looking for [%s]\n", __func__, name);
70 #endif
71
72 while((entry = readdir (current)) != NULL) {
73 #ifdef DEBUGX
74 g_message ("%s: found [%s]\n", __func__, entry->d_name);
75 #endif
76
77 if (!g_ascii_strcasecmp (name, entry->d_name)) {
78 char *ret;
79
80 #ifdef DEBUG
81 g_message ("%s: matched [%s] to [%s]\n", __func__,
82 entry->d_name, name);
83 #endif
84
85 ret = g_strdup (entry->d_name);
86 closedir (current);
87 return ret;
88 }
89 }
90
91 #ifdef DEBUG
92 g_message ("%s: returning NULL\n", __func__);
93 #endif
94
95 closedir (current);
96
97 return(NULL);
98 }
99
append_report(GString ** report,const gchar * format,...)100 static inline void append_report (GString **report, const gchar *format, ...)
101 {
102 va_list ap;
103 if (!*report)
104 *report = g_string_new ("");
105
106 va_start (ap, format);
107 g_string_append_vprintf (*report, format, ap);
108 va_end (ap);
109 }
110
do_mono_profiler_iomap(GString ** report,const char * pathname,const char * new_pathname)111 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
112 {
113 char *rep = NULL;
114 GString *tmp = report ? *report : NULL;
115
116 if (tmp) {
117 if (tmp->len > 0)
118 rep = g_string_free (tmp, FALSE);
119 else
120 g_string_free (tmp, TRUE);
121 *report = NULL;
122 }
123
124 MONO_PROFILER_RAISE (iomap_report, (rep, pathname, new_pathname));
125 g_free (rep);
126 }
127
mono_portability_find_file(const gchar * pathname,gboolean last_exists)128 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
129 {
130 GString *report = NULL;
131 gchar *ret;
132
133 if (!pathname || !pathname [0])
134 return NULL;
135 ret = mono_portability_find_file_internal (&report, pathname, last_exists);
136
137 if (report)
138 g_string_free (report, TRUE);
139
140 return ret;
141 }
142
143 /* Returns newly-allocated string or NULL on failure */
mono_portability_find_file_internal(GString ** report,const gchar * pathname,gboolean last_exists)144 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
145 {
146 gchar *new_pathname, **components, **new_components;
147 int num_components = 0, component = 0;
148 DIR *scanning = NULL;
149 size_t len;
150 gboolean drive_stripped = FALSE;
151 gboolean do_report = MONO_PROFILER_ENABLED (iomap_report);
152
153 if (IS_PORTABILITY_NONE) {
154 return(NULL);
155 }
156
157 if (do_report)
158 append_report (report, " - Requested file path: '%s'\n", pathname);
159
160 new_pathname = g_strdup (pathname);
161
162 #ifdef DEBUG
163 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
164 last_exists?"TRUE":"FALSE");
165 #endif
166
167 if (last_exists &&
168 access (new_pathname, F_OK) == 0) {
169 #ifdef DEBUG
170 g_message ("%s: Found it without doing anything\n", __func__);
171 #endif
172 return(new_pathname);
173 }
174
175 /* First turn '\' into '/' and strip any drive letters */
176 g_strdelimit (new_pathname, "\\", '/');
177
178 #ifdef DEBUG
179 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
180 new_pathname);
181 #endif
182
183 if (IS_PORTABILITY_DRIVE &&
184 g_ascii_isalpha (new_pathname[0]) &&
185 (new_pathname[1] == ':')) {
186 int len = strlen (new_pathname);
187
188 g_memmove (new_pathname, new_pathname+2, len - 2);
189 new_pathname[len - 2] = '\0';
190
191 if (do_report) {
192 append_report (report, " - Stripped drive letter.\n");
193 drive_stripped = TRUE;
194 }
195 #ifdef DEBUG
196 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
197 __func__, new_pathname);
198 #endif
199 }
200
201 len = strlen (new_pathname);
202 if (len > 1 && new_pathname [len - 1] == '/') {
203 new_pathname [len - 1] = 0;
204 #ifdef DEBUG
205 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
206 __func__, new_pathname);
207 #endif
208 }
209
210 if (last_exists &&
211 access (new_pathname, F_OK) == 0) {
212 #ifdef DEBUG
213 g_message ("%s: Found it\n", __func__);
214 #endif
215 if (do_report && drive_stripped)
216 do_mono_profiler_iomap (report, pathname, new_pathname);
217
218 return(new_pathname);
219 }
220
221 /* OK, have to work harder. Take each path component in turn
222 * and do a case-insensitive directory scan for it
223 */
224
225 if (!(IS_PORTABILITY_CASE)) {
226 g_free (new_pathname);
227 return(NULL);
228 }
229
230 components = g_strsplit (new_pathname, "/", 0);
231 if (components == NULL) {
232 /* This shouldn't happen */
233 g_free (new_pathname);
234 return(NULL);
235 }
236
237 while(components[num_components] != NULL) {
238 num_components++;
239 }
240 g_free (new_pathname);
241
242 if (num_components == 0){
243 return NULL;
244 }
245
246
247 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
248
249 if (num_components > 1) {
250 if (strcmp (components[0], "") == 0) {
251 /* first component blank, so start at / */
252 scanning = opendir ("/");
253 if (scanning == NULL) {
254 #ifdef DEBUG
255 g_message ("%s: opendir 1 error: %s", __func__,
256 g_strerror (errno));
257 #endif
258 g_strfreev (new_components);
259 g_strfreev (components);
260 return(NULL);
261 }
262
263 new_components[component++] = g_strdup ("");
264 } else {
265 DIR *current;
266 gchar *entry;
267
268 current = opendir (".");
269 if (current == NULL) {
270 #ifdef DEBUG
271 g_message ("%s: opendir 2 error: %s", __func__,
272 g_strerror (errno));
273 #endif
274 g_strfreev (new_components);
275 g_strfreev (components);
276 return(NULL);
277 }
278
279 entry = find_in_dir (current, components[0]);
280 if (entry == NULL) {
281 g_strfreev (new_components);
282 g_strfreev (components);
283 return(NULL);
284 }
285
286 scanning = opendir (entry);
287 if (scanning == NULL) {
288 #ifdef DEBUG
289 g_message ("%s: opendir 3 error: %s", __func__,
290 g_strerror (errno));
291 #endif
292 g_free (entry);
293 g_strfreev (new_components);
294 g_strfreev (components);
295 return(NULL);
296 }
297
298 new_components[component++] = entry;
299 }
300 } else {
301 if (last_exists) {
302 if (strcmp (components[0], "") == 0) {
303 /* First and only component blank */
304 new_components[component++] = g_strdup ("");
305 } else {
306 DIR *current;
307 gchar *entry;
308
309 current = opendir (".");
310 if (current == NULL) {
311 #ifdef DEBUG
312 g_message ("%s: opendir 4 error: %s",
313 __func__,
314 g_strerror (errno));
315 #endif
316 g_strfreev (new_components);
317 g_strfreev (components);
318 return(NULL);
319 }
320
321 entry = find_in_dir (current, components[0]);
322 if (entry == NULL) {
323 g_strfreev (new_components);
324 g_strfreev (components);
325 return(NULL);
326 }
327
328 new_components[component++] = entry;
329 }
330 } else {
331 new_components[component++] = g_strdup (components[0]);
332 }
333 }
334
335 #ifdef DEBUG
336 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
337 #endif
338
339 g_assert (component == 1);
340
341 for(; component < num_components; component++) {
342 gchar *entry;
343 gchar *path_so_far;
344
345 if (!last_exists &&
346 component == num_components -1) {
347 entry = g_strdup (components[component]);
348 closedir (scanning);
349 } else {
350 entry = find_in_dir (scanning, components[component]);
351 if (entry == NULL) {
352 g_strfreev (new_components);
353 g_strfreev (components);
354 return(NULL);
355 }
356 }
357
358 new_components[component] = entry;
359
360 if (component < num_components -1) {
361 path_so_far = g_strjoinv ("/", new_components);
362
363 scanning = opendir (path_so_far);
364 g_free (path_so_far);
365 if (scanning == NULL) {
366 g_strfreev (new_components);
367 g_strfreev (components);
368 return(NULL);
369 }
370 }
371 }
372
373 g_strfreev (components);
374
375 new_pathname = g_strjoinv ("/", new_components);
376
377 #ifdef DEBUG
378 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
379 new_pathname);
380 #endif
381
382 g_strfreev (new_components);
383
384 if ((last_exists &&
385 access (new_pathname, F_OK) == 0) ||
386 (!last_exists)) {
387 if (do_report && strcmp (pathname, new_pathname) != 0)
388 do_mono_profiler_iomap (report, pathname, new_pathname);
389
390 return(new_pathname);
391 }
392
393 g_free (new_pathname);
394 return(NULL);
395 }
396
397 #else /* DISABLE_PORTABILITY */
398
399 MONO_EMPTY_SOURCE_FILE (mono_io_portability);
400
401 #endif /* DISABLE_PORTABILITY */
402