From: Alan Date: Thu, 25 May 2023 05:41:50 +0000 (+0800) Subject: add fanotify tests X-Git-Url: http://git.roojs.org/?p=gitlive;a=commitdiff_plain;h=d8c5b52376aefeef8d265f038feff500003e98a2 add fanotify tests --- diff --git a/.gitignore b/.gitignore index 706f9c71..c4c71475 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 00000000..e18dc3f4 --- /dev/null +++ b/fanotify/fanotify-example.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + 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 index 00000000..343eb336 --- /dev/null +++ b/fanotify/fanotify-helper.c @@ -0,0 +1,153 @@ +#define _GNU_SOURCE /* Needed to get O_LARGEFILE definition */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 00000000..3406bc7f --- /dev/null +++ b/fanotify/fanotify-man.c @@ -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 + #include + #include + #include + #include + #include + #include + #include + + /* 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); + }