- About
- Building
- Usage
- Example
- Filesystem vs Mount
- Nix and Libc versions
- Updates to the fanotify API
- See Also
This repository is a Rust port of the Linux man-pages project's example
C program for the Linux fanotify API. It offers a simple
program called rfanotify that responds to and logs permission requests to
open a file on a monitored filesystem or mount. It also logs when a process
writes a file on a monitored filesystem/mount.
The fanotify API is a linux-specific API for notification and interception of
filesystem events. In some ways it is similar to inotify, but more powerful.
Unlike inotify based approaches to filesystem monitoring using fanotify has
several advantages:
- An entire filesystem/mount can be monitored with very low overhead.
- The
pidof the program causing the filesystem event is known. - The event handling program can deny file opens for the monitored filesystem.
For more information, see:
NOTE: This is my first attempt at writing Rust and it's likely not especially idomatic/clean. Kind PRs/suggestions welcome.
Since this project is written in Rust, you'll need to grab a Rust installation in order to compile it.
rfanotify was developed with Rust 1.34.0 (stable). It may work with other
versions, but this isn't guaranteed.
To build rfanotify:
git clone https://github.com/cpu/rfanotify && cd rfanotify
cargo build --releaseAfter building from source, run:
sudo ./target/release/rfanotify <directory>If no explicit directory argument is provided the filesystem/mount of the
current working directory is monitored.
You can avoid running rfanotify as root by instead giving the binary
the CAP_SYS_ADMIN capability, and then running it as a normal user:
sudo setcap cap_sys_admin+eip target/release/rfanotify
./target/release/rfanotify <directory>The rfanotify program adds a fanotify watch on the entire filesystem/mount
backing <directory>. When a process tries to open a file on the monitored
filesystem/mount a FAN_OPEN_PERM event is received and logged by rfanotify
and a FAN_ALLOW response is returned, allowing the open to complete. When a
process closes a file a FAN_CLOSE_WRITE event is received and logged.
Events are logged to stdout including the absolute path of the accessed file
and the program executable performing the access. Both of these values are
retrieved by fd using the Linux procfs. If the accessed file has
been deleted since the time the event was generated then the filename will have
"(deleted)" appended by readlink and the procfs. If the pid that generated
the access has terminated since the time the event was generated then the
gone pid is printed instead.
Here's an example of running rfanotify on a fresh Ubuntu 19.04 VM with a Linux
5.0.0-38-generic kernel.
First rfanotify is started inside of a screen session:
sudo rfanotifyNext, a separate window is created with ctrl-a c and a file is edited with vim:
vim /tmp/test.txtAfter exiting vim and switching back to the first screen window running
rfanotify you should see output like:
FAN_OPEN_PERM: File /usr/lib/x86_64-linux-gnu/utempter/utempter Exe /usr/bin/screen
FAN_OPEN_PERM: File /usr/lib/x86_64-linux-gnu/ld-2.29.so Exe /usr/lib/x86_64-linux-gnu/utempter/utempter
FAN_OPEN_PERM: File /etc/ld.so.cache Exe /usr/lib/x86_64-linux-gnu/utempter/utempter
<snipped>
FAN_CLOSE_WRITE: File /tmp/.test.txt.swx Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/.test.txt.swp Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /tmp/.test.txt.swp Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /usr/share/vim/vim81/scripts.vim Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /usr/share/vim/vim81/ftplugin/text.vim Exe /usr/bin/vim.basic
FAN_OPEN_PERM: File /tmp/test.txt Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/test.txt Exe /usr/bin/vim.basic
FAN_CLOSE_WRITE: File /tmp/.test.txt.swp Exe /usr/bin/vim.basicThe full program output can be viewed here.
On Linux kernel versions >= 4.2.0 rfanotify uses fanotify_mark with the
FAN_MARK_FILESYSTEM flag. On older versions FAN_MARK_MOUNT is used instead.
When marking a filesystem mount instead of a filesystem it is possible
events will be missed.
For example, consider if the device /dev/sdb1 is mounted to /mnt/example as well as having a bind mount to /mnt/example-b (e.g. mount --bind /mnt/example /mnt/example-b).
If rfanotify /mnt/example is run on a Linux kernel version >= 4.2.0 then events will be logged regardless of which mount is used. (e.g editing /mnt/example/foo.txt or /mnt/example-b/foo.txt will both log rfanotify events). The filesystem for /mnt/example is monitored.
If rfanotify /mnt/example is run on a Linux kernel version < 4.2.0 then events will be logged only for the /mnt/example mount. (e.g editing /mnt/example/foo.txt will log rfanotify events but editing /mnt/example-b/foo.txt will not. Only the mount of /mnt/example is monitored.
The fanotify system calls have been available since Linux 2.6.37 but FFI
bindings for Rust were not available in the nix and libc crates as of
March 2020.
The required FFI bindings were added to the libc crate in
April 2020 and released as part of v0.2.69.
The nix wrappers are a work in progress and require using a fork of the nix
crate that has rough initial fanotify support.
The fanotify API has been updated several times since it was enabled in Linux
2.6.37. The rfanotify code was written using the man page content from
release 4.04 of the Linux man-pages project and tested on Linux kernel version
4.4.0 and 5.0.0.
Linux 4.20 added FAN_MARK_FILESYSTEM to "enable monitoring of filesystem
events on all filesystem objects regardless of the mount where event was
generated". It also added FAN_REPORT_TID to get thread IDs that triggered
events.
Linux 5.0 added the ability to watch for when a file is opened with the intent
to execute (FAN_OPEN_EXEC).
Linux 5.1 added many of inotify's directory based events to fanotify
(e.g. FAN_ATTRIB, FAN_CREATE, etc). It also added a new fanotify_init
flag FAN_REPORT_FID that allows receiving an additional structure beyond the
base fanotify_event_metadata structure that contains information about the
filesystem object correlated with an event (e.g. a monitored directory or
mount).
The forked libc and nix crates used by this project do not (yet) implement
any of these updates (except for FAN_MARK_FILESYSTEM). This project does not
(yet) port the fanotify_fid.c example C program included in release 5.05 of
the Linux man-pages project.
Martin Pitt cleverly used the fanotify API to develop fatrace, a
program for reporting system wide file access events tailored towards reducing
power consumption.