android13/external/ltp/testcases/kernel/syscalls/fanotify/fanotify19.c

256 lines
5.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
*
* Started by Matthew Bobrowski <mbobrowski@mbobrowski.org>
*/
/*\
* [Description]
* This set of tests is to ensure that the unprivileged listener feature of
* fanotify is functioning as expected. The objective of this test file is
* to generate a sequence of events and ensure that the returned events
* contain the limited values that an unprivileged listener is expected
* to receive.
*/
#define _GNU_SOURCE
#include "config.h"
#include <pwd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "tst_test.h"
#ifdef HAVE_SYS_FANOTIFY_H
#include "fanotify.h"
#define EVENT_MAX 1024
#define EVENT_SIZE (sizeof (struct fanotify_event_metadata))
#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE)
#define EVENT_SET_MAX 48
#define BUF_SIZE 256
#define MOUNT_PATH "fs_mnt"
#define TEST_FILE MOUNT_PATH "/testfile"
static int fd_notify;
static char buf[BUF_SIZE];
static struct fanotify_event_metadata event_buf[EVENT_BUF_LEN];
static struct test_case_t {
const char *name;
unsigned int fork;
unsigned int event_count;
unsigned long long event_set[EVENT_SET_MAX];
} test_cases[] = {
{
"unprivileged listener - events by self",
0,
4,
{
FAN_OPEN,
FAN_ACCESS,
FAN_MODIFY,
FAN_CLOSE,
}
},
{
"unprivileged lisneter - events by child",
1,
4,
{
FAN_OPEN,
FAN_ACCESS,
FAN_MODIFY,
FAN_CLOSE,
}
}
};
static void generate_events(void)
{
int fd;
/* FAN_OPEN */
fd = SAFE_OPEN(TEST_FILE, O_RDWR);
/* FAN_ACCESS */
SAFE_READ(0, fd, buf, BUF_SIZE);
/* FAN_MODIFY */
SAFE_WRITE(1, fd, TEST_FILE, 1);
/* FAN_CLOSE */
SAFE_CLOSE(fd);
}
static void do_fork(void)
{
int status;
pid_t child;
child = SAFE_FORK();
if (child == 0) {
SAFE_CLOSE(fd_notify);
generate_events();
exit(0);
}
SAFE_WAITPID(child, &status, 0);
if (WIFEXITED(child) && WEXITSTATUS(child) != 0)
tst_brk(TBROK, "Child process terminated incorrectly. Aborting");
}
static void test_fanotify(unsigned int n)
{
int len = 0;
pid_t pid = getpid();
unsigned int test_number = 0;
struct fanotify_event_metadata *event;
struct test_case_t *tc = &test_cases[n];
tst_res(TINFO, "Test #%d %s", n, tc->name);
/* Initialize fanotify */
fd_notify = fanotify_init(FANOTIFY_REQUIRED_USER_INIT_FLAGS, O_RDONLY);
if (fd_notify < 0) {
if (errno == EPERM || errno == EINVAL) {
tst_res(TCONF,
"unprivileged fanotify not supported by kernel?");
return;
} else {
tst_brk(TBROK | TERRNO,
"fanotify_init(FAN_CLASS_NOTIF, O_RDONLY) failed");
}
}
/* Place mark on object */
if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ALL_EVENTS,
AT_FDCWD, TEST_FILE) < 0) {
tst_brk(TBROK | TERRNO,
"fanotify_mark(%d, FAN_MARK_ADD, %d, "
"AT_FDCWD, %s) failed",
fd_notify,
FAN_ALL_EVENTS,
TEST_FILE);
}
/* Generate events in either child or listening process */
if (tc->fork)
do_fork();
else
generate_events();
/* Read events from queue */
len = SAFE_READ(0, fd_notify, event_buf + len, EVENT_BUF_LEN - len);
event = event_buf;
/* Iterate over and validate events against expected result set */
while (FAN_EVENT_OK(event, len) && test_number < tc->event_count) {
if (!(event->mask & tc->event_set[test_number])) {
tst_res(TFAIL,
"Received unexpected event mask: mask=%llx "
"pid=%u fd=%d",
(unsigned long long) event->mask,
(unsigned) event->pid,
event->fd);
} else if ((!tc->fork && event->pid != pid) ||
(tc->fork && event->pid != 0)) {
tst_res(TFAIL,
"Received unexpected pid in event: "
"mask=%llx pid=%u (expected %u) fd=%d",
(unsigned long long) event->mask,
(unsigned) event->pid,
(tc->fork ? 0 : pid),
event->fd);
} else if (event->fd != FAN_NOFD) {
tst_res(TFAIL,
"Received unexpected file descriptor: "
"mask=%llx pid=%u fd=%d (expected %d)",
(unsigned long long) event->pid,
(unsigned) event->pid,
event->fd,
FAN_NOFD);
SAFE_CLOSE(event->fd);
} else {
tst_res(TPASS,
"Received event: mask=%llx, pid=%u fd=%d",
(unsigned long long) event->mask,
(unsigned) event->pid,
event->fd);
}
/* Non-permission events can be merged into a single event. */
event->mask &= ~tc->event_set[test_number];
if (event->mask == 0)
event = FAN_EVENT_NEXT(event, len);
test_number++;
}
/*
* Determine whether there is still unprocessed events remaining in the
* buffer. This is to cover the basis whereby the event processing loop
* terminates prematurely. In that case, we need to ensure that any
* event file descriptor that is open is closed so that the temporary
* filesystem can be unmounted.
*/
if (FAN_EVENT_OK(event, len)) {
tst_res(TFAIL,
"Event processing loop exited prematurely. Did NOT "
"finish processing events in buffer. Cleaning up.");
while (FAN_EVENT_OK(event, len)) {
if (event->fd != FAN_NOFD)
SAFE_CLOSE(event->fd);
event = FAN_EVENT_NEXT(event, len);
}
}
}
static void setup(void)
{
SAFE_FILE_PRINTF(TEST_FILE, "1");
SAFE_CHMOD(TEST_FILE, 0666);
/* Check for kernel fanotify support */
REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_FILE);
/* Relinquish privileged user */
if (geteuid() == 0) {
tst_res(TINFO,
"Running as privileged user, revoking.");
struct passwd *nobody = SAFE_GETPWNAM("nobody");
SAFE_SETUID(nobody->pw_uid);
}
}
static void cleanup(void)
{
if (fd_notify > 0)
SAFE_CLOSE(fd_notify);
}
static struct tst_test test = {
.test = test_fanotify,
.tcnt = ARRAY_SIZE(test_cases),
.setup = setup,
.cleanup = cleanup,
.forks_child = 1,
.needs_root = 1,
.mount_device = 1,
.mntpoint = MOUNT_PATH,
};
#else
TST_TEST_TCONF("system doesn't have required fanotify support");
#endif