xref: /illumos-gate/usr/src/test/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c (revision 9f76c6ed5b6ee0cc0bf631daca15ac3dc5fc70c4)
1 /*
2  * CDDL HEADER START
3  *
4  * This file and its contents are supplied under the terms of the
5  * Common Development and Distribution License ("CDDL"), version 1.0.
6  * You may only use this file in accordance with the terms of version
7  * 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this
10  * source.  A copy of the CDDL is also available via the Internet at
11  * http://www.illumos.org/license/CDDL.
12  *
13  * CDDL HEADER END
14  */
15 
16 /*
17  * Copyright (c) 2018 by Delphix. All rights reserved.
18  * Copyright 2020 Joyent, Inc.
19  */
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <libzfs_core.h>
28 #include <unistd.h>
29 
30 #include <sys/nvpair.h>
31 #include <sys/vdev_impl.h>
32 #include <sys/zfs_ioctl.h>
33 #include <sys/zfs_bootenv.h>
34 
35 /*
36  * Test the nvpair inputs for the non-legacy zfs ioctl commands.
37  */
38 
39 boolean_t unexpected_failures;
40 int zfs_fd;
41 const char *active_test;
42 
43 /*
44  * Tracks which zfs_ioc_t commands were tested
45  */
46 boolean_t ioc_tested[256];
47 
48 /*
49  * Legacy ioctls that are skipped (for now)
50  */
51 static unsigned ioc_skip[] = {
52 	ZFS_IOC_POOL_CREATE,
53 	ZFS_IOC_POOL_DESTROY,
54 	ZFS_IOC_POOL_IMPORT,
55 	ZFS_IOC_POOL_EXPORT,
56 	ZFS_IOC_POOL_CONFIGS,
57 	ZFS_IOC_POOL_STATS,
58 	ZFS_IOC_POOL_TRYIMPORT,
59 	ZFS_IOC_POOL_SCAN,
60 	ZFS_IOC_POOL_FREEZE,
61 	ZFS_IOC_POOL_UPGRADE,
62 	ZFS_IOC_POOL_GET_HISTORY,
63 
64 	ZFS_IOC_VDEV_ADD,
65 	ZFS_IOC_VDEV_REMOVE,
66 	ZFS_IOC_VDEV_SET_STATE,
67 	ZFS_IOC_VDEV_ATTACH,
68 	ZFS_IOC_VDEV_DETACH,
69 	ZFS_IOC_VDEV_SETPATH,
70 	ZFS_IOC_VDEV_SETFRU,
71 
72 	ZFS_IOC_OBJSET_STATS,
73 	ZFS_IOC_OBJSET_ZPLPROPS,
74 	ZFS_IOC_DATASET_LIST_NEXT,
75 	ZFS_IOC_SNAPSHOT_LIST_NEXT,
76 	ZFS_IOC_SET_PROP,
77 	ZFS_IOC_DESTROY,
78 	ZFS_IOC_RENAME,
79 	ZFS_IOC_RECV,
80 	ZFS_IOC_SEND,
81 	ZFS_IOC_INJECT_FAULT,
82 	ZFS_IOC_CLEAR_FAULT,
83 	ZFS_IOC_INJECT_LIST_NEXT,
84 	ZFS_IOC_ERROR_LOG,
85 	ZFS_IOC_CLEAR,
86 	ZFS_IOC_PROMOTE,
87 	ZFS_IOC_DSOBJ_TO_DSNAME,
88 	ZFS_IOC_OBJ_TO_PATH,
89 	ZFS_IOC_POOL_SET_PROPS,
90 	ZFS_IOC_POOL_GET_PROPS,
91 	ZFS_IOC_SET_FSACL,
92 	ZFS_IOC_GET_FSACL,
93 	ZFS_IOC_SHARE,
94 	ZFS_IOC_INHERIT_PROP,
95 	ZFS_IOC_SMB_ACL,
96 	ZFS_IOC_USERSPACE_ONE,
97 	ZFS_IOC_USERSPACE_MANY,
98 	ZFS_IOC_USERSPACE_UPGRADE,
99 	ZFS_IOC_OBJSET_RECVD_PROPS,
100 	ZFS_IOC_VDEV_SPLIT,
101 	ZFS_IOC_NEXT_OBJ,
102 	ZFS_IOC_DIFF,
103 	ZFS_IOC_TMP_SNAPSHOT,
104 	ZFS_IOC_OBJ_TO_STATS,
105 	ZFS_IOC_SPACE_WRITTEN,
106 	ZFS_IOC_POOL_REGUID,
107 	ZFS_IOC_SEND_PROGRESS,
108 
109 #ifndef __sun
110 	ZFS_IOC_EVENTS_NEXT,
111 	ZFS_IOC_EVENTS_CLEAR,
112 	ZFS_IOC_EVENTS_SEEK,
113 	ZFS_IOC_NEXTBOOT,
114 	ZFS_IOC_JAIL,
115 	ZFS_IOC_UNJAIL,
116 #else
117 	/* This is still a legacy ioctl in illumos */
118 	ZFS_IOC_POOL_REOPEN,
119 #endif
120 };
121 
122 
123 #define	IOC_INPUT_TEST(ioc, name, req, opt, err)		\
124 	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_FALSE)
125 
126 #define	IOC_INPUT_TEST_WILD(ioc, name, req, opt, err)		\
127 	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_TRUE)
128 
129 #define	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, wild)	\
130 	do {							\
131 		active_test = __func__ + 5;			\
132 		ioc_tested[ioc - ZFS_IOC_FIRST] = B_TRUE;	\
133 		(void) lzc_ioctl_test(ioc, name, req, opt, err, wild);	\
134 	} while (0)
135 
136 /*
137  * run a zfs ioctl command, verify expected results and log failures
138  */
139 static void
lzc_ioctl_run(zfs_ioc_t ioc,const char * name,nvlist_t * innvl,int expected)140 lzc_ioctl_run(zfs_ioc_t ioc, const char *name, nvlist_t *innvl, int expected)
141 {
142 	zfs_cmd_t zc = {"\0"};
143 	char *packed = NULL;
144 	const char *variant;
145 	size_t size = 0;
146 	int error = 0;
147 
148 	switch (expected) {
149 	case ZFS_ERR_IOC_ARG_UNAVAIL:
150 		variant = "unsupported input";
151 		break;
152 	case ZFS_ERR_IOC_ARG_REQUIRED:
153 		variant = "missing input";
154 		break;
155 	case ZFS_ERR_IOC_ARG_BADTYPE:
156 		variant = "invalid input type";
157 		break;
158 	default:
159 		variant = "valid input";
160 		break;
161 	}
162 
163 	packed = fnvlist_pack(innvl, &size);
164 	(void) strncpy(zc.zc_name, name, sizeof (zc.zc_name));
165 	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
166 	zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
167 	zc.zc_nvlist_src_size = size;
168 	zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
169 	zc.zc_nvlist_dst = (uint64_t)(uintptr_t)malloc(zc.zc_nvlist_dst_size);
170 
171 	if (ioctl(zfs_fd, ioc, &zc) != 0)
172 		error = errno;
173 
174 	if (error != expected) {
175 		unexpected_failures = B_TRUE;
176 		(void) fprintf(stderr, "%s: Unexpected result with %s, "
177 		    "error %d (expecting %d)\n",
178 		    active_test, variant, error, expected);
179 	}
180 
181 	fnvlist_pack_free(packed, size);
182 	free((void *)(uintptr_t)zc.zc_nvlist_dst);
183 }
184 
185 /*
186  * Test each ioc for the following ioctl input errors:
187  *   ZFS_ERR_IOC_ARG_UNAVAIL	an input argument is not supported by kernel
188  *   ZFS_ERR_IOC_ARG_REQUIRED	a required input argument is missing
189  *   ZFS_ERR_IOC_ARG_BADTYPE	an input argument has an invalid type
190  */
191 static int
lzc_ioctl_test(zfs_ioc_t ioc,const char * name,nvlist_t * required,nvlist_t * optional,int expected_error,boolean_t wildcard)192 lzc_ioctl_test(zfs_ioc_t ioc, const char *name, nvlist_t *required,
193     nvlist_t *optional, int expected_error, boolean_t wildcard)
194 {
195 	nvlist_t *input = fnvlist_alloc();
196 	nvlist_t *future = fnvlist_alloc();
197 	int error = 0;
198 
199 	if (required != NULL) {
200 		for (nvpair_t *pair = nvlist_next_nvpair(required, NULL);
201 		    pair != NULL; pair = nvlist_next_nvpair(required, pair)) {
202 			fnvlist_add_nvpair(input, pair);
203 		}
204 	}
205 	if (optional != NULL) {
206 		for (nvpair_t *pair = nvlist_next_nvpair(optional, NULL);
207 		    pair != NULL; pair = nvlist_next_nvpair(optional, pair)) {
208 			fnvlist_add_nvpair(input, pair);
209 		}
210 	}
211 
212 	/*
213 	 * Generic input run with 'optional' nvlist pair
214 	 */
215 	if (!wildcard)
216 		fnvlist_add_nvlist(input, "optional", future);
217 	lzc_ioctl_run(ioc, name, input, expected_error);
218 	if (!wildcard)
219 		fnvlist_remove(input, "optional");
220 
221 	/*
222 	 * Bogus input value
223 	 */
224 	if (!wildcard) {
225 		fnvlist_add_string(input, "bogus_input", "bogus");
226 		lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_UNAVAIL);
227 		fnvlist_remove(input, "bogus_input");
228 	}
229 
230 	/*
231 	 * Missing required inputs
232 	 */
233 	if (required != NULL) {
234 		nvlist_t *empty = fnvlist_alloc();
235 		lzc_ioctl_run(ioc, name, empty, ZFS_ERR_IOC_ARG_REQUIRED);
236 		nvlist_free(empty);
237 	}
238 
239 	/*
240 	 * Wrong nvpair type
241 	 */
242 	if (required != NULL || optional != NULL) {
243 		/*
244 		 * switch the type of one of the input pairs
245 		 */
246 		for (nvpair_t *pair = nvlist_next_nvpair(input, NULL);
247 		    pair != NULL; pair = nvlist_next_nvpair(input, pair)) {
248 			char pname[MAXNAMELEN];
249 			data_type_t ptype;
250 
251 			(void) strncpy(pname, nvpair_name(pair),
252 			    sizeof (pname));
253 			pname[sizeof (pname) - 1] = '\0';
254 			ptype = nvpair_type(pair);
255 			fnvlist_remove_nvpair(input, pair);
256 
257 			switch (ptype) {
258 			case DATA_TYPE_STRING:
259 				fnvlist_add_uint64(input, pname, 42);
260 				break;
261 			default:
262 				fnvlist_add_string(input, pname, "bogus");
263 				break;
264 			}
265 		}
266 		lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_BADTYPE);
267 	}
268 
269 	nvlist_free(future);
270 	nvlist_free(input);
271 
272 	return (error);
273 }
274 
275 static void
test_pool_sync(const char * pool)276 test_pool_sync(const char *pool)
277 {
278 	nvlist_t *required = fnvlist_alloc();
279 
280 	fnvlist_add_boolean_value(required, "force", B_TRUE);
281 
282 	IOC_INPUT_TEST(ZFS_IOC_POOL_SYNC, pool, required, NULL, 0);
283 
284 	nvlist_free(required);
285 }
286 
287 #ifndef sun
288 static void
test_pool_reopen(const char * pool)289 test_pool_reopen(const char *pool)
290 {
291 	nvlist_t *required = fnvlist_alloc();
292 
293 	fnvlist_add_boolean_value(required, "scrub_restart", B_FALSE);
294 
295 	IOC_INPUT_TEST(ZFS_IOC_POOL_REOPEN, pool, required, NULL, 0);
296 
297 	nvlist_free(required);
298 }
299 #endif
300 
301 static void
test_pool_checkpoint(const char * pool)302 test_pool_checkpoint(const char *pool)
303 {
304 	IOC_INPUT_TEST(ZFS_IOC_POOL_CHECKPOINT, pool, NULL, NULL, 0);
305 }
306 
307 static void
test_pool_discard_checkpoint(const char * pool)308 test_pool_discard_checkpoint(const char *pool)
309 {
310 	int err = lzc_pool_checkpoint(pool);
311 	if (err == 0 || err == ZFS_ERR_CHECKPOINT_EXISTS)
312 		IOC_INPUT_TEST(ZFS_IOC_POOL_DISCARD_CHECKPOINT, pool, NULL,
313 		    NULL, 0);
314 }
315 
316 static void
test_log_history(const char * pool)317 test_log_history(const char *pool)
318 {
319 	nvlist_t *required = fnvlist_alloc();
320 
321 	fnvlist_add_string(required, "message", "input check");
322 
323 	IOC_INPUT_TEST(ZFS_IOC_LOG_HISTORY, pool, required, NULL, 0);
324 
325 	nvlist_free(required);
326 }
327 
328 static void
test_create(const char * pool)329 test_create(const char *pool)
330 {
331 	char dataset[MAXNAMELEN + 32];
332 
333 	(void) snprintf(dataset, sizeof (dataset), "%s/create-fs", pool);
334 
335 	nvlist_t *required = fnvlist_alloc();
336 	nvlist_t *optional = fnvlist_alloc();
337 	nvlist_t *props = fnvlist_alloc();
338 
339 	fnvlist_add_int32(required, "type", DMU_OST_ZFS);
340 	fnvlist_add_uint64(props, "recordsize", 8192);
341 	fnvlist_add_nvlist(optional, "props", props);
342 
343 	IOC_INPUT_TEST(ZFS_IOC_CREATE, dataset, required, optional, 0);
344 
345 	nvlist_free(required);
346 	nvlist_free(optional);
347 }
348 
349 static void
test_snapshot(const char * pool,const char * snapshot)350 test_snapshot(const char *pool, const char *snapshot)
351 {
352 	nvlist_t *required = fnvlist_alloc();
353 	nvlist_t *optional = fnvlist_alloc();
354 	nvlist_t *snaps = fnvlist_alloc();
355 	nvlist_t *props = fnvlist_alloc();
356 
357 	fnvlist_add_boolean(snaps, snapshot);
358 	fnvlist_add_nvlist(required, "snaps", snaps);
359 
360 	fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
361 	fnvlist_add_nvlist(optional, "props", props);
362 
363 	IOC_INPUT_TEST(ZFS_IOC_SNAPSHOT, pool, required, optional, 0);
364 
365 	nvlist_free(props);
366 	nvlist_free(snaps);
367 	nvlist_free(optional);
368 	nvlist_free(required);
369 }
370 
371 static void
test_space_snaps(const char * snapshot)372 test_space_snaps(const char *snapshot)
373 {
374 	nvlist_t *required = fnvlist_alloc();
375 	fnvlist_add_string(required, "firstsnap", snapshot);
376 
377 	IOC_INPUT_TEST(ZFS_IOC_SPACE_SNAPS, snapshot, required, NULL, 0);
378 
379 	nvlist_free(required);
380 }
381 
382 static void
test_destroy_snaps(const char * pool,const char * snapshot)383 test_destroy_snaps(const char *pool, const char *snapshot)
384 {
385 	nvlist_t *required = fnvlist_alloc();
386 	nvlist_t *snaps = fnvlist_alloc();
387 
388 	fnvlist_add_boolean(snaps, snapshot);
389 	fnvlist_add_nvlist(required, "snaps", snaps);
390 
391 	IOC_INPUT_TEST(ZFS_IOC_DESTROY_SNAPS, pool, required, NULL, 0);
392 
393 	nvlist_free(snaps);
394 	nvlist_free(required);
395 }
396 
397 
398 static void
test_bookmark(const char * pool,const char * snapshot,const char * bookmark)399 test_bookmark(const char *pool, const char *snapshot, const char *bookmark)
400 {
401 	nvlist_t *required = fnvlist_alloc();
402 
403 	fnvlist_add_string(required, bookmark, snapshot);
404 
405 	IOC_INPUT_TEST_WILD(ZFS_IOC_BOOKMARK, pool, required, NULL, 0);
406 
407 	nvlist_free(required);
408 }
409 
410 static void
test_get_bookmarks(const char * dataset)411 test_get_bookmarks(const char *dataset)
412 {
413 	nvlist_t *optional = fnvlist_alloc();
414 
415 	fnvlist_add_boolean(optional, "guid");
416 	fnvlist_add_boolean(optional, "createtxg");
417 	fnvlist_add_boolean(optional, "creation");
418 
419 	IOC_INPUT_TEST_WILD(ZFS_IOC_GET_BOOKMARKS, dataset, NULL, optional, 0);
420 
421 	nvlist_free(optional);
422 }
423 
424 static void
test_destroy_bookmarks(const char * pool,const char * bookmark)425 test_destroy_bookmarks(const char *pool, const char *bookmark)
426 {
427 	nvlist_t *required = fnvlist_alloc();
428 
429 	fnvlist_add_boolean(required, bookmark);
430 
431 	IOC_INPUT_TEST_WILD(ZFS_IOC_DESTROY_BOOKMARKS, pool, required, NULL, 0);
432 
433 	nvlist_free(required);
434 }
435 
436 static void
test_clone(const char * snapshot,const char * clone)437 test_clone(const char *snapshot, const char *clone)
438 {
439 	nvlist_t *required = fnvlist_alloc();
440 	nvlist_t *optional = fnvlist_alloc();
441 	nvlist_t *props = fnvlist_alloc();
442 
443 	fnvlist_add_string(required, "origin", snapshot);
444 
445 	IOC_INPUT_TEST(ZFS_IOC_CLONE, clone, required, NULL, 0);
446 
447 	nvlist_free(props);
448 	nvlist_free(optional);
449 	nvlist_free(required);
450 }
451 
452 static void
test_rollback(const char * dataset,const char * snapshot)453 test_rollback(const char *dataset, const char *snapshot)
454 {
455 	nvlist_t *optional = fnvlist_alloc();
456 
457 	fnvlist_add_string(optional, "target", snapshot);
458 
459 	IOC_INPUT_TEST(ZFS_IOC_ROLLBACK, dataset, NULL, optional, B_FALSE);
460 
461 	nvlist_free(optional);
462 }
463 
464 static void
test_hold(const char * pool,const char * snapshot)465 test_hold(const char *pool, const char *snapshot)
466 {
467 	nvlist_t *required = fnvlist_alloc();
468 	nvlist_t *optional = fnvlist_alloc();
469 	nvlist_t *holds = fnvlist_alloc();
470 
471 	fnvlist_add_string(holds, snapshot, "libzfs_check_hold");
472 	fnvlist_add_nvlist(required, "holds", holds);
473 	fnvlist_add_int32(optional, "cleanup_fd", zfs_fd);
474 
475 	IOC_INPUT_TEST(ZFS_IOC_HOLD, pool, required, optional, 0);
476 
477 	nvlist_free(holds);
478 	nvlist_free(optional);
479 	nvlist_free(required);
480 }
481 
482 static void
test_get_holds(const char * snapshot)483 test_get_holds(const char *snapshot)
484 {
485 	IOC_INPUT_TEST(ZFS_IOC_GET_HOLDS, snapshot, NULL, NULL, 0);
486 }
487 
488 static void
test_release(const char * pool,const char * snapshot)489 test_release(const char *pool, const char *snapshot)
490 {
491 	nvlist_t *required = fnvlist_alloc();
492 	nvlist_t *release = fnvlist_alloc();
493 
494 	fnvlist_add_boolean(release, "libzfs_check_hold");
495 	fnvlist_add_nvlist(required, snapshot, release);
496 
497 	IOC_INPUT_TEST_WILD(ZFS_IOC_RELEASE, pool, required, NULL, 0);
498 
499 	nvlist_free(release);
500 	nvlist_free(required);
501 }
502 
503 
504 static void
test_send_new(const char * snapshot,int fd)505 test_send_new(const char *snapshot, int fd)
506 {
507 	nvlist_t *required = fnvlist_alloc();
508 	nvlist_t *optional = fnvlist_alloc();
509 
510 	fnvlist_add_int32(required, "fd", fd);
511 
512 	fnvlist_add_boolean(optional, "largeblockok");
513 	fnvlist_add_boolean(optional, "embedok");
514 	fnvlist_add_boolean(optional, "compressok");
515 	fnvlist_add_boolean(optional, "rawok");
516 
517 	/*
518 	 * TODO - Resumable send is harder to set up. So we currently
519 	 * ignore testing for that variant.
520 	 */
521 #if 0
522 	fnvlist_add_string(optional, "fromsnap", from);
523 	fnvlist_add_uint64(optional, "resume_object", resumeobj);
524 	fnvlist_add_uint64(optional, "resume_offset", offset);
525 #endif
526 	IOC_INPUT_TEST(ZFS_IOC_SEND_NEW, snapshot, required, optional, 0);
527 
528 	nvlist_free(optional);
529 	nvlist_free(required);
530 }
531 
532 #ifndef __sun
533 static void
test_recv_new(const char * dataset,int fd)534 test_recv_new(const char *dataset, int fd)
535 {
536 	dmu_replay_record_t drr = { 0 };
537 	nvlist_t *required = fnvlist_alloc();
538 	nvlist_t *optional = fnvlist_alloc();
539 	nvlist_t *props = fnvlist_alloc();
540 	char snapshot[MAXNAMELEN + 32];
541 	ssize_t count;
542 
543 	int cleanup_fd = open(ZFS_DEV, O_RDWR);
544 
545 	(void) snprintf(snapshot, sizeof (snapshot), "%s@replicant", dataset);
546 
547 	count = pread(fd, &drr, sizeof (drr), 0);
548 	if (count != sizeof (drr)) {
549 		(void) fprintf(stderr, "could not read stream: %s\n",
550 		    strerror(errno));
551 	}
552 
553 	fnvlist_add_string(required, "snapname", snapshot);
554 	fnvlist_add_byte_array(required, "begin_record", (uchar_t *)&drr,
555 	    sizeof (drr));
556 	fnvlist_add_int32(required, "input_fd", fd);
557 
558 	fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
559 	fnvlist_add_nvlist(optional, "localprops", props);
560 	fnvlist_add_boolean(optional, "force");
561 	fnvlist_add_int32(optional, "cleanup_fd", cleanup_fd);
562 
563 	/*
564 	 * TODO - Resumable receive is harder to set up. So we currently
565 	 * ignore testing for one.
566 	 */
567 #if 0
568 	fnvlist_add_nvlist(optional, "props", recvdprops);
569 	fnvlist_add_string(optional, "origin", origin);
570 	fnvlist_add_boolean(optional, "resumable");
571 	fnvlist_add_uint64(optional, "action_handle", *action_handle);
572 #endif
573 	IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional, EBADE);
574 
575 	nvlist_free(props);
576 	nvlist_free(optional);
577 	nvlist_free(required);
578 
579 	(void) close(cleanup_fd);
580 }
581 #endif
582 
583 static void
test_send_space(const char * snapshot1,const char * snapshot2)584 test_send_space(const char *snapshot1, const char *snapshot2)
585 {
586 	nvlist_t *optional = fnvlist_alloc();
587 
588 	fnvlist_add_string(optional, "from", snapshot1);
589 	fnvlist_add_boolean(optional, "largeblockok");
590 	fnvlist_add_boolean(optional, "embedok");
591 	fnvlist_add_boolean(optional, "compressok");
592 	fnvlist_add_boolean(optional, "rawok");
593 
594 	IOC_INPUT_TEST(ZFS_IOC_SEND_SPACE, snapshot2, NULL, optional, 0);
595 
596 	nvlist_free(optional);
597 }
598 
599 static void
test_remap(const char * dataset)600 test_remap(const char *dataset)
601 {
602 	IOC_INPUT_TEST(ZFS_IOC_REMAP, dataset, NULL, NULL, 0);
603 }
604 
605 static void
test_channel_program(const char * pool)606 test_channel_program(const char *pool)
607 {
608 	const char *program =
609 	    "arg = ...\n"
610 	    "argv = arg[\"argv\"]\n"
611 	    "return argv[1]";
612 	char *const argv[1] = { "Hello World!" };
613 	nvlist_t *required = fnvlist_alloc();
614 	nvlist_t *optional = fnvlist_alloc();
615 	nvlist_t *args = fnvlist_alloc();
616 
617 	fnvlist_add_string(required, "program", program);
618 	fnvlist_add_string_array(args, "argv", argv, 1);
619 	fnvlist_add_nvlist(required, "arg", args);
620 
621 	fnvlist_add_boolean_value(optional, "sync", B_TRUE);
622 	fnvlist_add_uint64(optional, "instrlimit", 1000 * 1000);
623 	fnvlist_add_uint64(optional, "memlimit", 8192 * 1024);
624 
625 	IOC_INPUT_TEST(ZFS_IOC_CHANNEL_PROGRAM, pool, required, optional, 0);
626 
627 	nvlist_free(args);
628 	nvlist_free(optional);
629 	nvlist_free(required);
630 }
631 
632 #define	WRAPPING_KEY_LEN	32
633 
634 static void
test_load_key(const char * dataset)635 test_load_key(const char *dataset)
636 {
637 	nvlist_t *required = fnvlist_alloc();
638 	nvlist_t *optional = fnvlist_alloc();
639 	nvlist_t *hidden = fnvlist_alloc();
640 	uint8_t keydata[WRAPPING_KEY_LEN] = {0};
641 
642 	fnvlist_add_uint8_array(hidden, "wkeydata", keydata, sizeof (keydata));
643 	fnvlist_add_nvlist(required, "hidden_args", hidden);
644 	fnvlist_add_boolean(optional, "noop");
645 
646 	IOC_INPUT_TEST(ZFS_IOC_LOAD_KEY, dataset, required, optional, EINVAL);
647 	nvlist_free(hidden);
648 	nvlist_free(optional);
649 	nvlist_free(required);
650 }
651 
652 static void
test_change_key(const char * dataset)653 test_change_key(const char *dataset)
654 {
655 	IOC_INPUT_TEST(ZFS_IOC_CHANGE_KEY, dataset, NULL, NULL, EINVAL);
656 }
657 
658 static void
test_unload_key(const char * dataset)659 test_unload_key(const char *dataset)
660 {
661 	IOC_INPUT_TEST(ZFS_IOC_UNLOAD_KEY, dataset, NULL, NULL, EACCES);
662 }
663 
664 static void
test_vdev_initialize(const char * pool)665 test_vdev_initialize(const char *pool)
666 {
667 	nvlist_t *required = fnvlist_alloc();
668 	nvlist_t *vdev_guids = fnvlist_alloc();
669 
670 	fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
671 	fnvlist_add_uint64(required, ZPOOL_INITIALIZE_COMMAND,
672 	    POOL_INITIALIZE_START);
673 	fnvlist_add_nvlist(required, ZPOOL_INITIALIZE_VDEVS, vdev_guids);
674 
675 	IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, NULL, EINVAL);
676 	nvlist_free(vdev_guids);
677 	nvlist_free(required);
678 }
679 
680 static void
test_vdev_trim(const char * pool)681 test_vdev_trim(const char *pool)
682 {
683 	nvlist_t *required = fnvlist_alloc();
684 	nvlist_t *optional = fnvlist_alloc();
685 	nvlist_t *vdev_guids = fnvlist_alloc();
686 
687 	fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
688 	fnvlist_add_uint64(required, ZPOOL_TRIM_COMMAND, POOL_TRIM_START);
689 	fnvlist_add_nvlist(required, ZPOOL_TRIM_VDEVS, vdev_guids);
690 	fnvlist_add_uint64(optional, ZPOOL_TRIM_RATE, 1ULL << 30);
691 	fnvlist_add_boolean_value(optional, ZPOOL_TRIM_SECURE, B_TRUE);
692 
693 	IOC_INPUT_TEST(ZFS_IOC_POOL_TRIM, pool, required, optional, EINVAL);
694 	nvlist_free(vdev_guids);
695 	nvlist_free(optional);
696 	nvlist_free(required);
697 }
698 
699 static int
zfs_destroy(const char * dataset)700 zfs_destroy(const char *dataset)
701 {
702 	zfs_cmd_t zc = {"\0"};
703 	int err;
704 
705 	(void) strncpy(zc.zc_name, dataset, sizeof (zc.zc_name));
706 	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
707 	zc.zc_objset_type = DMU_OST_ZFS;
708 	err = ioctl(zfs_fd, ZFS_IOC_DESTROY, &zc);
709 
710 	return (err == 0 ? 0 : errno);
711 }
712 
713 static void
test_get_bootenv(const char * pool)714 test_get_bootenv(const char *pool)
715 {
716 	IOC_INPUT_TEST(ZFS_IOC_GET_BOOTENV, pool, NULL, NULL, 0);
717 }
718 
719 static void
test_set_bootenv(const char * pool)720 test_set_bootenv(const char *pool)
721 {
722 	nvlist_t *required = fnvlist_alloc();
723 
724 	fnvlist_add_uint64(required, "version", VB_RAW);
725 	fnvlist_add_string(required, GRUB_ENVMAP, "test");
726 
727 	IOC_INPUT_TEST_WILD(ZFS_IOC_SET_BOOTENV, pool, required, NULL, 0);
728 
729 	nvlist_free(required);
730 }
731 
732 static void
zfs_ioc_input_tests(const char * pool)733 zfs_ioc_input_tests(const char *pool)
734 {
735 	char filepath[] = "/tmp/ioc_test_file_XXXXXX";
736 	char dataset[ZFS_MAX_DATASET_NAME_LEN];
737 	char snapbase[ZFS_MAX_DATASET_NAME_LEN + 32];
738 	char snapshot[ZFS_MAX_DATASET_NAME_LEN + 32];
739 	char bookmark[ZFS_MAX_DATASET_NAME_LEN + 32];
740 	char backup[ZFS_MAX_DATASET_NAME_LEN];
741 	char clone[ZFS_MAX_DATASET_NAME_LEN];
742 	int tmpfd, err;
743 
744 	/*
745 	 * Setup names and create a working dataset
746 	 */
747 	(void) snprintf(dataset, sizeof (dataset), "%s/test-fs", pool);
748 	(void) snprintf(snapbase, sizeof (snapbase), "%s@snapbase", dataset);
749 	(void) snprintf(snapshot, sizeof (snapshot), "%s@snapshot", dataset);
750 	(void) snprintf(bookmark, sizeof (bookmark), "%s#bookmark", dataset);
751 	(void) snprintf(clone, sizeof (clone), "%s/test-fs-clone", pool);
752 	(void) snprintf(backup, sizeof (backup), "%s/backup", pool);
753 
754 	err = lzc_create(dataset, LZC_DATSET_TYPE_ZFS, NULL, NULL, 0);
755 	if (err) {
756 		(void) fprintf(stderr, "could not create '%s': %s\n",
757 		    dataset, strerror(errno));
758 		exit(2);
759 	}
760 
761 	tmpfd = mkstemp(filepath);
762 	if (tmpfd < 0) {
763 		(void) fprintf(stderr, "could not create '%s': %s\n",
764 		    filepath, strerror(errno));
765 		exit(2);
766 	}
767 
768 	/*
769 	 * run a test for each ioctl
770 	 * Note that some test build on previous test operations
771 	 */
772 	test_pool_sync(pool);
773 #ifndef __sun
774 	test_pool_reopen(pool);
775 #endif
776 	test_pool_checkpoint(pool);
777 	test_pool_discard_checkpoint(pool);
778 	test_log_history(pool);
779 
780 	test_create(dataset);
781 	test_snapshot(pool, snapbase);
782 	test_snapshot(pool, snapshot);
783 
784 	test_space_snaps(snapshot);
785 	test_send_space(snapbase, snapshot);
786 	test_send_new(snapshot, tmpfd);
787 #ifndef __sun
788 	test_recv_new(backup, tmpfd);
789 #endif
790 
791 	test_bookmark(pool, snapshot, bookmark);
792 	test_get_bookmarks(dataset);
793 	test_destroy_bookmarks(pool, bookmark);
794 
795 	test_hold(pool, snapshot);
796 	test_get_holds(snapshot);
797 	test_release(pool, snapshot);
798 
799 	test_clone(snapshot, clone);
800 	(void) zfs_destroy(clone);
801 
802 	test_rollback(dataset, snapshot);
803 	test_destroy_snaps(pool, snapshot);
804 	test_destroy_snaps(pool, snapbase);
805 
806 	test_remap(dataset);
807 	test_channel_program(pool);
808 
809 	test_load_key(dataset);
810 	test_change_key(dataset);
811 	test_unload_key(dataset);
812 
813 	test_vdev_initialize(pool);
814 	test_vdev_trim(pool);
815 
816 	test_set_bootenv(pool);
817 	test_get_bootenv(pool);
818 
819 	/*
820 	 * cleanup
821 	 */
822 	zfs_cmd_t zc = {"\0"};
823 
824 	nvlist_t *snaps = fnvlist_alloc();
825 	fnvlist_add_boolean(snaps, snapshot);
826 	(void) lzc_destroy_snaps(snaps, B_FALSE, NULL);
827 	nvlist_free(snaps);
828 
829 	(void) zfs_destroy(dataset);
830 	(void) zfs_destroy(backup);
831 
832 	(void) close(tmpfd);
833 	(void) unlink(filepath);
834 
835 	/*
836 	 * All the unused slots should yield ZFS_ERR_IOC_CMD_UNAVAIL
837 	 */
838 	for (int i = 0; i < ARRAY_SIZE(ioc_skip); i++) {
839 		if (ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST])
840 			(void) fprintf(stderr, "cmd %d tested, not skipped!\n",
841 			    (int)(ioc_skip[i] - ZFS_IOC_FIRST));
842 
843 		ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST] = B_TRUE;
844 	}
845 
846 	(void) strncpy(zc.zc_name, pool, sizeof (zc.zc_name));
847 	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
848 
849 	for (unsigned ioc = ZFS_IOC_FIRST; ioc < ZFS_IOC_LAST; ioc++) {
850 		unsigned cmd = ioc - ZFS_IOC_FIRST;
851 
852 		if (ioc_tested[cmd])
853 			continue;
854 
855 		if (ioctl(zfs_fd, ioc, &zc) != 0 &&
856 		    errno != ZFS_ERR_IOC_CMD_UNAVAIL) {
857 			(void) fprintf(stderr, "cmd %d is missing a test case "
858 			    "(%d)\n", cmd, errno);
859 		}
860 	}
861 }
862 
863 enum zfs_ioc_ref {
864 #ifdef __FreeBSD__
865 	ZFS_IOC_BASE = 0,
866 #else
867 	ZFS_IOC_BASE = ('Z' << 8),
868 #endif
869 	ZFS_IOC_PLATFORM_BASE = ZFS_IOC_BASE + 0x80,
870 };
871 
872 /*
873  * Canonical reference check of /dev/zfs ioctl numbers.
874  * These cannot change and new ioctl numbers must be appended.
875  */
876 boolean_t
validate_ioc_values(void)877 validate_ioc_values(void)
878 {
879 	boolean_t result = B_TRUE;
880 
881 #define	CHECK(expr) do { \
882 	if (!(expr)) { \
883 		result = B_FALSE; \
884 		fprintf(stderr, "(%s) === FALSE\n", #expr); \
885 	} \
886 } while (0)
887 
888 	CHECK(ZFS_IOC_BASE + 0 == ZFS_IOC_POOL_CREATE);
889 	CHECK(ZFS_IOC_BASE + 1 == ZFS_IOC_POOL_DESTROY);
890 	CHECK(ZFS_IOC_BASE + 2 == ZFS_IOC_POOL_IMPORT);
891 	CHECK(ZFS_IOC_BASE + 3 == ZFS_IOC_POOL_EXPORT);
892 	CHECK(ZFS_IOC_BASE + 4 == ZFS_IOC_POOL_CONFIGS);
893 	CHECK(ZFS_IOC_BASE + 5 == ZFS_IOC_POOL_STATS);
894 	CHECK(ZFS_IOC_BASE + 6 == ZFS_IOC_POOL_TRYIMPORT);
895 	CHECK(ZFS_IOC_BASE + 7 == ZFS_IOC_POOL_SCAN);
896 	CHECK(ZFS_IOC_BASE + 8 == ZFS_IOC_POOL_FREEZE);
897 	CHECK(ZFS_IOC_BASE + 9 == ZFS_IOC_POOL_UPGRADE);
898 	CHECK(ZFS_IOC_BASE + 10 == ZFS_IOC_POOL_GET_HISTORY);
899 	CHECK(ZFS_IOC_BASE + 11 == ZFS_IOC_VDEV_ADD);
900 	CHECK(ZFS_IOC_BASE + 12 == ZFS_IOC_VDEV_REMOVE);
901 	CHECK(ZFS_IOC_BASE + 13 == ZFS_IOC_VDEV_SET_STATE);
902 	CHECK(ZFS_IOC_BASE + 14 == ZFS_IOC_VDEV_ATTACH);
903 	CHECK(ZFS_IOC_BASE + 15 == ZFS_IOC_VDEV_DETACH);
904 	CHECK(ZFS_IOC_BASE + 16 == ZFS_IOC_VDEV_SETPATH);
905 	CHECK(ZFS_IOC_BASE + 17 == ZFS_IOC_VDEV_SETFRU);
906 	CHECK(ZFS_IOC_BASE + 18 == ZFS_IOC_OBJSET_STATS);
907 	CHECK(ZFS_IOC_BASE + 19 == ZFS_IOC_OBJSET_ZPLPROPS);
908 	CHECK(ZFS_IOC_BASE + 20 == ZFS_IOC_DATASET_LIST_NEXT);
909 	CHECK(ZFS_IOC_BASE + 21 == ZFS_IOC_SNAPSHOT_LIST_NEXT);
910 	CHECK(ZFS_IOC_BASE + 22 == ZFS_IOC_SET_PROP);
911 	CHECK(ZFS_IOC_BASE + 23 == ZFS_IOC_CREATE);
912 	CHECK(ZFS_IOC_BASE + 24 == ZFS_IOC_DESTROY);
913 	CHECK(ZFS_IOC_BASE + 25 == ZFS_IOC_ROLLBACK);
914 	CHECK(ZFS_IOC_BASE + 26 == ZFS_IOC_RENAME);
915 	CHECK(ZFS_IOC_BASE + 27 == ZFS_IOC_RECV);
916 	CHECK(ZFS_IOC_BASE + 28 == ZFS_IOC_SEND);
917 	CHECK(ZFS_IOC_BASE + 29 == ZFS_IOC_INJECT_FAULT);
918 	CHECK(ZFS_IOC_BASE + 30 == ZFS_IOC_CLEAR_FAULT);
919 	CHECK(ZFS_IOC_BASE + 31 == ZFS_IOC_INJECT_LIST_NEXT);
920 	CHECK(ZFS_IOC_BASE + 32 == ZFS_IOC_ERROR_LOG);
921 	CHECK(ZFS_IOC_BASE + 33 == ZFS_IOC_CLEAR);
922 	CHECK(ZFS_IOC_BASE + 34 == ZFS_IOC_PROMOTE);
923 	CHECK(ZFS_IOC_BASE + 35 == ZFS_IOC_SNAPSHOT);
924 	CHECK(ZFS_IOC_BASE + 36 == ZFS_IOC_DSOBJ_TO_DSNAME);
925 	CHECK(ZFS_IOC_BASE + 37 == ZFS_IOC_OBJ_TO_PATH);
926 	CHECK(ZFS_IOC_BASE + 38 == ZFS_IOC_POOL_SET_PROPS);
927 	CHECK(ZFS_IOC_BASE + 39 == ZFS_IOC_POOL_GET_PROPS);
928 	CHECK(ZFS_IOC_BASE + 40 == ZFS_IOC_SET_FSACL);
929 	CHECK(ZFS_IOC_BASE + 41 == ZFS_IOC_GET_FSACL);
930 	CHECK(ZFS_IOC_BASE + 42 == ZFS_IOC_SHARE);
931 	CHECK(ZFS_IOC_BASE + 43 == ZFS_IOC_INHERIT_PROP);
932 	CHECK(ZFS_IOC_BASE + 44 == ZFS_IOC_SMB_ACL);
933 	CHECK(ZFS_IOC_BASE + 45 == ZFS_IOC_USERSPACE_ONE);
934 	CHECK(ZFS_IOC_BASE + 46 == ZFS_IOC_USERSPACE_MANY);
935 	CHECK(ZFS_IOC_BASE + 47 == ZFS_IOC_USERSPACE_UPGRADE);
936 	CHECK(ZFS_IOC_BASE + 48 == ZFS_IOC_HOLD);
937 	CHECK(ZFS_IOC_BASE + 49 == ZFS_IOC_RELEASE);
938 	CHECK(ZFS_IOC_BASE + 50 == ZFS_IOC_GET_HOLDS);
939 	CHECK(ZFS_IOC_BASE + 51 == ZFS_IOC_OBJSET_RECVD_PROPS);
940 	CHECK(ZFS_IOC_BASE + 52 == ZFS_IOC_VDEV_SPLIT);
941 	CHECK(ZFS_IOC_BASE + 53 == ZFS_IOC_NEXT_OBJ);
942 	CHECK(ZFS_IOC_BASE + 54 == ZFS_IOC_DIFF);
943 	CHECK(ZFS_IOC_BASE + 55 == ZFS_IOC_TMP_SNAPSHOT);
944 	CHECK(ZFS_IOC_BASE + 56 == ZFS_IOC_OBJ_TO_STATS);
945 	CHECK(ZFS_IOC_BASE + 57 == ZFS_IOC_SPACE_WRITTEN);
946 	CHECK(ZFS_IOC_BASE + 58 == ZFS_IOC_SPACE_SNAPS);
947 	CHECK(ZFS_IOC_BASE + 59 == ZFS_IOC_DESTROY_SNAPS);
948 	CHECK(ZFS_IOC_BASE + 60 == ZFS_IOC_POOL_REGUID);
949 	CHECK(ZFS_IOC_BASE + 61 == ZFS_IOC_POOL_REOPEN);
950 	CHECK(ZFS_IOC_BASE + 62 == ZFS_IOC_SEND_PROGRESS);
951 	CHECK(ZFS_IOC_BASE + 63 == ZFS_IOC_LOG_HISTORY);
952 	CHECK(ZFS_IOC_BASE + 64 == ZFS_IOC_SEND_NEW);
953 	CHECK(ZFS_IOC_BASE + 65 == ZFS_IOC_SEND_SPACE);
954 	CHECK(ZFS_IOC_BASE + 66 == ZFS_IOC_CLONE);
955 	CHECK(ZFS_IOC_BASE + 67 == ZFS_IOC_BOOKMARK);
956 	CHECK(ZFS_IOC_BASE + 68 == ZFS_IOC_GET_BOOKMARKS);
957 	CHECK(ZFS_IOC_BASE + 69 == ZFS_IOC_DESTROY_BOOKMARKS);
958 #ifndef __sun
959 	CHECK(ZFS_IOC_BASE + 71 == ZFS_IOC_RECV_NEW);
960 #endif
961 	CHECK(ZFS_IOC_BASE + 70 == ZFS_IOC_POOL_SYNC);
962 	CHECK(ZFS_IOC_BASE + 71 == ZFS_IOC_CHANNEL_PROGRAM);
963 	CHECK(ZFS_IOC_BASE + 72 == ZFS_IOC_LOAD_KEY);
964 	CHECK(ZFS_IOC_BASE + 73 == ZFS_IOC_UNLOAD_KEY);
965 	CHECK(ZFS_IOC_BASE + 74 == ZFS_IOC_CHANGE_KEY);
966 	CHECK(ZFS_IOC_BASE + 75 == ZFS_IOC_REMAP);
967 	CHECK(ZFS_IOC_BASE + 76 == ZFS_IOC_POOL_CHECKPOINT);
968 
969 #ifndef __sun
970 	CHECK(ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT);
971 	CHECK(ZFS_IOC_PLATFORM_BASE + 1 == ZFS_IOC_EVENTS_NEXT);
972 	CHECK(ZFS_IOC_PLATFORM_BASE + 2 == ZFS_IOC_EVENTS_CLEAR);
973 	CHECK(ZFS_IOC_PLATFORM_BASE + 3 == ZFS_IOC_EVENTS_SEEK);
974 #else
975 	CHECK(ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_DISCARD_CHECKPOINT);
976 	CHECK(ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_INITIALIZE);
977 	CHECK(ZFS_IOC_BASE + 79 == ZFS_IOC_POOL_TRIM);
978 	CHECK(ZFS_IOC_BASE + 80 == ZFS_IOC_REDACT);
979 	CHECK(ZFS_IOC_BASE + 81 == ZFS_IOC_GET_BOOKMARK_PROPS);
980 #endif
981 	CHECK(ZFS_IOC_PLATFORM_BASE + 7 == ZFS_IOC_SET_BOOTENV);
982 	CHECK(ZFS_IOC_PLATFORM_BASE + 8 == ZFS_IOC_GET_BOOTENV);
983 
984 #undef CHECK
985 
986 	return (result);
987 }
988 
989 int
main(int argc,const char * argv[])990 main(int argc, const char *argv[])
991 {
992 	if (argc != 2) {
993 		(void) fprintf(stderr, "usage: %s <pool>\n", argv[0]);
994 		exit(2);
995 	}
996 
997 	if (!validate_ioc_values()) {
998 		(void) fprintf(stderr, "WARNING: zfs_ioc_t has binary "
999 		    "incompatible command values\n");
1000 		exit(3);
1001 	}
1002 
1003 	(void) libzfs_core_init();
1004 	zfs_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
1005 	if (zfs_fd < 0) {
1006 		(void) fprintf(stderr, "open: %s\n", strerror(errno));
1007 		libzfs_core_fini();
1008 		exit(2);
1009 	}
1010 
1011 	zfs_ioc_input_tests(argv[1]);
1012 
1013 	(void) close(zfs_fd);
1014 	libzfs_core_fini();
1015 
1016 	return (unexpected_failures);
1017 }
1018