First set of licensing changes
[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  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
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>
28
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
34 #undef DEBUG
35
36 int _wapi_open (const char *pathname, int flags, mode_t mode)
37 {
38         int fd;
39         gchar *located_filename;
40         
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);
48                 }
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);
56                         
57                         if (located_filename == NULL) {
58                                 errno = saved_errno;
59                                 return (-1);
60                         }
61                         
62                         fd = open (located_filename, flags, mode);
63                         g_free (located_filename);
64                 }
65         }
66         
67         
68         return(fd);
69 }
70
71 int _wapi_access (const char *pathname, int mode)
72 {
73         int ret;
74         
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);
81                 
82                 if (located_filename == NULL) {
83                         errno = saved_errno;
84                         return(-1);
85                 }
86                 
87                 ret = access (located_filename, mode);
88                 g_free (located_filename);
89         }
90         
91         return(ret);
92 }
93
94 int _wapi_chmod (const char *pathname, mode_t mode)
95 {
96         int ret;
97         
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);
104                 
105                 if (located_filename == NULL) {
106                         errno = saved_errno;
107                         return(-1);
108                 }
109                 
110                 ret = chmod (located_filename, mode);
111                 g_free (located_filename);
112         }
113         
114         return(ret);
115 }
116
117 int _wapi_utime (const char *filename, const struct utimbuf *buf)
118 {
119         int ret;
120         
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);
127                 
128                 if (located_filename == NULL) {
129                         errno = saved_errno;
130                         return(-1);
131                 }
132                 
133                 ret = utime (located_filename, buf);
134                 g_free (located_filename);
135         }
136         
137         return(ret);
138 }
139
140 int _wapi_unlink (const char *pathname)
141 {
142         int ret;
143         
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);
150                 
151                 if (located_filename == NULL) {
152                         errno = saved_errno;
153                         return(-1);
154                 }
155                 
156                 ret = unlink (located_filename);
157                 g_free (located_filename);
158         }
159         
160         return(ret);
161 }
162
163 int _wapi_rename (const char *oldpath, const char *newpath)
164 {
165         int ret;
166         gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
167         
168         if (located_newpath == NULL) {
169                 ret = rename (oldpath, newpath);
170         } else {
171                 ret = rename (oldpath, located_newpath);
172         
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);
179                         
180                         if (located_oldpath == NULL) {
181                                 g_free (located_oldpath);
182                                 g_free (located_newpath);
183                         
184                                 errno = saved_errno;
185                                 return(-1);
186                         }
187                         
188                         ret = rename (located_oldpath, located_newpath);
189                         g_free (located_oldpath);
190                 }
191                 g_free (located_newpath);
192         }
193         
194         return(ret);
195 }
196
197 int _wapi_stat (const char *path, struct stat *buf)
198 {
199         int ret;
200         
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);
207                 
208                 if (located_filename == NULL) {
209                         errno = saved_errno;
210                         return(-1);
211                 }
212                 
213                 ret = stat (located_filename, buf);
214                 g_free (located_filename);
215         }
216         
217         return(ret);
218 }
219
220 int _wapi_lstat (const char *path, struct stat *buf)
221 {
222         int ret;
223         
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);
230                 
231                 if (located_filename == NULL) {
232                         errno = saved_errno;
233                         return(-1);
234                 }
235                 
236                 ret = lstat (located_filename, buf);
237                 g_free (located_filename);
238         }
239         
240         return(ret);
241 }
242
243 int _wapi_mkdir (const char *pathname, mode_t mode)
244 {
245         int ret;
246         gchar *located_filename = mono_portability_find_file (pathname, FALSE);
247         
248         if (located_filename == NULL) {
249                 ret = mkdir (pathname, mode);
250         } else {
251                 ret = mkdir (located_filename, mode);
252                 g_free (located_filename);
253         }
254         
255         return(ret);
256 }
257
258 int _wapi_rmdir (const char *pathname)
259 {
260         int ret;
261         
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);
268                 
269                 if (located_filename == NULL) {
270                         errno = saved_errno;
271                         return(-1);
272                 }
273                 
274                 ret = rmdir (located_filename);
275                 g_free (located_filename);
276         }
277         
278         return(ret);
279 }
280
281 int _wapi_chdir (const char *path)
282 {
283         int ret;
284         
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);
291                 
292                 if (located_filename == NULL) {
293                         errno = saved_errno;
294                         return(-1);
295                 }
296                 
297                 ret = chdir (located_filename);
298                 g_free (located_filename);
299         }
300         
301         return(ret);
302 }
303
304 gchar *_wapi_basename (const gchar *filename)
305 {
306         gchar *new_filename = g_strdup (filename), *ret;
307
308         if (IS_PORTABILITY_SET) {
309                 g_strdelimit (new_filename, "\\", '/');
310         }
311
312         if (IS_PORTABILITY_DRIVE &&
313             g_ascii_isalpha (new_filename[0]) &&
314             (new_filename[1] == ':')) {
315                 int len = strlen (new_filename);
316                 
317                 g_memmove (new_filename, new_filename + 2, len - 2);
318                 new_filename[len - 2] = '\0';
319         }
320         
321         ret = g_path_get_basename (new_filename);
322         g_free (new_filename);
323         
324         return(ret);
325 }
326
327 gchar *_wapi_dirname (const gchar *filename)
328 {
329         gchar *new_filename = g_strdup (filename), *ret;
330
331         if (IS_PORTABILITY_SET) {
332                 g_strdelimit (new_filename, "\\", '/');
333         }
334
335         if (IS_PORTABILITY_DRIVE &&
336             g_ascii_isalpha (new_filename[0]) &&
337             (new_filename[1] == ':')) {
338                 int len = strlen (new_filename);
339                 
340                 g_memmove (new_filename, new_filename + 2, len - 2);
341                 new_filename[len - 2] = '\0';
342         }
343         
344         ret = g_path_get_dirname (new_filename);
345         g_free (new_filename);
346         
347         return(ret);
348 }
349
350 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
351 {
352         GDir *ret;
353         
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;
362                 
363                 if (located_filename == NULL) {
364                         return(NULL);
365                 }
366                 
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);
371                 }
372         }
373         
374         return(ret);
375 }
376
377
378 static gint
379 file_compare (gconstpointer a, gconstpointer b)
380 {
381         gchar *astr = *(gchar **) a;
382         gchar *bstr = *(gchar **) b;
383
384         return strcmp (astr, bstr);
385 }
386
387 static gint
388 get_errno_from_g_file_error (gint error)
389 {
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;
499         }
500
501         return error;
502 }
503
504 /* scandir using glib */
505 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
506                        gchar ***namelist)
507 {
508         GError *error = NULL;
509         GDir *dir;
510         GPtrArray *names;
511         gint result;
512         wapi_glob_t glob_buf;
513         int flags = 0, i;
514         
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;
525                 }
526
527                 errno = errnum;
528                 return -1;
529         }
530
531         if (IS_PORTABILITY_CASE) {
532                 flags = WAPI_GLOB_IGNORECASE;
533         }
534         
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.
540                  * 
541                  * TODO: should this be a MONO_IOMAP option?
542                  */
543                 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
544                 gint result2;
545                 
546                 g_dir_rewind (dir);
547                 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
548
549                 g_free (pattern2);
550
551                 if (result != 0) {
552                         result = result2;
553                 }
554         }
555         
556         g_dir_close (dir);
557         if (glob_buf.gl_pathc == 0) {
558                 return(0);
559         } else if (result != 0) {
560                 return(-1);
561         }
562         
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]));
566         }
567
568         _wapi_globfree (&glob_buf);
569
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);
574
575                 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
576         } else {
577                 g_ptr_array_free (names, TRUE);
578         }
579
580         return result;
581 }