1
1
//go:build linux
2
2
// +build linux
3
3
4
- // Package fanotify library provides a simple API to monitor filesystem for events.
5
- //
6
- // The listener is initialized with flags automatically based on the kernel version. The mark flag features that specify the
7
- // the events to monitor a file/directory are validated and checked for valid combinations and validated against the kernel
8
- // version.
9
- //
10
- // fanotify has features spanning different kernel versions -
11
- //
12
- // For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
13
- // For Linux kernel versions 5.1 - 5.8 additional information about the underlying filesystem object is correlated to an event.
14
- // For Linux kernel version 5.9 or later the modified file name is made available in the event.
15
4
package fanotify
16
5
17
6
import (
32
21
ErrNilListener = errors .New ("nil listener" )
33
22
// ErrUnsupportedOnKernelVersion indicates the feature/flag is unavailable for the current kernel version
34
23
ErrUnsupportedOnKernelVersion = errors .New ("feature unsupported on current kernel version" )
24
+ // ErrWatchPath indicates path needs to be specified for watching
25
+ ErrWatchPath = errors .New ("missing watch path" )
35
26
)
36
27
37
28
// Action represents an event / operation on a particular file/directory
@@ -65,6 +56,7 @@ type Listener struct {
65
56
mountpoint * os.File
66
57
kernelMajorVersion int
67
58
kernelMinorVersion int
59
+ entireMount bool
68
60
watches map [string ]bool
69
61
stopper struct {
70
62
r * os.File
@@ -74,40 +66,39 @@ type Listener struct {
74
66
Events chan Event
75
67
}
76
68
77
- // NewListener returns a fanotify listener from which events
69
+ // NewListener returns a fanotify listener from which filesystem events
78
70
// can be read. Each listener supports listening to events
79
- // under a single mount point .
71
+ // under a single mountpoint .
80
72
//
81
73
// For cases where multiple mountpoints need to be monitored
82
74
// multiple listener instances need to be used.
83
75
//
84
- // `mountpointPath` can be any file/directory under the mount point being watched.
85
- // `maxEvents` defines the length of the buffered channel which holds the notifications. The minimum length is 4096.
86
- // `withName` setting this to true populates the file name under the watched parent.
76
+ // mountpoint can be any file/directory under the mount point being watched.
77
+ // Passing "true" to the entireMount flag monitors the entire mount point for marked
78
+ // events. Passing "false" allows specifying multiple paths (files/directories)
79
+ // under this mount point for monitoring filesystem events.
87
80
//
88
- // For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
89
- // For Linux kernel versions 5.1 - 5.8 additional information about the underlying filesystem object is correlated to an event.
90
- // For Linux kernel version 5.9 or later the modified file name is made available in the event .
81
+ // The function returns a new instance of the listener. The fanotify flags are set
82
+ // based on the running kernel version. ErrCapSysAdmin is returned if the process does not
83
+ // have CAP_SYS_ADM capability .
91
84
//
92
- // NOTE that this call requires CAP_SYS_ADMIN privilege
93
- func NewListener (mountPoint string ) (* Listener , error ) {
85
+ // - For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
86
+ // - For Linux kernel versions 5.1 till 5.8 (inclusive) additional information about the underlying filesystem object is correlated to an event.
87
+ // - For Linux kernel version 5.9 or later the modified file name is made available in the event.
88
+ func NewListener (mountPoint string , entireMount bool ) (* Listener , error ) {
94
89
capSysAdmin , err := checkCapSysAdmin ()
95
90
if err != nil {
96
91
return nil , err
97
92
}
98
93
if ! capSysAdmin {
99
94
return nil , ErrCapSysAdmin
100
95
}
101
- return newListener (mountPoint )
96
+ return newListener (mountPoint , entireMount )
102
97
}
103
98
104
99
// Start starts the listener and polls the fanotify event notification group for marked events.
105
100
// The events are pushed into the Listener's `Events` buffered channel.
106
- // The function panics if there nothing to watch.
107
101
func (l * Listener ) Start () {
108
- //if len(l.watches) == 0 {
109
- // panic("Nothing to watch. Add Directory/File to the listener to watch")
110
- //}
111
102
var fds [2 ]unix.PollFd
112
103
// Fanotify Fd
113
104
fds [0 ].Fd = int32 (l .fd )
@@ -155,21 +146,52 @@ func (l *Listener) Stop() {
155
146
close (l .Events )
156
147
}
157
148
158
- // AddWatch watches parent directory for specified actions
159
- func (l * Listener ) AddWatch (parentDir string , action Action ) error {
160
- return l .fanotifyMark (parentDir , unix .FAN_MARK_ADD , uint64 (action | unix .FAN_EVENT_ON_CHILD ), false )
149
+ // MarkMount adds, modifies or removes the fanotify mark (passed in as action) for the entire
150
+ // mountpoint. Passing true to remove, removes the mark from the mountpoint.
151
+ // This method returns an [ErrWatchPath] if the listener was not initialized to monitor
152
+ // the entire mountpoint. To mark specific files or directories use [AddWatch] method.
153
+ // The entire mount cannot be monitored for the following events:
154
+ // [FileCreated], [FileAttribChanged], [FileMovedFrom],
155
+ // [FileMovedTo], [WatchedFileDeleted]
156
+ // Passing any of these flags in action will return [ErrInvalidFlagCombination] error
157
+ func (l * Listener ) MarkMount (action Action , remove bool ) error {
158
+ if l .entireMount == false {
159
+ return ErrWatchPath
160
+ }
161
+ if action .Has (FileCreated ) || action .Has (FileAttribChanged ) || action .Has (FileMovedFrom ) || action .Has (FileMovedTo ) || action .Has (WatchedFileDeleted ) {
162
+ return ErrInvalidFlagCombination
163
+ }
164
+ if remove {
165
+ return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_REMOVE | unix .FAN_MARK_MOUNT , uint64 (action ), false )
166
+ }
167
+ return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_ADD | unix .FAN_MARK_MOUNT , uint64 (action ), false )
168
+ }
169
+
170
+ // AddWatch adds or modifies the fanotify mark for the specified path.
171
+ // The events are only raised for the specified directory and does raise events
172
+ // for subdirectories. Calling AddWatch to mark the entire mountpoint results in
173
+ // [os.ErrInvalid]. To mark the entire mountpoint use [MarkMount] method.
174
+ // Certain flag combinations are known to cause issues.
175
+ // - [FileCreated] cannot be or-ed / combined with FileClosed. The fanotify system does not generate any event for this combination.
176
+ // - [FileOpened] with any of the actions containing OrDirectory causes an event flood for the directory and then stopping raising any events at all.
177
+ // - [FileOrDirectoryOpened] with any of the other actions causes an event flood for the directory and then stopping raising any events at all.
178
+ func (l * Listener ) AddWatch (path string , action Action ) error {
179
+ if l .entireMount {
180
+ return os .ErrInvalid
181
+ }
182
+ return l .fanotifyMark (path , unix .FAN_MARK_ADD , uint64 (action | unix .FAN_EVENT_ON_CHILD ), false )
161
183
}
162
184
163
- // DeleteWatch stops watching the parent directory for the specified action
185
+ // DeleteWatch removes or modifies the fanotify mark for the specified path.
186
+ // Calling DeleteWatch on the listener initialized to monitor the entire mountpoint
187
+ // results in [os.ErrInvalid]. To modify the mark for the entire mountpoint use [MarkMount] method.
164
188
func (l * Listener ) DeleteWatch (parentDir string , action Action ) error {
189
+ if l .entireMount {
190
+ return os .ErrInvalid
191
+ }
165
192
return l .fanotifyMark (parentDir , unix .FAN_MARK_REMOVE , uint64 (action | unix .FAN_EVENT_ON_CHILD ), false )
166
193
}
167
194
168
- // WatchMountPoint watches the entire mount point for specified actions
169
- func (l * Listener ) WatchMountPoint (action Action ) error {
170
- return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_ADD | unix .FAN_MARK_MOUNT , uint64 (action ), false )
171
- }
172
-
173
195
// ClearWatch stops watching for all actions
174
196
func (l * Listener ) ClearWatch () error {
175
197
if l == nil {
0 commit comments