@@ -73,6 +73,8 @@ type Listener struct {
7373 fd int
7474 // flags passed to fanotify_init
7575 flags uint
76+ // markMask current fanotify mark mask
77+ markMask uint64
7678 // mount fd is the file descriptor of the mountpoint
7779 mountpoint * os.File
7880 kernelMajorVersion int
@@ -203,14 +205,23 @@ func (l *Listener) Stop() {
203205// for - [FileCreated], [FileAttribChanged], [FileMovedTo], [FileMovedFrom], [WatchedFileDeleted],
204206// [WatchedFileOrDirectoryDeleted], [FileDeleted], [FileOrDirectoryDeleted]
205207func (l * Listener ) WatchMount (eventTypes EventType ) error {
206- return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_ADD | unix .FAN_MARK_MOUNT , uint64 (eventTypes ))
208+ mask := l .getMaskAfterAdd (eventTypes )
209+ l .clearWatch ()
210+ l .markMask = uint64 (mask )
211+ return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_ADD | unix .FAN_MARK_MOUNT , uint64 (mask ))
207212}
208213
209214// UnwatchMount removes the notification marks for the entire mount point.
210215// This method returns an [ErrWatchPath] if the listener was not initialized to monitor
211216// the entire mount point. To unmark specific files or directories use [DeleteWatch] method.
212217func (l * Listener ) UnwatchMount (eventTypes EventType ) error {
213- return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_REMOVE | unix .FAN_MARK_MOUNT , uint64 (eventTypes ))
218+ if l .markMask == 0 {
219+ return l .clearWatch ()
220+ }
221+ remaining := l .getMaskAfterRemove (eventTypes )
222+ l .clearWatch ()
223+ l .markMask = uint64 (remaining )
224+ return l .fanotifyMark (l .mountpoint .Name (), unix .FAN_MARK_ADD | unix .FAN_MARK_MOUNT , uint64 (remaining ))
214225}
215226
216227// AddWatch adds or modifies the fanotify mark for the specified path.
@@ -221,14 +232,46 @@ func (l *Listener) UnwatchMount(eventTypes EventType) error {
221232// - [FileCreated] cannot be or-ed / combined with [FileClosed]. The fanotify system does not generate any event for this combination.
222233// - [FileOpened] with any of the event types containing OrDirectory causes an event flood for the directory and then stopping raising any events at all.
223234// - [FileOrDirectoryOpened] with any of the other event types causes an event flood for the directory and then stopping raising any events at all.
235+ //
236+ // NOTE:
237+ // Any event type that contains "OrDirectory" applies the OrDirectory mask to any other applicable
238+ // marks. For example adding [FileDeleted] and [FileOrDirectoryOpened] will apply "OrDirectory" to
239+ // [FileDeleted] thus the resulting mask will be set to [FileOrDirectoryOpened] and [FileOrDirectoryDeleted]
224240func (l * Listener ) AddWatch (path string , eventTypes EventType ) error {
225241 if l == nil {
226242 panic ("nil listener" )
227243 }
228244 if l .entireMount {
229245 return os .ErrInvalid
230246 }
231- return l .fanotifyMark (path , unix .FAN_MARK_ADD , uint64 (eventTypes | unix .FAN_EVENT_ON_CHILD ))
247+ mask := l .getMaskAfterAdd (eventTypes )
248+ l .clearWatch ()
249+ l .markMask = uint64 (mask )
250+ return l .fanotifyMark (path , unix .FAN_MARK_ADD , uint64 (mask | unix .FAN_EVENT_ON_CHILD ))
251+ }
252+
253+ // DeleteWatch removes/unmarks the fanotify mark for the specified path.
254+ // Calling DeleteWatch on the listener initialized to monitor the entire mount point
255+ // results in [os.ErrInvalid]. Use [UnwatchMount] for deleting marks on the mount point.
256+ func (l * Listener ) DeleteWatch (parentDir string , eventTypes EventType ) error {
257+ if l .entireMount {
258+ return os .ErrInvalid
259+ }
260+ if l .markMask == 0 {
261+ return l .clearWatch ()
262+ }
263+ remaining := l .getMaskAfterRemove (eventTypes )
264+ l .clearWatch ()
265+ l .markMask = uint64 (remaining )
266+ return l .fanotifyMark (parentDir , unix .FAN_MARK_ADD , uint64 (remaining | unix .FAN_EVENT_ON_CHILD ))
267+ }
268+
269+ // ClearWatch stops watching for all event types
270+ func (l * Listener ) ClearWatch () error {
271+ if l == nil {
272+ panic ("nil listener" )
273+ }
274+ return l .clearWatch ()
232275}
233276
234277// Allow sends an "allowed" response to the permission request event.
@@ -251,28 +294,6 @@ func (l *Listener) Deny(e Event) {
251294 unix .Write (l .fd , buf .Bytes ())
252295}
253296
254- // DeleteWatch removes/unmarks the fanotify mark for the specified path.
255- // Calling DeleteWatch on the listener initialized to monitor the entire mount point
256- // results in [os.ErrInvalid]. Use [UnwatchMount] for deleting marks on the mount point.
257- func (l * Listener ) DeleteWatch (parentDir string , eventTypes EventType ) error {
258- if l .entireMount {
259- return os .ErrInvalid
260- }
261- return l .fanotifyMark (parentDir , unix .FAN_MARK_REMOVE , uint64 (eventTypes | unix .FAN_EVENT_ON_CHILD ))
262- }
263-
264- // ClearWatch stops watching for all event types
265- func (l * Listener ) ClearWatch () error {
266- if l == nil {
267- panic ("nil listener" )
268- }
269- if err := unix .FanotifyMark (l .fd , unix .FAN_MARK_FLUSH , 0 , - 1 , "" ); err != nil {
270- return err
271- }
272- l .watches = make (map [string ]bool )
273- return nil
274- }
275-
276297// Has returns true if event types (e) contains the passed in event type (et).
277298func (e EventType ) Has (et EventType ) bool {
278299 return e & et == et
0 commit comments