2010-02-13 Jb Evain <jbevain@novell.com>
[mono-project.git] / mono / io-layer / io-portability.c
blobee56ec26e2179944d20a97f398fd33249ed5f857
1 /*
2 * io-portability.c: Optional filename mangling to try to cope with
3 * badly-written non-portable windows apps
5 * Author:
6 * Dick Porter (dick@ximian.com)
8 * Copyright (c) 2006 Novell, Inc.
9 */
11 #include <config.h>
12 #include <glib.h>
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/time.h>
22 #ifdef HAVE_DIRENT_H
23 # include <dirent.h>
24 #endif
25 #include <utime.h>
26 #include <sys/stat.h>
28 #include <mono/io-layer/mono-mutex.h>
29 #include <mono/io-layer/error.h>
30 #include <mono/io-layer/wapi_glob.h>
31 #include <mono/io-layer/io-portability.h>
32 #include <mono/utils/mono-io-portability.h>
33 #undef DEBUG
35 int _wapi_open (const char *pathname, int flags, mode_t mode)
37 int fd;
38 gchar *located_filename;
40 if (flags & O_CREAT) {
41 located_filename = mono_portability_find_file (pathname, FALSE);
42 if (located_filename == NULL) {
43 fd = open (pathname, flags, mode);
44 } else {
45 fd = open (located_filename, flags, mode);
46 g_free (located_filename);
48 } else {
49 fd = open (pathname, flags, mode);
50 if (fd == -1 &&
51 (errno == ENOENT || errno == ENOTDIR) &&
52 IS_PORTABILITY_SET) {
53 int saved_errno = errno;
54 located_filename = mono_portability_find_file (pathname, TRUE);
56 if (located_filename == NULL) {
57 errno = saved_errno;
58 return (-1);
61 fd = open (located_filename, flags, mode);
62 g_free (located_filename);
67 return(fd);
70 int _wapi_access (const char *pathname, int mode)
72 int ret;
74 ret = access (pathname, mode);
75 if (ret == -1 &&
76 (errno == ENOENT || errno == ENOTDIR) &&
77 IS_PORTABILITY_SET) {
78 int saved_errno = errno;
79 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
81 if (located_filename == NULL) {
82 errno = saved_errno;
83 return(-1);
86 ret = access (located_filename, mode);
87 g_free (located_filename);
90 return(ret);
93 int _wapi_chmod (const char *pathname, mode_t mode)
95 int ret;
97 ret = chmod (pathname, mode);
98 if (ret == -1 &&
99 (errno == ENOENT || errno == ENOTDIR) &&
100 IS_PORTABILITY_SET) {
101 int saved_errno = errno;
102 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
104 if (located_filename == NULL) {
105 errno = saved_errno;
106 return(-1);
109 ret = chmod (located_filename, mode);
110 g_free (located_filename);
113 return(ret);
116 int _wapi_utime (const char *filename, const struct utimbuf *buf)
118 int ret;
120 ret = utime (filename, buf);
121 if (ret == -1 &&
122 errno == ENOENT &&
123 IS_PORTABILITY_SET) {
124 int saved_errno = errno;
125 gchar *located_filename = mono_portability_find_file (filename, TRUE);
127 if (located_filename == NULL) {
128 errno = saved_errno;
129 return(-1);
132 ret = utime (located_filename, buf);
133 g_free (located_filename);
136 return(ret);
139 int _wapi_unlink (const char *pathname)
141 int ret;
143 ret = unlink (pathname);
144 if (ret == -1 &&
145 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
146 IS_PORTABILITY_SET) {
147 int saved_errno = errno;
148 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
150 if (located_filename == NULL) {
151 errno = saved_errno;
152 return(-1);
155 ret = unlink (located_filename);
156 g_free (located_filename);
159 return(ret);
162 int _wapi_rename (const char *oldpath, const char *newpath)
164 int ret;
165 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
167 if (located_newpath == NULL) {
168 ret = rename (oldpath, newpath);
169 } else {
170 ret = rename (oldpath, located_newpath);
172 if (ret == -1 &&
173 (errno == EISDIR || errno == ENAMETOOLONG ||
174 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
175 IS_PORTABILITY_SET) {
176 int saved_errno = errno;
177 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
179 if (located_oldpath == NULL) {
180 g_free (located_oldpath);
181 g_free (located_newpath);
183 errno = saved_errno;
184 return(-1);
187 ret = rename (located_oldpath, located_newpath);
188 g_free (located_oldpath);
190 g_free (located_newpath);
193 return(ret);
196 int _wapi_stat (const char *path, struct stat *buf)
198 int ret;
200 ret = stat (path, buf);
201 if (ret == -1 &&
202 (errno == ENOENT || errno == ENOTDIR) &&
203 IS_PORTABILITY_SET) {
204 int saved_errno = errno;
205 gchar *located_filename = mono_portability_find_file (path, TRUE);
207 if (located_filename == NULL) {
208 errno = saved_errno;
209 return(-1);
212 ret = stat (located_filename, buf);
213 g_free (located_filename);
216 return(ret);
219 int _wapi_lstat (const char *path, struct stat *buf)
221 int ret;
223 ret = lstat (path, buf);
224 if (ret == -1 &&
225 (errno == ENOENT || errno == ENOTDIR) &&
226 IS_PORTABILITY_SET) {
227 int saved_errno = errno;
228 gchar *located_filename = mono_portability_find_file (path, TRUE);
230 if (located_filename == NULL) {
231 errno = saved_errno;
232 return(-1);
235 ret = lstat (located_filename, buf);
236 g_free (located_filename);
239 return(ret);
242 int _wapi_mkdir (const char *pathname, mode_t mode)
244 int ret;
245 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
247 if (located_filename == NULL) {
248 ret = mkdir (pathname, mode);
249 } else {
250 ret = mkdir (located_filename, mode);
251 g_free (located_filename);
254 return(ret);
257 int _wapi_rmdir (const char *pathname)
259 int ret;
261 ret = rmdir (pathname);
262 if (ret == -1 &&
263 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
264 IS_PORTABILITY_SET) {
265 int saved_errno = errno;
266 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
268 if (located_filename == NULL) {
269 errno = saved_errno;
270 return(-1);
273 ret = rmdir (located_filename);
274 g_free (located_filename);
277 return(ret);
280 int _wapi_chdir (const char *path)
282 int ret;
284 ret = chdir (path);
285 if (ret == -1 &&
286 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
287 IS_PORTABILITY_SET) {
288 int saved_errno = errno;
289 gchar *located_filename = mono_portability_find_file (path, TRUE);
291 if (located_filename == NULL) {
292 errno = saved_errno;
293 return(-1);
296 ret = chdir (located_filename);
297 g_free (located_filename);
300 return(ret);
303 gchar *_wapi_basename (const gchar *filename)
305 gchar *new_filename = g_strdup (filename), *ret;
307 if (IS_PORTABILITY_SET) {
308 g_strdelimit (new_filename, "\\", '/');
311 if (IS_PORTABILITY_DRIVE &&
312 g_ascii_isalpha (new_filename[0]) &&
313 (new_filename[1] == ':')) {
314 int len = strlen (new_filename);
316 g_memmove (new_filename, new_filename + 2, len - 2);
317 new_filename[len - 2] = '\0';
320 ret = g_path_get_basename (new_filename);
321 g_free (new_filename);
323 return(ret);
326 gchar *_wapi_dirname (const gchar *filename)
328 gchar *new_filename = g_strdup (filename), *ret;
330 if (IS_PORTABILITY_SET) {
331 g_strdelimit (new_filename, "\\", '/');
334 if (IS_PORTABILITY_DRIVE &&
335 g_ascii_isalpha (new_filename[0]) &&
336 (new_filename[1] == ':')) {
337 int len = strlen (new_filename);
339 g_memmove (new_filename, new_filename + 2, len - 2);
340 new_filename[len - 2] = '\0';
343 ret = g_path_get_dirname (new_filename);
344 g_free (new_filename);
346 return(ret);
349 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
351 GDir *ret;
353 ret = g_dir_open (path, flags, error);
354 if (ret == NULL &&
355 ((*error)->code == G_FILE_ERROR_NOENT ||
356 (*error)->code == G_FILE_ERROR_NOTDIR ||
357 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
358 IS_PORTABILITY_SET) {
359 gchar *located_filename = mono_portability_find_file (path, TRUE);
360 GError *tmp_error = NULL;
362 if (located_filename == NULL) {
363 return(NULL);
366 ret = g_dir_open (located_filename, flags, &tmp_error);
367 g_free (located_filename);
368 if (tmp_error == NULL) {
369 g_clear_error (error);
373 return(ret);
377 static gint
378 file_compare (gconstpointer a, gconstpointer b)
380 gchar *astr = *(gchar **) a;
381 gchar *bstr = *(gchar **) b;
383 return strcmp (astr, bstr);
386 static gint
387 get_errno_from_g_file_error (gint error)
389 switch (error) {
390 #ifdef EACCESS
391 case G_FILE_ERROR_ACCES:
392 error = EACCES;
393 break;
394 #endif
395 #ifdef ENAMETOOLONG
396 case G_FILE_ERROR_NAMETOOLONG:
397 error = ENAMETOOLONG;
398 break;
399 #endif
400 #ifdef ENOENT
401 case G_FILE_ERROR_NOENT:
402 error = ENOENT;
403 break;
404 #endif
405 #ifdef ENOTDIR
406 case G_FILE_ERROR_NOTDIR:
407 error = ENOTDIR;
408 break;
409 #endif
410 #ifdef ENXIO
411 case G_FILE_ERROR_NXIO:
412 error = ENXIO;
413 break;
414 #endif
415 #ifdef ENODEV
416 case G_FILE_ERROR_NODEV:
417 error = ENODEV;
418 break;
419 #endif
420 #ifdef EROFS
421 case G_FILE_ERROR_ROFS:
422 error = EROFS;
423 break;
424 #endif
425 #ifdef ETXTBSY
426 case G_FILE_ERROR_TXTBSY:
427 error = ETXTBSY;
428 break;
429 #endif
430 #ifdef EFAULT
431 case G_FILE_ERROR_FAULT:
432 error = EFAULT;
433 break;
434 #endif
435 #ifdef ELOOP
436 case G_FILE_ERROR_LOOP:
437 error = ELOOP;
438 break;
439 #endif
440 #ifdef ENOSPC
441 case G_FILE_ERROR_NOSPC:
442 error = ENOSPC;
443 break;
444 #endif
445 #ifdef ENOMEM
446 case G_FILE_ERROR_NOMEM:
447 error = ENOMEM;
448 break;
449 #endif
450 #ifdef EMFILE
451 case G_FILE_ERROR_MFILE:
452 error = EMFILE;
453 break;
454 #endif
455 #ifdef ENFILE
456 case G_FILE_ERROR_NFILE:
457 error = ENFILE;
458 break;
459 #endif
460 #ifdef EBADF
461 case G_FILE_ERROR_BADF:
462 error = EBADF;
463 break;
464 #endif
465 #ifdef EINVAL
466 case G_FILE_ERROR_INVAL:
467 error = EINVAL;
468 break;
469 #endif
470 #ifdef EPIPE
471 case G_FILE_ERROR_PIPE:
472 error = EPIPE;
473 break;
474 #endif
475 #ifdef EAGAIN
476 case G_FILE_ERROR_AGAIN:
477 error = EAGAIN;
478 break;
479 #endif
480 #ifdef EINTR
481 case G_FILE_ERROR_INTR:
482 error = EINTR;
483 break;
484 #endif
485 #ifdef EWIO
486 case G_FILE_ERROR_IO:
487 error = EIO;
488 break;
489 #endif
490 #ifdef EPERM
491 case G_FILE_ERROR_PERM:
492 error = EPERM;
493 break;
494 #endif
495 case G_FILE_ERROR_FAILED:
496 error = ERROR_INVALID_PARAMETER;
497 break;
500 return error;
503 /* scandir using glib */
504 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
505 gchar ***namelist)
507 GError *error = NULL;
508 GDir *dir;
509 GPtrArray *names;
510 gint result;
511 wapi_glob_t glob_buf;
512 int flags = 0, i;
514 dir = _wapi_g_dir_open (dirname, 0, &error);
515 if (dir == NULL) {
516 /* g_dir_open returns ENOENT on directories on which we don't
517 * have read/x permission */
518 gint errnum = get_errno_from_g_file_error (error->code);
519 g_error_free (error);
520 if (errnum == ENOENT &&
521 !_wapi_access (dirname, F_OK) &&
522 _wapi_access (dirname, R_OK|X_OK)) {
523 errnum = EACCES;
526 errno = errnum;
527 return -1;
530 if (IS_PORTABILITY_CASE) {
531 flags = WAPI_GLOB_IGNORECASE;
534 result = _wapi_glob (dir, pattern, flags, &glob_buf);
535 if (g_str_has_suffix (pattern, ".*")) {
536 /* Special-case the patterns ending in '.*', as
537 * windows also matches entries with no extension with
538 * this pattern.
540 * TODO: should this be a MONO_IOMAP option?
542 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
543 gint result2;
545 g_dir_rewind (dir);
546 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
548 g_free (pattern2);
550 if (result != 0) {
551 result = result2;
555 g_dir_close (dir);
556 if (glob_buf.gl_pathc == 0) {
557 return(0);
558 } else if (result != 0) {
559 return(-1);
562 names = g_ptr_array_new ();
563 for (i = 0; i < glob_buf.gl_pathc; i++) {
564 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
567 _wapi_globfree (&glob_buf);
569 result = names->len;
570 if (result > 0) {
571 g_ptr_array_sort (names, file_compare);
572 g_ptr_array_set_size (names, result + 1);
574 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
575 } else {
576 g_ptr_array_free (names, TRUE);
579 return result;