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