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