Skip to content

Commit a9ea5c2

Browse files
authored
Fix Event API (#4)
Cleanup Watch API Fix broken event watcher (flags were incorrect) Add validation to check mark flags and mask Clean up code
1 parent b4eec0b commit a9ea5c2

File tree

3 files changed

+166
-87
lines changed

3 files changed

+166
-87
lines changed

fanotify_api.go

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ var (
2121
ErrUnsupportedOnKernelVersion = errors.New("feature unsupported on current kernel version")
2222
)
2323

24+
// Action represents an event / operation on a particular file/directory
25+
type Action uint64
26+
2427
// Event represents a notification from the kernel for the file, directory
2528
// or a filesystem marked for watching.
2629
type Event struct {
@@ -33,7 +36,9 @@ type Event struct {
3336
// only with kernels 5.9 or higher.
3437
FileName string
3538
// Mask holds bit mask representing the operation
36-
Mask uint64
39+
Mask Action
40+
// Pid Process ID of the process that caused the event
41+
Pid int32
3742
}
3843

3944
// Listener represents a fanotify notification group that holds a list of files,
@@ -144,52 +149,23 @@ func (l *Listener) Stop() {
144149
close(l.Events)
145150
}
146151

147-
// AddDir adds the specified directory to listener's watch
148-
// list. If `dir` is not a directory then an error is returned.
149-
// If `dir` is a symbolic link the link is followed.
150-
func (l *Listener) AddDir(dir string, events uint64) error {
151-
var flags uint
152-
flags = unix.FAN_MARK_ADD | unix.FAN_MARK_ONLYDIR
153-
return l.fanotifyMark(dir, flags, events, false)
154-
}
155-
156-
// RemoveDir removes the specified directory from the listener's
157-
// watch list.
158-
func (l *Listener) RemoveDir(dir string, events uint64) error {
159-
var flags uint
160-
flags = unix.FAN_MARK_REMOVE | unix.FAN_MARK_ONLYDIR
161-
return l.fanotifyMark(dir, flags, events, true)
162-
}
163-
164-
// AddLink adds the specified symbolic link to the listener's watch list. The link
165-
// is not followed. The link itself is marked for watching.
166-
func (l *Listener) AddLink(linkName string, events uint64) error {
167-
var flags uint
168-
flags = unix.FAN_MARK_ADD | unix.FAN_MARK_DONT_FOLLOW
169-
return l.fanotifyMark(linkName, flags, events, false)
170-
}
171-
172-
// RemoveLink removes the specified symbolic link from the listener's watch list.
173-
func (l *Listener) RemoveLink(linkName string, events uint64) error {
174-
var flags uint
175-
flags = unix.FAN_MARK_REMOVE | unix.FAN_MARK_DONT_FOLLOW
176-
return l.fanotifyMark(linkName, flags, events, true)
152+
// AddWatch watches parent directory for specified actions
153+
func (l *Listener) AddWatch(parentDir string, action Action) error {
154+
return l.fanotifyMark(parentDir, unix.FAN_MARK_ADD, uint64(action|unix.FAN_EVENT_ON_CHILD), false)
177155
}
178156

179-
// AddPath adds the specified path name (file or directory) to the listener's
180-
// watch list.
181-
func (l *Listener) AddPath(path string, events uint64) error {
182-
return l.fanotifyMark(path, unix.FAN_MARK_ADD, events, false)
157+
// DeleteWatch stops watching the parent directory for the specified action
158+
func (l *Listener) DeleteWatch(parentDir string, action Action) error {
159+
return l.fanotifyMark(parentDir, unix.FAN_MARK_REMOVE, uint64(action|unix.FAN_EVENT_ON_CHILD), false)
183160
}
184161

185-
// RemovePath removes the specified path name (file or directory) from the
186-
// listener's watch list.
187-
func (l *Listener) RemovePath(path string, events uint64) error {
188-
return l.fanotifyMark(path, unix.FAN_MARK_REMOVE, events, true)
162+
// WatchMountPoint watches the entire mount point for specified actions
163+
func (l *Listener) WatchMountPoint(action Action) error {
164+
return l.fanotifyMark(l.mountpoint.Name(), unix.FAN_MARK_ADD|unix.FAN_MARK_MOUNT, uint64(action), false)
189165
}
190166

191-
// RemoveAll removes all the watch elements from the listener.
192-
func (l *Listener) RemoveAll() error {
167+
// ClearWatch stops watching for all actions
168+
func (l *Listener) ClearWatch() error {
193169
if l == nil {
194170
return ErrNilListener
195171
}

fanotify_event.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,37 @@ func flagsValid(flags uint) error {
9393
return nil
9494
}
9595

96-
// Check if specified flags are supported for the given
96+
func fanotifyMarkMaskValid(mask uint64) error {
97+
isSet := func(n, k uint64) bool {
98+
return n&k == k
99+
}
100+
if isSet(mask, unix.FAN_MARK_MOUNT) && (isSet(mask, unix.FAN_CREATE) || isSet(mask, unix.FAN_ATTRIB) || isSet(mask, unix.FAN_MOVE) || isSet(mask, unix.FAN_DELETE_SELF)) {
101+
return errors.New("mountpoint cannot be watched for create, attrib, move or delete self actions")
102+
}
103+
return nil
104+
}
105+
106+
func markFlagsValid(flags uint64) bool {
107+
return false
108+
}
109+
110+
// Check if specified fanotify_init flags are supported for the given
97111
// kernel version. If none of the defined flags are specified
98112
// then the basic option works on any kernel version.
99-
func checkFlagsKernelSupport(flags uint, maj, min int) bool {
113+
func fanotifyInitFlagsKernelSupport(flags uint, maj, min int) bool {
100114
type kernelVersion struct {
101115
maj int
102116
min int
103117
}
118+
// fanotify init flags
104119
var flagPerKernelVersion = map[uint]kernelVersion{
105120
unix.FAN_ENABLE_AUDIT: {4, 15},
106121
unix.FAN_REPORT_FID: {5, 1},
107122
unix.FAN_REPORT_DIR_FID: {5, 9},
108123
unix.FAN_REPORT_NAME: {5, 9},
109124
unix.FAN_REPORT_DFID_NAME: {5, 9},
110125
}
126+
111127
check := func(n, k uint, w, x int) (bool, error) {
112128
if n&k == k {
113129
if maj > w {
@@ -131,6 +147,48 @@ func checkFlagsKernelSupport(flags uint, maj, min int) bool {
131147
return true
132148
}
133149

150+
// Check if specified fanotify_mark flags are supported for the given
151+
// kernel version. If none of the defined flags are specified
152+
// then the basic option works on any kernel version.
153+
func fanotifyMarkFlagsKernelSupport(flags uint64, maj, min int) bool {
154+
type kernelVersion struct {
155+
maj int
156+
min int
157+
}
158+
// fanotify mark flags
159+
var fanotifyMarkFlags = map[uint64]kernelVersion{
160+
unix.FAN_OPEN_EXEC: {5, 0},
161+
unix.FAN_ATTRIB: {5, 1},
162+
unix.FAN_CREATE: {5, 1},
163+
unix.FAN_DELETE: {5, 1},
164+
unix.FAN_DELETE_SELF: {5, 1},
165+
unix.FAN_MOVED_FROM: {5, 1},
166+
unix.FAN_MOVED_TO: {5, 1},
167+
}
168+
169+
check := func(n, k uint64, w, x int) (bool, error) {
170+
if n&k == k {
171+
if maj > w {
172+
return true, nil
173+
} else if maj == w && min >= x {
174+
return true, nil
175+
}
176+
return false, nil
177+
}
178+
return false, errors.New("flag not set")
179+
}
180+
for flag, ver := range fanotifyMarkFlags {
181+
if v, err := check(flags, flag, ver.maj, ver.min); err != nil {
182+
continue // flag not set; check other flags
183+
} else {
184+
return v
185+
}
186+
}
187+
// if none of these flags were specified then the basic option
188+
// works on any kernel version
189+
return true
190+
}
191+
134192
func fanotifyEventOK(meta *unix.FanotifyEventMetadata, n int) bool {
135193
return (n >= int(sizeOfFanotifyEventMetadata) &&
136194
meta.Event_len >= sizeOfFanotifyEventMetadata &&
@@ -145,8 +203,8 @@ func newListener(mountpointPath string, flags, eventFlags, maxEvents uint) (*Lis
145203
if err := flagsValid(flags); err != nil {
146204
return nil, fmt.Errorf("%w: %v", ErrInvalidFlagCombination, err)
147205
}
148-
if !checkFlagsKernelSupport(flags, maj, min) {
149-
panic("some of the flags specified are not supported on the current kernel")
206+
if !fanotifyInitFlagsKernelSupport(flags, maj, min) {
207+
panic("some of the flags specified are not supported on the current kernel; refer to the documentation")
150208
}
151209
fd, err := unix.FanotifyInit(flags, eventFlags)
152210
if err != nil {
@@ -189,6 +247,12 @@ func (l *Listener) fanotifyMark(path string, flags uint, mask uint64, remove boo
189247
if l == nil {
190248
return ErrNilListener
191249
}
250+
if !fanotifyMarkFlagsKernelSupport(mask, l.kernelMajorVersion, l.kernelMinorVersion) {
251+
panic("some of the mark mask combinations specified are not supported on the current kernel; refer to the documentation")
252+
}
253+
if err := fanotifyMarkMaskValid(mask); err != nil {
254+
return fmt.Errorf("%v: %w", err, ErrInvalidFlagCombination)
255+
}
192256
_, found := l.watches[path]
193257
if found {
194258
if remove {
@@ -292,7 +356,8 @@ func (l *Listener) readEvents() error {
292356
event := Event{
293357
Fd: int(metadata.Fd),
294358
Path: string(name[:n1]),
295-
Mask: metadata.Mask,
359+
Mask: Action(metadata.Mask),
360+
Pid: metadata.Pid,
296361
}
297362
l.Events <- event
298363

@@ -334,7 +399,8 @@ func (l *Listener) readEvents() error {
334399
Fd: fd,
335400
Path: pathName,
336401
FileName: fileName,
337-
Mask: metadata.Mask,
402+
Mask: Action(metadata.Mask),
403+
Pid: metadata.Pid,
338404
}
339405
l.Events <- event
340406
i += int(metadata.Event_len)

fanotify_event_types.go

Lines changed: 77 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,81 @@ package fanotify
33
import "golang.org/x/sys/unix"
44

55
const (
6-
// FileOrDirAccessedEvent create an event when a file or directory is accessed
7-
FileOrDirAccessedEvent uint64 = unix.FAN_ACCESS
8-
// FileModifiedEvent create an event when a file is modified
9-
FileModifiedEvent uint64 = unix.FAN_MODIFY
10-
// FileClosedEvent create an event when a file is closed
11-
FileClosedEvent uint64 = unix.FAN_CLOSE_WRITE | unix.FAN_CLOSE_NOWRITE
12-
// FileOrDirOpenedEvent create an event when a file or directory is opened
13-
FileOrDirOpenedEvent uint64 = unix.FAN_OPEN
14-
// FileOpenedForExecEvent create an event when a file is opened with the intent to be executed.
15-
FileOpenedForExecEvent uint64 = unix.FAN_OPEN_EXEC
16-
// FileOrDirMetadataChangedEvent create an event when a file or directory attributes have changed.
17-
FileOrDirMetadataChangedEvent uint64 = unix.FAN_ATTRIB
18-
// DirectoryEvent create an event when directory is opened, read or closed.
19-
DirectoryEvent uint64 = unix.FAN_ONDIR
20-
// FileCreatedInMarkedParentEvent create event when a file is created under a marked parent directory.
21-
FileCreatedInMarkedParentEvent uint64 = unix.FAN_CREATE | unix.FAN_EVENT_ON_CHILD
22-
// DirectoryCreatedInMarkedParentEvent create event when a directory is created under a marked parent directory.
23-
DirectoryCreatedInMarkedParentEvent uint64 = unix.FAN_ONDIR | unix.FAN_CREATE | unix.FAN_EVENT_ON_CHILD
24-
// FileDeletedInMarkedParentEvent create event when a file is deleted under a marked parent directory.
25-
FileDeletedInMarkedParentEvent uint64 = unix.FAN_DELETE | unix.FAN_EVENT_ON_CHILD
26-
// DirectoryDeletedInMarkedParentEvent create event when a directory is deleted under a marked parent directory.
27-
DirectoryDeletedInMarkedParentEvent uint64 = unix.FAN_ONDIR | unix.FAN_DELETE | unix.FAN_EVENT_ON_CHILD
28-
// MarkedFileDeletedEvent create event when a marked file is deleted.
29-
MarkedFileDeletedEvent uint64 = unix.FAN_DELETE_SELF
30-
// MarkedDirectoryDeletedEvent create an event when a marked directory is deleted.
31-
MarkedDirectoryDeletedEvent uint64 = unix.FAN_ONDIR | unix.FAN_DELETE_SELF
32-
// FileMovedFromMarkedParentEvent create an event when file has been moved from a marked parent directory.
33-
FileMovedFromMarkedParentEvent uint64 = unix.FAN_MOVED_FROM | unix.FAN_EVENT_ON_CHILD
34-
// DirMovedFromMarkedParentEvent create an event when a directory has been moved from a marked parent directory.
35-
DirMovedFromMarkedParentEvent uint64 = unix.FAN_ONDIR | unix.FAN_MOVED_FROM | unix.FAN_EVENT_ON_CHILD
36-
// FileMovedToMarkedParentEvent create an event when file has been moved to a marked parent directory.
37-
FileMovedToMarkedParentEvent uint64 = unix.FAN_MOVED_TO | unix.FAN_EVENT_ON_CHILD
38-
// DirMovedToMarkedParentEvent create an event when a directory has been moved to a marked parent directory.
39-
DirMovedToMarkedParentEvent uint64 = unix.FAN_ONDIR | unix.FAN_MOVED_TO | unix.FAN_EVENT_ON_CHILD
40-
// MarkedFileOrDirectoryHasMovedEvent create an event when a marked file or directory has moved.
41-
MarkedFileOrDirectoryHasMovedEvent uint64 = unix.FAN_MOVE_SELF
42-
// QueueOverflowedEvent create an event when the kernel event queue has overflowed.
43-
QueueOverflowedEvent uint64 = unix.FAN_Q_OVERFLOW
44-
// FileOrDirectoryMovedEvent create an event when a file or directory has moved.
45-
FileOrDirectoryMovedEvent uint64 = FileMovedFromMarkedParentEvent | FileMovedToMarkedParentEvent | DirMovedFromMarkedParentEvent | DirMovedToMarkedParentEvent
6+
// FileAccessed event when a file is accessed
7+
FileAccessed Action = unix.FAN_ACCESS
8+
9+
// FileOrDirectoryAccessed event when a file or directory is accessed
10+
FileOrDirectoryAccessed Action = unix.FAN_ACCESS | unix.FAN_ONDIR
11+
12+
// FileModified event when a file is modified
13+
FileModified Action = unix.FAN_MODIFY
14+
15+
// FileClosed event when a file is closed
16+
FileClosed Action = unix.FAN_CLOSE_WRITE | unix.FAN_CLOSE_NOWRITE
17+
18+
// FileOpened event when a file is opened
19+
FileOpened Action = unix.FAN_OPEN
20+
21+
// FileOrDirectoryOpened event when a file or directory is opened
22+
FileOrDirectoryOpened Action = unix.FAN_OPEN | unix.FAN_ONDIR
23+
24+
// FileOpenedForExec event when a file is opened with the intent to be executed.
25+
// Requires Linux kernel 5.0 or later
26+
FileOpenedForExec Action = unix.FAN_OPEN_EXEC
27+
28+
// FileAttribChanged event when a file attribute has changed
29+
// Requires Linux kernel 5.1 or later (requires FID)
30+
FileAttribChanged Action = unix.FAN_ATTRIB
31+
32+
// FileOrDirAttribChanged event when a file or directory attribute has changed
33+
// Requires Linux kernel 5.1 or later (requires FID)
34+
FileOrDirAttribChanged Action = unix.FAN_ATTRIB | unix.FAN_ONDIR
35+
36+
// FileCreated event when file a has been created
37+
// Requires Linux kernel 5.1 or later (requires FID)
38+
FileCreated Action = unix.FAN_CREATE
39+
40+
// FileOrDirCreated event when a file or directory has been created
41+
// Requires Linux kernel 5.1 or later (requires FID)
42+
FileOrDirCreated Action = unix.FAN_CREATE | unix.FAN_ONDIR
43+
44+
// FileDeleted event when file a has been deleted
45+
// Requires Linux kernel 5.1 or later (requires FID)
46+
FileDeleted Action = unix.FAN_DELETE
47+
48+
// FileOrDirDeleted event when a file or directory has been deleted
49+
// Requires Linux kernel 5.1 or later (requires FID)
50+
FileOrDirDeleted Action = unix.FAN_DELETE | unix.FAN_ONDIR
51+
52+
// WatchedFileDeleted event when a watched file has been deleted
53+
// Requires Linux kernel 5.1 or later (requires FID)
54+
WatchedFileDeleted Action = unix.FAN_DELETE_SELF
55+
56+
// WatchedFileOrDirDeleted event when a watched file or directory has been deleted
57+
// Requires Linux kernel 5.1 or later (requires FID)
58+
WatchedFileOrDirDeleted Action = unix.FAN_DELETE_SELF | unix.FAN_ONDIR
59+
60+
// FileMovedFrom event when a file has been moved from the watched directory
61+
// Requires Linux kernel 5.1 or later (requires FID)
62+
FileMovedFrom Action = unix.FAN_MOVED_FROM
63+
64+
// FileOrDirMovedFrom event when a file or directory has been moved from the watched directory
65+
// Requires Linux kernel 5.1 or later (requires FID)
66+
FileOrDirMovedFrom Action = unix.FAN_MOVED_FROM | unix.FAN_ONDIR
67+
68+
// FileMovedTo event when a file has been moved to the watched directory
69+
// Requires Linux kernel 5.1 or later (requires FID)
70+
FileMovedTo Action = unix.FAN_MOVED_TO
71+
72+
// FileOrDirMovedTo event when a file or directory has been moved to the watched directory
73+
// Requires Linux kernel 5.1 or later (requires FID)
74+
FileOrDirMovedTo Action = unix.FAN_MOVED_TO | unix.FAN_ONDIR
75+
76+
// WatchedFileMoved event when a watched file has moved
77+
// Requires Linux kernel 5.1 or later (requires FID)
78+
WatchedFileMoved Action = unix.FAN_MOVE_SELF
79+
80+
// WatchedFileOrDirMoved event when a watched file or directory has moved
81+
// Requires Linux kernel 5.1 or later (requires FID)
82+
WatchedFileOrDirMoved Action = unix.FAN_MOVE_SELF | unix.FAN_ONDIR
4683
)

0 commit comments

Comments
 (0)