blob: e0d51302536c57f1c6b9730db3eceadb762d9517 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2019 CTERA Networks. All Rights Reserved.
*
* Started by Amir Goldstein <amir73il@gmail.com>
* Modified by Matthew Bobrowski <mbobrowski@mbobrowski.org>
*
* DESCRIPTION
* Test file that has been purposely designed to verify
* FAN_REPORT_FID functionality while using newly defined dirent
* events.
*/
#define _GNU_SOURCE
#include "config.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include "tst_test.h"
#include "fanotify.h"
#if defined(HAVE_SYS_FANOTIFY_H)
#include <sys/fanotify.h>
#define BUF_SIZE 256
#define EVENT_MAX 256
#define MOUNT_POINT "mntpoint"
#define TEST_DIR MOUNT_POINT"/test_dir"
#define DIR1 TEST_DIR"/dir1"
#define DIR2 TEST_DIR"/dir2"
#define FILE1 TEST_DIR"/file1"
#define FILE2 TEST_DIR"/file2"
#if defined(HAVE_NAME_TO_HANDLE_AT)
struct event_t {
unsigned long long mask;
__kernel_fsid_t fsid;
struct file_handle handle;
char buf[MAX_HANDLE_SZ];
};
static int fanotify_fd;
static char events_buf[BUF_SIZE];
static struct event_t event_set[EVENT_MAX];
static void do_test(void)
{
int i, fd, len, count = 0;
struct file_handle *event_file_handle;
struct fanotify_event_metadata *metadata;
struct fanotify_event_info_fid *event_fid;
if (fanotify_mark(fanotify_fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
FAN_CREATE | FAN_DELETE | FAN_ATTRIB |
FAN_MOVED_FROM | FAN_MOVED_TO |
FAN_DELETE_SELF | FAN_ONDIR,
AT_FDCWD, TEST_DIR) == -1) {
if (errno == ENODEV)
tst_brk(TCONF,
"FAN_REPORT_FID not supported on %s "
"filesystem", tst_device->fs_type);
tst_brk(TBROK | TERRNO,
"fanotify_mark(%d, FAN_MARK_ADD, FAN_CREATE | "
"FAN_DELETE | FAN_MOVED_FROM | FAN_MOVED_TO | "
"FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed",
fanotify_fd, TEST_DIR);
}
/* Generate a sequence of events */
event_set[count].mask = FAN_CREATE | FAN_MOVED_FROM | FAN_MOVED_TO | \
FAN_DELETE;
event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
fanotify_get_fid(TEST_DIR, &event_set[count].fsid,
&event_set[count].handle);
count++;
fd = SAFE_CREAT(FILE1, 0644);
SAFE_CLOSE(fd);
SAFE_RENAME(FILE1, FILE2);
event_set[count].mask = FAN_ATTRIB | FAN_DELETE_SELF;
event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
fanotify_get_fid(FILE2, &event_set[count].fsid,
&event_set[count].handle);
count++;
SAFE_UNLINK(FILE2);
/*
* Generate a sequence of events on a directory. Subsequent events
* are merged, so it's required that we set FAN_ONDIR once in
* order to acknowledge that changes related to a subdirectory
* took place. Events on subdirectories are not merged with events
* on non-subdirectories.
*/
event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVED_FROM | \
FAN_MOVED_TO | FAN_DELETE;
event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
fanotify_get_fid(TEST_DIR, &event_set[count].fsid,
&event_set[count].handle);
count++;
SAFE_MKDIR(DIR1, 0755);
SAFE_RENAME(DIR1, DIR2);
event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF;
event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
fanotify_get_fid(DIR2, &event_set[count].fsid,
&event_set[count].handle);
count++;
SAFE_RMDIR(DIR2);
/* Read events from the event queue */
len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
/* Process each event in buffer */
for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
FAN_EVENT_OK(metadata, len);
metadata = FAN_EVENT_NEXT(metadata,len), i++) {
event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
event_file_handle = (struct file_handle *) event_fid->handle;
if (i >= count) {
tst_res(TFAIL,
"got unnecessary event: mask=%llx "
"pid=%u fd=%d",
(unsigned long long) metadata->mask,
metadata->pid,
metadata->fd);
metadata->mask = 0;
} else if (metadata->fd != FAN_NOFD) {
tst_res(TFAIL,
"Received unexpected file descriptor %d in "
"event. Expected to get FAN_NOFD(%d)",
metadata->fd, FAN_NOFD);
} else if (metadata->mask != event_set[i].mask) {
tst_res(TFAIL,
"Got event: mask=%llx (expected %llx) "
"pid=%u fd=%d",
(unsigned long long) metadata->mask,
event_set[i].mask,
(unsigned) metadata->pid,
metadata->fd);
} else if (metadata->pid != getpid()) {
tst_res(TFAIL,
"Got event: mask=%llx pid=%u "
"(expected %u) fd=%d",
(unsigned long long) metadata->mask,
(unsigned) metadata->pid,
(unsigned) getpid(),
metadata->fd);
} else if (event_file_handle->handle_bytes !=
event_set[i].handle.handle_bytes) {
tst_res(TFAIL,
"Got event: handle_bytes (%x) returned in "
"event does not equal handle_bytes (%x) "
"retunred in name_to_handle_at(2)",
event_file_handle->handle_bytes,
event_set[i].handle.handle_bytes);
} else if (event_file_handle->handle_type !=
event_set[i].handle.handle_type) {
tst_res(TFAIL,
"handle_type (%x) returned in event does not "
"equal to handle_type (%x) returned in "
"name_to_handle_at(2)",
event_file_handle->handle_type,
event_set[i].handle.handle_type);
} else if (memcmp(event_file_handle->f_handle,
event_set[i].handle.f_handle,
event_set[i].handle.handle_bytes)
!= 0) {
tst_res(TFAIL,
"event_file_handle->f_handle does not match "
"handle.f_handle returned in "
"name_to_handle_at(2)");
} else if (memcmp(&event_fid->fsid, &event_set[i].fsid,
sizeof(event_set[i].fsid)) != 0) {
tst_res(TFAIL,
"event_fid->fsid != stats.f_fsid that was "
"obtained via statfs(2)");
} else {
tst_res(TPASS,
"Got event: mask=%llx, pid=%u, "
"fid=%x.%x.%lx values",
metadata->mask,
getpid(),
FSID_VAL_MEMBER(event_fid->fsid, 0),
FSID_VAL_MEMBER(event_fid->fsid, 1),
*(unsigned long *)
event_file_handle->f_handle);
}
}
for (; i < count; i++)
tst_res(TFAIL,
"Didn't receive event: mask=%llx",
event_set[i].mask);
}
static void do_setup(void)
{
int fd;
/* Check kernel for fanotify support */
fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
SAFE_CLOSE(fd);
fanotify_fd = fanotify_init(FAN_REPORT_FID, O_RDONLY);
if (fanotify_fd == -1) {
if (errno == EINVAL)
tst_brk(TCONF,
"FAN_REPORT_FID not supported in kernel");
tst_brk(TBROK | TERRNO,
"fanotify_init(FAN_REPORT_FID, O_RDONLY) failed");
}
SAFE_MKDIR(TEST_DIR, 0755);
}
static void do_cleanup(void)
{
if (fanotify_fd > 0)
SAFE_CLOSE(fanotify_fd);
}
static struct tst_test test = {
.needs_root = 1,
.mount_device = 1,
.mntpoint = MOUNT_POINT,
.all_filesystems = 1,
.test_all = do_test,
.setup = do_setup,
.cleanup = do_cleanup
};
#else
TST_TEST_TCONF("System does not have required name_to_handle_at() support");
#endif
#else
TST_TEST_TCONF("System does not have required fanotify support");
#endif