[Facades] Remove duplicate System.IO.Compression
[mono-project.git] / mono / io-layer / io-portability.c
blob282cbe93a29b8e6f60923db0fafba329529ac924
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 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
13 #include <glib.h>
14 #include <stdio.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #ifdef HAVE_DIRENT_H
24 # include <dirent.h>
25 #endif
26 #include <utime.h>
27 #include <sys/stat.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>
34 #undef DEBUG
36 int _wapi_open (const char *pathname, int flags, mode_t mode)
38 int fd;
39 gchar *located_filename;
41 if (flags & O_CREAT) {
42 located_filename = mono_portability_find_file (pathname, FALSE);
43 if (located_filename == NULL) {
44 fd = open (pathname, flags, mode);
45 } else {
46 fd = open (located_filename, flags, mode);
47 g_free (located_filename);
49 } else {
50 fd = open (pathname, flags, mode);
51 if (fd == -1 &&
52 (errno == ENOENT || errno == ENOTDIR) &&
53 IS_PORTABILITY_SET) {
54 int saved_errno = errno;
55 located_filename = mono_portability_find_file (pathname, TRUE);
57 if (located_filename == NULL) {
58 errno = saved_errno;
59 return (-1);
62 fd = open (located_filename, flags, mode);
63 g_free (located_filename);
68 return(fd);
71 int _wapi_access (const char *pathname, int mode)
73 int ret;
75 ret = access (pathname, mode);
76 if (ret == -1 &&
77 (errno == ENOENT || errno == ENOTDIR) &&
78 IS_PORTABILITY_SET) {
79 int saved_errno = errno;
80 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
82 if (located_filename == NULL) {
83 errno = saved_errno;
84 return(-1);
87 ret = access (located_filename, mode);
88 g_free (located_filename);
91 return(ret);
94 int _wapi_chmod (const char *pathname, mode_t mode)
96 int ret;
98 ret = chmod (pathname, mode);
99 if (ret == -1 &&
100 (errno == ENOENT || errno == ENOTDIR) &&
101 IS_PORTABILITY_SET) {
102 int saved_errno = errno;
103 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
105 if (located_filename == NULL) {
106 errno = saved_errno;
107 return(-1);
110 ret = chmod (located_filename, mode);
111 g_free (located_filename);
114 return(ret);
117 int _wapi_utime (const char *filename, const struct utimbuf *buf)
119 int ret;
121 ret = utime (filename, buf);
122 if (ret == -1 &&
123 errno == ENOENT &&
124 IS_PORTABILITY_SET) {
125 int saved_errno = errno;
126 gchar *located_filename = mono_portability_find_file (filename, TRUE);
128 if (located_filename == NULL) {
129 errno = saved_errno;
130 return(-1);
133 ret = utime (located_filename, buf);
134 g_free (located_filename);
137 return(ret);
140 int _wapi_unlink (const char *pathname)
142 int ret;
144 ret = unlink (pathname);
145 if (ret == -1 &&
146 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
147 IS_PORTABILITY_SET) {
148 int saved_errno = errno;
149 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
151 if (located_filename == NULL) {
152 errno = saved_errno;
153 return(-1);
156 ret = unlink (located_filename);
157 g_free (located_filename);
160 return(ret);
163 int _wapi_rename (const char *oldpath, const char *newpath)
165 int ret;
166 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
168 if (located_newpath == NULL) {
169 ret = rename (oldpath, newpath);
170 } else {
171 ret = rename (oldpath, located_newpath);
173 if (ret == -1 &&
174 (errno == EISDIR || errno == ENAMETOOLONG ||
175 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
176 IS_PORTABILITY_SET) {
177 int saved_errno = errno;
178 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
180 if (located_oldpath == NULL) {
181 g_free (located_oldpath);
182 g_free (located_newpath);
184 errno = saved_errno;
185 return(-1);
188 ret = rename (located_oldpath, located_newpath);
189 g_free (located_oldpath);
191 g_free (located_newpath);
194 return(ret);
197 int _wapi_stat (const char *path, struct stat *buf)
199 int ret;
201 ret = stat (path, buf);
202 if (ret == -1 &&
203 (errno == ENOENT || errno == ENOTDIR) &&
204 IS_PORTABILITY_SET) {
205 int saved_errno = errno;
206 gchar *located_filename = mono_portability_find_file (path, TRUE);
208 if (located_filename == NULL) {
209 errno = saved_errno;
210 return(-1);
213 ret = stat (located_filename, buf);
214 g_free (located_filename);
217 return(ret);
220 int _wapi_lstat (const char *path, struct stat *buf)
222 int ret;
224 ret = lstat (path, buf);
225 if (ret == -1 &&
226 (errno == ENOENT || errno == ENOTDIR) &&
227 IS_PORTABILITY_SET) {
228 int saved_errno = errno;
229 gchar *located_filename = mono_portability_find_file (path, TRUE);
231 if (located_filename == NULL) {
232 errno = saved_errno;
233 return(-1);
236 ret = lstat (located_filename, buf);
237 g_free (located_filename);
240 return(ret);
243 int _wapi_mkdir (const char *pathname, mode_t mode)
245 int ret;
246 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
248 if (located_filename == NULL) {
249 ret = mkdir (pathname, mode);
250 } else {
251 ret = mkdir (located_filename, mode);
252 g_free (located_filename);
255 return(ret);
258 int _wapi_rmdir (const char *pathname)
260 int ret;
262 ret = rmdir (pathname);
263 if (ret == -1 &&
264 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
265 IS_PORTABILITY_SET) {
266 int saved_errno = errno;
267 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
269 if (located_filename == NULL) {
270 errno = saved_errno;
271 return(-1);
274 ret = rmdir (located_filename);
275 g_free (located_filename);
278 return(ret);
281 int _wapi_chdir (const char *path)
283 int ret;
285 ret = chdir (path);
286 if (ret == -1 &&
287 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
288 IS_PORTABILITY_SET) {
289 int saved_errno = errno;
290 gchar *located_filename = mono_portability_find_file (path, TRUE);
292 if (located_filename == NULL) {
293 errno = saved_errno;
294 return(-1);
297 ret = chdir (located_filename);
298 g_free (located_filename);
301 return(ret);
304 gchar *_wapi_basename (const gchar *filename)
306 gchar *new_filename = g_strdup (filename), *ret;
308 if (IS_PORTABILITY_SET) {
309 g_strdelimit (new_filename, "\\", '/');
312 if (IS_PORTABILITY_DRIVE &&
313 g_ascii_isalpha (new_filename[0]) &&
314 (new_filename[1] == ':')) {
315 int len = strlen (new_filename);
317 g_memmove (new_filename, new_filename + 2, len - 2);
318 new_filename[len - 2] = '\0';
321 ret = g_path_get_basename (new_filename);
322 g_free (new_filename);
324 return(ret);
327 gchar *_wapi_dirname (const gchar *filename)
329 gchar *new_filename = g_strdup (filename), *ret;
331 if (IS_PORTABILITY_SET) {
332 g_strdelimit (new_filename, "\\", '/');
335 if (IS_PORTABILITY_DRIVE &&
336 g_ascii_isalpha (new_filename[0]) &&
337 (new_filename[1] == ':')) {
338 int len = strlen (new_filename);
340 g_memmove (new_filename, new_filename + 2, len - 2);
341 new_filename[len - 2] = '\0';
344 ret = g_path_get_dirname (new_filename);
345 g_free (new_filename);
347 return(ret);
350 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
352 GDir *ret;
354 ret = g_dir_open (path, flags, error);
355 if (ret == NULL &&
356 ((*error)->code == G_FILE_ERROR_NOENT ||
357 (*error)->code == G_FILE_ERROR_NOTDIR ||
358 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
359 IS_PORTABILITY_SET) {
360 gchar *located_filename = mono_portability_find_file (path, TRUE);
361 GError *tmp_error = NULL;
363 if (located_filename == NULL) {
364 return(NULL);
367 ret = g_dir_open (located_filename, flags, &tmp_error);
368 g_free (located_filename);
369 if (tmp_error == NULL) {
370 g_clear_error (error);
374 return(ret);
378 static gint
379 file_compare (gconstpointer a, gconstpointer b)
381 gchar *astr = *(gchar **) a;
382 gchar *bstr = *(gchar **) b;
384 return strcmp (astr, bstr);
387 static gint
388 get_errno_from_g_file_error (gint error)
390 switch (error) {
391 #ifdef EACCESS
392 case G_FILE_ERROR_ACCES:
393 error = EACCES;
394 break;
395 #endif
396 #ifdef ENAMETOOLONG
397 case G_FILE_ERROR_NAMETOOLONG:
398 error = ENAMETOOLONG;
399 break;
400 #endif
401 #ifdef ENOENT
402 case G_FILE_ERROR_NOENT:
403 error = ENOENT;
404 break;
405 #endif
406 #ifdef ENOTDIR
407 case G_FILE_ERROR_NOTDIR:
408 error = ENOTDIR;
409 break;
410 #endif
411 #ifdef ENXIO
412 case G_FILE_ERROR_NXIO:
413 error = ENXIO;
414 break;
415 #endif
416 #ifdef ENODEV
417 case G_FILE_ERROR_NODEV:
418 error = ENODEV;
419 break;
420 #endif
421 #ifdef EROFS
422 case G_FILE_ERROR_ROFS:
423 error = EROFS;
424 break;
425 #endif
426 #ifdef ETXTBSY
427 case G_FILE_ERROR_TXTBSY:
428 error = ETXTBSY;
429 break;
430 #endif
431 #ifdef EFAULT
432 case G_FILE_ERROR_FAULT:
433 error = EFAULT;
434 break;
435 #endif
436 #ifdef ELOOP
437 case G_FILE_ERROR_LOOP:
438 error = ELOOP;
439 break;
440 #endif
441 #ifdef ENOSPC
442 case G_FILE_ERROR_NOSPC:
443 error = ENOSPC;
444 break;
445 #endif
446 #ifdef ENOMEM
447 case G_FILE_ERROR_NOMEM:
448 error = ENOMEM;
449 break;
450 #endif
451 #ifdef EMFILE
452 case G_FILE_ERROR_MFILE:
453 error = EMFILE;
454 break;
455 #endif
456 #ifdef ENFILE
457 case G_FILE_ERROR_NFILE:
458 error = ENFILE;
459 break;
460 #endif
461 #ifdef EBADF
462 case G_FILE_ERROR_BADF:
463 error = EBADF;
464 break;
465 #endif
466 #ifdef EINVAL
467 case G_FILE_ERROR_INVAL:
468 error = EINVAL;
469 break;
470 #endif
471 #ifdef EPIPE
472 case G_FILE_ERROR_PIPE:
473 error = EPIPE;
474 break;
475 #endif
476 #ifdef EAGAIN
477 case G_FILE_ERROR_AGAIN:
478 error = EAGAIN;
479 break;
480 #endif
481 #ifdef EINTR
482 case G_FILE_ERROR_INTR:
483 error = EINTR;
484 break;
485 #endif
486 #ifdef EWIO
487 case G_FILE_ERROR_IO:
488 error = EIO;
489 break;
490 #endif
491 #ifdef EPERM
492 case G_FILE_ERROR_PERM:
493 error = EPERM;
494 break;
495 #endif
496 case G_FILE_ERROR_FAILED:
497 error = ERROR_INVALID_PARAMETER;
498 break;
501 return error;
504 /* scandir using glib */
505 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
506 gchar ***namelist)
508 GError *error = NULL;
509 GDir *dir;
510 GPtrArray *names;
511 gint result;
512 wapi_glob_t glob_buf;
513 int flags = 0, i;
515 dir = _wapi_g_dir_open (dirname, 0, &error);
516 if (dir == NULL) {
517 /* g_dir_open returns ENOENT on directories on which we don't
518 * have read/x permission */
519 gint errnum = get_errno_from_g_file_error (error->code);
520 g_error_free (error);
521 if (errnum == ENOENT &&
522 !_wapi_access (dirname, F_OK) &&
523 _wapi_access (dirname, R_OK|X_OK)) {
524 errnum = EACCES;
527 errno = errnum;
528 return -1;
531 if (IS_PORTABILITY_CASE) {
532 flags = WAPI_GLOB_IGNORECASE;
535 result = _wapi_glob (dir, pattern, flags, &glob_buf);
536 if (g_str_has_suffix (pattern, ".*")) {
537 /* Special-case the patterns ending in '.*', as
538 * windows also matches entries with no extension with
539 * this pattern.
541 * TODO: should this be a MONO_IOMAP option?
543 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
544 gint result2;
546 g_dir_rewind (dir);
547 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
549 g_free (pattern2);
551 if (result != 0) {
552 result = result2;
556 g_dir_close (dir);
557 if (glob_buf.gl_pathc == 0) {
558 return(0);
559 } else if (result != 0) {
560 return(-1);
563 names = g_ptr_array_new ();
564 for (i = 0; i < glob_buf.gl_pathc; i++) {
565 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
568 _wapi_globfree (&glob_buf);
570 result = names->len;
571 if (result > 0) {
572 g_ptr_array_sort (names, file_compare);
573 g_ptr_array_set_size (names, result + 1);
575 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
576 } else {
577 g_ptr_array_free (names, TRUE);
580 return result;