source: trunk/src/sh_fInotify.c@ 491

Last change on this file since 491 was 488, checked in by katerina, 9 years ago

Fix for tickets #386 (silent check) and #387 (linux audit support).

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