0

On subsequent file deletions (anywhere in the filesystem) fanotify reports the root directory as having its attributes modified. This seems an anomalous bug.

The test code at the end of this question (tested using Ubuntu 23 and an ext4 filesystem) will report attribute changes and deletions on the filesystem. When any file is deleted its parent path is reported as a delete event. On subsequent deletions the root path is returned as an attribute change event just before the deletion event is returned. Why is the root directory returned? This seems an anomalous bug. Here are the results of the test code for four file deletions within the /home/test/tmp/tmp2 directory:

Listening for delete and attribute changes on filesystem
Attribute change event
Delete event
Event at path: /home/test/tmp/tmp2
Attribute change event
Event at path: /
Delete event
Event at path: /home/test/tmp/tmp2
Attribute change event
Event at path: /
Delete event
Event at path: /home/test/tmp/tmp2
Attribute change event
Event at path: /
Delete event
Event at path: /home/test/tmp/tmp2

Note that fanotify code must be run as root.

#define _GNU_SOURCE // Required for open_by_handle_at

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/fanotify.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h> // Required for open_by_handle_at

void die(const char *msg) {
    perror(msg);
    exit(1);
}

int main(void) {
    int fd = fanotify_init(FAN_REPORT_FID, O_RDONLY);
    if (fd == -1) {
        die("fanotify_init");
    }

    if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
                      FAN_DELETE | FAN_ATTRIB,
                      AT_FDCWD, "/") == -1) {
        die("fanotify_mark");
    }

    printf("Listening for delete and attribute changes on filesystem\n");

    char buffer[4096];
    while (1) {
        ssize_t len = read(fd, buffer, sizeof(buffer));
        if (len == -1 && errno != EAGAIN) {
            die("read");
        }

        if (len <= 0) {
            continue;
        }

        struct fanotify_event_metadata *metadata;
        metadata = (struct fanotify_event_metadata *)buffer;

        while (FAN_EVENT_OK(metadata, len)) {
            if (metadata->mask & FAN_DELETE) {
                printf("Delete event\n");
            }
            if (metadata->mask & FAN_ATTRIB) {
                printf("Attribute change event\n");
            }

            struct fanotify_event_info_fid *fid;
            fid = (struct fanotify_event_info_fid *)(metadata + 1);
            int event_fd = open_by_handle_at(AT_FDCWD, (struct file_handle *)fid->handle, O_RDONLY);
            char path[PATH_MAX];
            sprintf(path, "/proc/self/fd/%d", event_fd);
            char filePath[PATH_MAX];
            ssize_t pathLen = readlink(path, filePath, sizeof(filePath) - 1);
            if (pathLen != -1) {
                filePath[pathLen] = '\0';
                printf("Event at path: %s\n", filePath);
            }
            close(event_fd);

            if (metadata->fd != -1) {
                close(metadata->fd);
            }

            metadata = FAN_EVENT_NEXT(metadata, len);
        }
    }

    return 0;
}
Jonathan
  • 1,007
  • 16
  • 12

0 Answers0