mocha: show actual file in Diff dialog (WEB-15907)
[idea/community.git] / native / fsNotifier / linux / inotify.c
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "fsnotifier.h"
18
19 #include <dirent.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/inotify.h>
26 #include <sys/stat.h>
27 #include <syslog.h>
28 #include <unistd.h>
29
30 #if defined(__i386__)
31 __asm__(".symver memcpy,memcpy@GLIBC_2.0");
32 #elif defined(__amd64__)
33 __asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
34 #endif
35
36
37 #define WATCH_COUNT_NAME "/proc/sys/fs/inotify/max_user_watches"
38
39 #define DEFAULT_SUBDIR_COUNT 5
40
41 typedef struct __watch_node {
42   int wd;
43   struct __watch_node* parent;
44   array* kids;
45   int path_len;
46   char path[];
47 } watch_node;
48
49 static int inotify_fd = -1;
50 static int watch_count = 0;
51 static table* watches;
52 static bool limit_reached = false;
53 static void (* callback)(const char*, int) = NULL;
54
55 #define EVENT_SIZE (sizeof(struct inotify_event))
56 #define EVENT_BUF_LEN (2048 * (EVENT_SIZE + 16))
57 static char event_buf[EVENT_BUF_LEN];
58
59 static char path_buf[2 * PATH_MAX];
60
61 static void read_watch_descriptors_count();
62 static void watch_limit_reached();
63
64
65 bool init_inotify() {
66   inotify_fd = inotify_init();
67   if (inotify_fd < 0) {
68     int e = errno;
69     userlog(LOG_ERR, "inotify_init: %s", strerror(e));
70     if (e == EMFILE) {
71       message(MSG_INSTANCE_LIMIT);
72     }
73     return false;
74   }
75   userlog(LOG_DEBUG, "inotify fd: %d", get_inotify_fd());
76
77   read_watch_descriptors_count();
78   if (watch_count <= 0) {
79     close(inotify_fd);
80     inotify_fd = -1;
81     return false;
82   }
83   userlog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
84
85   watches = table_create(watch_count);
86   if (watches == NULL) {
87     userlog(LOG_ERR, "out of memory");
88     close(inotify_fd);
89     inotify_fd = -1;
90     return false;
91   }
92
93   return true;
94 }
95
96 static void read_watch_descriptors_count() {
97   FILE* f = fopen(WATCH_COUNT_NAME, "r");
98   if (f == NULL) {
99     userlog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
100     return;
101   }
102
103   char* str = read_line(f);
104   if (str == NULL) {
105     userlog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
106   }
107   else {
108     watch_count = atoi(str);
109   }
110
111   fclose(f);
112 }
113
114
115 void set_inotify_callback(void (* _callback)(const char*, int)) {
116   callback = _callback;
117 }
118
119
120 int get_inotify_fd() {
121   return inotify_fd;
122 }
123
124
125 #define EVENT_MASK IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_MOVE_SELF
126
127 static int add_watch(int path_len, watch_node* parent) {
128   int wd = inotify_add_watch(inotify_fd, path_buf, EVENT_MASK);
129   if (wd < 0) {
130     if (errno == EACCES || errno == ENOENT) {
131       userlog(LOG_DEBUG, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
132       return ERR_IGNORE;
133     }
134     else if (errno == ENOSPC) {
135       userlog(LOG_WARNING, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
136       watch_limit_reached();
137       return ERR_CONTINUE;
138     }
139     else {
140       userlog(LOG_ERR, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
141       return ERR_ABORT;
142     }
143   }
144   else {
145     userlog(LOG_DEBUG, "watching %s: %d", path_buf, wd);
146   }
147
148   watch_node* node = table_get(watches, wd);
149   if (node != NULL) {
150     if (node->wd != wd) {
151       userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s)", wd, path_buf, node->wd, node->path);
152       return ERR_ABORT;
153     }
154     else if (strcmp(node->path, path_buf) != 0) {
155       char buf1[PATH_MAX], buf2[PATH_MAX];
156       const char* normalized1 = realpath(node->path, buf1);
157       const char* normalized2 = realpath(path_buf, buf2);
158       if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) {
159         userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path_buf, node->path);
160         return ERR_ABORT;
161       }
162       else {
163         userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path_buf, node->path, normalized1);
164         return ERR_IGNORE;
165       }
166     }
167
168     return wd;
169   }
170
171   node = malloc(sizeof(watch_node) + path_len + 1);
172   CHECK_NULL(node, ERR_ABORT);
173   memcpy(node->path, path_buf, path_len + 1);
174   node->path_len = path_len;
175   node->wd = wd;
176   node->parent = parent;
177   node->kids = NULL;
178
179   if (parent != NULL) {
180     if (parent->kids == NULL) {
181       parent->kids = array_create(DEFAULT_SUBDIR_COUNT);
182       CHECK_NULL(parent->kids, ERR_ABORT);
183     }
184     CHECK_NULL(array_push(parent->kids, node), ERR_ABORT);
185   }
186
187   if (table_put(watches, wd, node) == NULL) {
188     userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path_buf);
189     return ERR_ABORT;
190   }
191
192   return wd;
193 }
194
195 static void watch_limit_reached() {
196   if (!limit_reached) {
197     limit_reached = true;
198     message(MSG_WATCH_LIMIT);
199   }
200 }
201
202 static void rm_watch(int wd, bool update_parent) {
203   watch_node* node = table_get(watches, wd);
204   if (node == NULL) {
205     return;
206   }
207
208   userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->path, node->wd, node);
209
210   if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
211     userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno));
212   }
213
214   for (int i=0; i<array_size(node->kids); i++) {
215     watch_node* kid = array_get(node->kids, i);
216     if (kid != NULL) {
217       rm_watch(kid->wd, false);
218     }
219   }
220
221   if (update_parent && node->parent != NULL) {
222     for (int i=0; i<array_size(node->parent->kids); i++) {
223       if (array_get(node->parent->kids, i) == node) {
224         array_put(node->parent->kids, i, NULL);
225         break;
226       }
227     }
228   }
229
230   array_delete(node->kids);
231   free(node);
232   table_put(watches, wd, NULL);
233 }
234
235
236 static int walk_tree(int path_len, watch_node* parent, bool recursive, array* mounts) {
237   for (int j=0; j<array_size(mounts); j++) {
238     char* mount = array_get(mounts, j);
239     if (strncmp(path_buf, mount, strlen(mount)) == 0) {
240       userlog(LOG_DEBUG, "watch path '%s' crossed mount point '%s' - skipping", path_buf, mount);
241       return ERR_IGNORE;
242     }
243   }
244
245   DIR* dir = NULL;
246   if (recursive) {
247     if ((dir = opendir(path_buf)) == NULL) {
248       if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) {
249         userlog(LOG_DEBUG, "opendir(%s): %d", path_buf, errno);
250         return ERR_IGNORE;
251       }
252       else {
253         userlog(LOG_ERR, "opendir(%s): %s", path_buf, strerror(errno));
254         return ERR_CONTINUE;
255       }
256     }
257   }
258
259   int id = add_watch(path_len, parent);
260
261   if (dir == NULL) {
262     return id;
263   }
264   else if (id < 0) {
265     closedir(dir);
266     return id;
267   }
268
269   path_buf[path_len] = '/';
270
271   struct dirent* entry;
272   while ((entry = readdir(dir)) != NULL) {
273     if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
274       continue;
275     }
276     if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_DIR) {
277       continue;
278     }
279
280     int name_len = strlen(entry->d_name);
281     memcpy(path_buf + path_len + 1, entry->d_name, name_len + 1);
282
283     if (entry->d_type == DT_UNKNOWN) {
284       struct stat st;
285       if (stat(path_buf, &st) != 0) {
286         userlog(LOG_DEBUG, "(DT_UNKNOWN) stat(%s): %d", path_buf, errno);
287         continue;
288       }
289       if (!S_ISDIR(st.st_mode)) {
290         continue;
291       }
292     }
293
294     int subdir_id = walk_tree(path_len + 1 + name_len, table_get(watches, id), recursive, mounts);
295     if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
296       rm_watch(id, true);
297       id = subdir_id;
298       break;
299     }
300   }
301
302   closedir(dir);
303   return id;
304 }
305
306
307 int watch(const char* root, array* mounts) {
308   bool recursive = true;
309   if (root[0] == '|') {
310     root++;
311     recursive = false;
312   }
313
314   int path_len = strlen(root);
315   if (root[path_len - 1] == '/') {
316     --path_len;
317   }
318
319   struct stat st;
320   if (stat(root, &st) != 0) {
321     if (errno == ENOENT) {
322       return ERR_MISSING;
323     }
324     else if (errno == EACCES || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOTDIR) {
325       userlog(LOG_INFO, "stat(%s): %s", root, strerror(errno));
326       return ERR_CONTINUE;
327     }
328     else {
329       userlog(LOG_ERR, "stat(%s): %s", root, strerror(errno));
330       return ERR_ABORT;
331     }
332   }
333
334   if (S_ISREG(st.st_mode)) {
335     recursive = false;
336   }
337   else if (!S_ISDIR(st.st_mode)) {
338     userlog(LOG_WARNING, "unexpected node type: %s, %d", root, st.st_mode);
339     return ERR_IGNORE;
340   }
341
342   memcpy(path_buf, root, path_len);
343   path_buf[path_len] = '\0';
344   return walk_tree(path_len, NULL, recursive, mounts);
345 }
346
347
348 void unwatch(int id) {
349   rm_watch(id, true);
350 }
351
352
353 static bool process_inotify_event(struct inotify_event* event) {
354   watch_node* node = table_get(watches, event->wd);
355   if (node == NULL) {
356     return true;
357   }
358
359   bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR;
360   userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path);
361
362   int path_len = node->path_len;
363   memcpy(path_buf, node->path, path_len + 1);
364   if (event->len > 0) {
365     path_buf[path_len] = '/';
366     int name_len = strlen(event->name);
367     memcpy(path_buf + path_len + 1, event->name, name_len + 1);
368     path_len += name_len + 1;
369   }
370
371   if (callback != NULL) {
372     (*callback)(path_buf, event->mask);
373   }
374
375   if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) {
376     int result = walk_tree(path_len, node, true, NULL);
377     if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
378       return false;
379     }
380   }
381
382   if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) {
383     for (int i=0; i<array_size(node->kids); i++) {
384       watch_node* kid = array_get(node->kids, i);
385       if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) {
386         rm_watch(kid->wd, false);
387         array_put(node->kids, i, NULL);
388         break;
389       }
390     }
391   }
392
393   return true;
394 }
395
396
397 bool process_inotify_input() {
398   ssize_t len = read(inotify_fd, event_buf, EVENT_BUF_LEN);
399   if (len < 0) {
400     userlog(LOG_ERR, "read: %s", strerror(errno));
401     return false;
402   }
403
404   int i = 0;
405   while (i < len) {
406     struct inotify_event* event = (struct inotify_event*) &event_buf[i];
407     i += EVENT_SIZE + event->len;
408
409     if (event->mask & IN_IGNORED) {
410       continue;
411     }
412     if (event->mask & IN_Q_OVERFLOW) {
413       userlog(LOG_INFO, "event queue overflow");
414       continue;
415     }
416
417     if (!process_inotify_event(event)) {
418       return false;
419     }
420   }
421
422   return true;
423 }
424
425
426 void close_inotify() {
427   if (watches != NULL) {
428     table_delete(watches);
429   }
430
431   if (inotify_fd >= 0) {
432     close(inotify_fd);
433   }
434 }