11//go:build linux
22// +build linux
33
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.
154package fanotify
165
176import (
3221 ErrNilListener = errors .New ("nil listener" )
3322 // ErrUnsupportedOnKernelVersion indicates the feature/flag is unavailable for the current kernel version
3423 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" )
3526)
3627
3728// Action represents an event / operation on a particular file/directory
@@ -65,6 +56,7 @@ type Listener struct {
6556 mountpoint * os.File
6657 kernelMajorVersion int
6758 kernelMinorVersion int
59+ entireMount bool
6860 watches map [string ]bool
6961 stopper struct {
7062 r * os.File
@@ -74,40 +66,39 @@ type Listener struct {
7466 Events chan Event
7567}
7668
77- // NewListener returns a fanotify listener from which events
69+ // NewListener returns a fanotify listener from which filesystem events
7870// can be read. Each listener supports listening to events
79- // under a single mount point .
71+ // under a single mountpoint .
8072//
8173// For cases where multiple mountpoints need to be monitored
8274// multiple listener instances need to be used.
8375//
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.
8780//
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 .
9184//
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 ) {
9489 capSysAdmin , err := checkCapSysAdmin ()
9590 if err != nil {
9691 return nil , err
9792 }
9893 if ! capSysAdmin {
9994 return nil , ErrCapSysAdmin
10095 }
101- return newListener (mountPoint )
96+ return newListener (mountPoint , entireMount )
10297}
10398
10499// Start starts the listener and polls the fanotify event notification group for marked events.
105100// The events are pushed into the Listener's `Events` buffered channel.
106- // The function panics if there nothing to watch.
107101func (l * Listener ) Start () {
108- //if len(l.watches) == 0 {
109- // panic("Nothing to watch. Add Directory/File to the listener to watch")
110- //}
111102 var fds [2 ]unix.PollFd
112103 // Fanotify Fd
113104 fds [0 ].Fd = int32 (l .fd )
@@ -155,21 +146,52 @@ func (l *Listener) Stop() {
155146 close (l .Events )
156147}
157148
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 )
161183}
162184
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.
164188func (l * Listener ) DeleteWatch (parentDir string , action Action ) error {
189+ if l .entireMount {
190+ return os .ErrInvalid
191+ }
165192 return l .fanotifyMark (parentDir , unix .FAN_MARK_REMOVE , uint64 (action | unix .FAN_EVENT_ON_CHILD ), false )
166193}
167194
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-
173195// ClearWatch stops watching for all actions
174196func (l * Listener ) ClearWatch () error {
175197 if l == nil {
0 commit comments