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 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 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 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 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 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