Skip to content

Commit 83b7881

Browse files
committed
Refactor; update tests to identify known issues
1 parent fb02aa1 commit 83b7881

File tree

3 files changed

+136
-29
lines changed

3 files changed

+136
-29
lines changed

README.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ func main() {
2222
2323
flag.StringVar(&listenPath, "listen-path", "", "path to watch events")
2424
flag.Parse()
25-
2625
if listenPath == "" {
2726
fmt.Println("missing listen path")
2827
os.Exit(1)
@@ -41,21 +40,20 @@ func main() {
4140
fanotify.FileModified |
4241
fanotify.FileOpenedForExec |
4342
fanotify.FileAttribChanged |
44-
fanotify.FileOrDirAttribChanged |
43+
fanotify.FileOrDirectoryAttribChanged |
4544
fanotify.FileCreated |
46-
fanotify.FileOrDirCreated |
45+
fanotify.FileOrDirectoryCreated |
4746
fanotify.FileDeleted |
48-
fanotify.FileOrDirDeleted |
47+
fanotify.FileOrDirectoryDeleted |
4948
fanotify.WatchedFileDeleted |
50-
fanotify.WatchedFileOrDirDeleted |
49+
fanotify.WatchedFileOrDirectoryDeleted |
5150
fanotify.FileMovedFrom |
52-
fanotify.FileOrDirMovedFrom |
51+
fanotify.FileOrDirectoryMovedFrom |
5352
fanotify.FileMovedTo |
54-
fanotify.FileOrDirMovedTo |
53+
fanotify.FileOrDirectoryMovedTo |
5554
fanotify.WatchedFileMoved |
56-
fanotify.WatchedFileOrDirMoved
57-
58-
listener.AddWatch(listenPath, fanotify.FileOrDirectoryAccessed)
55+
fanotify.WatchedFileOrDirectoryMoved
56+
listener.AddWatch(listenPath, actions)
5957
go listener.Start()
6058
i := 1
6159
for event := range listener.Events {
@@ -69,3 +67,13 @@ func main() {
6967
}
7068
}
7169
```
70+
71+
## Known Issues
72+
73+
Certain flag combinations / actions cause issues with event reporting.
74+
75+
- `fanotify.FileCreated` (`unix.FAN_CREATE`) cannot be or-ed / combined with `fanotify.FileClosed` (`unix.FAN_CLOSE_WRITE` or `unix.FAN_CLOSE_NOWRITE`). The `fanotify` event notification group does not generate any event for this combination.
76+
77+
- Using `fanotify.FileOpened` with any of the actions containing `OrDirectory` (`unix.FAN_ONDIR`) causes an event flood for the directory and then stopping raising any events at all.
78+
79+
- `fanotifyFileOrDirectoryOpened` with any of the other actions causes an event flood for the directory and then stopping raising any events at all.

fanotify_event_types.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const (
2222
FileClosed Action = unix.FAN_CLOSE_WRITE | unix.FAN_CLOSE_NOWRITE
2323

2424
// FileOpened event when a file is opened
25-
// BUG Using FileOpened flag with any OnDir or OnDirectory actions
25+
// BUG Using FileOpened flag with any OrDirectory actions
2626
// causes an event flood and complete stoppage of events. The flag
2727
// can be used with other file only flags or by itself
2828
// without any errors/issues.
@@ -41,56 +41,56 @@ const (
4141
// Requires Linux kernel 5.1 or later (requires FID)
4242
FileAttribChanged Action = unix.FAN_ATTRIB
4343

44-
// FileOrDirAttribChanged event when a file or directory attribute has changed
44+
// FileOrDirectoryAttribChanged event when a file or directory attribute has changed
4545
// Requires Linux kernel 5.1 or later (requires FID)
46-
FileOrDirAttribChanged Action = unix.FAN_ATTRIB | unix.FAN_ONDIR
46+
FileOrDirectoryAttribChanged Action = unix.FAN_ATTRIB | unix.FAN_ONDIR
4747

4848
// FileCreated event when file a has been created
4949
// Requires Linux kernel 5.1 or later (requires FID)
5050
// BUG FileCreated does not work with FileClosed, FileClosedAfterWrite or FileClosedWithNoWrite
5151
FileCreated Action = unix.FAN_CREATE
5252

53-
// FileOrDirCreated event when a file or directory has been created
53+
// FileOrDirectoryCreated event when a file or directory has been created
5454
// Requires Linux kernel 5.1 or later (requires FID)
55-
FileOrDirCreated Action = unix.FAN_CREATE | unix.FAN_ONDIR
55+
FileOrDirectoryCreated Action = unix.FAN_CREATE | unix.FAN_ONDIR
5656

5757
// FileDeleted event when file a has been deleted
5858
// Requires Linux kernel 5.1 or later (requires FID)
5959
FileDeleted Action = unix.FAN_DELETE
6060

61-
// FileOrDirDeleted event when a file or directory has been deleted
61+
// FileOrDirectoryDeleted event when a file or directory has been deleted
6262
// Requires Linux kernel 5.1 or later (requires FID)
63-
FileOrDirDeleted Action = unix.FAN_DELETE | unix.FAN_ONDIR
63+
FileOrDirectoryDeleted Action = unix.FAN_DELETE | unix.FAN_ONDIR
6464

6565
// WatchedFileDeleted event when a watched file has been deleted
6666
// Requires Linux kernel 5.1 or later (requires FID)
6767
WatchedFileDeleted Action = unix.FAN_DELETE_SELF
6868

69-
// WatchedFileOrDirDeleted event when a watched file or directory has been deleted
69+
// WatchedFileOrDirectoryDeleted event when a watched file or directory has been deleted
7070
// Requires Linux kernel 5.1 or later (requires FID)
71-
WatchedFileOrDirDeleted Action = unix.FAN_DELETE_SELF | unix.FAN_ONDIR
71+
WatchedFileOrDirectoryDeleted Action = unix.FAN_DELETE_SELF | unix.FAN_ONDIR
7272

7373
// FileMovedFrom event when a file has been moved from the watched directory
7474
// Requires Linux kernel 5.1 or later (requires FID)
7575
FileMovedFrom Action = unix.FAN_MOVED_FROM
7676

77-
// FileOrDirMovedFrom event when a file or directory has been moved from the watched directory
77+
// FileOrDirectoryMovedFrom event when a file or directory has been moved from the watched directory
7878
// Requires Linux kernel 5.1 or later (requires FID)
79-
FileOrDirMovedFrom Action = unix.FAN_MOVED_FROM | unix.FAN_ONDIR
79+
FileOrDirectoryMovedFrom Action = unix.FAN_MOVED_FROM | unix.FAN_ONDIR
8080

8181
// FileMovedTo event when a file has been moved to the watched directory
8282
// Requires Linux kernel 5.1 or later (requires FID)
8383
FileMovedTo Action = unix.FAN_MOVED_TO
8484

85-
// FileOrDirMovedTo event when a file or directory has been moved to the watched directory
85+
// FileOrDirectoryMovedTo event when a file or directory has been moved to the watched directory
8686
// Requires Linux kernel 5.1 or later (requires FID)
87-
FileOrDirMovedTo Action = unix.FAN_MOVED_TO | unix.FAN_ONDIR
87+
FileOrDirectoryMovedTo Action = unix.FAN_MOVED_TO | unix.FAN_ONDIR
8888

8989
// WatchedFileMoved event when a watched file has moved
9090
// Requires Linux kernel 5.1 or later (requires FID)
9191
WatchedFileMoved Action = unix.FAN_MOVE_SELF
9292

93-
// WatchedFileOrDirMoved event when a watched file or directory has moved
93+
// WatchedFileOrDirectoryMoved event when a watched file or directory has moved
9494
// Requires Linux kernel 5.1 or later (requires FID)
95-
WatchedFileOrDirMoved Action = unix.FAN_MOVE_SELF | unix.FAN_ONDIR
95+
WatchedFileOrDirectoryMoved Action = unix.FAN_MOVE_SELF | unix.FAN_ONDIR
9696
)

fanotify_test.go

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func TestWithCapSysAdmFanotifyFileOrDirectoryCreated(t *testing.T) {
299299
watchDir := t.TempDir()
300300

301301
t.Logf("Watch Directory: %s", watchDir)
302-
action := FileOrDirCreated
302+
action := FileOrDirectoryCreated
303303
l.AddWatch(watchDir, action)
304304
go l.Start()
305305
defer l.Stop()
@@ -345,7 +345,7 @@ func TestWithCapSysAdmFanotifyFileDeleted(t *testing.T) {
345345
}
346346
}
347347

348-
func TestWithCapSysAdmFanotifyFileOrDirDeleted(t *testing.T) {
348+
func TestWithCapSysAdmFanotifyFileOrDirectoryDeleted(t *testing.T) {
349349

350350
l, err := NewListener("/", 4096, true)
351351
assert.Nil(t, err)
@@ -357,7 +357,7 @@ func TestWithCapSysAdmFanotifyFileOrDirDeleted(t *testing.T) {
357357
assert.Nil(t, err)
358358

359359
t.Logf("Watch Directory: %s", watchDir)
360-
action := FileOrDirDeleted
360+
action := FileOrDirectoryDeleted
361361
l.AddWatch(watchDir, action)
362362
go l.Start()
363363
defer l.Stop()
@@ -390,7 +390,7 @@ func TestMultipleEvents(t *testing.T) {
390390
defer l.Stop()
391391

392392
watchDir := t.TempDir()
393-
actions := FileOrDirCreated.Or(FileModified.Or(FileDeleted))
393+
actions := FileOrDirectoryCreated.Or(FileModified.Or(FileDeleted))
394394
l.AddWatch(watchDir, actions)
395395
testFile := fmt.Sprintf("%s/test.txt", watchDir)
396396
pid, err := runAsCmd("touch", testFile) // create file
@@ -437,3 +437,102 @@ func TestMultipleEvents(t *testing.T) {
437437
t.Logf("Received: (%s)", event)
438438
}
439439
}
440+
441+
// FileCreated and FileClosed combination does not raise any events
442+
func TestWithCapSysAdmMarkCreateCloseBug(t *testing.T) {
443+
l, err := NewListener("/", 4096, true)
444+
assert.Nil(t, err)
445+
assert.NotNil(t, l)
446+
go l.Start()
447+
defer l.Stop()
448+
449+
watchDir := t.TempDir()
450+
actions := FileCreated.Or(FileClosed)
451+
l.AddWatch(watchDir, actions)
452+
testFile := fmt.Sprintf("%s/test.txt", watchDir)
453+
pid, err := runAsCmd("touch", testFile) // create file
454+
assert.Nil(t, err)
455+
select {
456+
case <-time.After(100 * time.Millisecond):
457+
assert.Failf(t, "BUG: no events after file create", "confirmed that no events are raised if mark contains unix.FAN_CREATE|unix.FAN_CLOSE")
458+
case event := <-l.Events:
459+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
460+
assert.Equal(t, event.Pid, pid)
461+
t.Logf("Received: (%s)", event)
462+
}
463+
464+
// cat the file to simulate close after read
465+
pid, err = runAsCmd("cat", testFile)
466+
assert.Nil(t, err)
467+
select {
468+
case <-time.After(100 * time.Millisecond):
469+
assert.Failf(t, "BUG: no events after file close", "confirmed that no events are raised if mark contains unix.FAN_CREATE|unix.FAN_CLOSE")
470+
case event := <-l.Events:
471+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
472+
assert.Equal(t, event.Pid, pid)
473+
t.Logf("Received: (%s)", event)
474+
}
475+
}
476+
477+
// FileCreated and FileClosed combination does not raise any events
478+
func TestWithCapSysAdmMarkFileOrDirectoryOpenedBug(t *testing.T) {
479+
// setup the file for modification
480+
watchDir := t.TempDir()
481+
testFile := fmt.Sprintf("%s/test.txt", watchDir)
482+
_, err := runAsCmd("touch", testFile) // create file
483+
assert.Nil(t, err)
484+
485+
// start the listener
486+
l, err := NewListener("/", 4096, true)
487+
assert.Nil(t, err)
488+
assert.NotNil(t, l)
489+
go l.Start()
490+
defer l.Stop()
491+
var actions Action
492+
actions =
493+
FileAccessed |
494+
FileOrDirectoryAccessed |
495+
FileModified |
496+
FileOpenedForExec |
497+
FileAttribChanged |
498+
FileOrDirectoryAttribChanged |
499+
FileCreated |
500+
FileOrDirectoryCreated |
501+
FileDeleted |
502+
FileOrDirectoryDeleted |
503+
WatchedFileDeleted |
504+
WatchedFileOrDirectoryDeleted |
505+
FileMovedFrom |
506+
FileOrDirectoryMovedFrom |
507+
FileMovedTo |
508+
FileOrDirectoryMovedTo |
509+
WatchedFileMoved |
510+
WatchedFileOrDirectoryMoved |
511+
FileOrDirectoryOpened
512+
l.AddWatch(watchDir, actions)
513+
514+
// cat the file to simulate close after read
515+
_, err = runAsCmd("cat", testFile)
516+
assert.Nil(t, err)
517+
n := 0
518+
for len(l.Events) > 0 {
519+
e := <-l.Events
520+
t.Logf("drain-event: %v", e)
521+
n++
522+
}
523+
t.Logf("BUG: received %d events; while cat would otherwise raise 1 FileAccessed event", n)
524+
525+
// BUG more duplicate flood events or no further events are received past this
526+
// attribute change file
527+
pid, err := runAsCmd("chmod", "+x", testFile)
528+
assert.Nil(t, err)
529+
select {
530+
case <-time.After(100 * time.Millisecond):
531+
assert.Failf(t, "BUG: no events after chmod", "no events after the duplicate event flood")
532+
case event := <-l.Events:
533+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
534+
assert.Equal(t, event.Pid, pid)
535+
assert.True(t, event.Actions.Has(FileAttribChanged))
536+
t.Logf("Received: (%s)", event)
537+
}
538+
}

0 commit comments

Comments
 (0)