[runtime] AddWatcher icall must look up the path in an IOMAP-aware way
[mono.git] / mono / metadata / filewatcher.c
1 /*
2  * filewatcher.c: File System Watcher internal calls
3  *
4  * Authors:
5  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6  *
7  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/filewatcher.h>
17 #include <mono/metadata/marshal.h>
18 #include <mono/utils/mono-dl.h>
19 #include <mono/utils/mono-io-portability.h>
20 #ifdef HOST_WIN32
21
22 /*
23  * TODO:
24  * We use the managed watcher on windows, so the code inside this #if is never used
25  */
26 gint
27 ves_icall_System_IO_FSW_SupportsFSW (void)
28 {
29         return 1;
30 }
31
32 gboolean
33 ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn,
34                                                MonoString **filename,
35                                                gint *code,
36                                                gint *reqnum)
37 {
38         return FALSE;
39 }
40
41 #else
42
43 static int (*FAMNextEvent) (gpointer, gpointer);
44
45 gint
46 ves_icall_System_IO_FSW_SupportsFSW (void)
47 {
48 #if HAVE_KQUEUE
49         return 3;
50 #else
51         MonoDl *fam_module;
52         int lib_used = 4; /* gamin */
53         int inotify_instance;
54         void *iter;
55         char *err;
56
57         MONO_ARCH_SAVE_REGS;
58
59         inotify_instance = ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ();
60         if (inotify_instance != -1) {
61                 close (inotify_instance);
62                 return 5; /* inotify */
63         }
64
65         iter = NULL;
66         fam_module = mono_dl_open ("libgamin-1.so", MONO_DL_LAZY, NULL);
67         if (fam_module == NULL) {
68                 lib_used = 2; /* FAM */
69                 iter = NULL;
70                 fam_module = mono_dl_open ("libfam.so", MONO_DL_LAZY, NULL);
71         }
72
73         if (fam_module == NULL)
74                 return 0;
75
76         err = mono_dl_symbol (fam_module, "FAMNextEvent", (gpointer *) &FAMNextEvent);
77         g_free (err);
78         if (FAMNextEvent == NULL)
79                 return 0;
80
81         return lib_used;
82 #endif
83 }
84
85 /* Almost copied from fam.h. Weird, I know */
86 typedef struct {
87         gint reqnum;
88 } FAMRequest;
89
90 typedef struct FAMEvent {
91         gpointer fc;
92         FAMRequest fr;
93         gchar *hostname;
94         gchar filename [PATH_MAX];
95         gpointer userdata;
96         gint code;
97 } FAMEvent;
98
99 gboolean
100 ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn,
101                                                MonoString **filename,
102                                                gint *code,
103                                                gint *reqnum)
104 {
105         FAMEvent ev;
106
107         MONO_ARCH_SAVE_REGS;
108
109         if (FAMNextEvent (conn, &ev) == 1) {
110                 *filename = mono_string_new (mono_domain_get (), ev.filename);
111                 *code = ev.code;
112                 *reqnum = ev.fr.reqnum;
113                 return TRUE;
114         }
115
116         return FALSE;
117 }
118 #endif
119
120 #if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) && !defined(__NR_inotify_init)
121 #  if defined(__i386__)
122 #     define __NR_inotify_init          291
123 #  elif defined(__x86_64__)
124 #     define __NR_inotify_init          253
125 #  elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__)
126 #     define __NR_inotify_init          275
127 #  elif defined (__s390__) || defined (__s390x__)
128 #     define __NR_inotify_init          284
129 #  elif defined(__sparc__) || defined (__sparc64__)
130 #     define __NR_inotify_init          151
131 #  elif defined (__ia64__)
132 #     define __NR_inotify_init          1277
133 #  elif defined (__arm__)
134 #     define __NR_inotify_init          316
135 #  elif defined(__alpha__)
136 #     define __NR_inotify_init          444
137 #  endif
138 #ifdef __NR_inotify_init
139 #  ifndef __NR_inotify_add_watch
140 #    define __NR_inotify_add_watch (__NR_inotify_init + 1)
141 #  endif
142 #  ifndef __NR_inotify_rm_watch
143 #    define __NR_inotify_rm_watch (__NR_inotify_init + 2)
144 #  endif
145 #endif
146 #endif
147
148 #if !defined(__linux__) || !defined(__NR_inotify_init)
149 int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
150 {
151         return -1;
152 }
153
154 int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask)
155 {
156         return -1;
157 }
158
159 int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
160 {
161         return -1;
162 }
163 #else
164 #include <errno.h>
165
166 int
167 ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
168 {
169         return syscall (__NR_inotify_init);
170 }
171
172 int
173 ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *name, gint32 mask)
174 {
175         char *str, *path;
176         int retval;
177
178         MONO_ARCH_SAVE_REGS;
179
180         if (name == NULL)
181                 return -1;
182
183         str = mono_string_to_utf8 (name);
184         path = mono_portability_find_file (str, TRUE);
185         if (!path)
186                 path = str;
187
188         retval = syscall (__NR_inotify_add_watch, fd, path, mask);
189         if (retval < 0) {
190                 switch (errno) {
191                 case EACCES:
192                         errno = ERROR_ACCESS_DENIED;
193                         break;
194                 case EBADF:
195                         errno = ERROR_INVALID_HANDLE;
196                         break;
197                 case EFAULT:
198                         errno = ERROR_INVALID_ACCESS;
199                         break;
200                 case EINVAL:
201                         errno = ERROR_INVALID_DATA;
202                         break;
203                 case ENOMEM:
204                         errno = ERROR_NOT_ENOUGH_MEMORY;
205                         break;
206                 case ENOSPC:
207                         errno = ERROR_TOO_MANY_OPEN_FILES;
208                         break;
209                 default:
210                         errno = ERROR_GEN_FAILURE;
211                         break;
212                 }
213                 mono_marshal_set_last_error ();
214         }
215         if (path != str)
216                 g_free (path);
217         g_free (str);
218         return retval;
219 }
220
221 int
222 ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
223 {
224         return syscall (__NR_inotify_rm_watch, fd, watch_descriptor);
225 }
226 #endif
227