#include <errno.h>
#include <fcntl.h>
#include <libzfs.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <zone.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "testlib.h"
#include "mevent.h"
#define MB (1024 * 1024)
static char *cookie = "Chocolate chip with fudge stripes";
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static void
callback(int fd, enum ev_type ev, void *arg)
{
static off_t size = 0;
struct stat st;
ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE);
ASSERT_PTR_EQ(("bad cookie"), arg, cookie);
if (fstat(fd, &st) != 0)
FAIL_ERRNO("fstat failed");
ASSERT_INT64_NEQ(("Size has not changed"), size, st.st_size);
size = st.st_size;
pthread_mutex_lock(&mtx);
pthread_cond_signal(&cv);
VERBOSE(("wakeup"));
pthread_mutex_unlock(&mtx);
}
static void
destroy_zpool(libzfs_handle_t *zfshdl, zpool_handle_t *poolhdl,
zfs_handle_t *volhdl)
{
if (volhdl != NULL) {
if (zfs_destroy(volhdl, B_FALSE) != 0) {
FAIL(("Failed to destroy ZVOL - %s",
libzfs_error_description(zfshdl)));
}
}
if (poolhdl != NULL) {
if (zpool_destroy(poolhdl, testlib_prog) != 0) {
FAIL(("Failed to destroy ZPOOL - %s",
libzfs_error_description(zfshdl)));
}
}
}
static void
create_zpool(libzfs_handle_t *zfshdl, const char *pool, const char *file)
{
nvlist_t *nvroot, *props;
nvlist_t *vdevs[1];
nvroot = fnvlist_alloc();
props = fnvlist_alloc();
vdevs[0] = fnvlist_alloc();
fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_PATH, file);
fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE);
fnvlist_add_uint64(vdevs[0], ZPOOL_CONFIG_IS_LOG, 0);
fnvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT);
fnvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, vdevs, 1);
fnvlist_add_string(props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), ZFS_MOUNTPOINT_NONE);
if (zpool_create(zfshdl, pool, nvroot, NULL, props) != 0) {
FAIL(("Failed to create ZPOOL %s using %s - %s",
pool, file, libzfs_error_description(zfshdl)));
}
VERBOSE(("Created ZFS pool %s", pool));
}
static bool
create_zvol(libzfs_handle_t *zfshdl, const char *vol)
{
nvlist_t *volprops;
int err;
volprops = fnvlist_alloc();
fnvlist_add_uint64(volprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1 * MB);
err = zfs_create(zfshdl, vol, ZFS_TYPE_VOLUME, volprops);
if (err != 0) {
(void) printf("Failed to create ZVOL %s - %s",
vol, libzfs_error_description(zfshdl));
return (false);
}
VERBOSE(("Created ZVOL %s", vol));
return (true);
}
int
main(int argc, const char **argv)
{
libzfs_handle_t *zfshdl;
char *template, *pool, *vol, *backend;
struct mevent *evp;
zpool_handle_t *poolhdl = NULL;
zfs_handle_t *volhdl = NULL;
int err, fd;
start_test(argv[0], 10);
set_mevent_file_poll_interval_ms(1000);
if (getzoneid() != GLOBAL_ZONEID)
FAIL(("Can only be run in the global zone"));
if ((zfshdl = libzfs_init()) == NULL)
FAIL_ERRNO("Could not open ZFS library");
template = strdup("/tmp/mevent.vnode.zvol.XXXXXX");
ASSERT_PTR_NEQ(("strdup"), template, NULL);
fd = mkstemp(template);
if (fd == -1)
FAIL_ERRNO("Couldn't create temporary file with mkstemp");
VERBOSE(("Opened temporary file at '%s'", template));
err = asprintf(&pool, "mevent_test_%d", getpid());
ASSERT_INT_NEQ(("asprintf pool"), err, -1);
err = asprintf(&vol, "%s/test_zvol_%d", pool, getpid());
ASSERT_INT_NEQ(("asprintf vol"), err, -1);
err = asprintf(&backend, "/dev/zvol/rdsk/%s", vol);
ASSERT_INT_NEQ(("asprintf backend"), err, -1);
err = ftruncate(fd, 64 * MB);
if (err != 0)
FAIL_ERRNO("ftruncate");
(void) close(fd);
fd = -1;
create_zpool(zfshdl, pool, template);
if ((poolhdl = zpool_open(zfshdl, pool)) == NULL) {
(void) printf("Could not open ZPOOL - %s\n",
libzfs_error_description(zfshdl));
err = EXIT_FAIL;
goto out;
}
if (!create_zvol(zfshdl, vol)) {
err = EXIT_FAIL;
goto out;
}
if ((volhdl = zfs_open(zfshdl, vol, ZFS_TYPE_VOLUME)) == NULL) {
(void) printf("Could not open ZFS volume - %s\n",
libzfs_error_description(zfshdl));
err = EXIT_FAIL;
goto out;
}
if ((fd = open(backend, O_RDWR)) == -1) {
(void) printf("Failed to open '%s': %s\n",
backend, strerror(errno));
err = EXIT_FAIL;
goto out;
}
VERBOSE(("Opened backend %s", backend));
start_event_thread();
evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie);
if (evp == NULL) {
(void) printf("mevent_add returned NULL\n");
err = EXIT_FAIL;
goto out;
}
for (uint_t i = 2; i < 4; i++) {
ssize_t written;
char buf[64];
if (lseek(fd, 0, SEEK_SET) == -1)
FAIL_ERRNO("lseek");
written = write(fd, cookie, strlen(cookie));
if (written < 0)
FAIL_ERRNO("bad write");
ASSERT_INT64_EQ(("write cookie", i), written, strlen(cookie));
(void) snprintf(buf, sizeof (buf), "%llu", i * MB);
VERBOSE(("Setting volsize to %s", buf));
if (zfs_prop_set(volhdl,
zfs_prop_to_name(ZFS_PROP_VOLSIZE), buf) != 0) {
(void) printf("Failed to increase ZFS volume size\n");
pthread_mutex_unlock(&mtx);
err = EXIT_FAIL;
goto out;
}
pthread_mutex_lock(&mtx);
pthread_cond_wait(&cv, &mtx);
pthread_mutex_unlock(&mtx);
}
(void) mevent_disable(evp);
err = EXIT_PASS;
out:
(void) close(fd);
destroy_zpool(zfshdl, poolhdl, volhdl);
(void) libzfs_fini(zfshdl);
(void) unlink(template);
if (err == EXIT_PASS)
PASS();
exit(err);
}