250 lines
6.7 KiB
C
250 lines
6.7 KiB
C
// 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
|