root/samples/fanotify/fs-monitor.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2021, Collabora Ltd.
 */

#define _GNU_SOURCE
#include <errno.h>
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/fanotify.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef __GLIBC__
#include <asm-generic/int-ll64.h>
#endif

#ifndef FAN_FS_ERROR
#define FAN_FS_ERROR            0x00008000
#define FAN_EVENT_INFO_TYPE_ERROR       5

struct fanotify_event_info_error {
        struct fanotify_event_info_header hdr;
        __s32 error;
        __u32 error_count;
};
#endif

#ifndef FILEID_INO32_GEN
#define FILEID_INO32_GEN        1
#endif

#ifndef FILEID_INVALID
#define FILEID_INVALID          0xff
#endif

static void print_fh(struct file_handle *fh)
{
        int i;
        uint32_t *h = (uint32_t *) fh->f_handle;

        printf("\tfh: ");
        for (i = 0; i < fh->handle_bytes; i++)
                printf("%hhx", fh->f_handle[i]);
        printf("\n");

        printf("\tdecoded fh: ");
        if (fh->handle_type == FILEID_INO32_GEN)
                printf("inode=%u gen=%u\n", h[0], h[1]);
        else if (fh->handle_type == FILEID_INVALID && !fh->handle_bytes)
                printf("Type %d (Superblock error)\n", fh->handle_type);
        else
                printf("Type %d (Unknown)\n", fh->handle_type);

}

static void handle_notifications(char *buffer, int len)
{
        struct fanotify_event_metadata *event =
                (struct fanotify_event_metadata *) buffer;
        struct fanotify_event_info_header *info;
        struct fanotify_event_info_error *err;
        struct fanotify_event_info_fid *fid;
        int off;

        for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) {

                if (event->mask != FAN_FS_ERROR) {
                        printf("unexpected FAN MARK: %llx\n",
                                                        (unsigned long long)event->mask);
                        goto next_event;
                }

                if (event->fd != FAN_NOFD) {
                        printf("Unexpected fd (!= FAN_NOFD)\n");
                        goto next_event;
                }

                printf("FAN_FS_ERROR (len=%d)\n", event->event_len);

                for (off = sizeof(*event) ; off < event->event_len;
                     off += info->len) {
                        info = (struct fanotify_event_info_header *)
                                ((char *) event + off);

                        switch (info->info_type) {
                        case FAN_EVENT_INFO_TYPE_ERROR:
                                err = (struct fanotify_event_info_error *) info;

                                printf("\tGeneric Error Record: len=%d\n",
                                       err->hdr.len);
                                printf("\terror: %d\n", err->error);
                                printf("\terror_count: %d\n", err->error_count);
                                break;

                        case FAN_EVENT_INFO_TYPE_FID:
                                fid = (struct fanotify_event_info_fid *) info;

                                printf("\tfsid: %x%x\n",
#if defined(__GLIBC__)
                                       fid->fsid.val[0], fid->fsid.val[1]);
#else
                                       fid->fsid.__val[0], fid->fsid.__val[1]);
#endif
                                print_fh((struct file_handle *) &fid->handle);
                                break;

                        default:
                                printf("\tUnknown info type=%d len=%d:\n",
                                       info->info_type, info->len);
                        }
                }
next_event:
                printf("---\n\n");
        }
}

int main(int argc, char **argv)
{
        int fd;

        char buffer[BUFSIZ];

        if (argc < 2) {
                printf("Missing path argument\n");
                return 1;
        }

        fd = fanotify_init(FAN_CLASS_NOTIF|FAN_REPORT_FID, O_RDONLY);
        if (fd < 0)
                errx(1, "fanotify_init");

        if (fanotify_mark(fd, FAN_MARK_ADD|FAN_MARK_FILESYSTEM,
                          FAN_FS_ERROR, AT_FDCWD, argv[1])) {
                errx(1, "fanotify_mark");
        }

        while (1) {
                int n = read(fd, buffer, BUFSIZ);

                if (n < 0)
                        errx(1, "read");

                handle_notifications(buffer, n);
        }

        return 0;
}