From 5c7a82a1c8276a0ea67cdbdc5a917ec88bb1082a Mon Sep 17 00:00:00 2001
From: Daniel McCarney <daniel@binaryparadox.net>
Date: Mon, 2 Mar 2020 17:50:37 -0500
Subject: [PATCH] linux: add fanotify(7) API bindings.

The `fanotify` API[0] is a linux-specific API for notification and interception
of filesystem events. In some ways it is similar to `inotify`, but with
different advantages/tradeoffs. It is particularly well suited to full
filesystem/mount monitoring (vs per directory) and for allowing/denying access
to files (`inotify` lacks this capability).

The `fanotify` API has been updated several times since it was enabled in Linux
2.6.37. Presently I've only included support for the original `fanotify`
features, and the `FAN_MARK_FILESYSTEM` addition made in Linux 4.20. There are
subsequent updates in 5.0 and 5.1 not covered in this initial commit.

This commit adds the relevant constants and types from
`uapi/linux/fanotify.h`[1] and two new functions (`fanotify_init`[2] and
`fanotify_wrap`[3]) to `src/unix/linux_like/linux/mod.rs`. While I believe this
API is also present on Android I have presently limited my attention to Linux.

Although this commit focuses on Linux 4.20.x's `fanotify` API/constants I have
skipped adding constants for `FAN_ALL_CLASS_BITS`, `FAN_ALL_INIT_FLAGS`,
`FAN_ALL_MARK_FLAGS`, `FAN_ALL_EVENTS`, `FAN_ALL_PERM_EVENTS` and
`FAN_ALL_OUTGOING_EVENTS` even though they are present in this kernel version's
headers. These defines were deprecated[4] in later releases with instructions to
not use them in new programs or extend them with new values. It would be a shame
for new Rust programs to use deprecated #defines!

[0]: http://man7.org/linux/man-pages/man7/fanotify.7.html
[1]: https://github.com/torvalds/linux/blob/d54f4fba889b205e9cd8239182ca5d27d0ac3bc2/include/uapi/linux/fanotify.h
[2]: http://man7.org/linux/man-pages/man2/fanotify_init.2.html
[3]: http://man7.org/linux/man-pages/man2/fanotify_mark.2.html
[4]: https://github.com/torvalds/linux/commit/23c9deeb3285d34fd243abb3d6b9f07db60c3cf4#diff-4c9ca62be6bf38cc08f7ea9daf16e379
---
 libc-test/build.rs                    |  1 +
 src/unix/linux_like/linux/align.rs    | 11 ++++++
 src/unix/linux_like/linux/gnu/mod.rs  |  7 ++++
 src/unix/linux_like/linux/mod.rs      | 53 +++++++++++++++++++++++++++
 src/unix/linux_like/linux/musl/mod.rs | 10 +++++
 src/unix/linux_like/linux/no_align.rs | 11 ++++++
 6 files changed, 93 insertions(+)

diff --git a/libc-test/build.rs b/libc-test/build.rs
index 1f7152e4..a1b7ad7f 100644
--- a/libc-test/build.rs
+++ b/libc-test/build.rs
@@ -2258,6 +2258,7 @@ fn test_linux(target: &str) {
         "linux/sockios.h",
         "linux/vm_sockets.h",
         "sys/auxv.h",
+        "sys/fanotify.h",
     }
 
     // note: aio.h must be included before sys/mount.h
diff --git a/src/unix/linux_like/linux/align.rs b/src/unix/linux_like/linux/align.rs
index 09bf8c85..01e00839 100644
--- a/src/unix/linux_like/linux/align.rs
+++ b/src/unix/linux_like/linux/align.rs
@@ -39,6 +39,17 @@ macro_rules! expand_align {
                 #[doc(hidden)]
                 size: [u8; ::__SIZEOF_PTHREAD_CONDATTR_T],
             }
+
+            #[repr(align(8))]
+            pub struct fanotify_event_metadata {
+                pub event_len: __u32,
+                pub vers: __u8,
+                pub reserved: __u8,
+                pub metadata_len: __u16,
+                pub mask: __u64,
+                pub fd: ::c_int,
+                pub pid: ::c_int,
+            }
         }
 
         s_no_extra_traits! {
diff --git a/src/unix/linux_like/linux/gnu/mod.rs b/src/unix/linux_like/linux/gnu/mod.rs
index 48272f4b..9c3038bf 100644
--- a/src/unix/linux_like/linux/gnu/mod.rs
+++ b/src/unix/linux_like/linux/gnu/mod.rs
@@ -1206,6 +1206,13 @@ extern "C" {
         len: ::size_t,
         flags: ::c_uint,
     ) -> ::ssize_t;
+    pub fn fanotify_mark(
+        fd: ::c_int,
+        flags: ::c_uint,
+        mask: u64,
+        dirfd: ::c_int,
+        path: *const ::c_char,
+    ) -> ::c_int;
 }
 
 #[link(name = "util")]
diff --git a/src/unix/linux_like/linux/mod.rs b/src/unix/linux_like/linux/mod.rs
index 8a6a6f09..6a7b5e96 100644
--- a/src/unix/linux_like/linux/mod.rs
+++ b/src/unix/linux_like/linux/mod.rs
@@ -477,6 +477,11 @@ s! {
         pub len: u32
     }
 
+    pub struct fanotify_response {
+        pub fd: ::c_int,
+        pub response: __u32,
+    }
+
     pub struct sockaddr_vm {
         pub svm_family: ::sa_family_t,
         pub svm_reserved1: ::c_ushort,
@@ -2417,6 +2422,53 @@ pub const IN_ALL_EVENTS: u32 = IN_ACCESS
 pub const IN_CLOEXEC: ::c_int = O_CLOEXEC;
 pub const IN_NONBLOCK: ::c_int = O_NONBLOCK;
 
+// uapi/linux/fanotify.h
+pub const FAN_ACCESS: u64 = 0x0000_0001;
+pub const FAN_MODIFY: u64 = 0x0000_0002;
+pub const FAN_CLOSE_WRITE: u64 = 0x0000_0008;
+pub const FAN_CLOSE_NOWRITE: u64 = 0x0000_0010;
+pub const FAN_OPEN: u64 = 0x0000_0020;
+
+pub const FAN_Q_OVERFLOW: u64 = 0x0000_4000;
+
+pub const FAN_OPEN_PERM: u64 = 0x0001_0000;
+pub const FAN_ACCESS_PERM: u64 = 0x0002_0000;
+
+pub const FAN_ONDIR: u64 = 0x4000_0000;
+
+pub const FAN_EVENT_ON_CHILD: u64 = 0x0800_0000;
+
+pub const FAN_CLOSE: u64 = FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE;
+
+pub const FAN_CLOEXEC: ::c_uint = 0x0000_0001;
+pub const FAN_NONBLOCK: ::c_uint = 0x0000_0002;
+
+pub const FAN_CLASS_NOTIF: ::c_uint = 0x0000_0000;
+pub const FAN_CLASS_CONTENT: ::c_uint = 0x0000_0004;
+pub const FAN_CLASS_PRE_CONTENT: ::c_uint = 0x0000_0008;
+
+pub const FAN_UNLIMITED_QUEUE: ::c_uint = 0x0000_0010;
+pub const FAN_UNLIMITED_MARKS: ::c_uint = 0x0000_0020;
+
+pub const FAN_MARK_ADD: ::c_uint = 0x0000_0001;
+pub const FAN_MARK_REMOVE: ::c_uint = 0x0000_0002;
+pub const FAN_MARK_DONT_FOLLOW: ::c_uint = 0x0000_0004;
+pub const FAN_MARK_ONLYDIR: ::c_uint = 0x0000_0008;
+pub const FAN_MARK_INODE: ::c_uint = 0x0000_0000;
+pub const FAN_MARK_MOUNT: ::c_uint = 0x0000_0010;
+// NOTE: FAN_MARK_FILESYSTEM requires Linux Kernel >= 4.20.0
+pub const FAN_MARK_FILESYSTEM: ::c_uint = 0x0000_0100;
+pub const FAN_MARK_IGNORED_MASK: ::c_uint = 0x0000_0020;
+pub const FAN_MARK_IGNORED_SURV_MODIFY: ::c_uint = 0x0000_0040;
+pub const FAN_MARK_FLUSH: ::c_uint = 0x0000_0080;
+
+pub const FANOTIFY_METADATA_VERSION: u8 = 3;
+
+pub const FAN_ALLOW: u32 = 0x01;
+pub const FAN_DENY: u32 = 0x02;
+
+pub const FAN_NOFD: ::c_int = -1;
+
 pub const FUTEX_WAIT: ::c_int = 0;
 pub const FUTEX_WAKE: ::c_int = 1;
 pub const FUTEX_FD: ::c_int = 2;
@@ -3304,6 +3356,7 @@ extern "C" {
         path: *const ::c_char,
         mask: u32,
     ) -> ::c_int;
+    pub fn fanotify_init(flags: ::c_uint, event_f_flags: ::c_uint) -> ::c_int;
 }
 
 cfg_if! {
diff --git a/src/unix/linux_like/linux/musl/mod.rs b/src/unix/linux_like/linux/musl/mod.rs
index 73c562c3..df539ad4 100644
--- a/src/unix/linux_like/linux/musl/mod.rs
+++ b/src/unix/linux_like/linux/musl/mod.rs
@@ -420,6 +420,16 @@ extern "C" {
         needle: *const ::c_void,
         needlelen: ::size_t,
     ) -> *mut ::c_void;
+    // Musl targets need the `mask` argument of `fanotify_mark` be specified
+    // `::c_ulonglong` instead of `u64` or there will be a type mismatch between
+    // `long long unsigned int` and the expected `uint64_t`.
+    pub fn fanotify_mark(
+        fd: ::c_int,
+        flags: ::c_uint,
+        mask: ::c_ulonglong,
+        dirfd: ::c_int,
+        path: *const ::c_char,
+    ) -> ::c_int;
 }
 
 cfg_if! {
diff --git a/src/unix/linux_like/linux/no_align.rs b/src/unix/linux_like/linux/no_align.rs
index a59edcb9..7393d70a 100644
--- a/src/unix/linux_like/linux/no_align.rs
+++ b/src/unix/linux_like/linux/no_align.rs
@@ -35,6 +35,17 @@ macro_rules! expand_align {
                 __align: [::c_int; 0],
                 size: [u8; ::__SIZEOF_PTHREAD_CONDATTR_T],
             }
+
+            pub struct fanotify_event_metadata {
+                __align: [::c_long; 0],
+                pub event_len: __u32,
+                pub vers: __u8,
+                pub reserved: __u8,
+                pub metadata_len: __u16,
+                pub mask: __u64,
+                pub fd: ::c_int,
+                pub pid: ::c_int,
+            }
         }
 
         s_no_extra_traits! {
-- 
GitLab