fanotify

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2022 License: MIT Imports: 11 Imported by: 4

README

Fanotify Library

Fanotify library provides a simple API to monitor filesystem for events.

The listener is initialized with flags automatically based on the kernel version. The mark flag features that specify the the events to monitor a file/directory are validated and checked for valid combinations and validated against the kernel version.

fanotify has features spanning different kernel versions -

  • For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
  • For Linux kernel versions 5.1 - 5.8 additional information about the underlying filesystem object is correlated to an event.
  • For Linux kernel version 5.9 or later the modified file name is made available in the event.

Example: Listener watching for events

package main

import (
        "flag"
        "fmt"
        "os"

        "github.com/opcoder0/fanotify"
)

func main() {
        var listenPath string

        flag.StringVar(&listenPath, "listen-path", "", "path to watch events")
        flag.Parse()
        if listenPath == "" {
                fmt.Println("missing listen path")
                os.Exit(1)
        }
        mountPoint := "/"
        listener, err := fanotify.NewListener(mountPoint, false)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println("Listening to events for:", listenPath)
	var eventTypes EventType
	eventTypes =
		fanotify.FileAccessed |
			fanotify.FileOrDirectoryAccessed |
			fanotify.FileModified |
			fanotify.FileOpenedForExec |
			fanotify.FileAttribChanged |
			fanotify.FileOrDirectoryAttribChanged |
			fanotify.FileCreated |
			fanotify.FileOrDirectoryCreated |
			fanotify.FileDeleted |
			fanotify.FileOrDirectoryDeleted |
			fanotify.WatchedFileDeleted |
			fanotify.WatchedFileOrDirectoryDeleted |
			fanotify.FileMovedFrom |
			fanotify.FileOrDirectoryMovedFrom |
			fanotify.FileMovedTo |
			fanotify.FileOrDirectoryMovedTo |
			fanotify.WatchedFileMoved |
			fanotify.WatchedFileOrDirectoryMoved
        listener.AddWatch(listenPath, eventTypes)
        go listener.Start()
        i := 1
        for event := range listener.Events {
                fmt.Println(event)
                if i == 5 {
                        fmt.Println("Enough events. Stopping...")
                        listener.Stop()
                        break
                }
                i++
        }
}

Known Issues

Certain flag combinations / event types cause issues with event reporting.

  • 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.

  • Using fanotify.FileOpened with any of the event types containing OrDirectory (unix.FAN_ONDIR) causes an event flood for the directory and then stopping raising any events at all.

  • fanotifyFileOrDirectoryOpened with any of the other event types causes an event flood for the directory and then stopping raising any events at all.

Tests

Running tests require CAP_SYS_ADM privilege. To run the tests make sure to add go to the sudo PATH.

The command runs all the tests except the ones that test the flag bugs mentioned in the "Known Issues" section above -

sudo go test -v

To run the tests with flag issues -

sudo go test -v -bug

Documentation

Overview

Package fanotify library provides a simple API to monitor filesystem for events.

The listener is initialized with flags automatically based on the kernel version. The mark flag features that specify the the events to monitor a file/directory are validated and checked for valid combinations and validated against the kernel version.

fanotify system has features spanning different kernel versions:

  • For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
  • For Linux kernel versions 5.1 to 5.8 additional information about the underlying filesystem object is correlated to an event.
  • For Linux kernel version 5.9 or later the modified file name is made available in the event.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrCapSysAdmin indicates caller is missing CAP_SYS_ADMIN permissions
	ErrCapSysAdmin = errors.New("require CAP_SYS_ADMIN capability")
	// ErrInvalidFlagCombination indicates the bit/combination of flags are invalid
	ErrInvalidFlagCombination = errors.New("invalid flag bits")
	// ErrNilListener indicates the listener is nil
	ErrNilListener = errors.New("nil listener")
	// ErrUnsupportedOnKernelVersion indicates the feature/flag is unavailable for the current kernel version
	ErrUnsupportedOnKernelVersion = errors.New("feature unsupported on current kernel version")
	// ErrWatchPath indicates path needs to be specified for watching
	ErrWatchPath = errors.New("missing watch path")
)

Functions

This section is empty.

Types

type Event

type Event struct {
	// Fd is the open file descriptor for the file/directory being watched
	Fd int
	// Path holds the name of the parent directory
	Path string
	// FileName holds the name of the file under the watched parent. The value is only available
	// when NewListener is created by passing `true` with `withName` argument. The feature is available
	// only with kernels 5.9 or higher.
	FileName string
	// EventTypes holds bit mask representing the operations
	EventTypes EventType
	// Pid Process ID of the process that caused the event
	Pid int
}

Event represents a notification from the kernel for the file, directory or a filesystem marked for watching.

func (Event) String added in v0.2.0

func (e Event) String() string

type EventType added in v0.2.0

type EventType uint64

EventType represents an event / operation on a particular file/directory

const (
	// FileAccessed event when a file is accessed
	FileAccessed EventType = unix.FAN_ACCESS

	// FileOrDirectoryAccessed event when a file or directory is accessed
	FileOrDirectoryAccessed EventType = unix.FAN_ACCESS | unix.FAN_ONDIR

	// FileModified event when a file is modified
	FileModified EventType = unix.FAN_MODIFY

	// FileClosedAfterWrite event when a file is closed
	FileClosedAfterWrite EventType = unix.FAN_CLOSE_WRITE

	// FileClosedWithNoWrite event when a file is closed without writing
	FileClosedWithNoWrite EventType = unix.FAN_CLOSE_NOWRITE

	// FileClosed event when a file is closed after write or no write
	FileClosed EventType = unix.FAN_CLOSE_WRITE | unix.FAN_CLOSE_NOWRITE

	// FileOpened event when a file is opened
	// BUG Using FileOpened flag with any OrDirectory event types
	// causes an event flood and complete stoppage of events. The flag
	// can be used with other file only flags or by itself
	// without any errors/issues.
	FileOpened EventType = unix.FAN_OPEN

	// FileOrDirectoryOpened event when a file or directory is opened
	// BUG Using FileOrDirectoryOpened causes an event flood and complete
	// stoppage of events. The flag by itself without any errors/issues.
	FileOrDirectoryOpened EventType = unix.FAN_OPEN | unix.FAN_ONDIR

	// FileOpenedForExec event when a file is opened with the intent to be executed.
	// Requires Linux kernel 5.0 or later
	FileOpenedForExec EventType = unix.FAN_OPEN_EXEC

	// FileAttribChanged event when a file attribute has changed
	// Requires Linux kernel 5.1 or later (requires FID)
	FileAttribChanged EventType = unix.FAN_ATTRIB

	// FileOrDirectoryAttribChanged event when a file or directory attribute has changed
	// Requires Linux kernel 5.1 or later (requires FID)
	FileOrDirectoryAttribChanged EventType = unix.FAN_ATTRIB | unix.FAN_ONDIR

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

	// FileOrDirectoryCreated event when a file or directory has been created
	// Requires Linux kernel 5.1 or later (requires FID)
	FileOrDirectoryCreated EventType = unix.FAN_CREATE | unix.FAN_ONDIR

	// FileDeleted event when file a has been deleted
	// Requires Linux kernel 5.1 or later (requires FID)
	FileDeleted EventType = unix.FAN_DELETE

	// FileOrDirectoryDeleted event when a file or directory has been deleted
	// Requires Linux kernel 5.1 or later (requires FID)
	FileOrDirectoryDeleted EventType = unix.FAN_DELETE | unix.FAN_ONDIR

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

	// WatchedFileOrDirectoryDeleted event when a watched file or directory has been deleted
	// Requires Linux kernel 5.1 or later (requires FID)
	WatchedFileOrDirectoryDeleted EventType = unix.FAN_DELETE_SELF | unix.FAN_ONDIR

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

	// FileOrDirectoryMovedFrom event when a file or directory has been moved from the watched directory
	// Requires Linux kernel 5.1 or later (requires FID)
	FileOrDirectoryMovedFrom EventType = unix.FAN_MOVED_FROM | unix.FAN_ONDIR

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

	// FileOrDirectoryMovedTo event when a file or directory has been moved to the watched directory
	// Requires Linux kernel 5.1 or later (requires FID)
	FileOrDirectoryMovedTo EventType = unix.FAN_MOVED_TO | unix.FAN_ONDIR

	// WatchedFileMoved event when a watched file has moved
	// Requires Linux kernel 5.1 or later (requires FID)
	WatchedFileMoved EventType = unix.FAN_MOVE_SELF

	// WatchedFileOrDirectoryMoved event when a watched file or directory has moved
	// Requires Linux kernel 5.1 or later (requires FID)
	WatchedFileOrDirectoryMoved EventType = unix.FAN_MOVE_SELF | unix.FAN_ONDIR
)

func (EventType) Has added in v0.2.0

func (eventTypes EventType) Has(et EventType) bool

Has returns true if event types contains the passed in event type (et).

func (EventType) Or added in v0.2.0

func (eventTypes EventType) Or(et EventType) EventType

Or appends the specified event types to the set of event types to watch for

func (EventType) String added in v0.2.0

func (a EventType) String() string

String prints event types

type Listener

type Listener struct {

	// Events a buffered channel holding fanotify notifications for the watched file/directory.
	Events chan Event
	// contains filtered or unexported fields
}

Listener represents a fanotify notification group that holds a list of files, directories and filesystems under a given mountpoint for which events shall be created.

func NewListener

func NewListener(mountPoint string, entireMount bool) (*Listener, error)

NewListener returns a fanotify listener from which filesystem events can be read. Each listener supports listening to events under a single mountpoint.

For cases where multiple mountpoints need to be monitored multiple listener instances need to be used.

mountpoint can be any file/directory under the mount point being watched. Passing "true" to the entireMount flag monitors the entire mount point for marked events. Passing "false" allows specifying multiple paths (files/directories) under this mount point for monitoring filesystem events.

The function returns a new instance of the listener. The fanotify flags are set based on the running kernel version. ErrCapSysAdmin is returned if the process does not have CAP_SYS_ADM capability.

  • For Linux kernel version 5.0 and earlier no additional information about the underlying filesystem object is available.
  • For Linux kernel versions 5.1 till 5.8 (inclusive) additional information about the underlying filesystem object is correlated to an event.
  • For Linux kernel version 5.9 or later the modified file name is made available in the event.
Example
package main

import (
	"log"

	"github.com/opcoder0/fanotify"
)

func main() {
	if _, err := fanotify.NewListener("/", true); err != nil {
		log.Fatal("Cannot create listener for mount /", err)
	}
}

func (*Listener) AddWatch

func (l *Listener) AddWatch(path string, eventTypes EventType) error

AddWatch adds or modifies the fanotify mark for the specified path. The events are only raised for the specified directory and does raise events for subdirectories. Calling AddWatch to mark the entire mountpoint results in os.ErrInvalid. To mark the entire mountpoint use [MarkMount] method. Certain flag combinations are known to cause issues.

  • FileCreated cannot be or-ed / combined with FileClosed. The fanotify system does not generate any event for this combination.
  • FileOpened with any of the event types containing OrDirectory causes an event flood for the directory and then stopping raising any events at all.
  • FileOrDirectoryOpened with any of the other event types causes an event flood for the directory and then stopping raising any events at all.
Example
package main

import (
	"log"

	"github.com/opcoder0/fanotify"
)

func main() {
	var listener *fanotify.Listener
	listener, err := fanotify.NewListener("/", false)
	if err != nil {
		log.Fatal("Cannot create listener for mount /", err)
	}
	listener.AddWatch("/home/user", fanotify.FileModified)
}
Example (All)
package main

import (
	"log"

	"github.com/opcoder0/fanotify"
)

func main() {
	var listener *fanotify.Listener
	var eventTypes fanotify.EventType

	listener, err := fanotify.NewListener("/", false)
	if err != nil {
		log.Fatal("Cannot create listener for path /", err)
	}
	eventTypes = fanotify.FileAccessed |
		fanotify.FileOrDirectoryAccessed |
		fanotify.FileModified |
		fanotify.FileOpenedForExec |
		fanotify.FileAttribChanged |
		fanotify.FileOrDirectoryAttribChanged |
		fanotify.FileCreated |
		fanotify.FileOrDirectoryCreated |
		fanotify.FileDeleted |
		fanotify.FileOrDirectoryDeleted |
		fanotify.WatchedFileDeleted |
		fanotify.WatchedFileOrDirectoryDeleted |
		fanotify.FileMovedFrom |
		fanotify.FileOrDirectoryMovedFrom |
		fanotify.FileMovedTo |
		fanotify.FileOrDirectoryMovedTo |
		fanotify.WatchedFileMoved |
		fanotify.WatchedFileOrDirectoryMoved
	listener.AddWatch("/home/user", eventTypes)
}

func (*Listener) ClearWatch

func (l *Listener) ClearWatch() error

ClearWatch stops watching for all event types

func (*Listener) DeleteWatch

func (l *Listener) DeleteWatch(parentDir string, eventTypes EventType) error

DeleteWatch removes or modifies the fanotify mark for the specified path. Calling DeleteWatch on the listener initialized to monitor the entire mountpoint results in os.ErrInvalid. To modify the mark for the entire mountpoint use [MarkMount] method.

func (*Listener) MarkMount added in v0.2.0

func (l *Listener) MarkMount(eventTypes EventType, remove bool) error

MarkMount adds, modifies or removes the fanotify mark (passed in as eventTypes) for the entire mountpoint. Passing true to remove, removes the mark from the mountpoint. This method returns an ErrWatchPath if the listener was not initialized to monitor the entire mountpoint. To mark specific files or directories use [AddWatch] method. The entire mount cannot be monitored for the following events: FileCreated, FileAttribChanged, FileMovedFrom, FileMovedTo, WatchedFileDeleted Passing any of these flags in eventTypes will return ErrInvalidFlagCombination error

func (*Listener) Start

func (l *Listener) Start()

Start starts the listener and polls the fanotify event notification group for marked events. The events are pushed into the Listener's `Events` buffered channel.

func (*Listener) Stop

func (l *Listener) Stop()

Stop stops the listener and closes the notification group and the events channel

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL