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