source: trunk/src/sh_fInotify.c@ 446

Last change on this file since 446 was 433, checked in by katerina, 12 years ago

Fix for ticket #338 (steady growth of memory usage).

File size: 18.4 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2011 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/***************************************************************************
21 *
22 * This file provides a module for samhain to use inotify for file checking.
23 *
24 */
25
26#include "config_xor.h"
27
28#if (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
29
30#include "samhain.h"
31#include "sh_utils.h"
32#include "sh_modules.h"
33#include "sh_pthread.h"
34#include "sh_inotify.h"
35#include "sh_unix.h"
36#include "sh_hash.h"
37#include "sh_files.h"
38#include "sh_ignore.h"
39
40#define FIL__ _("sh_fInotify.c")
41
42sh_watches sh_file_watches = SH_INOTIFY_INITIALIZER;
43
44#if defined(HAVE_SYS_INOTIFY_H)
45
46static sh_watches sh_file_missing = SH_INOTIFY_INITIALIZER;
47
48#include <sys/inotify.h>
49
50/* --- Configuration ------- */
51
52static int ShfInotifyActive = S_FALSE;
53
54static unsigned long ShfInotifyWatches = 0;
55
56static int sh_fInotify_active(const char *s)
57{
58 int value;
59
60 SL_ENTER(_("sh_fInotify_active"));
61 value = sh_util_flagval(s, &ShfInotifyActive);
62 if (value == 0 && ShfInotifyActive != S_FALSE)
63 {
64 sh.flag.inotify |= SH_INOTIFY_USE;
65 sh.flag.inotify |= SH_INOTIFY_DOSCAN;
66 sh.flag.inotify |= SH_INOTIFY_NEEDINIT;
67 }
68 if (value == 0 && ShfInotifyActive == S_FALSE)
69 {
70 sh.flag.inotify = 0;
71 }
72 SL_RETURN((value), _("sh_fInotify_active"));
73}
74
75static int sh_fInotify_watches(const char *s)
76{
77 int retval = -1;
78 char * foo;
79 unsigned long value;
80
81 SL_ENTER(_("sh_fInotify_watches"));
82
83 value = strtoul(s, &foo, 0);
84 if (*foo == '\0')
85 {
86 ShfInotifyWatches = (value > 2147483647) ? 2147483647 /* MAX_INT_32 */: value;
87 retval = 0;
88 }
89 SL_RETURN((retval), _("sh_fInotify_watches"));
90}
91
92
93sh_rconf sh_fInotify_table[] = {
94 {
95 N_("inotifyactive"),
96 sh_fInotify_active,
97 },
98 {
99 N_("inotifywatches"),
100 sh_fInotify_watches,
101 },
102 {
103 NULL,
104 NULL
105 }
106};
107
108/* --- End Configuration --- */
109
110static int sh_fInotify_init_internal(void);
111static int sh_fInotify_process(struct inotify_event * event);
112static int sh_fInotify_report(struct inotify_event * event, char * filename,
113 int class, unsigned long check_mask, int ftype, int rdepth);
114
115int sh_fInotify_init(struct mod_type * arg)
116{
117#ifndef HAVE_PTHREAD
118 (void) arg;
119 return SH_MOD_FAILED;
120#else
121
122 if (ShfInotifyActive == S_FALSE)
123 return SH_MOD_FAILED;
124
125 if (sh.flag.checkSum == SH_CHECK_INIT)
126 return SH_MOD_FAILED;
127
128 if (arg != NULL && arg->initval < 0 &&
129 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
130 {
131 /* Init from main thread */
132 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_DOSCAN; );
133 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_NEEDINIT; );
134
135 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
136 {
137 return SH_MOD_THREAD;
138 }
139 else
140 {
141 sh.flag.inotify = 0;
142 return SH_MOD_FAILED;
143 }
144 }
145 else if (arg != NULL && arg->initval < 0 &&
146 (sh.flag.isdaemon != S_TRUE && sh.flag.loop != S_TRUE))
147 {
148 sh.flag.inotify = 0;
149 return SH_MOD_FAILED;
150 }
151 else if (arg != NULL && arg->initval == SH_MOD_THREAD &&
152 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
153 {
154 /* Reconfigure from main thread */
155 /* sh_fInotify_init_internal(); */
156 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_DOSCAN; );
157 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_NEEDINIT; );
158 return SH_MOD_THREAD;
159 }
160
161 /* Within thread, init */
162 return sh_fInotify_init_internal();
163#endif
164}
165
166int sh_fInotify_run()
167{
168 ssize_t len = -1;
169 char * buffer;
170 static int count = 0;
171 static int count2 = 0;
172
173 if (ShfInotifyActive == S_FALSE)
174 {
175 return SH_MOD_FAILED;
176 }
177
178 if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) ||
179 (sh.flag.inotify & SH_INOTIFY_NEEDINIT))
180 {
181 if (0 != sh_fInotify_init_internal())
182 {
183 return SH_MOD_FAILED;
184 }
185 }
186
187 buffer = SH_ALLOC(16384);
188
189 /* Blocking read from inotify file descriptor.
190 */
191 len = sh_inotify_read_timeout(buffer, 16384, 1);
192
193 if (len > 0)
194 {
195 struct inotify_event *event;
196 int i = 0;
197
198 while (i < len)
199 {
200 event = (struct inotify_event *) &(buffer[i]);
201
202 sh_fInotify_process(event);
203
204 i += sizeof (struct inotify_event) + event->len;
205 }
206
207 if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) ||
208 (sh.flag.inotify & SH_INOTIFY_NEEDINIT))
209 {
210 if (0 != sh_fInotify_init_internal())
211 {
212 SH_FREE(buffer);
213 return SH_MOD_FAILED;
214 }
215 }
216 }
217
218 /* Re-scan 'dormant' list of sh_file_missing.
219 */
220 sh_inotify_recheck_watches (&sh_file_watches, &sh_file_missing);
221
222 ++count;
223 ++count2;
224
225 if (count >= 10)
226 {
227 count = 0; /* Re-expand glob patterns to discover added files. */
228 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_INSCAN; );
229 sh_files_check_globFilePatterns();
230 SH_INOTIFY_IFUSED( sh.flag.inotify &= ~SH_INOTIFY_INSCAN; );
231 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_NEEDINIT; );
232 }
233
234 if (count2 >= 300)
235 {
236 count2 = 0; /* Update baseline database. */
237 if (sh.flag.checkSum == SH_CHECK_CHECK && sh.flag.update == S_TRUE)
238 sh_hash_writeout ();
239 }
240
241 SH_FREE(buffer);
242 return 0;
243}
244
245/* We block in the read() call on the inotify descriptor,
246 * so we always run.
247 */
248int sh_fInotify_timer(time_t tcurrent)
249{
250 (void) tcurrent;
251 return 1;
252}
253
254int sh_fInotify_cleanup()
255{
256 sh_inotify_purge_dormant(&sh_file_watches);
257 sh_inotify_remove(&sh_file_watches);
258 sh_inotify_init(&sh_file_watches);
259 return 0;
260}
261
262int sh_fInotify_reconf()
263{
264 sh.flag.inotify = 0;
265
266 ShfInotifyWatches = 0;
267 ShfInotifyActive = 0;
268
269 return sh_fInotify_cleanup();
270}
271
272#define PROC_WATCHES_MAX _("/proc/sys/fs/inotify/max_user_watches")
273
274static void sh_fInotify_set_nwatches()
275{
276 static int fails = 0;
277
278 if (ShfInotifyWatches == 0 || fails == 1)
279 return;
280
281 if (0 == access(PROC_WATCHES_MAX, R_OK|W_OK)) /* flawfinder: ignore */
282 {
283 FILE * fd;
284
285 if (NULL != (fd = fopen(PROC_WATCHES_MAX, "r+")))
286 {
287 char str[128];
288 char * ret;
289 char * ptr;
290 unsigned long wn;
291
292 str[0] = '\0';
293 ret = fgets(str, 128, fd);
294 if (ret && *str != '\0')
295 {
296 wn = strtoul(str, &ptr, 0);
297 if (*ptr == '\0' || *ptr == '\n')
298 {
299 if (wn < ShfInotifyWatches)
300 {
301 sl_snprintf(str, sizeof(str), "%lu\n", ShfInotifyWatches);
302 (void) fseek(fd, 0L, SEEK_SET);
303 fputs(str, fd);
304 }
305 sl_fclose(FIL__, __LINE__, fd);
306 return;
307 }
308 }
309 sl_fclose(FIL__, __LINE__, fd);
310 }
311 }
312 SH_MUTEX_LOCK(mutex_thread_nolog);
313 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
314 _("Cannot set max_user_watches"),
315 _("sh_fInotify_set_nwatches"));
316 SH_MUTEX_UNLOCK(mutex_thread_nolog);
317 fails = 1;
318 return;
319}
320
321/* The watch fd is thread specific. To have it in the fInotify thread,
322 * the main thread writes a list of files/dirs to watch, and here we
323 * now pop files from the list to add watches for them.
324 */
325static int sh_fInotify_init_internal()
326{
327 char * filename;
328 int class;
329 int type;
330 int rdepth;
331 unsigned long check_mask;
332 int retval;
333 int errnum;
334
335 if (ShfInotifyActive == S_FALSE)
336 return SH_MOD_FAILED;
337
338 /* Wait until file scan is finished.
339 */
340 while((sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0)
341 {
342 retry_msleep(1,0);
343
344 if (ShfInotifyActive == S_FALSE)
345 return SH_MOD_FAILED;
346 }
347
348 sh_fInotify_set_nwatches();
349
350 while (NULL != (filename = sh_inotify_pop_dormant(&sh_file_watches,
351 &class, &check_mask,
352 &type, &rdepth)))
353 {
354 retval = sh_inotify_add_watch(filename, &sh_file_watches, &errnum,
355 class, check_mask, type, rdepth);
356
357 if (retval < 0)
358 {
359 char errbuf[SH_ERRBUF_SIZE];
360
361 sh_error_message(errnum, errbuf, sizeof(errbuf));
362
363 if ((errnum == ENOENT) || (errnum == EEXIST))
364 {
365 /* (1) Did it exist at init ?
366 */
367 if (sh_hash_have_it (filename) >= 0)
368 {
369 /* (2) Do we want to report on it ?
370 */
371 if (S_FALSE == sh_ignore_chk_del(filename))
372 {
373 char * epath = sh_util_safe_name (filename);
374
375 SH_MUTEX_LOCK(mutex_thread_nolog);
376 sh_error_handle( SH_ERR_ALL /* debug */,
377 FIL__, __LINE__, errnum, MSG_E_SUBGPATH,
378 errbuf, _("sh_fInotify_init_internal"), epath);
379 SH_MUTEX_UNLOCK(mutex_thread_nolog);
380 SH_FREE(epath);
381 }
382 }
383 }
384 else
385 {
386 SH_MUTEX_LOCK(mutex_thread_nolog);
387 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
388 errbuf, _("sh_fInotify_init_internal"));
389 SH_MUTEX_UNLOCK(mutex_thread_nolog);
390 }
391 }
392 SH_FREE(filename);
393 }
394
395 /* Need this because mod_check() may run after
396 * DOSCAN is finished, hence wouldn't call init().
397 */
398 SH_INOTIFY_IFUSED( sh.flag.inotify &= ~SH_INOTIFY_NEEDINIT; );
399
400 return 0;
401}
402
403static void sh_fInotify_logmask(struct inotify_event * event)
404{
405 char dbgbuf[256];
406
407 sl_strlcpy (dbgbuf, "inotify mask: ", sizeof(dbgbuf));
408
409 if (event->mask & IN_ACCESS) sl_strlcat(dbgbuf, "IN_ACCESS ", sizeof(dbgbuf));
410 if (event->mask & IN_ATTRIB) sl_strlcat(dbgbuf, "IN_ATTRIB ", sizeof(dbgbuf));
411 if (event->mask & IN_CLOSE_WRITE) sl_strlcat(dbgbuf, "IN_CLOSE_WRITE ", sizeof(dbgbuf));
412 if (event->mask & IN_CLOSE_NOWRITE) sl_strlcat(dbgbuf, "IN_CLOSE_NOWRITE ", sizeof(dbgbuf));
413 if (event->mask & IN_CREATE) sl_strlcat(dbgbuf, "IN_CREATE ", sizeof(dbgbuf));
414 if (event->mask & IN_DELETE) sl_strlcat(dbgbuf, "IN_DELETE ", sizeof(dbgbuf));
415 if (event->mask & IN_DELETE_SELF) sl_strlcat(dbgbuf, "IN_DELETE_SELF ", sizeof(dbgbuf));
416 if (event->mask & IN_MODIFY) sl_strlcat(dbgbuf, "IN_MODIFY ", sizeof(dbgbuf));
417 if (event->mask & IN_MOVE_SELF) sl_strlcat(dbgbuf, "IN_MOVE_SELF ", sizeof(dbgbuf));
418 if (event->mask & IN_MOVED_FROM) sl_strlcat(dbgbuf, "IN_MOVED_FROM ", sizeof(dbgbuf));
419 if (event->mask & IN_MOVED_TO) sl_strlcat(dbgbuf, "IN_MOVED_TO ", sizeof(dbgbuf));
420 if (event->mask & IN_OPEN) sl_strlcat(dbgbuf, "IN_OPEN ", sizeof(dbgbuf));
421 if (event->mask & IN_IGNORED) sl_strlcat(dbgbuf, "IN_IGNORED ", sizeof(dbgbuf));
422 if (event->mask & IN_ISDIR) sl_strlcat(dbgbuf, "IN_ISDIR ", sizeof(dbgbuf));
423 if (event->mask & IN_Q_OVERFLOW) sl_strlcat(dbgbuf, "IN_Q_OVERFLOW ", sizeof(dbgbuf));
424 if (event->mask & IN_UNMOUNT) sl_strlcat(dbgbuf, "IN_UNMOUNT ", sizeof(dbgbuf));
425
426 /* fprintf(stderr, "FIXME: %s\n", dbgbuf); */
427
428 SH_MUTEX_LOCK(mutex_thread_nolog);
429 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
430 dbgbuf, _("sh_fInotify_process"));
431 SH_MUTEX_UNLOCK(mutex_thread_nolog);
432}
433
434static int sh_fInotify_process(struct inotify_event * event)
435{
436 int class;
437 int ftype;
438 int rdepth;
439 unsigned long check_mask;
440 char * filename;
441 extern int flag_err_debug;
442
443 if (flag_err_debug == SL_TRUE)
444 {
445 sh_fInotify_logmask(event);
446 }
447
448 if (event->wd >= 0)
449 {
450 filename = sh_inotify_search_item(&sh_file_watches, event->wd,
451 &class, &check_mask, &ftype, &rdepth);
452
453 if (filename)
454 {
455 sh_fInotify_report(event, filename, class, check_mask, ftype, rdepth);
456 SH_FREE(filename);
457 }
458 else if (sh.flag.inotify & SH_INOTIFY_NEEDINIT)
459 {
460 return 1;
461 }
462 else if ((event->mask & IN_UNMOUNT) == 0 && (event->mask & IN_IGNORED) == 0)
463 {
464 /* Remove watch ? Seems reasonable. */
465 sh_inotify_rm_watch(NULL, NULL, event->wd);
466
467 SH_MUTEX_LOCK(mutex_thread_nolog);
468 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, event->wd, MSG_E_SUBGEN,
469 _("Watch removed: file path unknown"),
470 _("sh_fInotify_process"));
471 SH_MUTEX_UNLOCK(mutex_thread_nolog);
472 }
473 }
474 else if ((event->mask & IN_Q_OVERFLOW) != 0)
475 {
476 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_DOSCAN; );
477 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_NEEDINIT; );
478
479 SH_MUTEX_LOCK(mutex_thread_nolog);
480 sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, event->wd, MSG_E_SUBGEN,
481 _("Inotify queue overflow"),
482 _("sh_fInotify_process"));
483 SH_MUTEX_UNLOCK(mutex_thread_nolog);
484 return 1;
485 }
486
487 return 0;
488}
489
490void sh_fInotify_report_add(char * path, int class, unsigned long check_mask)
491{
492 if (S_FALSE == sh_ignore_chk_new(path))
493 {
494 int reported = 0;
495
496 sh_files_clear_file_reported(path);
497
498 sh_files_search_file(path, &class, &check_mask, &reported);
499
500 sh_files_filecheck (class, check_mask, path, NULL,
501 &reported, 0);
502 if (SH_FFLAG_REPORTED_SET(reported))
503 sh_files_set_file_reported(path);
504 }
505 return;
506}
507
508
509static void sh_fInotify_report_miss(char * name, int level)
510{
511 char * tmp = sh_util_safe_name (name);
512
513 SH_MUTEX_LOCK(mutex_thread_nolog);
514 sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS, tmp);
515 SH_MUTEX_UNLOCK(mutex_thread_nolog);
516 ++sh.statistics.files_report;
517 SH_FREE(tmp);
518 return;
519}
520
521static int sh_fInotify_report_change (struct inotify_event * event,
522 char * path, char * filename,
523 int class, unsigned long check_mask, int ftype)
524{
525 int reported;
526 int ret;
527
528
529 if (S_FALSE == sh_ignore_chk_mod(path))
530 {
531 ret = sh_files_search_file(path, &class, &check_mask, &reported);
532
533 if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE))
534 {
535 ; /* do nothing, watch was for directory monitored as file only */
536 }
537 else
538 {
539 sh_files_filecheck (class, check_mask, filename,
540 (event->len > 0) ? event->name : NULL,
541 &reported, 0);
542 }
543 }
544 return 0;
545}
546
547
548static int sh_fInotify_report_missing (struct inotify_event * event,
549 char * path,
550 int class, unsigned long check_mask, int ftype)
551{
552 int reported;
553 int isdir = (event->mask & IN_ISDIR);
554 int level = (class == SH_LEVEL_ALLIGNORE) ?
555 ShDFLevel[class] :
556 ShDFLevel[(isdir == 0) ? SH_ERR_T_FILE : SH_ERR_T_DIR];
557
558 if (S_FALSE == sh_ignore_chk_del(path))
559 {
560 if (0 != hashreport_missing(path, level))
561 {
562 int ret = sh_files_search_file(path, &class, &check_mask, &reported);
563
564 if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE))
565 {
566 ; /* do nothing, watch was for directory monitored as file only */
567 }
568 else
569 {
570 /* Removal of a directory triggers:
571 * (1) IN_DELETE IN_ISDIR
572 * (2) IN_DELETE_SELF
573 */
574 if ((event->mask & IN_DELETE_SELF) == 0)
575 sh_fInotify_report_miss(path, level);
576 }
577 }
578 }
579
580#ifndef REPLACE_OLD
581 sh_hash_set_visited_true(path);
582#else
583 sh_hash_set_missing(path);
584#endif
585 if (sh.flag.reportonce == S_TRUE)
586 sh_files_set_file_reported(path);
587
588 /* Move to 'dormant' list, if not file within directory.
589 */
590 if (event->len == 0)
591 sh_inotify_rm_watch(&sh_file_watches, &sh_file_missing, event->wd);
592
593 return 0;
594}
595
596static int sh_fInotify_report_added (struct inotify_event * event,
597 char * path, char * filename,
598 int class, unsigned long check_mask,
599 int ftype, int rdepth)
600{
601 if (S_FALSE == sh_ignore_chk_new(path))
602 {
603 int reported;
604 int ret;
605 int retD = 0;
606 int rdepthD = rdepth;
607
608 sh_files_clear_file_reported(path);
609
610 ret = sh_files_search_file(path, &class, &check_mask, &reported);
611
612 if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE))
613 {
614 ; /* do nothing, watch was for directory monitored as file only */
615 }
616 else
617 {
618 int classD = class;
619 int reportedD = reported;
620 unsigned long check_maskD = check_mask;
621
622 if (event->mask & IN_ISDIR)
623 {
624 retD = sh_files_search_dir(path, &classD, &check_maskD,
625 &reportedD, &rdepthD);
626 if (retD != 0)
627 {
628 if (ret == 0)
629 {
630 class = classD;
631 check_mask = check_maskD;
632 }
633 }
634 }
635
636 sh_files_filecheck (class, check_mask, filename,
637 (event->len > 0) ? event->name : NULL,
638 &reported, 0);
639
640 if (event->mask & IN_ISDIR)
641 {
642 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_INSCAN; );
643 sh_files_checkdir (classD, check_maskD, rdepthD,
644 path, (event->len > 0) ? event->name : NULL);
645 SH_INOTIFY_IFUSED( sh.flag.inotify &= ~SH_INOTIFY_INSCAN; );
646 SH_INOTIFY_IFUSED( sh.flag.inotify |= SH_INOTIFY_NEEDINIT; );
647 sh_dirs_reset ();
648 sh_files_reset ();
649 }
650
651 }
652
653 if (SH_FFLAG_REPORTED_SET(reported))
654 sh_files_set_file_reported(path);
655
656 if ((ret != 0) || (event->mask & IN_ISDIR))
657 {
658 sh_inotify_add_watch(path, &sh_file_watches, &ret,
659 class, check_mask,
660 (event->mask & IN_ISDIR)?SH_INOTIFY_DIR:SH_INOTIFY_FILE,
661 rdepthD);
662 }
663 }
664 return 0;
665}
666
667static int sh_fInotify_report(struct inotify_event * event, char * filename,
668 int class, unsigned long check_mask, int ftype, int rdepth)
669{
670 char * fullpath = NULL;
671 char * path;
672
673 if (event->len > 0)
674 {
675 fullpath = sh_util_strconcat(filename, "/", event->name, NULL);
676 path = fullpath;
677 }
678 else
679 {
680 path = filename;
681 }
682
683 if ( (event->mask & (IN_ATTRIB|IN_MODIFY)) != 0)
684 {
685 sh_fInotify_report_change (event, path, filename,
686 class, check_mask, ftype);
687 }
688 else if ((event->mask & (IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF|IN_MOVED_FROM)) != 0)
689 {
690 sh_fInotify_report_missing (event, path,
691 class, check_mask, ftype);
692 }
693 else if((event->mask & (IN_CREATE|IN_MOVED_TO)) != 0)
694 {
695 sh_fInotify_report_added (event, path, filename,
696 class, check_mask,
697 ftype, rdepth);
698 }
699
700 if (fullpath)
701 SH_FREE(fullpath);
702
703 return 0;
704}
705
706
707#endif
708
709#endif
Note: See TracBrowser for help on using the repository browser.