1*251becc8SAndy Fiddaman /*
2*251becc8SAndy Fiddaman  * This file and its contents are supplied under the terms of the
3*251becc8SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4*251becc8SAndy Fiddaman  * You may only use this file in accordance with the terms of version
5*251becc8SAndy Fiddaman  * 1.0 of the CDDL.
6*251becc8SAndy Fiddaman  *
7*251becc8SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8*251becc8SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9*251becc8SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10*251becc8SAndy Fiddaman  */
11*251becc8SAndy Fiddaman 
12*251becc8SAndy Fiddaman /*
13*251becc8SAndy Fiddaman  * Copyright 2018 Joyent, Inc.
14*251becc8SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
15*251becc8SAndy Fiddaman  */
16*251becc8SAndy Fiddaman 
17*251becc8SAndy Fiddaman #include <errno.h>
18*251becc8SAndy Fiddaman #include <fcntl.h>
19*251becc8SAndy Fiddaman #include <libzfs.h>
20*251becc8SAndy Fiddaman #include <pthread.h>
21*251becc8SAndy Fiddaman #include <signal.h>
22*251becc8SAndy Fiddaman #include <stdio.h>
23*251becc8SAndy Fiddaman #include <stdlib.h>
24*251becc8SAndy Fiddaman #include <strings.h>
25*251becc8SAndy Fiddaman #include <unistd.h>
26*251becc8SAndy Fiddaman #include <zone.h>
27*251becc8SAndy Fiddaman 
28*251becc8SAndy Fiddaman #include <sys/types.h>
29*251becc8SAndy Fiddaman #include <sys/stat.h>
30*251becc8SAndy Fiddaman 
31*251becc8SAndy Fiddaman #include "testlib.h"
32*251becc8SAndy Fiddaman #include "mevent.h"
33*251becc8SAndy Fiddaman 
34*251becc8SAndy Fiddaman #define	MB (1024 * 1024)
35*251becc8SAndy Fiddaman 
36*251becc8SAndy Fiddaman static char *cookie = "Chocolate chip with fudge stripes";
37*251becc8SAndy Fiddaman 
38*251becc8SAndy Fiddaman static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
39*251becc8SAndy Fiddaman static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
40*251becc8SAndy Fiddaman 
41*251becc8SAndy Fiddaman static void
callback(int fd,enum ev_type ev,void * arg)42*251becc8SAndy Fiddaman callback(int fd, enum ev_type ev, void *arg)
43*251becc8SAndy Fiddaman {
44*251becc8SAndy Fiddaman 	static off_t size = 0;
45*251becc8SAndy Fiddaman 	struct stat st;
46*251becc8SAndy Fiddaman 
47*251becc8SAndy Fiddaman 	ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE);
48*251becc8SAndy Fiddaman 	ASSERT_PTR_EQ(("bad cookie"), arg, cookie);
49*251becc8SAndy Fiddaman 
50*251becc8SAndy Fiddaman 	if (fstat(fd, &st) != 0)
51*251becc8SAndy Fiddaman 		FAIL_ERRNO("fstat failed");
52*251becc8SAndy Fiddaman 
53*251becc8SAndy Fiddaman 	ASSERT_INT64_NEQ(("Size has not changed"), size, st.st_size);
54*251becc8SAndy Fiddaman 	size = st.st_size;
55*251becc8SAndy Fiddaman 
56*251becc8SAndy Fiddaman 	pthread_mutex_lock(&mtx);
57*251becc8SAndy Fiddaman 	pthread_cond_signal(&cv);
58*251becc8SAndy Fiddaman 	VERBOSE(("wakeup"));
59*251becc8SAndy Fiddaman 	pthread_mutex_unlock(&mtx);
60*251becc8SAndy Fiddaman }
61*251becc8SAndy Fiddaman 
62*251becc8SAndy Fiddaman static void
destroy_zpool(libzfs_handle_t * zfshdl,zpool_handle_t * poolhdl,zfs_handle_t * volhdl)63*251becc8SAndy Fiddaman destroy_zpool(libzfs_handle_t *zfshdl, zpool_handle_t *poolhdl,
64*251becc8SAndy Fiddaman     zfs_handle_t *volhdl)
65*251becc8SAndy Fiddaman {
66*251becc8SAndy Fiddaman 	if (volhdl != NULL) {
67*251becc8SAndy Fiddaman 		if (zfs_destroy(volhdl, B_FALSE) != 0) {
68*251becc8SAndy Fiddaman 			FAIL(("Failed to destroy ZVOL - %s",
69*251becc8SAndy Fiddaman 			    libzfs_error_description(zfshdl)));
70*251becc8SAndy Fiddaman 		}
71*251becc8SAndy Fiddaman 	}
72*251becc8SAndy Fiddaman 
73*251becc8SAndy Fiddaman 	if (poolhdl != NULL) {
74*251becc8SAndy Fiddaman 		if (zpool_destroy(poolhdl, testlib_prog) != 0) {
75*251becc8SAndy Fiddaman 			FAIL(("Failed to destroy ZPOOL - %s",
76*251becc8SAndy Fiddaman 			    libzfs_error_description(zfshdl)));
77*251becc8SAndy Fiddaman 		}
78*251becc8SAndy Fiddaman 	}
79*251becc8SAndy Fiddaman }
80*251becc8SAndy Fiddaman 
81*251becc8SAndy Fiddaman static void
create_zpool(libzfs_handle_t * zfshdl,const char * pool,const char * file)82*251becc8SAndy Fiddaman create_zpool(libzfs_handle_t *zfshdl, const char *pool, const char *file)
83*251becc8SAndy Fiddaman {
84*251becc8SAndy Fiddaman 	nvlist_t *nvroot, *props;
85*251becc8SAndy Fiddaman 	nvlist_t *vdevs[1];
86*251becc8SAndy Fiddaman 
87*251becc8SAndy Fiddaman 	nvroot = fnvlist_alloc();
88*251becc8SAndy Fiddaman 	props = fnvlist_alloc();
89*251becc8SAndy Fiddaman 	vdevs[0] = fnvlist_alloc();
90*251becc8SAndy Fiddaman 
91*251becc8SAndy Fiddaman 	fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_PATH, file);
92*251becc8SAndy Fiddaman 	fnvlist_add_string(vdevs[0], ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE);
93*251becc8SAndy Fiddaman 	fnvlist_add_uint64(vdevs[0], ZPOOL_CONFIG_IS_LOG, 0);
94*251becc8SAndy Fiddaman 
95*251becc8SAndy Fiddaman 	fnvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT);
96*251becc8SAndy Fiddaman 	fnvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, vdevs, 1);
97*251becc8SAndy Fiddaman 
98*251becc8SAndy Fiddaman 	fnvlist_add_string(props,
99*251becc8SAndy Fiddaman 	    zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), ZFS_MOUNTPOINT_NONE);
100*251becc8SAndy Fiddaman 
101*251becc8SAndy Fiddaman 	if (zpool_create(zfshdl, pool, nvroot, NULL, props) != 0) {
102*251becc8SAndy Fiddaman 		FAIL(("Failed to create ZPOOL %s using %s - %s",
103*251becc8SAndy Fiddaman 		    pool, file, libzfs_error_description(zfshdl)));
104*251becc8SAndy Fiddaman 	}
105*251becc8SAndy Fiddaman 
106*251becc8SAndy Fiddaman 	VERBOSE(("Created ZFS pool %s", pool));
107*251becc8SAndy Fiddaman }
108*251becc8SAndy Fiddaman 
109*251becc8SAndy Fiddaman static bool
create_zvol(libzfs_handle_t * zfshdl,const char * vol)110*251becc8SAndy Fiddaman create_zvol(libzfs_handle_t *zfshdl, const char *vol)
111*251becc8SAndy Fiddaman {
112*251becc8SAndy Fiddaman 	nvlist_t *volprops;
113*251becc8SAndy Fiddaman 	int err;
114*251becc8SAndy Fiddaman 
115*251becc8SAndy Fiddaman 	volprops = fnvlist_alloc();
116*251becc8SAndy Fiddaman 	fnvlist_add_uint64(volprops,
117*251becc8SAndy Fiddaman 	    zfs_prop_to_name(ZFS_PROP_VOLSIZE), 1 * MB);
118*251becc8SAndy Fiddaman 
119*251becc8SAndy Fiddaman 	err = zfs_create(zfshdl, vol, ZFS_TYPE_VOLUME, volprops);
120*251becc8SAndy Fiddaman 	if (err != 0) {
121*251becc8SAndy Fiddaman 		(void) printf("Failed to create ZVOL %s - %s",
122*251becc8SAndy Fiddaman 		    vol, libzfs_error_description(zfshdl));
123*251becc8SAndy Fiddaman 		return (false);
124*251becc8SAndy Fiddaman 	}
125*251becc8SAndy Fiddaman 
126*251becc8SAndy Fiddaman 	VERBOSE(("Created ZVOL %s", vol));
127*251becc8SAndy Fiddaman 	return (true);
128*251becc8SAndy Fiddaman }
129*251becc8SAndy Fiddaman 
130*251becc8SAndy Fiddaman int
main(int argc,const char ** argv)131*251becc8SAndy Fiddaman main(int argc, const char **argv)
132*251becc8SAndy Fiddaman {
133*251becc8SAndy Fiddaman 	libzfs_handle_t *zfshdl;
134*251becc8SAndy Fiddaman 	char *template, *pool, *vol, *backend;
135*251becc8SAndy Fiddaman 	struct mevent *evp;
136*251becc8SAndy Fiddaman 	zpool_handle_t *poolhdl = NULL;
137*251becc8SAndy Fiddaman 	zfs_handle_t *volhdl = NULL;
138*251becc8SAndy Fiddaman 	int err, fd;
139*251becc8SAndy Fiddaman 
140*251becc8SAndy Fiddaman 	start_test(argv[0], 10);
141*251becc8SAndy Fiddaman 	set_mevent_file_poll_interval_ms(1000);
142*251becc8SAndy Fiddaman 
143*251becc8SAndy Fiddaman 	if (getzoneid() != GLOBAL_ZONEID)
144*251becc8SAndy Fiddaman 		FAIL(("Can only be run in the global zone"));
145*251becc8SAndy Fiddaman 
146*251becc8SAndy Fiddaman 	if ((zfshdl = libzfs_init()) == NULL)
147*251becc8SAndy Fiddaman 		FAIL_ERRNO("Could not open ZFS library");
148*251becc8SAndy Fiddaman 
149*251becc8SAndy Fiddaman 	template = strdup("/tmp/mevent.vnode.zvol.XXXXXX");
150*251becc8SAndy Fiddaman 	ASSERT_PTR_NEQ(("strdup"), template, NULL);
151*251becc8SAndy Fiddaman 	fd = mkstemp(template);
152*251becc8SAndy Fiddaman 	if (fd == -1)
153*251becc8SAndy Fiddaman 		FAIL_ERRNO("Couldn't create temporary file with mkstemp");
154*251becc8SAndy Fiddaman 	VERBOSE(("Opened temporary file at '%s'", template));
155*251becc8SAndy Fiddaman 
156*251becc8SAndy Fiddaman 	err = asprintf(&pool, "mevent_test_%d", getpid());
157*251becc8SAndy Fiddaman 	ASSERT_INT_NEQ(("asprintf pool"), err, -1);
158*251becc8SAndy Fiddaman 
159*251becc8SAndy Fiddaman 	err = asprintf(&vol, "%s/test_zvol_%d", pool, getpid());
160*251becc8SAndy Fiddaman 	ASSERT_INT_NEQ(("asprintf vol"), err, -1);
161*251becc8SAndy Fiddaman 
162*251becc8SAndy Fiddaman 	err = asprintf(&backend, "/dev/zvol/rdsk/%s", vol);
163*251becc8SAndy Fiddaman 	ASSERT_INT_NEQ(("asprintf backend"), err, -1);
164*251becc8SAndy Fiddaman 
165*251becc8SAndy Fiddaman 	err = ftruncate(fd, 64 * MB);
166*251becc8SAndy Fiddaman 	if (err != 0)
167*251becc8SAndy Fiddaman 		FAIL_ERRNO("ftruncate");
168*251becc8SAndy Fiddaman 	(void) close(fd);
169*251becc8SAndy Fiddaman 	fd = -1;
170*251becc8SAndy Fiddaman 
171*251becc8SAndy Fiddaman 	/*
172*251becc8SAndy Fiddaman 	 * Create the pool as late as possible to reduce the risk of leaving
173*251becc8SAndy Fiddaman 	 * a test pool hanging around.
174*251becc8SAndy Fiddaman 	 */
175*251becc8SAndy Fiddaman 	create_zpool(zfshdl, pool, template);
176*251becc8SAndy Fiddaman 
177*251becc8SAndy Fiddaman 	if ((poolhdl = zpool_open(zfshdl, pool)) == NULL) {
178*251becc8SAndy Fiddaman 		(void) printf("Could not open ZPOOL - %s\n",
179*251becc8SAndy Fiddaman 		    libzfs_error_description(zfshdl));
180*251becc8SAndy Fiddaman 		err = EXIT_FAIL;
181*251becc8SAndy Fiddaman 		goto out;
182*251becc8SAndy Fiddaman 	}
183*251becc8SAndy Fiddaman 
184*251becc8SAndy Fiddaman 	if (!create_zvol(zfshdl, vol)) {
185*251becc8SAndy Fiddaman 		err = EXIT_FAIL;
186*251becc8SAndy Fiddaman 		goto out;
187*251becc8SAndy Fiddaman 	}
188*251becc8SAndy Fiddaman 
189*251becc8SAndy Fiddaman 	if ((volhdl = zfs_open(zfshdl, vol, ZFS_TYPE_VOLUME)) == NULL) {
190*251becc8SAndy Fiddaman 		(void) printf("Could not open ZFS volume - %s\n",
191*251becc8SAndy Fiddaman 		    libzfs_error_description(zfshdl));
192*251becc8SAndy Fiddaman 		err = EXIT_FAIL;
193*251becc8SAndy Fiddaman 		goto out;
194*251becc8SAndy Fiddaman 	}
195*251becc8SAndy Fiddaman 
196*251becc8SAndy Fiddaman 	if ((fd = open(backend, O_RDWR)) == -1) {
197*251becc8SAndy Fiddaman 		(void) printf("Failed to open '%s': %s\n",
198*251becc8SAndy Fiddaman 		    backend, strerror(errno));
199*251becc8SAndy Fiddaman 		err = EXIT_FAIL;
200*251becc8SAndy Fiddaman 		goto out;
201*251becc8SAndy Fiddaman 	}
202*251becc8SAndy Fiddaman 	VERBOSE(("Opened backend %s", backend));
203*251becc8SAndy Fiddaman 
204*251becc8SAndy Fiddaman 	start_event_thread();
205*251becc8SAndy Fiddaman 
206*251becc8SAndy Fiddaman 	evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie);
207*251becc8SAndy Fiddaman 	if (evp == NULL) {
208*251becc8SAndy Fiddaman 		(void) printf("mevent_add returned NULL\n");
209*251becc8SAndy Fiddaman 		err = EXIT_FAIL;
210*251becc8SAndy Fiddaman 		goto out;
211*251becc8SAndy Fiddaman 	}
212*251becc8SAndy Fiddaman 
213*251becc8SAndy Fiddaman 	for (uint_t i = 2; i < 4; i++) {
214*251becc8SAndy Fiddaman 		ssize_t written;
215*251becc8SAndy Fiddaman 		char buf[64];
216*251becc8SAndy Fiddaman 
217*251becc8SAndy Fiddaman 		/*
218*251becc8SAndy Fiddaman 		 * Check that a write to the volume does not trigger an event.
219*251becc8SAndy Fiddaman 		 */
220*251becc8SAndy Fiddaman 		if (lseek(fd, 0, SEEK_SET) == -1)
221*251becc8SAndy Fiddaman 			FAIL_ERRNO("lseek");
222*251becc8SAndy Fiddaman 		written = write(fd, cookie, strlen(cookie));
223*251becc8SAndy Fiddaman 		if (written < 0)
224*251becc8SAndy Fiddaman 			FAIL_ERRNO("bad write");
225*251becc8SAndy Fiddaman 		ASSERT_INT64_EQ(("write cookie", i), written, strlen(cookie));
226*251becc8SAndy Fiddaman 
227*251becc8SAndy Fiddaman 		(void) snprintf(buf, sizeof (buf), "%llu", i * MB);
228*251becc8SAndy Fiddaman 		VERBOSE(("Setting volsize to %s", buf));
229*251becc8SAndy Fiddaman 
230*251becc8SAndy Fiddaman 		if (zfs_prop_set(volhdl,
231*251becc8SAndy Fiddaman 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), buf) != 0) {
232*251becc8SAndy Fiddaman 			(void) printf("Failed to increase ZFS volume size\n");
233*251becc8SAndy Fiddaman 			pthread_mutex_unlock(&mtx);
234*251becc8SAndy Fiddaman 			err = EXIT_FAIL;
235*251becc8SAndy Fiddaman 			goto out;
236*251becc8SAndy Fiddaman 		}
237*251becc8SAndy Fiddaman 
238*251becc8SAndy Fiddaman 		/* Wait for the size change to be processed */
239*251becc8SAndy Fiddaman 		pthread_mutex_lock(&mtx);
240*251becc8SAndy Fiddaman 		pthread_cond_wait(&cv, &mtx);
241*251becc8SAndy Fiddaman 		pthread_mutex_unlock(&mtx);
242*251becc8SAndy Fiddaman 	}
243*251becc8SAndy Fiddaman 
244*251becc8SAndy Fiddaman 	(void) mevent_disable(evp);
245*251becc8SAndy Fiddaman 
246*251becc8SAndy Fiddaman 	err = EXIT_PASS;
247*251becc8SAndy Fiddaman 
248*251becc8SAndy Fiddaman out:
249*251becc8SAndy Fiddaman 
250*251becc8SAndy Fiddaman 	(void) close(fd);
251*251becc8SAndy Fiddaman 	destroy_zpool(zfshdl, poolhdl, volhdl);
252*251becc8SAndy Fiddaman 	(void) libzfs_fini(zfshdl);
253*251becc8SAndy Fiddaman 	(void) unlink(template);
254*251becc8SAndy Fiddaman 
255*251becc8SAndy Fiddaman 	if (err == EXIT_PASS)
256*251becc8SAndy Fiddaman 		PASS();
257*251becc8SAndy Fiddaman 
258*251becc8SAndy Fiddaman 	exit(err);
259*251becc8SAndy Fiddaman }