【问题标题】:Why the fanotify_fid Linux manpage example code fails (on open_by_handle_at())?为什么 fanotify_fid Linux 手册页示例代码失败(在 open_by_handle_at() 上)?
【发布时间】:2021-01-03 13:11:22
【问题描述】:

我正在 Linux 5.4 上测试 fanotify(也在 5.8 上测试过);对于测试,我使用的是 fanotify(7) 手册页中的 fanotify_fid.c 示例。

现在,代码似乎很糟糕——我认为至少有几个错误——但我还是设法让它在一定程度上正常工作。

有一个我无法解决的问题 - open_by_handle_at() 失败,Invalid argument

以下是我使用的示例代码,带有注释;它是原始代码的简化和更正版本;它适用于 gcc 和 clang。
为了测试它,执行它,然后在另一个终端中执行mktemp(或者只是在/tmp下创建一个文件/目录)。

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/fanotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define BUF_SIZE 512

int main(int argc, char **argv) {
  char *filename = "/tmp";

  int fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, 0);
  if (fd == -1)
    exit(EXIT_FAILURE);

  int ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_ONLYDIR, FAN_CREATE | FAN_ONDIR, AT_FDCWD, filename);
  if (ret == -1)
    exit(EXIT_FAILURE);

  char events_buf[BUF_SIZE];
  char path[PATH_MAX], procfd_path[PATH_MAX];

  ssize_t len = read(fd, (void *)&events_buf, sizeof(events_buf));
  if (len == -1 && errno != EAGAIN)
    exit(EXIT_FAILURE);

  for (
    struct fanotify_event_metadata *metadata = (struct fanotify_event_metadata *)events_buf;
    FAN_EVENT_OK(metadata, len);
    metadata = FAN_EVENT_NEXT(metadata, len)
  ) {
    struct fanotify_event_info_fid *fid = (struct fanotify_event_info_fid *)(metadata + 1);

    // The mangpage `BUF_SIZE` is 256; this causes `info_type` to be 64 instead of 0
    // (when running `mktemp`), which causes an error.
    if (fid->hdr.info_type != FAN_EVENT_INFO_TYPE_FID) {
      fprintf(stderr, "Received unexpected event info type: %i.\n", fid->hdr.info_type);
      exit(EXIT_FAILURE);
    }

    if (metadata->mask == FAN_CREATE)
      printf("FAN_CREATE (file/directory created)\n");

    struct file_handle *file_handle = (struct file_handle *)fid->handle;

    // The manpage condition is `(ret == -1)`, which seems to be a bug.
    int event_fd = open_by_handle_at(AT_FDCWD, file_handle, O_RDONLY);
    if (event_fd == -1) {
      printf("File handle hex: ");
      for (int i = 0; i < sizeof(struct file_handle); i++)
        printf("%02x ", ((unsigned char *)file_handle)[i]);
      printf("\n");

      perror("open_by_handle_at");
      exit(EXIT_FAILURE);
    }

    snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d", event_fd);

    ssize_t path_len = readlink(procfd_path, path, sizeof(path) - 1);
    if (path_len == -1) {
        perror("readlink");
        exit(EXIT_FAILURE);
    }

    close(event_fd);
  }
}

【问题讨论】:

  • 运行这个时你的当前目录是什么?
  • @stark 错误发生与当前位置无关;我已经尝试过任意一个,来自/tmp (cd /tmp; sudo /path/to/a.out)。
  • 从手册页中,第二个参数必须是“之前调用 name_to_handle_at() 返回的文件句柄”
  • 在 fanotify 头文件中,变量字段所属的结构,被显式声明为open_by_handle_at(2)的参数:git.io/JLd6V
  • 示例中是一个(其他)错误。我已经用解决方案创建了一个答案。

标签: c linux filesystems file-watcher fanotify


【解决方案1】:

5.4 fanotify(7) 手册页示例有很多错误,其中一个是open_by_handle_at() 的第一个参数应该是挂载/目录fd,而不是AT_FDCWD

已更正 sn-ps,来自 5.10 man page

mount_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
if (mount_fd == -1) { /* error handling */ }

// ...

event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
if (event_fd == -1) { /* error handling */ }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多