Hey folks, Looks like there's a regression with inotify since the rewrite to use fanotify. Events are simply missing and not being delivered to userspace. Here's a simple test case, just compile and run it: ----8<--------8<--------8<--------8<--------8<--------8<--------8<---- #include #include #include #include #include #include #include #include #include enum { WD = 0x01, FOO = 0x02, BAR = 0x04, SUBDIR_WD = 0x08, SUBDIR = 0x10, SUBDIR_FOO = 0x20, ALL = 0x3f }; int main (int argc, char *argv[]) { int fd; char dirname[PATH_MAX]; char filename[PATH_MAX]; int ret; FILE * fp; int wd; int subdir_wd; char buf[4096]; int expected; ssize_t len; size_t sz; fd = inotify_init (); assert (fd >= 0); /* Create a temporary directory with two files and a sub-directory * containing just one file. */ strcpy (dirname, "/tmp/inotify_test_XXXXXX"); assert (mkdtemp (dirname)); strcpy (filename, dirname); strcat (filename, "/subdir"); ret = mkdir (filename, 0755); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/subdir/foo"); fp = fopen (filename, "w"); assert (fp); fprintf (fp, "file in a sub-directory\n"); ret = fclose (fp); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/foo"); fp = fopen (filename, "w"); assert (fp); fprintf (fp, "this is a test\n"); ret = fclose (fp); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/bar"); fp = fopen (filename, "w"); assert (fp); fprintf (fp, "this is another test\n"); ret = fclose (fp); assert (ret == 0); /* Watch those directories for deletions */ wd = inotify_add_watch (fd, dirname, IN_DELETE); assert (wd >= 0); strcpy (filename, dirname); strcat (filename, "/subdir"); subdir_wd = inotify_add_watch (fd, filename, IN_DELETE); assert (wd >= 0); /* Clean up the directory */ strcpy (filename, dirname); strcat (filename, "/subdir/foo"); ret = unlink (filename); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/subdir"); ret = rmdir (filename); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/bar"); ret = unlink (filename); assert (ret == 0); strcpy (filename, dirname); strcat (filename, "/foo"); ret = unlink (filename); assert (ret == 0); ret = rmdir (dirname); assert (ret == 0); /* Read the inotify events */ expected = ALL; while (expected) { struct inotify_event *ev; ssize_t off; printf ("Waiting for:"); if (expected & WD) printf (" wd"); if (expected & FOO) printf (" foo"); if (expected & BAR) printf (" bar"); if (expected & SUBDIR_WD) printf (" subdir_wd"); if (expected & SUBDIR) printf (" subdir"); if (expected & SUBDIR_FOO) printf (" subdir/foo"); printf ("\n"); len = read (fd, buf, sizeof buf); assert (len > 0); off = 0; while (off < len) { ev = (struct inotify_event *)(buf + off); sz = sizeof (struct inotify_event) + ev->len; off += sz; /* Eliminate the silly */ assert (! (ev->mask & IN_Q_OVERFLOW)); assert (! (ev->mask & IN_UNMOUNT)); /* Print the event for debugging */ printf ("Got "); if (ev->mask & IN_DELETE) printf ("DELETE "); if (ev->mask & IN_IGNORED) printf ("IGNORED "); if (ev->mask & IN_ISDIR) printf ("ISDIR "); if (ev->len) printf ("\"%.*s\" ", ev->len, ev->name); printf ("(%d)\n", ev->wd); /* Expect the file in the sub-directory to go */ if ((ev->wd == subdir_wd) && (ev->mask & IN_DELETE) && (! (ev->mask & IN_ISDIR)) && (strncmp (ev->name, "foo", ev->len) == 0)) expected &= ~SUBDIR_FOO; /* Expect the sub-directory to go */ if ((ev->wd == wd) && (ev->mask & IN_DELETE) && (ev->mask & IN_ISDIR) && (strncmp (ev->name, "subdir", ev->len) == 0)) expected &= ~SUBDIR; /* Expect an IS_IGNORED for the sub-directory watch */ if ((ev->wd == subdir_wd) && (ev->mask & IN_IGNORED)) expected &= ~SUBDIR_WD; /* Expect the first file in the directory to go */ if ((ev->wd == wd) && (ev->mask & IN_DELETE) && (! (ev->mask & IN_ISDIR)) && (strncmp (ev->name, "foo", ev->len) == 0)) expected &= ~FOO; /* Expect the second file in the directory to go */ if ((ev->wd == wd) && (ev->mask & IN_DELETE) && (! (ev->mask & IN_ISDIR)) && (strncmp (ev->name, "bar", ev->len) == 0)) expected &= ~BAR; /* And finally expect an IS_IGNORED for the directory */ if ((ev->wd == wd) && (ev->mask & IN_IGNORED)) expected &= ~WD; } } ret = close (fd); assert (ret == 0); printf ("\n"); printf ("All good!\n"); return 0; } ---->8-------->8-------->8-------->8-------->8-------->8-------->8---- With 2.6.30, you see (as you'd expect) an inotify delete event for each of the files and the sub-directory along with the IN_IGNORED for the sub-directory and directory as the watch gets cleaned up by the kernel: Waiting for: wd foo bar subdir_wd subdir subdir/foo Got DELETE "foo" (2) Got DELETE ISDIR "subdir" (1) Got IGNORED (2) Got DELETE "bar" (1) Got DELETE "foo" (1) Got IGNORED (1) All good! But with 2.6.31-rc2, the event for the "foo" file isn't delivered: Waiting for: wd foo bar subdir_wd subdir subdir/foo Got DELETE "foo" (2) Got DELETE ISDIR "subdir" (1) Got IGNORED (2) Got DELETE "bar" (1) Got IGNORED (1) Waiting for: foo This doesn't seem to be related to the removal of the parent directory, without removing that we still hang waiting for the event for that file. Scott -- Scott James Remnant scott@ubuntu.com