add fanotify tests
authorAlan <alan@roojs.com>
Thu, 25 May 2023 05:41:50 +0000 (13:41 +0800)
committerAlan <alan@roojs.com>
Thu, 25 May 2023 05:41:50 +0000 (13:41 +0800)
.gitignore
fanotify/fanotify-example.c [new file with mode: 0644]
fanotify/fanotify-helper.c [new file with mode: 0644]
fanotify/fanotify-man.c [new file with mode: 0644]

index 706f9c7..c4c7147 100644 (file)
@@ -1,4 +1,4 @@
-*.c
+/*.c
 *.c.valatmp
 *.o
 c/*.o
diff --git a/fanotify/fanotify-example.c b/fanotify/fanotify-example.c
new file mode 100644 (file)
index 0000000..e18dc3f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *   A simple tester of fanotify in the Linux kernel.
+ *
+ *   This program is released in the Public Domain.
+ *
+ *   Compile with:
+ *     $> gcc -o /tmp/fanotify-example fanotify-example.c
+ * 
+ *   Run as:
+ *     $> /tmp/fanotify-example 
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/fanotify.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+ int main(int argc, char *argv[]){
+    int fan;
+    char buf[4096];
+    char fdpath[32];
+    char path[PATH_MAX + 1];
+    ssize_t buflen, linklen;
+    struct fanotify_event_metadata *metadata;
+
+    // Init fanotify structure
+//    fan = fanotify_init(FAN_REPORT_FID, O_RDWR);
+    // FAN_CLASS_CONTENT << only for root.
+    fan = fanotify_init(FAN_CLOEXEC | FAN_CLASS_NOTIF | FAN_REPORT_FID ,
+                              O_RDONLY | O_LARGEFILE);
+    
+    if(fan == -1){
+        perror("fanotify_init");
+        // exit(EXIT_FAILURE);
+    }
+        int ret = fanotify_mark(fan, FAN_MARK_ADD    ,  FAN_CREATE | FAN_DELETE | FAN_MODIFY | FAN_EVENT_ON_CHILD,  
+        AT_FDCWD, "/home/alan/gitlive/gitlive/fanotify"
+    );  
+       
+       
+    if(ret == -1){
+        perror("fanotify_mark");
+        exit(EXIT_FAILURE);
+    }
+
+    while(1){
+        buflen = read(fan, buf, sizeof(buf));
+        metadata = (struct fanotify_event_metadata*)&buf;
+         printf("got something\n");
+        while(FAN_EVENT_OK(metadata, buflen)){
+            if (metadata->mask & FAN_Q_OVERFLOW){
+                printf("Queue overflow!\n");
+                continue;
+            }
+            printf("look at /proc/self/fd/%d\n", metadata->fd);
+            // Resolve path, using automatically opened fd
+            sprintf(fdpath, "/proc/self/fd/%d", metadata->fd);
+            linklen = readlink(fdpath, path, sizeof(path) - 1);
+            path[linklen] = '\0';
+            printf("%s\n", path);
+
+            close(metadata->fd);
+            metadata = FAN_EVENT_NEXT(metadata, buflen);
+        }
+    }
+}
+
diff --git a/fanotify/fanotify-helper.c b/fanotify/fanotify-helper.c
new file mode 100644 (file)
index 0000000..343eb33
--- /dev/null
@@ -0,0 +1,153 @@
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fanotify.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Read all available fanotify events from the file descriptor 'fd'. */
+
+#define BUF_SIZE  1024
+
+void fanotify_read(int fd)
+{
+     int  ret, event_fd, mount_fd;
+    ssize_t len, path_len;
+    char path[PATH_MAX];
+    char procfd_path[PATH_MAX];
+    char events_buf[BUF_SIZE];
+    struct file_handle *file_handle;
+    struct fanotify_event_metadata *metadata;
+    struct fanotify_event_info_fid *fid;
+    const char *file_name;
+    struct stat sb;
+    
+     mount_fd = open("/", O_DIRECTORY | O_RDONLY);
+      if (mount_fd == -1) {
+               perror("cant open mount");
+               exit(EXIT_FAILURE);
+           }
+    printf("Listening for events.\n");
+    
+    /* Read events from the event queue into a buffer. */
+    
+    len = read(fd, events_buf, sizeof(events_buf));
+    if (len == -1 && errno != EAGAIN) {
+        perror("read");
+        exit(EXIT_FAILURE);
+    }
+    
+    /* Process all events within the buffer. */
+    
+    for (metadata = (struct fanotify_event_metadata *) events_buf;
+            FAN_EVENT_OK(metadata, len);
+            metadata = FAN_EVENT_NEXT(metadata, len)) {
+        
+        
+        fid = (struct fanotify_event_info_fid *) (metadata + 1);
+        
+        
+        
+        file_handle = (struct file_handle *) fid->handle;
+    
+        // Ensure that the event info is of the correct type. 
+    
+        if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_FID ||
+            fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID) {
+            printf("Got FID or DFID.\n");
+            file_name = NULL;
+        } else if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
+            file_name = file_handle->f_handle +
+                        file_handle->handle_bytes;
+            printf("Got DFID_NAME %s.\n",file_name);                        
+        } else {
+            fprintf(stderr, "Received unexpected event info type.\n");
+            exit(EXIT_FAILURE);
+        }
+    
+        if (metadata->mask == FAN_CREATE)
+            printf("FAN_CREATE (file created):\n");
+    
+        if (metadata->mask == (FAN_CREATE | FAN_ONDIR))
+            printf("FAN_CREATE | FAN_ONDIR (subdirectory created):\n");
+    
+    
+    
+        // what is fsid??
+        /* - from
+         *
+         https://github.com/GNOME/tracker-miners/blob/38596491ead21b1ee6db99f2623ac6f127b6da34/src/miners/fs/tracker-monitor-fanotify.c#L356
+         
+         seems to create a hashtable for all monitored files?
+         
+         *handle = (HandleData *) &fid->fsid;
+               fid_bytes = create_bytes_for_handle (handle);
+               data = g_hash_table_lookup (monitor->handles, fid_bytes);
+               g_bytes_unref (fid_bytes);
+               *
+        
+     /* metadata->fd is set to FAN_NOFD when the group identifies
+        objects by file handles.  To obtain a file descriptor for
+        the file object corresponding to an event you can use the
+        struct file_handle that's provided within the
+        fanotify_event_info_fid in conjunction with the
+        open_by_handle_at(2) system call.  A check for ESTALE is
+        done to accommodate for the situation where the file handle
+        for the object was deleted prior to this system call. */
+     
+
+        event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
+        if (event_fd == -1) {
+            if (errno == ESTALE) {
+                printf("File handle is no longer valid. "
+                        "File has been deleted\n");
+                continue;
+            } else {
+                perror("open_by_handle_at");
+                exit(EXIT_FAILURE);
+            }
+        }
+    
+        snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
+                event_fd);
+    
+        /* Retrieve and print the path of the modified dentry. */
+    
+        path_len = readlink(procfd_path, path, sizeof(path) - 1);
+        if (path_len == -1) {
+            perror("readlink");
+            exit(EXIT_FAILURE);
+        }
+    
+        path[path_len] = '\0';
+        printf("\tDirectory '%s' has been modified.\n", path);
+    
+        if (file_name) {
+            ret = fstatat(event_fd, file_name, &sb, 0);
+            if (ret == -1) {
+                if (errno != ENOENT) {
+                    perror("fstatat");
+                    exit(EXIT_FAILURE);
+                }
+                printf("\tEntry '%s' does not exist.\n", file_name);
+            } else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
+                printf("\tEntry '%s' is a subdirectory.\n", file_name);
+            } else {
+                printf("\tEntry '%s' is not a subdirectory.\n",
+                        file_name);
+            }
+        }
+    
+        /* Close associated file descriptor for this event. */
+    
+        close(event_fd);
+    }
+    
+    printf("All events processed successfully. Program exiting.\n");
+    exit(EXIT_SUCCESS);
+}
\ No newline at end of file
diff --git a/fanotify/fanotify-man.c b/fanotify/fanotify-man.c
new file mode 100644 (file)
index 0000000..3406bc7
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *   A simple tester of fanotify in the Linux kernel.
+ *
+ *   This program is released in the Public Domain.
+ *
+ *   Compile with:
+ *     $> gcc -o /tmp/fanotify-man fanotify-man.c
+ * 
+ *   Run as:
+ *     $> /tmp/fanotify-example 
+ */
+    #define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+       #include <errno.h>
+       #include <fcntl.h>
+       #include <limits.h>
+       #include <poll.h>
+       #include <stdio.h>
+       #include <stdlib.h>
+       #include <sys/fanotify.h>
+       #include <unistd.h>
+
+       /* Read all available fanotify events from the file descriptor 'fd'. */
+
+       static void
+       handle_events(int fd)
+       {
+           const struct fanotify_event_metadata *metadata;
+           struct fanotify_event_metadata buf[200];
+           ssize_t len;
+           char path[PATH_MAX];
+           ssize_t path_len;
+           char procfd_path[PATH_MAX];
+           struct fanotify_response response;
+
+           /* Loop while events can be read from fanotify file descriptor. */
+
+           for (;;) {
+
+               /* Read some events. */
+
+               len = read(fd, buf, sizeof(buf));
+               if (len == -1 && errno != EAGAIN) {
+                   perror("read");
+                   exit(EXIT_FAILURE);
+               }
+
+               /* Check if end of available data reached. */
+
+               if (len <= 0)
+                   break;
+
+               /* Point to the first event in the buffer. */
+
+               metadata = buf;
+
+               /* Loop over all events in the buffer. */
+
+               while (FAN_EVENT_OK(metadata, len)) {
+
+                   /* Check that run-time and compile-time structures match. */
+
+                   if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+                       fprintf(stderr,
+                               "Mismatch of fanotify metadata version.\n");
+                       exit(EXIT_FAILURE);
+                   }
+
+                   /* metadata->fd contains either FAN_NOFD, indicating a
+                      queue overflow, or a file descriptor (a nonnegative
+                      integer). Here, we simply ignore queue overflow. */
+
+                   if (metadata->fd >= 0) {
+
+                       /* Handle open permission event. */
+
+                       if (metadata->mask & FAN_OPEN_PERM) {
+                           printf("FAN_OPEN_PERM: ");
+
+                           /* Allow file to be opened. */
+
+                           response.fd = metadata->fd;
+                           response.response = FAN_ALLOW;
+                           write(fd, &response, sizeof(response));
+                       }
+
+                       /* Handle closing of writable file event. */
+
+                       if (metadata->mask & FAN_CLOSE_WRITE)
+                           printf("FAN_CLOSE_WRITE: ");
+
+                       /* Retrieve and print pathname of the accessed file. */
+
+                       snprintf(procfd_path, sizeof(procfd_path),
+                                "/proc/self/fd/%d", metadata->fd);
+                       path_len = readlink(procfd_path, path,
+                                           sizeof(path) - 1);
+                       if (path_len == -1) {
+                           perror("readlink");
+                           exit(EXIT_FAILURE);
+                       }
+
+                       path[path_len] = '\0';
+                       printf("File %s\n", path);
+
+                       /* Close the file descriptor of the event. */
+
+                       close(metadata->fd);
+                   }
+
+                   /* Advance to next event. */
+
+                   metadata = FAN_EVENT_NEXT(metadata, len);
+               }
+           }
+       }
+
+       int
+       main(int argc, char *argv[])
+       {
+           char buf;
+           int fd, poll_num;
+           nfds_t nfds;
+           struct pollfd fds[2];
+
+           /* Check mount point is supplied. */
+
+           if (argc != 2) {
+               fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
+               exit(EXIT_FAILURE);
+           }
+
+           printf("Press enter key to terminate.\n");
+
+           /* Create the file descriptor for accessing the fanotify API. */
+
+           fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+                              O_RDONLY | O_LARGEFILE);
+           if (fd == -1) {
+               perror("fanotify_init");
+               exit(EXIT_FAILURE);
+           }
+
+           /* Mark the mount for:
+              - permission events before opening files
+              - notification events after closing a write-enabled
+                file descriptor. */
+
+           if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+                             FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD,
+                             argv[1]) == -1) {
+               perror("fanotify_mark");
+               exit(EXIT_FAILURE);
+           }
+
+           /* Prepare for polling. */
+
+           nfds = 2;
+
+           fds[0].fd = STDIN_FILENO;       /* Console input */
+           fds[0].events = POLLIN;
+
+           fds[1].fd = fd;                 /* Fanotify input */
+           fds[1].events = POLLIN;
+
+           /* This is the loop to wait for incoming events. */
+
+           printf("Listening for events.\n");
+
+           while (1) {
+               poll_num = poll(fds, nfds, -1);
+               if (poll_num == -1) {
+                   if (errno == EINTR)     /* Interrupted by a signal */
+                       continue;           /* Restart poll() */
+
+                   perror("poll");         /* Unexpected error */
+                   exit(EXIT_FAILURE);
+               }
+
+               if (poll_num > 0) {
+                   if (fds[0].revents & POLLIN) {
+
+                       /* Console input is available: empty stdin and quit. */
+
+                       while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
+                           continue;
+                       break;
+                   }
+
+                   if (fds[1].revents & POLLIN) {
+
+                       /* Fanotify events are available. */
+
+                       handle_events(fd);
+                   }
+               }
+           }
+
+           printf("Listening for events stopped.\n");
+           exit(EXIT_SUCCESS);
+       }