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