Add missing quote to string added in 156ab8c5fc89c6fa9bda6ba2d1e240ca80c39bbe
[mono-project.git] / mono / io-layer / io-portability.c
blobb9674955380f25eadf513556862ec3e54f9ea919
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/error.h>
29 #include <mono/io-layer/wapi_glob.h>
30 #include <mono/io-layer/io-portability.h>
31 #include <mono/utils/mono-io-portability.h>
33 #include <mono/utils/mono-mutex.h>
35 #undef DEBUG
37 int _wapi_open (const char *pathname, int flags, mode_t mode)
39 int fd;
40 gchar *located_filename;
42 if (flags & O_CREAT) {
43 located_filename = mono_portability_find_file (pathname, FALSE);
44 if (located_filename == NULL) {
45 fd = open (pathname, flags, mode);
46 } else {
47 fd = open (located_filename, flags, mode);
48 g_free (located_filename);
50 } else {
51 fd = open (pathname, flags, mode);
52 if (fd == -1 &&
53 (errno == ENOENT || errno == ENOTDIR) &&
54 IS_PORTABILITY_SET) {
55 int saved_errno = errno;
56 located_filename = mono_portability_find_file (pathname, TRUE);
58 if (located_filename == NULL) {
59 errno = saved_errno;
60 return (-1);
63 fd = open (located_filename, flags, mode);
64 g_free (located_filename);
69 return(fd);
72 int _wapi_access (const char *pathname, int mode)
74 int ret;
76 ret = access (pathname, mode);
77 if (ret == -1 &&
78 (errno == ENOENT || errno == ENOTDIR) &&
79 IS_PORTABILITY_SET) {
80 int saved_errno = errno;
81 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
83 if (located_filename == NULL) {
84 errno = saved_errno;
85 return(-1);
88 ret = access (located_filename, mode);
89 g_free (located_filename);
92 return(ret);
95 int _wapi_chmod (const char *pathname, mode_t mode)
97 int ret;
99 ret = chmod (pathname, mode);
100 if (ret == -1 &&
101 (errno == ENOENT || errno == ENOTDIR) &&
102 IS_PORTABILITY_SET) {
103 int saved_errno = errno;
104 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
106 if (located_filename == NULL) {
107 errno = saved_errno;
108 return(-1);
111 ret = chmod (located_filename, mode);
112 g_free (located_filename);
115 return(ret);
118 int _wapi_utime (const char *filename, const struct utimbuf *buf)
120 int ret;
122 ret = utime (filename, buf);
123 if (ret == -1 &&
124 errno == ENOENT &&
125 IS_PORTABILITY_SET) {
126 int saved_errno = errno;
127 gchar *located_filename = mono_portability_find_file (filename, TRUE);
129 if (located_filename == NULL) {
130 errno = saved_errno;
131 return(-1);
134 ret = utime (located_filename, buf);
135 g_free (located_filename);
138 return(ret);
141 int _wapi_unlink (const char *pathname)
143 int ret;
145 ret = unlink (pathname);
146 if (ret == -1 &&
147 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
148 IS_PORTABILITY_SET) {
149 int saved_errno = errno;
150 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
152 if (located_filename == NULL) {
153 errno = saved_errno;
154 return(-1);
157 ret = unlink (located_filename);
158 g_free (located_filename);
161 return(ret);
164 int _wapi_rename (const char *oldpath, const char *newpath)
166 int ret;
167 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
169 if (located_newpath == NULL) {
170 ret = rename (oldpath, newpath);
171 } else {
172 ret = rename (oldpath, located_newpath);
174 if (ret == -1 &&
175 (errno == EISDIR || errno == ENAMETOOLONG ||
176 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
177 IS_PORTABILITY_SET) {
178 int saved_errno = errno;
179 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
181 if (located_oldpath == NULL) {
182 g_free (located_oldpath);
183 g_free (located_newpath);
185 errno = saved_errno;
186 return(-1);
189 ret = rename (located_oldpath, located_newpath);
190 g_free (located_oldpath);
192 g_free (located_newpath);
195 return(ret);
198 int _wapi_stat (const char *path, struct stat *buf)
200 int ret;
202 ret = stat (path, buf);
203 if (ret == -1 &&
204 (errno == ENOENT || errno == ENOTDIR) &&
205 IS_PORTABILITY_SET) {
206 int saved_errno = errno;
207 gchar *located_filename = mono_portability_find_file (path, TRUE);
209 if (located_filename == NULL) {
210 errno = saved_errno;
211 return(-1);
214 ret = stat (located_filename, buf);
215 g_free (located_filename);
218 return(ret);
221 int _wapi_lstat (const char *path, struct stat *buf)
223 int ret;
225 ret = lstat (path, buf);
226 if (ret == -1 &&
227 (errno == ENOENT || errno == ENOTDIR) &&
228 IS_PORTABILITY_SET) {
229 int saved_errno = errno;
230 gchar *located_filename = mono_portability_find_file (path, TRUE);
232 if (located_filename == NULL) {
233 errno = saved_errno;
234 return(-1);
237 ret = lstat (located_filename, buf);
238 g_free (located_filename);
241 return(ret);
244 int _wapi_mkdir (const char *pathname, mode_t mode)
246 int ret;
247 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
249 if (located_filename == NULL) {
250 ret = mkdir (pathname, mode);
251 } else {
252 ret = mkdir (located_filename, mode);
253 g_free (located_filename);
256 return(ret);
259 int _wapi_rmdir (const char *pathname)
261 int ret;
263 ret = rmdir (pathname);
264 if (ret == -1 &&
265 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
266 IS_PORTABILITY_SET) {
267 int saved_errno = errno;
268 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
270 if (located_filename == NULL) {
271 errno = saved_errno;
272 return(-1);
275 ret = rmdir (located_filename);
276 g_free (located_filename);
279 return(ret);
282 int _wapi_chdir (const char *path)
284 int ret;
286 ret = chdir (path);
287 if (ret == -1 &&
288 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
289 IS_PORTABILITY_SET) {
290 int saved_errno = errno;
291 gchar *located_filename = mono_portability_find_file (path, TRUE);
293 if (located_filename == NULL) {
294 errno = saved_errno;
295 return(-1);
298 ret = chdir (located_filename);
299 g_free (located_filename);
302 return(ret);
305 gchar *_wapi_basename (const gchar *filename)
307 gchar *new_filename = g_strdup (filename), *ret;
309 if (IS_PORTABILITY_SET) {
310 g_strdelimit (new_filename, "\\", '/');
313 if (IS_PORTABILITY_DRIVE &&
314 g_ascii_isalpha (new_filename[0]) &&
315 (new_filename[1] == ':')) {
316 int len = strlen (new_filename);
318 g_memmove (new_filename, new_filename + 2, len - 2);
319 new_filename[len - 2] = '\0';
322 ret = g_path_get_basename (new_filename);
323 g_free (new_filename);
325 return(ret);
328 gchar *_wapi_dirname (const gchar *filename)
330 gchar *new_filename = g_strdup (filename), *ret;
332 if (IS_PORTABILITY_SET) {
333 g_strdelimit (new_filename, "\\", '/');
336 if (IS_PORTABILITY_DRIVE &&
337 g_ascii_isalpha (new_filename[0]) &&
338 (new_filename[1] == ':')) {
339 int len = strlen (new_filename);
341 g_memmove (new_filename, new_filename + 2, len - 2);
342 new_filename[len - 2] = '\0';
345 ret = g_path_get_dirname (new_filename);
346 g_free (new_filename);
348 return(ret);
351 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
353 GDir *ret;
355 ret = g_dir_open (path, flags, error);
356 if (ret == NULL &&
357 ((*error)->code == G_FILE_ERROR_NOENT ||
358 (*error)->code == G_FILE_ERROR_NOTDIR ||
359 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
360 IS_PORTABILITY_SET) {
361 gchar *located_filename = mono_portability_find_file (path, TRUE);
362 GError *tmp_error = NULL;
364 if (located_filename == NULL) {
365 return(NULL);
368 ret = g_dir_open (located_filename, flags, &tmp_error);
369 g_free (located_filename);
370 if (tmp_error == NULL) {
371 g_clear_error (error);
375 return(ret);
379 static gint
380 file_compare (gconstpointer a, gconstpointer b)
382 gchar *astr = *(gchar **) a;
383 gchar *bstr = *(gchar **) b;
385 return strcmp (astr, bstr);
388 static gint
389 get_errno_from_g_file_error (gint error)
391 switch (error) {
392 #ifdef EACCESS
393 case G_FILE_ERROR_ACCES:
394 error = EACCES;
395 break;
396 #endif
397 #ifdef ENAMETOOLONG
398 case G_FILE_ERROR_NAMETOOLONG:
399 error = ENAMETOOLONG;
400 break;
401 #endif
402 #ifdef ENOENT
403 case G_FILE_ERROR_NOENT:
404 error = ENOENT;
405 break;
406 #endif
407 #ifdef ENOTDIR
408 case G_FILE_ERROR_NOTDIR:
409 error = ENOTDIR;
410 break;
411 #endif
412 #ifdef ENXIO
413 case G_FILE_ERROR_NXIO:
414 error = ENXIO;
415 break;
416 #endif
417 #ifdef ENODEV
418 case G_FILE_ERROR_NODEV:
419 error = ENODEV;
420 break;
421 #endif
422 #ifdef EROFS
423 case G_FILE_ERROR_ROFS:
424 error = EROFS;
425 break;
426 #endif
427 #ifdef ETXTBSY
428 case G_FILE_ERROR_TXTBSY:
429 error = ETXTBSY;
430 break;
431 #endif
432 #ifdef EFAULT
433 case G_FILE_ERROR_FAULT:
434 error = EFAULT;
435 break;
436 #endif
437 #ifdef ELOOP
438 case G_FILE_ERROR_LOOP:
439 error = ELOOP;
440 break;
441 #endif
442 #ifdef ENOSPC
443 case G_FILE_ERROR_NOSPC:
444 error = ENOSPC;
445 break;
446 #endif
447 #ifdef ENOMEM
448 case G_FILE_ERROR_NOMEM:
449 error = ENOMEM;
450 break;
451 #endif
452 #ifdef EMFILE
453 case G_FILE_ERROR_MFILE:
454 error = EMFILE;
455 break;
456 #endif
457 #ifdef ENFILE
458 case G_FILE_ERROR_NFILE:
459 error = ENFILE;
460 break;
461 #endif
462 #ifdef EBADF
463 case G_FILE_ERROR_BADF:
464 error = EBADF;
465 break;
466 #endif
467 #ifdef EINVAL
468 case G_FILE_ERROR_INVAL:
469 error = EINVAL;
470 break;
471 #endif
472 #ifdef EPIPE
473 case G_FILE_ERROR_PIPE:
474 error = EPIPE;
475 break;
476 #endif
477 #ifdef EAGAIN
478 case G_FILE_ERROR_AGAIN:
479 error = EAGAIN;
480 break;
481 #endif
482 #ifdef EINTR
483 case G_FILE_ERROR_INTR:
484 error = EINTR;
485 break;
486 #endif
487 #ifdef EWIO
488 case G_FILE_ERROR_IO:
489 error = EIO;
490 break;
491 #endif
492 #ifdef EPERM
493 case G_FILE_ERROR_PERM:
494 error = EPERM;
495 break;
496 #endif
497 case G_FILE_ERROR_FAILED:
498 error = ERROR_INVALID_PARAMETER;
499 break;
502 return error;
505 /* scandir using glib */
506 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
507 gchar ***namelist)
509 GError *error = NULL;
510 GDir *dir;
511 GPtrArray *names;
512 gint result;
513 wapi_glob_t glob_buf;
514 int flags = 0, i;
516 dir = _wapi_g_dir_open (dirname, 0, &error);
517 if (dir == NULL) {
518 /* g_dir_open returns ENOENT on directories on which we don't
519 * have read/x permission */
520 gint errnum = get_errno_from_g_file_error (error->code);
521 g_error_free (error);
522 if (errnum == ENOENT &&
523 !_wapi_access (dirname, F_OK) &&
524 _wapi_access (dirname, R_OK|X_OK)) {
525 errnum = EACCES;
528 errno = errnum;
529 return -1;
532 if (IS_PORTABILITY_CASE) {
533 flags = WAPI_GLOB_IGNORECASE;
536 result = _wapi_glob (dir, pattern, flags, &glob_buf);
537 if (g_str_has_suffix (pattern, ".*")) {
538 /* Special-case the patterns ending in '.*', as
539 * windows also matches entries with no extension with
540 * this pattern.
542 * TODO: should this be a MONO_IOMAP option?
544 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
545 gint result2;
547 g_dir_rewind (dir);
548 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
550 g_free (pattern2);
552 if (result != 0) {
553 result = result2;
557 g_dir_close (dir);
558 if (glob_buf.gl_pathc == 0) {
559 return(0);
560 } else if (result != 0) {
561 return(-1);
564 names = g_ptr_array_new ();
565 for (i = 0; i < glob_buf.gl_pathc; i++) {
566 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
569 _wapi_globfree (&glob_buf);
571 result = names->len;
572 if (result > 0) {
573 g_ptr_array_sort (names, file_compare);
574 g_ptr_array_set_size (names, result + 1);
576 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
577 } else {
578 g_ptr_array_free (names, TRUE);
581 return result;