merge 99630:99762
[mono.git] / mono / io-layer / io-portability.c
1 /*
2  * io-portability.c:  Optional filename mangling to try to cope with
3  *                      badly-written non-portable windows apps
4  *
5  * Author:
6  *      Dick Porter (dick@ximian.com)
7  *
8  * Copyright (c) 2006 Novell, Inc.
9  */
10
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 <dirent.h>
22 #include <utime.h>
23 #include <sys/stat.h>
24
25 #include <mono/io-layer/mono-mutex.h>
26 #include <mono/io-layer/error.h>
27 #include <mono/io-layer/wapi_glob.h>
28 #include <mono/io-layer/io-portability.h>
29 #include <mono/utils/mono-io-portability.h>
30
31 #undef DEBUG
32
33 int _wapi_open (const char *pathname, int flags, mode_t mode)
34 {
35         int fd;
36         gchar *located_filename;
37         
38         if (flags & O_CREAT) {
39                 located_filename = mono_portability_find_file (pathname, FALSE);
40                 if (located_filename == NULL) {
41                         fd = open (pathname, flags, mode);
42                 } else {
43                         fd = open (located_filename, flags, mode);
44                         g_free (located_filename);
45                 }
46         } else {
47                 fd = open (pathname, flags, mode);
48                 if (fd == -1 &&
49                     (errno == ENOENT || errno == ENOTDIR) &&
50                     IS_PORTABILITY_SET) {
51                         int saved_errno = errno;
52                         located_filename = mono_portability_find_file (pathname, TRUE);
53                         
54                         if (located_filename == NULL) {
55                                 errno = saved_errno;
56                                 return (-1);
57                         }
58                         
59                         fd = open (located_filename, flags, mode);
60                         g_free (located_filename);
61                 }
62         }
63         
64         
65         return(fd);
66 }
67
68 int _wapi_access (const char *pathname, int mode)
69 {
70         int ret;
71         
72         ret = access (pathname, mode);
73         if (ret == -1 &&
74             (errno == ENOENT || errno == ENOTDIR) &&
75             IS_PORTABILITY_SET) {
76                 int saved_errno = errno;
77                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
78                 
79                 if (located_filename == NULL) {
80                         errno = saved_errno;
81                         return(-1);
82                 }
83                 
84                 ret = access (located_filename, mode);
85                 g_free (located_filename);
86         }
87         
88         return(ret);
89 }
90
91 int _wapi_chmod (const char *pathname, mode_t mode)
92 {
93         int ret;
94         
95         ret = chmod (pathname, mode);
96         if (ret == -1 &&
97             (errno == ENOENT || errno == ENOTDIR) &&
98             IS_PORTABILITY_SET) {
99                 int saved_errno = errno;
100                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
101                 
102                 if (located_filename == NULL) {
103                         errno = saved_errno;
104                         return(-1);
105                 }
106                 
107                 ret = chmod (located_filename, mode);
108                 g_free (located_filename);
109         }
110         
111         return(ret);
112 }
113
114 int _wapi_utime (const char *filename, const struct utimbuf *buf)
115 {
116         int ret;
117         
118         ret = utime (filename, buf);
119         if (ret == -1 &&
120             errno == ENOENT &&
121             IS_PORTABILITY_SET) {
122                 int saved_errno = errno;
123                 gchar *located_filename = mono_portability_find_file (filename, TRUE);
124                 
125                 if (located_filename == NULL) {
126                         errno = saved_errno;
127                         return(-1);
128                 }
129                 
130                 ret = utime (located_filename, buf);
131                 g_free (located_filename);
132         }
133         
134         return(ret);
135 }
136
137 int _wapi_unlink (const char *pathname)
138 {
139         int ret;
140         
141         ret = unlink (pathname);
142         if (ret == -1 &&
143             (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
144             IS_PORTABILITY_SET) {
145                 int saved_errno = errno;
146                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
147                 
148                 if (located_filename == NULL) {
149                         errno = saved_errno;
150                         return(-1);
151                 }
152                 
153                 ret = unlink (located_filename);
154                 g_free (located_filename);
155         }
156         
157         return(ret);
158 }
159
160 int _wapi_rename (const char *oldpath, const char *newpath)
161 {
162         int ret;
163         gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
164         
165         if (located_newpath == NULL) {
166                 ret = rename (oldpath, newpath);
167         } else {
168                 ret = rename (oldpath, located_newpath);
169         
170                 if (ret == -1 &&
171                     (errno == EISDIR || errno == ENAMETOOLONG ||
172                      errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
173                     IS_PORTABILITY_SET) {
174                         int saved_errno = errno;
175                         gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
176                         
177                         if (located_oldpath == NULL) {
178                                 g_free (located_oldpath);
179                                 g_free (located_newpath);
180                         
181                                 errno = saved_errno;
182                                 return(-1);
183                         }
184                         
185                         ret = rename (located_oldpath, located_newpath);
186                         g_free (located_oldpath);
187                 }
188                 g_free (located_newpath);
189         }
190         
191         return(ret);
192 }
193
194 int _wapi_stat (const char *path, struct stat *buf)
195 {
196         int ret;
197         
198         ret = stat (path, buf);
199         if (ret == -1 &&
200             (errno == ENOENT || errno == ENOTDIR) &&
201             IS_PORTABILITY_SET) {
202                 int saved_errno = errno;
203                 gchar *located_filename = mono_portability_find_file (path, TRUE);
204                 
205                 if (located_filename == NULL) {
206                         errno = saved_errno;
207                         return(-1);
208                 }
209                 
210                 ret = stat (located_filename, buf);
211                 g_free (located_filename);
212         }
213         
214         return(ret);
215 }
216
217 int _wapi_lstat (const char *path, struct stat *buf)
218 {
219         int ret;
220         
221         ret = lstat (path, buf);
222         if (ret == -1 &&
223             (errno == ENOENT || errno == ENOTDIR) &&
224             IS_PORTABILITY_SET) {
225                 int saved_errno = errno;
226                 gchar *located_filename = mono_portability_find_file (path, TRUE);
227                 
228                 if (located_filename == NULL) {
229                         errno = saved_errno;
230                         return(-1);
231                 }
232                 
233                 ret = lstat (located_filename, buf);
234                 g_free (located_filename);
235         }
236         
237         return(ret);
238 }
239
240 int _wapi_mkdir (const char *pathname, mode_t mode)
241 {
242         int ret;
243         gchar *located_filename = mono_portability_find_file (pathname, FALSE);
244         
245         if (located_filename == NULL) {
246                 ret = mkdir (pathname, mode);
247         } else {
248                 ret = mkdir (located_filename, mode);
249                 g_free (located_filename);
250         }
251         
252         return(ret);
253 }
254
255 int _wapi_rmdir (const char *pathname)
256 {
257         int ret;
258         
259         ret = rmdir (pathname);
260         if (ret == -1 &&
261             (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
262             IS_PORTABILITY_SET) {
263                 int saved_errno = errno;
264                 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
265                 
266                 if (located_filename == NULL) {
267                         errno = saved_errno;
268                         return(-1);
269                 }
270                 
271                 ret = rmdir (located_filename);
272                 g_free (located_filename);
273         }
274         
275         return(ret);
276 }
277
278 int _wapi_chdir (const char *path)
279 {
280         int ret;
281         
282         ret = chdir (path);
283         if (ret == -1 &&
284             (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
285             IS_PORTABILITY_SET) {
286                 int saved_errno = errno;
287                 gchar *located_filename = mono_portability_find_file (path, TRUE);
288                 
289                 if (located_filename == NULL) {
290                         errno = saved_errno;
291                         return(-1);
292                 }
293                 
294                 ret = chdir (located_filename);
295                 g_free (located_filename);
296         }
297         
298         return(ret);
299 }
300
301 gchar *_wapi_basename (const gchar *filename)
302 {
303         gchar *new_filename = g_strdup (filename), *ret;
304
305         g_strdelimit (new_filename, "\\", '/');
306
307         if (IS_PORTABILITY_DRIVE &&
308             g_ascii_isalpha (new_filename[0]) &&
309             (new_filename[1] == ':')) {
310                 int len = strlen (new_filename);
311                 
312                 g_memmove (new_filename, new_filename + 2, len - 2);
313                 new_filename[len - 2] = '\0';
314         }
315         
316         ret = g_path_get_basename (new_filename);
317         g_free (new_filename);
318         
319         return(ret);
320 }
321
322 gchar *_wapi_dirname (const gchar *filename)
323 {
324         gchar *new_filename = g_strdup (filename), *ret;
325
326         g_strdelimit (new_filename, "\\", '/');
327
328         if (IS_PORTABILITY_DRIVE &&
329             g_ascii_isalpha (new_filename[0]) &&
330             (new_filename[1] == ':')) {
331                 int len = strlen (new_filename);
332                 
333                 g_memmove (new_filename, new_filename + 2, len - 2);
334                 new_filename[len - 2] = '\0';
335         }
336         
337         ret = g_path_get_dirname (new_filename);
338         g_free (new_filename);
339         
340         return(ret);
341 }
342
343 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
344 {
345         GDir *ret;
346         
347         ret = g_dir_open (path, flags, error);
348         if (ret == NULL &&
349             ((*error)->code == G_FILE_ERROR_NOENT ||
350              (*error)->code == G_FILE_ERROR_NOTDIR ||
351              (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
352             IS_PORTABILITY_SET) {
353                 gchar *located_filename = mono_portability_find_file (path, TRUE);
354                 GError *tmp_error = NULL;
355                 
356                 if (located_filename == NULL) {
357                         return(NULL);
358                 }
359                 
360                 ret = g_dir_open (located_filename, flags, &tmp_error);
361                 g_free (located_filename);
362                 if (tmp_error == NULL) {
363                         g_clear_error (error);
364                 }
365         }
366         
367         return(ret);
368 }
369
370
371 static gint
372 file_compare (gconstpointer a, gconstpointer b)
373 {
374         gchar *astr = *(gchar **) a;
375         gchar *bstr = *(gchar **) b;
376
377         return strcmp (astr, bstr);
378 }
379
380 static gint
381 get_errno_from_g_file_error (gint error)
382 {
383         switch (error) {
384 #ifdef EACCESS
385         case G_FILE_ERROR_ACCES:
386                 error = EACCES;
387                 break;
388 #endif
389 #ifdef ENAMETOOLONG
390         case G_FILE_ERROR_NAMETOOLONG:
391                 error = ENAMETOOLONG;
392                 break;
393 #endif
394 #ifdef ENOENT
395         case G_FILE_ERROR_NOENT:
396                 error = ENOENT;
397                 break;
398 #endif
399 #ifdef ENOTDIR
400         case G_FILE_ERROR_NOTDIR:
401                 error = ENOTDIR;
402                 break;
403 #endif
404 #ifdef ENXIO
405         case G_FILE_ERROR_NXIO:
406                 error = ENXIO;
407                 break;
408 #endif
409 #ifdef ENODEV
410         case G_FILE_ERROR_NODEV:
411                 error = ENODEV;
412                 break;
413 #endif
414 #ifdef EROFS
415         case G_FILE_ERROR_ROFS:
416                 error = EROFS;
417                 break;
418 #endif
419 #ifdef ETXTBSY
420         case G_FILE_ERROR_TXTBSY:
421                 error = ETXTBSY;
422                 break;
423 #endif
424 #ifdef EFAULT
425         case G_FILE_ERROR_FAULT:
426                 error = EFAULT;
427                 break;
428 #endif
429 #ifdef ELOOP
430         case G_FILE_ERROR_LOOP:
431                 error = ELOOP;
432                 break;
433 #endif
434 #ifdef ENOSPC
435         case G_FILE_ERROR_NOSPC:
436                 error = ENOSPC;
437                 break;
438 #endif
439 #ifdef ENOMEM
440         case G_FILE_ERROR_NOMEM:
441                 error = ENOMEM;
442                 break;
443 #endif
444 #ifdef EMFILE
445         case G_FILE_ERROR_MFILE:
446                 error = EMFILE;
447                 break;
448 #endif
449 #ifdef ENFILE
450         case G_FILE_ERROR_NFILE:
451                 error = ENFILE;
452                 break;
453 #endif
454 #ifdef EBADF
455         case G_FILE_ERROR_BADF:
456                 error = EBADF;
457                 break;
458 #endif
459 #ifdef EINVAL
460         case G_FILE_ERROR_INVAL:
461                 error = EINVAL;
462                 break;
463 #endif
464 #ifdef EPIPE
465         case G_FILE_ERROR_PIPE:
466                 error = EPIPE;
467                 break;
468 #endif
469 #ifdef EAGAIN
470         case G_FILE_ERROR_AGAIN:
471                 error = EAGAIN;
472                 break;
473 #endif
474 #ifdef EINTR
475         case G_FILE_ERROR_INTR:
476                 error = EINTR;
477                 break;
478 #endif
479 #ifdef EWIO
480         case G_FILE_ERROR_IO:
481                 error = EIO;
482                 break;
483 #endif
484 #ifdef EPERM
485         case G_FILE_ERROR_PERM:
486                 error = EPERM;
487                 break;
488 #endif
489         case G_FILE_ERROR_FAILED:
490                 error = ERROR_INVALID_PARAMETER;
491                 break;
492         }
493
494         return error;
495 }
496
497 /* scandir using glib */
498 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
499                        gchar ***namelist)
500 {
501         GError *error = NULL;
502         GDir *dir;
503         GPtrArray *names;
504         gint result;
505         wapi_glob_t glob_buf;
506         int flags = 0, i;
507         
508         dir = _wapi_g_dir_open (dirname, 0, &error);
509         if (dir == NULL) {
510                 /* g_dir_open returns ENOENT on directories on which we don't
511                  * have read/x permission */
512                 gint errnum = get_errno_from_g_file_error (error->code);
513                 g_error_free (error);
514                 if (errnum == ENOENT &&
515                     !_wapi_access (dirname, F_OK) &&
516                     _wapi_access (dirname, R_OK|X_OK)) {
517                         errnum = EACCES;
518                 }
519
520                 errno = errnum;
521                 return -1;
522         }
523
524         if (IS_PORTABILITY_CASE) {
525                 flags = WAPI_GLOB_IGNORECASE;
526         }
527         
528         result = _wapi_glob (dir, pattern, flags, &glob_buf);
529         if (g_str_has_suffix (pattern, ".*")) {
530                 /* Special-case the patterns ending in '.*', as
531                  * windows also matches entries with no extension with
532                  * this pattern.
533                  * 
534                  * TODO: should this be a MONO_IOMAP option?
535                  */
536                 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
537                 gint result2;
538                 
539                 g_dir_rewind (dir);
540                 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
541
542                 g_free (pattern2);
543
544                 if (result != 0) {
545                         result = result2;
546                 }
547         }
548         
549         g_dir_close (dir);
550         if (glob_buf.gl_pathc == 0) {
551                 return(0);
552         } else if (result != 0) {
553                 return(-1);
554         }
555         
556         names = g_ptr_array_new ();
557         for (i = 0; i < glob_buf.gl_pathc; i++) {
558                 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
559         }
560
561         _wapi_globfree (&glob_buf);
562
563         result = names->len;
564         if (result > 0) {
565                 g_ptr_array_sort (names, file_compare);
566                 g_ptr_array_set_size (names, result + 1);
567
568                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
569         } else {
570                 g_ptr_array_free (names, TRUE);
571         }
572
573         return result;
574 }